You ignore rotations.
If you rotate the upper arm, you want to rotate the lower arm, hands etc. with it.
But currently you only propagate translation to child bones:
auto matrix = parentMatrix * Mat4::Translate(jointPos);
jointPos tells where the bone has its center of rotation, but you also need the rotation the bone should have.
Usually you need something like this:
Mat4 transform;
transform.SetTranslation(jointPos);
transform.SetRotation(joint->rotation); // rotation can be given as quaternions ideally, or eventually euler angles
auto matrix = parentMatrix * transform;
That's surely the culprit and what you want to work on first.
Besides, a recursive function is not needed to transform a character skeleton.
Usually bones are ordered, so parents come always first if we simply iterate over all bones.
For ragdolls i have this hard coded to give an example:
enum bone_names
{
HIP,
BELLY,
CHEST,
NECK,
HEAD,
L_THIGH,
L_SHIN,
L_FOOT,
L_TOE,
R_THIGH,
R_SHIN,
R_FOOT,
R_TOE,
L_SHOULDER,
L_BICEP,
L_FOREARM,
L_HAND,
R_SHOULDER,
R_BICEP,
R_FOREARM,
R_HAND,
NUM_BONES,
};
Iterating in such order ensures all hierarchical dependencies are already known for any bone. But ofc. you need to store relevant data, in this case the actual transformation matrix per bone.
Code to create a skeleton pose from given joint angles then looks like this for me:
void PoseFromJointTargets (
BodyPose *bodyPose, // transfrom for each bone
JointState *jointStates) // animation angles for each joint
{
for (int i=1; i<NUM_BONES; i++)
{
RagdollJoint &joint = joints[i];
JointState &state = jointStates[i];
int i0 = joint.bodyIndex[0];
int i1 = joint.bodyIndex[1];
Mat4 bodyMatrix = (i0 == i // (working with physics data structures, i need to branch depending on which of the two bodies per joint represents the parent in the hierarchy. That's not needed with animation data structures
.)
? joint.PredictBodyPose0 (bodyPose[i1].GetBodyMatrix(), state.targetOrn) // due to iterating in proper sequence, it's guaranteed the parent bodyPose has been already calculated.
: joint.PredictBodyPose1 (bodyPose[i0].GetBodyMatrix(), state.targetOrn));
bodyPose[i].SetBodyMatrix(bodyMatrix); // calculating and storing the current bodyPose
}
}
Notice it's a simple loop, and i neither need recursion nor a stack.
Now imagine to write a file exporter for some animation editor, to generate the bvh file.
You might use recursion because memory order of bones can change as artists edit the model.
You will start with the root node, which usually is the hip or some ground bone parenting the hip. Then you traverse its children as usual and you write to file each bone in this same to down order you traverse them.
After that, the sequence of bones in the file is already properly ordered hierarchically.
And thus, for a game runtime there should be never a need for expensive traversals to animate skeletons. ; )