So I've been trying to implement a revolute joint using quaternion constraints as described by Claude Lacoursière in Game Physics Pearls, but without too much luck. It seems to only work when the bodies are not rotated and the joint frame is axis-aligned.
I've attached a pdf I made this morning of my derivation and what follows is some of the relevant code:
inline mat4_t L_matrix(const quat_t &q)
{
mat4_t result;
result.a = q.w; result.e = -q.z; result.i = q.y; result.m = q.x;
result.b = q.z; result.f = q.w; result.j = -q.x; result.n = q.y;
result.c = -q.y; result.g = q.x; result.k = q.w; result.o = q.z;
result.d = -q.x; result.h = -q.y; result.l = -q.z; result.p = q.w;
return result;
}
inline mat4x3_t GT_matrix(const quat_t &q)
{
mat4x3_t result;
result.a = q.w; result.e = -q.z; result.i = q.y;
result.b = q.z; result.f = q.w; result.j = -q.x;
result.c = -q.y; result.g = q.x; result.k = q.w;
result.d = -q.x; result.h = -q.y; result.l = -q.z;
return result;
}
inline mat4_t R_matrix(const quat_t &q)
{
mat4_t result;
result.a = q.w; result.e = q.z; result.i = -q.y; result.m = q.x;
result.b = -q.z; result.f = q.w; result.j = q.x; result.n = q.y;
result.c = q.y; result.g = -q.x; result.k = q.w; result.o = q.z;
result.d = -q.x; result.h = -q.y; result.l = -q.z; result.p = q.w;
return result;
}
inline mat4x3_t ET_matrix(const quat_t &q)
{
mat4x3_t result;
result.a = q.w; result.e = q.z; result.i = -q.y;
result.b = -q.z; result.f = q.w; result.j = q.x;
result.c = q.y; result.g = -q.x; result.k = q.w;
result.d = -q.x; result.h = -q.y; result.l = -q.z;
return result;
}
Initialisation:
quat_t e = conjugate(body1->xform.q) * frame;
quat_t f = conjugate(body2->xform.q) * frame;
mat2x4_t P;
P.a = 1.0f; P.c = 0.0f; P.e = 0.0f; P.g = 0.0f;
P.b = 0.0f; P.d = 1.0f; P.f = 0.0f; P.h = 0.0f;
joint->P = P * R_matrix(f) * transpose(L_matrix(e));
Pre-step:
quat_t rq = conjugate(body1->xform.q) * body2->xform.q;
mat2x3_t G1 = -0.5f * joint->P * ET_matrix(rq);
mat2x3_t G2 = 0.5f * joint->P * GT_matrix(rq);
joint->G1 = G1;
joint->G2 = G2;
mat2_t K = (G1 * body1->mass_ang_world_inv * transpose(G1))
+ (G2 * body2->mass_ang_world_inv * transpose(G2));
joint->inv_K = inverse(K);
Solving the velocity constraint:
const mat2x3_t &G1 = joint->G1;
const mat2x3_t &G2 = joint->G2;
vec2_t GV = (G1 * body1->vel_ang) + (G2 * body2->vel_ang);
vec2_t lambda = joint->inv_K * -GV;
body1->vel_ang += body1->mass_ang_world_inv * transpose(G1) * lambda;
body2->vel_ang += body2->mass_ang_world_inv * transpose(G2) * lambda;
It would be greatly appreciated if someone who has experience with this (Dirk Gregorius?) to take a look at this and point out where I'm going wrong.