Алгоритм извлечения углов из матрицы вида и построение новой с сохранением ориентации

Я делаю приложение где пользователь может вращать камеру как орбит камеру. Только без ограничений. Пишу я на c++. Для математики использую библиотеку glm. Также пользователь может и взаимодействовать с координатами этой камеры. Собственно тут всё работает. Но я заметил что камера может вращаться очень много раз. И в результате вектор поворота может быть представлен как {-2500, 650, 180}. Причём я посмотрел подобное есть и во многих других 3D редакторах и никто с этим ничего не делает. Но я подумал что лучше всё таки стоит это всё выровнять. Вращение осуществляется так:

    mRotation = glm::rotate(mRotation, transform.rotation.y, glm::vec3(normalizedAxis, 0.0f, 0.0f));
    mRotation = glm::rotate(mRotation, transform.rotation.x, glm::vec3(0.0f, normalizedAxis, 0.0f));
    mRotation = glm::rotate(mRotation, transform.rotation.z, glm::vec3(0.0f, 0.0f, normalizedAxis));
mView = mTranslationMat * glm::mat4_cast(mRotation) * mTranslationCenter;
mViewInverse = glm::inverse(mView);
  • mRotation - это квартерион вращения (мне нужно так, но поведение одинаково и в обычной mat4).
  • normalizedAxis - позже отпишу.

Новые координаты вращения я извлекаю из матрицы вида используя следующую функцию:

    float x, y, z;
    glm::extractEulerAngleXYZ(mView, x, y, z);
    if (transform.rotation.x < -glm::half_pi<float>() || transform.rotation.x > glm::half_pi<float>()) { // Значения нужно обратить если сменилась ориентация
        x = -x;
        y = -y;
    }
    transform.rotation = { y, x, z };

Функция вызывается при отжатии кнопки вращения (т.е. не в процессе, а после). И оно работает. При достижении 90° по оси x, к примеру, исходные координаты меняются (как и требовалось) и заменяются более оптимальными. Это вроде как в графике называется "вращение квартериона", если не ошибаюсь. Вот только когда это происходит конечная матрица вида выходит инвертированной по оси z. Т.е. мы смотрели спереди. Данные обновились и теперь вид сзади. Я в принципе понимаю почему. Я же меняю по факту только вращение не меняя квартерион трансляции. При этом кстати, если я открою blender, создам 2 камеры - одну с оригинальными координатами, другую с извлечёнными, эти камеры будут идентичным (в смысле находится в одной и той же точке, и смотреть в одну и другую точку). Наверное стоит привести пример:

Оригинальные:

X: -504.4 Y: -199.2 Z: -180

Извлеченные:

X: 35.6001 Y: 19.2001 Z: 0

Для решения этой проблемы я и ввёл переменную normalizedAxis. Теперь если произошло переворачивание то вращение мы будем нормализовать не к 1.0f, а к -1.0f.

Я меняю код функции извлечения координат:

    if (transform.rotation.x < -glm::half_pi<float>() || transform.rotation.x > glm::half_pi<float>()) {
        x = -x;
        y = -y;
        normalizedAxis = -1.0f;
    } else
        normalizedAxis = 1.0f;

Теперь ничего не переворачивается но другая проблема. Раз мы изменили normalizedAxis, то и вращение стало работать по другому. Теперь при вращении лево поворачивает направо, а право налево, к примеру. Я не придумал ничего лучше чем методом подбора добиться нужного результата:

void Camera::rotate(float angleX, float angleY)
{
    auto v = std::round(transform.rotation.z / 0.00001f) * 0.00001f;
    angleX = up().y > 0.0f ? angleX : -angleX;
    if (v == 0.0f) {
        if (normalizedAxis == -1.0f) {
            angleX = -angleX;
            angleY = -angleY;
        }
    } else if (normalizedAxis == -1.0f && (transform.rotation.z < -glm::half_pi<float>() || transform.rotation.z > glm::half_pi<float>())) {
        angleY = -angleY;
    } else {
        angleX = -angleX;
    }
    transform.rotation.x += angleX;
    transform.rotation.y += angleY;
    ....
    ....
    ....
    /// Тут происходит вращение
}

Просто одна из вещей которая меня бесит в программировании - это делать много if/else когда не знаешь что делать. И я сделал также. Меня в принципе устраивает и этот результат, но я не могу не спросить. Есть решение лучше? Может быть есть какой-то алгоритм для сопоставления ориентации и выходных углов. Ну или просто совет :)


Ответы (0 шт):