tl;dr: When animating a model, each joint moves correctly, but not relative to its parent joint.
I am working on a skeletal animation system using a custom built IQE loader and renderer in Lua. Nearly everything is working at this point, except the skeleton seems to be disjointed when animating. Each joint translates, rotates, and scales correctly but is not taking the position of its parent into account, thus creating some awful problems.
In referencing the IQM spec and demo, I cannot for the life of me find out what is going wrong. My Lua code is (as far as I can tell) identical to the reference C++.
Calculating Base Joint Matrices:
local base = self.active_animation.base
local inverse_base = self.active_animation.inverse_base
for i, joint in ipairs(self.data.joint) do
local pose = joint.pq
local pos = { pose[1], pose[2], pose[3] }
local rot = matrix.quaternion(pose[4], pose[5], pose[6], pose[7])
local scale = { pose[8], pose[9], pose[10] }
local m = matrix.matrix4x4()
m = m:translate(pos)
m = m:rotate(rot)
m = m:scale(scale)
local inv = m:invert()
if joint.parent > 0 then
base[i] = base[joint.parent] * m
inverse_base[i] = inv * inverse_base[joint.parent]
else
base[i] = m
inverse_base[i] = inv
end
end
Calculating Animation Frame Matrices
local buffer = {}
local base = self.active_animation.base
local inverse_base = self.active_animation.inverse_base
for k, pq in ipairs(self.active_animation.frame[self.active_animation.current_frame].pq) do
local joint = self.data.joint[k]
local pose = pq
local pos = { pose[1], pose[2], pose[3] }
local rot = matrix.quaternion(pose[4], pose[5], pose[6], pose[7])
local scale = { pose[8], pose[9], pose[10] }
local m = matrix.matrix4x4()
m = m:translate(pos)
m = m:rotate(rot)
m = m:scale(scale)
local f = matrix.matrix4x4()
if joint.parent > 0 then
f = base[joint.parent] * m * inverse_base[k]
else
f = m * inverse_base[k]
end
table.insert(buffer, f:to_vec4s())
end
The full code is here for further examination. The relevant code is in /libs/iqe.lua and is near the bottom in the functions IQE:buffer() and IQE:send_frame(). This code runs on a custom version of the LOVE game framework, and a Windows binary (and batch file) is included.
Final note: Our matrix code has been verified against other implementations and several tests.