Алгоритм извлечения углов из матрицы вида и построение новой с сохранением ориентации
Я делаю приложение где пользователь может вращать камеру как орбит камеру. Только без ограничений. Пишу я на 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 когда не знаешь что делать. И я сделал также. Меня в принципе устраивает и этот результат, но я не могу не спросить. Есть решение лучше? Может быть есть какой-то алгоритм для сопоставления ориентации и выходных углов. Ну или просто совет :)