I've been working on implementing animation using BVH files and I think I finally understand how to get the relevant data.
The part I am most confused with is how I should now apply and combine this translation and rotation data to have the animation work.
The localTranslation and localRotation matrices store essentially the translation and rotation to get the model in the appropriate pose so that when per-frame rotations are applied from bone_rotations, the animation will play as its a matter of just rotating the bones. I am confused on how you are supposed to combine these matrices.
My main confusion is about the parentMatrix. Some sources suggest it transforms the joint to bone-space, while others say it transforms to the global coordinate system. When I apply it to jointPos as: jointPos = parentMatrix * jointPos
, my skeleton renders correctly in a hierarchical manner. Without this multiplication, it doesn’t. I’ve attached images to illustrate this and code showing how I am rendering this pose and how im storing the translation and rotation data which I then need to apply. Can anyone clarify this for me?
void Skeleton::drawJoint(Mat& viewMatrix, Mat4 parentMatrix, Joint* joint, float scale, int currentFrame)
{
vec3 offset_from_parent = vec3(
joint->jointOffsetX,
joint->jointOffsetY,
joint->jointOffsetZ
);
auto jointPos = vec3(0.0f, 0.0f, 0.0f);
// This is data to get the skeleton in the right pose
Mat localTranslation = Mat::Identity();
Mat localRotation = Mat::Identity();
for(int i = 0; i < joint->jointChannel.size(); i++)
{
const std::string& channel = joint->jointChannel[i];
float value = frameData[frame][BVH_CHANNEL[channel]];
if(channel == "Xposition")
{
localTranslation = localTranslation * Mat::Translate(vec3(value, 0.0f, 0.0f));
}
if(channel == "Yposition")
{
localTranslation = localTranslation * Mat::Translate(vec3(0.0f, value, 0.0f));
}
if(channel == "Zposition")
{
localTranslation = localTranslation * Mat::Translate(vec3(0.0f, 0.0f, value));
}
if(channel == "Xrotation")
{
localRotation = localRotation * Mat::RotateX(value);
}
if(channel == "Yrotation")
{
localRotation = localRotation * Mat::RotateY(value);
}
if(channel == "Zrotation")
{
localRotation = localRotation * Mat::RotateZ(value);
}
}
auto localTransformation = localTranslation * localRotation;
// is parent for global transformation?
auto globalTransformation = parentMatrix * localTransformation;
// multiply the parent matrix to get offset in parent coordinate system
jointPos = offset_from_parent;
vec3 localPos = localTransformation * jointPos;
// this will give rotation per frame to apply which will "play" the anim.
auto boneRot = bone_rotations[frame][joint->id];
// BVH stores rotation as Z,Y,X so, boneRot.x would be rotation for z
auto z = boneRot.x;
auto y = boneRot.y;
auto x = boneRot.z;
//z,y,x multiply like: x,y,Mat::RotateX(x) * Mat::RotateZ(z)
auto localMatrix = Mat::Translate(jointPos);
auto localBoneRot = (Mat::RotateX(z) * Mat::RotateY(y) * Mat::RotateZ(x));
auto matrix = localMatrix;
for(auto& child : joint->Children)
{
vec3 end = matrix * vec3(child.jointOffsetX, child.jointOffsetY, child.jointOffsetZ);
drawCylinder(viewMatrix, jointPos, end);
// Recursively render the child joint
drawJoint(viewMatrix, matrix, &child, scale, currentFrame);
}
}