У меня проблемы с вращением GameObjects в моем движке. Я пытаюсь вращаться двумя способами.
я использую MathGeoLib рассчитать математику в двигателе.
Первый способ: Правильно вращается вокруг оси, но если я хочу повернуть назад, если я не сделаю это в обратном порядке, вращение не будет работать должным образом.
например:
Повернуть ось X на 50 градусов, Повернуть ось Y на 30 градусов -> Повернуть ось Y -50 градусов, Повернуть ось X -30 градусов. Работает.
Повернуть ось X на 50 градусов, Повернуть ось Y на 30 градусов -> Повернуть ось X на -50 градусов, Повернуть ось Y -30 градусов. Не имеет.
Код:
void ComponentTransform::SetRotation(float3 euler_rotation)
{
float3 diff = euler_rotation - editor_rotation;
editor_rotation = euler_rotation;
math::Quat mod = math::Quat::FromEulerXYZ(diff.x * DEGTORAD, diff.y * DEGTORAD, diff.z * DEGTORAD);
quat_rotation = quat_rotation * mod;
UpdateMatrix();
}
Второй способ: Начинает хорошо вращаться вокруг оси, но после нескольких вращений он перестает правильно вращаться вокруг оси, но если я поверну его обратно, независимо от порядка вращения, он будет работать, а не как в первом случае.
Код:
void ComponentTransform::SetRotation(float3 euler_rotation)
{
editor_rotation = euler_rotation;
quat_rotation = math::Quat::FromEulerXYZ(euler_rotation.x * DEGTORAD, euler_rotation.y * DEGTORAD, euler_rotation.z * DEGTORAD);
UpdateMatrix();
}
Остальной код:
#define DEGTORAD 0.0174532925199432957f
void ComponentTransform::UpdateMatrix()
{
if (!this->GetGameObject()->IsParent())
{
//Get parent transform component
ComponentTransform* parent_transform = (ComponentTransform*)this->GetGameObject()->GetParent()->GetComponent(Component::CompTransform);
//Create matrix from position, rotation(quaternion) and scale
transform_matrix = math::float4x4::FromTRS(position, quat_rotation, scale);
//Multiply the object transform by parent transform
transform_matrix = parent_transform->transform_matrix * transform_matrix;
//If object have childs, call this function in childs objects
for (std::list<GameObject*>::iterator it = this->GetGameObject()->childs.begin(); it != this->GetGameObject()->childs.end(); it++)
{
ComponentTransform* child_transform = (ComponentTransform*)(*it)->GetComponent(Component::CompTransform);
child_transform->UpdateMatrix();
}
}
else
{
//Create matrix from position, rotation(quaternion) and scale
transform_matrix = math::float4x4::FromTRS(position, quat_rotation, scale);
//If object have childs, call this function in childs objects
for (std::list<GameObject*>::iterator it = this->GetGameObject()->childs.begin(); it != this->GetGameObject()->childs.end(); it++)
{
ComponentTransform* child_transform = (ComponentTransform*)(*it)->GetComponent(Component::CompTransform);
child_transform->UpdateMatrix();
}
}
}
MathGeoLib:
Quat MUST_USE_RESULT Quat::FromEulerXYZ(float x, float y, float z) { return (Quat::RotateX(x) * Quat::RotateY(y) * Quat::RotateZ(z)).Normalized(); }
Quat MUST_USE_RESULT Quat::RotateX(float angle)
{
return Quat(float3(1,0,0), angle);
}
Quat MUST_USE_RESULT Quat::RotateY(float angle)
{
return Quat(float3(0,1,0), angle);
}
Quat MUST_USE_RESULT Quat::RotateZ(float angle)
{
return Quat(float3(0,0,1), angle);
}
Quat(const float3 &rotationAxis, float rotationAngleRadians) { SetFromAxisAngle(rotationAxis, rotationAngleRadians); }
void Quat::SetFromAxisAngle(const float3 &axis, float angle)
{
assume1(axis.IsNormalized(), axis);
assume1(MATH_NS::IsFinite(angle), angle);
float sinz, cosz;
SinCos(angle*0.5f, sinz, cosz);
x = axis.x * sinz;
y = axis.y * sinz;
z = axis.z * sinz;
w = cosz;
}
Любая помощь?
Благодарю.
Использование углов Эйлера и / или кватернионов добавляет некоторые ограничения, так как это создает особенности, которые, если не обработаны правильно, будут делать глупые вещи. К сожалению, почти все новые 3D-игры используют его неправильно. Вы можете обнаружить их по известным вещам, таким как:
Вместо этого я использую кумулятивные матрицы преобразования:
Прочитайте весь материал (особенно разницу между локальным и глобальным вращением), затем в последних 3 ссылках вы получили примеры того, как это сделать на C ++ (также прочитайте все 3, особенно о сохранении точности …).
Идея состоит в том, чтобы иметь матрицу, представляющую вашу систему координат объекта. И когда вы поворачиваете (мышью, клавиатурой, NAV, AI, …), вы поворачиваете матрицу (постепенно). То же самое касается движения. Таким образом, они не имеют ограничений или особенностей. Но и у этого подхода есть свои проблемы:
Оба разрешимы относительно легко.
Теперь, когда вы вращаетесь вокруг локальных осей, вы должны учитывать, что при каждом вращении вокруг некоторой оси вы меняете две другие. Поэтому, если вы хотите вернуться в исходное состояние, вам нужно изменить порядок вращения, потому что:
rotate around x by 30deg
rotate around y by 40deg
это не то же самое, что:
rotate around y by 40deg
rotate around x by 30deg
С накопительной матрицей, если вы хотите вернуться назад, вы можете итеративно вести свой корабль до тех пор, пока он не будет направлен в желаемые направления, или запомнить исходную матрицу и вычислить повороты, необходимые для выполнения по одной оси за раз. Или преобразовать разность матриц в кватернион и повторить этот единственный поворот …
Других решений пока нет …