Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

At least I think it is. I have the following functions:

  • kmQuaternionRotationAxis - Constructs a Quaternion from an axis and angle
  • kmMat4RotationQuaternion - Constructs a 4x4 matrix from a quaternion
  • kmMat4RotationAxisAngle - Constructs a 4x4 matrix from an axis and angle

My problem is, when I run the following code:

kmQuaternion q;
kmMat4 initialized;
kmMat4 quaternion_rotated;

kmQuaternionRotationAxis(&q, &KM_VEC3_POS_Z, kmDegreesToRadians(90));
kmMat4RotationAxisAngle(&initialized, &KM_VEC3_POS_Z, kmDegreesToRadians(90));

kmMat4RotationQuaternion(&quaternion_rotated, &q);

I would expect *quaternion_rotated* to match initialized, but they don't.

Initialized:

 | 0 -1  0  0 |
 | 1  0  0  0 |
 | 0  0  1  0 |
 | 0  0  0  1 |

quaternion_rotated:

 | 0  1  0  0 |
 | -1 0  0  0 |
 | 0  0  1  0 |
 | 0  0  0  1 |

The contents of initialized looks like I would expect, but I can't figure out how quaternion_rotated is wrong. I've included all the code below, where is my conversion code wrong?

kmQuaternion* kmQuaternionRotationAxis(kmQuaternion* pOut, const kmVec3* pV, kmScalar angle)
{
    kmScalar rad = angle * 0.5f;
    kmScalar scale  = sinf(rad);

    pOut->w = cosf(rad);
    pOut->x = pV->x * scale;
    pOut->y = pV->y * scale;
    pOut->z = pV->z * scale;

    kmQuaternionNormalize(pOut, pOut);

    return pOut;
}

kmMat4* kmMat4RotationAxisAngle(kmMat4* pOut, const kmVec3* axis, kmScalar radians)
{
    kmScalar rcos = cosf(radians);
    kmScalar rsin = sinf(radians);

    kmVec3 normalizedAxis;
    kmVec3Normalize(&normalizedAxis, axis);

    pOut->mat[0] = rcos + normalizedAxis.x * normalizedAxis.x * (1 - rcos);
    pOut->mat[1] = normalizedAxis.z * rsin + normalizedAxis.y * normalizedAxis.x * (1 - rcos);
    pOut->mat[2] = -normalizedAxis.y * rsin + normalizedAxis.z * normalizedAxis.x * (1 - rcos);
    pOut->mat[3] = 0.0f;

    pOut->mat[4] = -normalizedAxis.z * rsin + normalizedAxis.x * normalizedAxis.y * (1 - rcos);
    pOut->mat[5] = rcos + normalizedAxis.y * normalizedAxis.y * (1 - rcos);
    pOut->mat[6] = normalizedAxis.x * rsin + normalizedAxis.z * normalizedAxis.y * (1 - rcos);
    pOut->mat[7] = 0.0f;

    pOut->mat[8] = normalizedAxis.y * rsin + normalizedAxis.x * normalizedAxis.z * (1 - rcos);
    pOut->mat[9] = -normalizedAxis.x * rsin + normalizedAxis.y * normalizedAxis.z * (1 - rcos);
    pOut->mat[10] = rcos + normalizedAxis.z * normalizedAxis.z * (1 - rcos);
    pOut->mat[11] = 0.0f;

    pOut->mat[12] = 0.0f;
    pOut->mat[13] = 0.0f;
    pOut->mat[14] = 0.0f;
    pOut->mat[15] = 1.0f;

    return pOut;
}

kmMat4* kmMat4RotationQuaternion(kmMat4* pOut, const kmQuaternion* pQ)
{    
    double xx = pQ->x * pQ->x;
    double xy = pQ->x * pQ->y;
    double xz = pQ->x * pQ->z;
    double xw = pQ->x * pQ->w;

    double yy = pQ->y * pQ->y;
    double yz = pQ->y * pQ->z;
    double yw = pQ->y * pQ->w;

    double zz = pQ->z * pQ->z;
    double zw = pQ->z * pQ->w;

    pOut->mat[0] = 1 - 2 * (yy + zz);
    pOut->mat[1] = 2 * (xy - zw);
    pOut->mat[2] = 2 * (xz + yw);
    pOut->mat[3] = 0;

    pOut->mat[4] = 2 * (xy + zw);
    pOut->mat[5] = 1 - 2 * (xx + zz);
    pOut->mat[6] = 2 * (yz - xw);
    pOut->mat[7] = 0.0;

    pOut->mat[8] = 2 * (xz - yw);
    pOut->mat[9] = 2 * (yz + xw);
    pOut->mat[10] = 1 - 2 * (xx + yy);
    pOut->mat[11] = 0.0;

    pOut->mat[12] = 0.0;
    pOut->mat[13] = 0.0;
    pOut->mat[14] = 0.0;
    pOut->mat[15] = 1.0;

    return pOut;
}
share|improve this question

1 Answer 1

up vote 1 down vote accepted

Your problem is that you didn't implement kmMat4RotationAxisAngle like this:

kmMat4* kmMat4RotationAxisAngle(kmMat4* pOut, const kmVec3* axis, kmScalar radians)
{
  kmQuaternion quat;
  kmQuaternionRotationAxis(&quat, axis, radians);
  kmMat4RotationQuaternion(pOut, &quat);
  return pOut;
}

That sounds flippant, but the best way to make sure two pieces of code stay in sync is to implement one in terms of the other.

That being said, your main problem is that you transposed your quaternion to matrix logic. Here's the proper matrix for that; your version puts the rows in the columns.

Yet another reason to not reinvent the wheel and use someone else's matrix library.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.