Невозможно создать кват из эйлеровых углов с углом рыскания выше 90 glm - PullRequest
0 голосов
/ 01 марта 2019

Вот немного фона : я работаю над игровым движком, и недавно я добавил физический движок (в данном случае Physx).проблема в том, что мой класс преобразования использует углы Эйлера для вращения, а класс преобразования физического движка использует углы Эйлера.поэтому я просто реализовал метод, чтобы изменить класс преобразования на преобразование физического движка и обратно.Это работает хорошо, но я обнаружил странную ошибку.

Поведение, которое я получаю:

Когда угол поворота (второй элемент вектора Эйлера) вращения выше 90 градусов, он больше не заставляет объект вращаться наось у и начало возятся с полем и креном (странные тряски пропускают от 0 до 180 и много назад).Инструменты отладки показывают, что вращение не превышает 91, а достигает 90.0003 макс. Я передаю градусы в радианы. Пример: Чтобы показать эту ошибку, у меня есть куб с вращающимся скриптом Python:

from TOEngine import * 

class rotate:
    direction = vec3(0,10,0)
    def Start(self):
        pass
    def Update(self,deltaTime):
        transform.Rotate(self.direction*deltaTime*5)       
        pass

Сам движок написан на cpp, но у меня есть система сценариев, работающая со встроенным python.TOEngine - это просто мой модуль, а сам скрипт просто вращает куб каждый кадр.Куб начинается с 0, 0, 0 вращения и вращается нормально, но останавливается и поворачивается на 90 градусов и начинает дрожать.

Это происходит только тогда, когда физическая система включена, поэтому я знаю, что ошибка должна быть в методе передачивращение от эйлера к квату и обратно каждый кадр с использованием glm.

Вот фактический проблемный код :

void RigidBody::SetTransform(Transform transform)
{
    glm::vec3 axis = transform.rotation;
    rigidbody->setGlobalPose(PxTransform(*(PxVec3*)&transform.position,*(PxQuat*)&glm::quat(glm::radians(transform.rotation))));//Attention Over Here
}

Transform RigidBody::GetTransform()
{
    auto t = rigidbody->getGlobalPose();
    return Transform(*(glm::vec3*)&t.p, glm::degrees(glm::eulerAngles(*(glm::quat*)&t.q)), entity->transform.scale);
}

Избегайте странного типа ударов PxQuat в основном то же самоепоскольку glm :: quat и PxVec3 в основном совпадают с glm :: vec3.Я ожидаю, что этот код будет передаваться между классом преобразования физического движка и моим классом преобразования, изменяя угол поворота с угла падения Эйлера на кват с радианами ( жесткая часть ).

И внутрифизическая система:

void PreUpdate(float deltaTime)override {   //Set Physics simulation changes to the scene
mScene->fetchResults(true);
for (auto entity : Events::scene->entities)
    for (auto component : entity->components)
        if (component->GetName() == "RigidBody")
            entity->transform = ((RigidBody*)component)->GetTransform();    //This is running on the cube entity
}
void PostUpdate(float deltaTime)override {  //Set Scene changes To Physics simulation
for (auto entity : Events::scene->entities)
    for (auto component : entity->components)
        if (component->GetName() == "RigidBody")
            ((RigidBody*)component)->SetTransform(entity->transform);//This is running on the cube entity
mScene->simulate(deltaTime);
} 

PreUpdate запускается перед обновлением каждого кадра PostUpdate запускается после обновления каждого кадра.метод Update (показанный в приведенном выше сценарии), как следует из названия, запускается при обновлении ... (между PreUpdate и PostUpdate).Куб имеет компонент твердого тела.То, что я ожидаю получить: вращающийся куб, который не прекращает вращаться, когда достигает 90 градусов.

Я знаю, что это немного сложно.Я старался изо всех сил, чтобы объяснить ошибку, я полагаю, что проблема заключается в изменении углов Эйлера на quat.

1 Ответ

0 голосов
/ 07 марта 2019

Что касается преобразования из PxQuat в glm::quat, прочитайте документацию по https://en.cppreference.com/w/cpp/language/explicit_cast и https://en.cppreference.com/w/cpp/language/reinterpret_cast и поищите неопределенное поведение на странице reinterpret_cast.Насколько я могу судить, такое приведение в стиле c не гарантированно работает и даже не желательно.Пока я отвлекаюсь на этом этапе, но помните, что у вас есть два варианта этого преобразования.

glm::quat glmQuat = GenerateQuat();
physx::PxQuat someQuat = *(physx::PxQuat*)(&glmQuat); //< (1)
physx::PxQuat someOtherQuat = ConvertGlmQuatToPxQuat(glmQuat); //< (2)

(1) Этот параметр может привести к неопределенному поведению, но, что более важно, вы не сохранили копию,Этот оператор обязательно вызовет 1 вызов конструктора копии.

(2) Эта опция, из-за оптимизации возвращаемого значения, также приведет к одиночной конструкции physx::PxQuat.

Такпо сути, выбирая опцию (1), вы не экономите на затратах, а рискуете неопределенным поведением.С опцией (2) стоимость та же, но код теперь соответствует стандартам.Теперь вернемся к исходной точке.

Обычно я делал бы все, что в моих силах, чтобы избежать использования углов Эйлера, поскольку они подвержены ошибкам и намного более запутанны, чем кватернионы.Тем не менее, это простой тест, который вы можете настроить для проверки вашего кватернионного преобразования из углов Эйлера (пока не включайте физические значения в это).

Вам необходимо сгенерировать следующие методы.

glm::mat3 CreateRotationMatrix(glm::vec3 rotationDegrees);
glm::mat3 CreateRotationMatrix(glm::quat inputQuat);
glm::quat ConvertEulerAnglesToQuat(glm::vec3 rotationDegrees);

, а затем ваш псевдокод для теста будет выглядеть следующим образом:

for (auto angles : allPossibleAngleCombinations) {
    auto expectedRotationMatrix = CreateRotationMatrix(angles);
    auto convertedQuat = ConvertEulerAnglesToQuat(angles);
    auto actualRotationMatrix = CreateRotationMatrix(convertedQuat);
    ASSERT(expectedRotationMatrix, actualRotationMatrix);
}

Только если этот тест пройдет для вас, вы сможете посмотреть на следующую проблему преобразования их в PxQuat.Я предполагаю, что этот тест провалится для вас.Один совет, который я бы дал, заключается в том, что один из углов ввода (зависит от соглашения) должен быть ограничен по дальности.Скажем, если вы ограничите угол поворота в пределах -90, 90 градусов, скорее всего, ваш тест будет успешным.Это связано с тем, что существует неуникальная комбинация углов Эйлера, которая может привести к одной и той же матрице вращения.

...