skinning with quaternion + position

Hello,

I’m experimenting with skinning and trying to do the calculation directly with the quaternion, but I’m stuck getting the translation to be the same as with the matrix. I

In the code-block below I know that the matrix is correct (mesh is displayed correctly). Now instead of using the matrix, I’m trying to do the same but directly using the quaternion. For verifying, I create a matrix out of the Quaternion + vector. The problem is that the 3x3 rotation-part of that matrix is correct but the translation isn’t.

Question: How do I calculate the worldspace-translation using the current localspace-quaternion/translation + parent-worldspace-quaternion/translation ?

void _computeWorldPose(const Model& model, const Animation& anim, size_t frame, KeyFrame& keyframe)
{
        // reserve space for the output
	keyframe.pose.resize(model.rig.size());
	keyframe.poseQ.resize(model.rig.size());
	keyframe.poseT.resize(model.rig.size());

	for (int b=0; b<model.rig.size(); b++)
	{
		// anim-bone-index may have different index as model-bone-index, lookup by name
		int abid = anim.getIndexByName(model.rig[b].name);
	
		if(abid!=-1)
		{
			Quaternion qq;
			vector3 pos;
			float scale;
			unsigned int parentid = model.rig[b].ParentID;
		
			anim.GetDataForKeyframe(abid, frame, qq, pos, scale);
		
			// prepare the matrix
			Quaternion q = qq.Inverse();   // (not entirely sure why, but with this the mesh is rendered correctly... ) 
			matrix4 m4 = q.ToMatrix4();
			m4.set_translation(pos);
		
                        // do calculation as Matrix and as Quaternion
			keyframe.pose[b] = keyframe.pose[parentid] * m4;
			keyframe.poseQ[b] = q * keyframe.poseQ[parentid]; 
			keyframe.poseT[b] = q.transform(pos) + keyframe.poseT[parentid]; // ? what needs to be done here ?

			// verify that we get the same result 
			matrix4 m(keyframe.poseQ[b], keyframe.poseT[b]);
			if (keyframe.pose[b] != m) {
				assert(0); // this fires, with the 3x3-rotation being identical, but translation is different
			}
		
		}
		else
		{
			// "Root"
			keyframe.pose[b].identity();
			keyframe.poseQ[b] = Quaternion();
			keyframe.poseT[b] = VECTOR3_ZERO;
		}
	}
}

If you partition matrices into blocks, so that you have matrices whose elements are matrices, the rules for multiplication are the same as for matrices of scalars.

So:


[R1 T1] * [R2 T2] = [R1*R2+T1*0 R1*T2+T1*1]
[ 0  1]   [ 0  1]   [ 0*R2+ 1*0  0*T2+ 1*1]

 = [R1*R2 R1*T2+T1]
   [    0        1]

Where R1,R2 are the 3x3 rotation matrices, T1,T2 are the 3x1 translation vectors, 0 is a 1x3 zero vector [0,0,0] and 1 is a 1x1 unit matrix [1].

IOW, transform the right-hand translation by the left-hand rotation and add the left-hand translation to give the overall translation. Multiply the rotations to get the final rotation.

If the rotations are quaternions rather than matrices, it just means that the matrixmatrix->matrix and matrixvector->vector operations become quaternionquaternion->quaternion and quaternionvector->vector respectively.

Before posting I tried “all” combinations of multiplying things together (none worked), read your comment and tried the code below and it instantly worked!
So now the cpu-side quaternion-calculation gives the same result as the cpu-side matrix calculation. Your explanation was really helpful, thanks!

keyframe.poseT[b] = keyframe.poseQ[parentid].transform(pos) + keyframe.poseT[parentid];

Going to make a dual-quaternion out of it now and try and use that in the shader instead of the matrix…

This might help you:

[CODE=glsl]
struct dquat
{
vec4 real;
vec4 dual;
};

// v - the vertex; dq - dual quaternion
vec3 dq_transform(in vec3 v,in dquat dq)
{
return (cross(dq.real.xyz,cross(dq.real.xyz,v)+vdq.real.w+dq.dual.xyz)+dq.dual.xyzdq.real.w-dq.real.xyz*dq.dual.w)*2.0+v;
}