Matrices and Quaternions! Solved

Started by Exempt, February 21, 2015, 23:52:27

Previous topic - Next topic

Exempt

Hello!

I'm learning how to do quaternion rotation for use in my game (mainly for skeletal animation...what a pain..) and I've finally for a working rotation (I think)! For my question, is it normal to first turn a quaternion into a rotation matrix4 then multiply the transformation matrix4 of the object with the new rotation matrix4? This has been the only way I've gotten this to work or is there a better way to go about this?

Cornix

You could have a look how these people do it here:
http://forum.lwjgl.org/index.php?topic=5695.0

Or perhaps you may simply use their classes instead of writing your own.

Exempt

I'm not really making a library to deal with quaternions since I'm using the older version of LWJGL there is already a library. I just don't know if it's normal or even a good idea to take a quaternion and turn it into a matrix then multiply that new rotation matrix with my transformation matrix for a object in the world.

Kai

I am not sure why exactly you are asking. Maybe you can clear the motivation for your question a bit. Are you having troubles with either matrices or quaternions or how they can be converted into each other?
Matrices give you a single way for representing and dealing with any transformations, and they are, unlike quaternions, natively supported by OpenGL and GLSL and the whole rendering pipeline.
Additionally, matrices have the nice property that you can concatenate many matrices representing various transformations (translation, scaling, rotation, shearing, projection) together to save matrix multiplications in the end.
Of course you can also concatenate quaternions, but you would then need to have two different computation paths in your shader, one for quaternions for computing rotations, and one for matrices for anything else. That would be quite cumbersome. And in case you are using the fixed-function pipeline, then matrices really are your only option to transport transformations to the rendering pipeline.
So, it is perfectly reasonable to convert a quaternion to a matrix, in order for OpenGL and GLSL to handle them in the standard way.

Exempt

That's pretty much what I wanted to know. Thanks

Exempt

Sorry, I do have another question on this subject. Is there a more direction way to apply a quaternion orientation to a matrix4f? This is what I do right now to create my final transformation matrix.

If you see something wrong in here please tell me cause I'm not great at matrix math and quaternions.. well the math behind that is well..insane
public static Matrix4f createTransformationMatrix(Vector3f translation, Quaternion rotation, float scale){
		Matrix4f matrix = new Matrix4f();
		matrix.setIdentity();
		Matrix4f.translate(translation, matrix, matrix);
		Matrix4f.mul(matrix, convertQuaternionToMatrix4f(rotation), matrix);
		Matrix4f.scale(new Vector3f(scale, scale, scale), matrix, matrix);
		return matrix;
	}


public static Matrix4f convertQuaternionToMatrix4f(Quaternion q)
    {
        Matrix4f matrix = new Matrix4f();
        matrix.m00 = 1.0f - 2.0f * ( q.getY() * q.getY() + q.getZ() * q.getZ() );
        matrix.m01 = 2.0f * (q.getX() * q.getY() + q.getZ() * q.getW());
        matrix.m02 = 2.0f * (q.getX() * q.getZ() - q.getY() * q.getW());
        matrix.m03 = 0.0f;

        matrix.m10 = 2.0f * ( q.getX() * q.getY() - q.getZ() * q.getW() );
        matrix.m11 = 1.0f - 2.0f * ( q.getX() * q.getX() + q.getZ() * q.getZ() );
        matrix.m12 = 2.0f * (q.getZ() * q.getY() + q.getX() * q.getW() );
        matrix.m13 = 0.0f;

        matrix.m20 = 2.0f * ( q.getX() * q.getZ() + q.getY() * q.getW() );
        matrix.m21 = 2.0f * ( q.getY() * q.getZ() - q.getX() * q.getW() );
        matrix.m22 = 1.0f - 2.0f * ( q.getX() * q.getX() + q.getY() * q.getY() );
        matrix.m23 = 0.0f;

        matrix.m30 = 0;
        matrix.m31 = 0;
        matrix.m32 = 0;
        matrix.m33 = 1.0f;
 
        return matrix;
    }


Neoptolemus

There is possibly a more direct way by simply applying all of the transformations directly in the same step rather than calling other methods in the process. You'll start getting code that looks extremely ugly, but when you're doing potentially thousands of these kinds of calculations per frame you're going to want to avoid creating too many temporary objects with such a short lifespan. If you take a look at the library I put together (Cornix supplied the link) you'll see that I avoid creating any objects, and just try to do everything as direct as possible.

I do like your method for creating a transformation matrix in one easy step though, I'll see about implementing my own version if you don't mind ;)

By the way, the library Kai and I are putting together should be compatible with LWJGL 2.x as it uses the same naming conventions (though I've dropped Matrix4f.rotate() in favour of using quaternions...), but otherwise feel free to copy and paste bits out of it if you prefer using your own setup.

Neoptolemus

Hi again. I had a go at creating a more direct approach on the train home. Here is what I came up with:

    public static void createTransformationMatrix(Vector3f position, Vector3f scale, Quaternion rotation, Matrix4f dest) {

        float q00 = 2.0f * rotation.x * rotation.x;
        float q11 = 2.0f * rotation.y * rotation.y;
        float q22 = 2.0f * rotation.z * rotation.z;
        float q01 = 2.0f * rotation.x * rotation.y;
        float q02 = 2.0f * rotation.x * rotation.z;
        float q03 = 2.0f * rotation.x * rotation.w;
        float q12 = 2.0f * rotation.y * rotation.z;
        float q13 = 2.0f * rotation.y * rotation.w;
        float q23 = 2.0f * rotation.z * rotation.w;
        
        dest.m00 = (1.0f - q11 - q22) * scale.x;
        dest.m01 = (q01 + q23) * scale.x;
        dest.m02 = (q02 - q13) * scale.x;
        dest.m03 = 0.0f;
        dest.m10 = (q01 - q23) * scale.y;
        dest.m11 = (1.0f - q22 - q00) * scale.y;
        dest.m12 = (q12 + q03) * scale.y;
        dest.m13 = 0.0f;
        dest.m20 = (q02 + q13) * scale.z;
        dest.m21 = (q12 - q03) * scale.z;
        dest.m22 = (1.0f - q11 - q00) * scale.z;
        dest.m23 = 0.0f;
        dest.m30 = position.x;
        dest.m31 = position.y;
        dest.m32 = position.z;
        dest.m33 = 1.0f;
    }


I'm quite pleased with how much I was able to simplify it actually, and all my testing so far shows it produces identical results to the "old" method (using translate, rotate and scale). Feel free to use it if you like and let me know if you run into any problems!

Exempt

That does look about right to me, I'll give it a test later myself, thanks. I'll take another look at the LWJGL3 classes you wrote, I guess sooner or later that will be the way to go.