PDA

View Full Version : skinning with quaternion + position



kalmiya
05-20-2017, 01:00 AM
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;
}
}
}

GClements
05-20-2017, 02:50 AM
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 matrix*matrix->matrix and matrix*vector->vector operations become quaternion*quaternion->quaternion and quaternion*vector->vector respectively.

kalmiya
05-21-2017, 01:05 AM
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..

Nokturnis
05-25-2017, 05:59 AM
This might help you:


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)+v*dq.real. w+dq.dual.xyz)+dq.dual.xyz*dq.real.w-dq.real.xyz*dq.dual.w)*2.0+v;
}