0

I'm having problems rotating GameObjects in my engine. I'm trying to rotate in 2 ways. I'm using MathGeoLib to calculate maths in the engine.

First way: Rotates correctly around axis but if I want to rotate back, if I don't do it following the inverse order then rotation doesn't work properly.

e.g:

Rotate X axis 50 degrees, Rotate Y axis 30 degrees -> Rotate Y axis -50 degrees, Rotate X axis -30 degrees. Works.

Rotate X axis 50 degrees, Rotate Y axis 30 degrees -> Rotate X axis -50 degrees, Rotate Y axis -30 degrees. Doesn't.

enter image description here

Code:

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(); 
}

Second way: Starts rotating good around axis but after rotating some times, then it stops to rotate correctly around axis, but if I rotate it back regardless of the rotation order it works, not like the first way.

enter image description here

Code:

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(); 
}

Rest of code:

#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;
}

Any help?

Thanks.

Tino Tano
  • 159
  • 1
  • 13

1 Answers1

0

Using Euler angles and or Quaternions adds some limitations as it creates singularities which if not handled correctly will make silly things. Sadly almost all new 3D games using it wrongly. You can detect those by the well known things like:

  • sometimes your view get to very different angle that should not be there
  • object can not rotate anymore in some direction
  • object start rotating around different axises than it should
  • view jumps around singularity pole
  • view is spinning or flipping until you move/turn again (not the one caused by optic mouse error)

I am using cumulative transform matrices instead:

Read the whole stuff (especially difference between local and global rotations) then in last 3 links you got C++ examples of how to do this (also read all 3 especially the preserving accuracy ...).

The idea is to have matrix representing your object coordinate system. And when ever you rotate (by mouse, keyboard, NAV,AI,...) you rotate the matrix (incrementally). The same goes for movement. This way they are no limitations or singularities. But also this approach has its problems:

  • lose of accuracy with time (read the preserving accuracy example to deal with this)
  • no knowledge about the Euler angles (the angles can be computed from the matrix however)

Both are solvable relatively easily.

Now when you are rotating around local axises you need to take into account that with every rotation around some axis you change the other two. So if you want to get to the original state you need to reverse order of rotations because:

rotate around x by 30deg
rotate around y by 40deg 

is not the same as:

rotate around y by 40deg 
rotate around x by 30deg

With cumulative matrix if you want to get back you can either iteratively drive your ship until it faces desired directions or remember original matrix and compute the rotations needed to be done one axis at a time. Or convert the matrix difference into quaternion and iterate that single rotation...

Spektre
  • 49,595
  • 11
  • 110
  • 380