I'm currently designing the rigging system of my game engine.
Currently, all bones and joints store a local position and rotation matrix. These are multiplied (rotation * position) to create a local space matrix. This is then multiplied by the local space matrix of the part it is attached to. This leads all the way back to the base bone, whose local space matrix is multiplied back to the world matrix of the model. Essentially, every joint and bone stores a matrix which represents its position and rotation relative to the part it is attached to.
To clarify: Final world matrix of Bone 1 = Model (world matrix) * Base Bone (local matrix) * Joint (local matrix) * Bone 1 (local matrix).
This is undoubtedly wrong, as the mesh is horribly contorted and the bones seem to be lost in the world space, translating strangely as I adjust the joint's rotation.
Basically I was under the impression that given a matrix that transforms Object A into world space, if you know the matrix that transforms from Object A to Object B, all you have to do to transform Object B into world space is use Object A (world matrix) * Object B (local to Object A matrix).
Little bit of code
Bone.cpp, UpdateMatrices() function
if (socketPtr != nullptr)
{
posMatrix = XMMatrixIdentity();
rotMatrix = XMMatrixRotationRollPitchYawFromVector(socketVector);
localMatrix = rotMatrix * posMatrix;
worldMatrix = socketPtr->GetWorldMatrix() * localMatrix;
}
else
{
posMatrix = XMMatrixTranslationFromVector(rigPtr->GetBasePositionVector());
rotMatrix = XMMatrixTranslationFromVector(rigPtr->GetBaseRotationVector());
localMatrix = rotMatrix * posMatrix;
worldMatrix = rigPtr->GetWorldMatrix() * localMatrix;
}
Where socketPtr is a pointer to the joint to which the bone is socketed, if this is null then the bone is the base bone of the rig. socketVector represents the pitch, yaw, and roll rotation offset of the bone in the socket.
Joint.cpp, UpdateMatrices() function
if(isAttachedAtEnd) posMatrix = XMMatrixTranslationFromVector(attachPtr->GetEndPosVector());
else posMatrix = XMMatrixIdentity();
XMMATRIX attachRotationMatrix = XMMatrixRotationRollPitchYawFromVector(attachVector);
XMMATRIX jointRotationMatrix = XMMatrixRotationRollPitchYawFromVector(jointRotVector);
rotMatrix = attachRotationMatrix * jointRotationMatrix;
localMatrix = rotMatrix * posMatrix;
worldMatrix = attachPtr->GetWorldMatrix() * localMatrix;
Where attachPtr is a pointer to the Bone that the joint is attached to, attachVector represents the pitch, yaw and roll offset of the attachment to the bone, and the jointRotVector is the pitch, yaw, roll rotation of the joint.
I know it might be hard for you to reproduce this, getting a "minimal working example" is rather difficult for a project as complex as this, because there are hundreds of lines required for interfacing through the WinAPI and getting all the DirectX environment stuff set up.
world = parent * local
thenlocal = translation * rotation
, notrotation * translation
. We want to rotate around 0, then move that 0 point to our local origin. If we do it in the opposite order, we orbit around the parent's (0,0,0) instead of our local origin point. \$\endgroup\$ – DMGregory♦ Dec 18 '20 at 12:40