I'm trying to convert my code to use dual quaternion skinning instead of matrix skinning because i just can't get the skinning matrix created correctly from bones+weights using matrices.
Edit: just to clarify, if each vert has only one bone the matrix version did skin correctly.
But it's just not working and i really don't understand why, i'm using code i found here:
http://www.chinedufn.com/dual-quaternion-shader-explained/
http://donw.io/post/dual-quaternion-skinning/
I've looked at the assorted papers often brought up in the answers to these questions, but for the most part i just don't understand them. I'm not even entirely sure how the [x, y, z, w] of a glm::quat correspond to the [1, i, j, k] of a quaternion--I think that w might be the scalar because it goes first in the actual byte order of the glm::quat struct, but i'm not sure.
Anyway, in my shader bindings i have:
void gltfShader::bindBones(const std::vector<glm::mat4> & bones)
{
std::vector<glm::fdualquat> dual_quats(std::min<int>(bones.size(), 64));
glm::quat r;
glm::vec3 t, s, sk;
glm::vec4 pr;
glm::fdualquat dq;
for(size_t i = 0; i < dual_quats.size(); ++i)
{
glm::decompose(bones[i], s, r, t, sk, pr);
dq[0] = r;
dq[1] = glm::quat(t.x, t.y, t.z, 0) * r * .5f;
dual_quats[i] = dq;
}
glUniformMatrix2x4fv(u_bones, dual_quats.size(), GL_FALSE, (float*) &dual_quats[0]);
}
In the vertex shader:
mat2x4 GetBoneTransform(ivec4 joints, vec4 weights)
{
float sum_weight = weights.x + weights.y + weights.z + weights.w;
// Fetch bones
mat2x4 dq0 = u_bones[joints.x];
mat2x4 dq1 = u_bones[joints.y];
mat2x4 dq2 = u_bones[joints.z];
mat2x4 dq3 = u_bones[joints.w];
// Ensure all bone transforms are in the same neighbourhood
weights.y *= sign(dot(dq0[0], dq1[0]));
weights.z *= sign(dot(dq0[0], dq2[0]));
weights.w *= sign(dot(dq0[0], dq3[0]));
// Blend
mat2x4 result =
weights.x * dq0 +
weights.y * dq1 +
weights.z * dq2 +
weights.w * dq3;
result[0][3] += int(sum_weight < 1) * (1 - sum_weight);
// Normalise
float norm = length(result[0]);
return result / norm;
}
mat4 GetSkinMatrix()
{
mat2x4 bone = GetBoneTransform(a_joints0, a_weights0);
vec4 r = bone[0];
vec4 t = bone[1];
return mat4(
1.0 - (2.0 * r.y * r.y) - (2.0 * r.z * r.z),
(2.0 * r.x * r.y) + (2.0 * r.w * r.z),
(2.0 * r.x * r.z) - (2.0 * r.w * r.y),
0.0,
(2.0 * r.x * r.y) - (2.0 * r.w * r.z),
1.0 - (2.0 * r.x * r.x) - (2.0 * r.z * r.z),
(2.0 * r.y * r.z) + (2.0 * r.w * r.x),
0.0,
(2.0 * r.x * r.z) + (2.0 * r.w * r.y),
(2.0 * r.y * r.z) - (2.0 * r.w * r.x),
1.0 - (2.0 * r.x * r.x) - (2.0 * r.y * r.y),
0.0,
2.0 * (-t.w * r.x + t.x * r.w - t.y * r.z + t.z * r.y),
2.0 * (-t.w * r.y + t.x * r.z + t.y * r.w - t.z * r.x),
2.0 * (-t.w * r.z - t.x * r.y + t.y * r.x + t.z * r.w),
1);
}
EDIT: video of the results:
The one on the left is supposed to be a walk cycle, the one on the right is supposed to be this:
https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/Monster