user1182183
user1182183

Reputation:

How to rotate a GLM quaternion correctly?

I want to rotate my car by 90 degrees to the left in the game I have.

When I use this code:

glm::quat rot(info.Rotation.w,info.Rotation.x,info.Rotation.y,info.Rotation.z);
glm::quat done(glm::rotate(rot,glm::eulerAngles(rot)+glm::vec3(90.0f,0.0,0.0)));
info.Rotation.x = done.x;
info.Rotation.y = done.y;
info.Rotation.z = done.z;
info.Rotation.w = done.w;

The car gets a weird rotation.

However the following codes doesn't change the cars rotation at all (just what I expected, just to be sure that GLM is compatible with the quats from the game):

glm::quat rot(info.Rotation.w,info.Rotation.x,info.Rotation.y,info.Rotation.z);
glm::quat done(rot);
info.Rotation.x = done.x;
info.Rotation.y = done.y;
info.Rotation.z = done.z;
info.Rotation.w = done.w;

and whenever I try this to check if the rotation is changed with it:

glm::quat rot(info.Rotation.w,info.Rotation.x,info.Rotation.y,info.Rotation.z);
glm::quat done(glm::rotate(rot,vec3(0.0,0.0,0.0)));
info.Rotation.x = done.x;
info.Rotation.y = done.y;
info.Rotation.z = done.z;
info.Rotation.w = done.w;

the cars rotation is just set to the 0,0,0,0 rotations in the game. I expected the rotations to remain untouched with this code because I expected the following code to rotate the car by 90 degrees left:

glm::quat rot(info.Rotation.w,info.Rotation.x,info.Rotation.y,info.Rotation.z);
glm::quat done(glm::rotate(rot,vec3(90.0,0.0,0.0)));
info.Rotation.x = done.x;
info.Rotation.y = done.y;
info.Rotation.z = done.z;
info.Rotation.w = done.w;

but that doesn't work the way I want. It just sets the rotation, instead of adding it to rot.

What am I doing wrong?

Upvotes: 8

Views: 23807

Answers (3)

Stephan Guingor
Stephan Guingor

Reputation: 166

This is a simple function I wrote that will rotate (degrees) with a reference axis. You can create a quaternion by creating it in separate parts, the w and (x,y,z) components. GLM has an initializer that takes this, then you can cast it to a 4x4 or 3x3 matrix for transformations.

void QuaternionRotate(const glm::vec3& axis, float angle)
{
    float angleRad = glm::radians(angle);
    auto& axisNorm = glm::normalize(axis);

    float w = glm::cos(angleRad / 2);
    float v = glm::sin(angleRad / 2);
    glm::vec3 qv = axisNorm * v;

    glm::quat quaternion(w, qv);
    glm::mat4 quatTransform = glm::mat4_cast(quaternion);

    // Now with the quaternion transform you rotate any vector or compound it with another transformation matrix
}

Upvotes: 0

Alex North-Keys
Alex North-Keys

Reputation: 4373

[While this isn't GLM, the ordering of quaternions in the multiply is still pretty clear, and that's usually the problem]

I've used code like this to avoid gimbal lock (since any solution introducing gimbal lock into code that already has quaternions is just too ironic to consider).

This is C code, and the QuaternionFromAngles() and QuaternionMultiply() are overwriting the target of the first parameter. world->axis6_input_rotation is just a Quaternionf_t. The input is from a 6-axis controller, which is a bit more free form that your vehicle sim unless you're actually passing vectors around in your code.

typedef struct { float w, x, y, z; } Quaternionf_t; 

void GuiMotion6axis(World_t *world, Port_t *port,
                    int x,  int y,  int z,
                    int xr, int yr, int zr)
{
    // convert spaceball input to World->rotation (a quaternion)
    //    Source http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToQuaternion/index.htm
    const float scale = 0.0004; // should factor in the time delta it covers as well.
    float xrf = (float)xr * scale;
    float yrf = (float)yr * scale;
    float zrf = (float)zr * scale;

    QuaternionFromAngles(& world->axis6_input_rotation, xrf, yrf, zrf);
    QuaternionMultiply(& world->rotation,  // worldrot = inputrot * worldrot
                       & world->axis6_input_rotation,  // a read-only use
                       & world->rotation               // a read-only use
                       );

    world->position.x += (float)x * scale;  // really should factor in the
    world->position.y += (float)y * scale;  //   elasped time.
    world->position.z += (float)z * scale;
    return;
}

Upvotes: 1

Kirk Backus
Kirk Backus

Reputation: 4886

If you don't care about gimbal lock, than this should work.

glm::quat rot(info.Rotation.w,info.Rotation.x,info.Rotation.y,info.Rotation.z);
glm::quat rot_euler_angles = glm::gtx::quaternion::eulerAngles(rot);
rot_euler_angles.x += 90;

glm::quat done(glm::rotate(rot,rot_euler_angles));
info.Rotation.x = done.x;
info.Rotation.y = done.y;
info.Rotation.z = done.z;
info.Rotation.w = done.w;

I think this is also valid

glm::vec3 rot(90.0*(float)M_PI/180.0, 0, 0);
info.Rotation = glm::normalize(info.Rotation * glm::quat(rot));

Quaternions are fantastic because they can be compounded to make very complex rotations.

Upvotes: 0

Related Questions