Jump to content
  • Advertisement
Sign in to follow this  
LevyDee

Skeletal animation system

This topic is 2525 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

So in the pursuit of knowledge and a higher understanding of how "games" work, I have been developing a homemade game engine and am starting work on a skeletal animation system. I did some research on this and looked into how this concept is generally tackled. You have a hierarchy of bones, each with one parent, and possible multiple children. So when you transform a bone in 3d space, that bone's children, and the children of the children all are affected. My only loose end, is I do not understand the math behind how the bones interact with each other.

So I guess I have two questions.

1.Is my understanding of a "general" concept of a skeletal animation system correct?

2. How would one transform the child bone to act accordingly to what the parent has done? So for example, if you were to rotate bone1 45 degrees on the x-axis, how would you keep its child(bone2), attached at the joint, and move it accordingly? This is my biggest issue,
understanding the math behind it.

Thanks guys!

Share this post


Link to post
Share on other sites
Advertisement
@1. Yes

@2. Matrices make it all better. Also, quaternions.

So basically a matrix can store translation, rotation, position. If you have all of that information, and your bone hierarchy set up correctly, it should all just come together and work. When you multiply matrices together they 'stack' or their effect 'stacks'. Matrices are what you use to mathematically enforce hierarchy.

The quaternion does purly rotation, but using quaternions to store the rotation will help you to interpolate the bones correctly later. So for your bones you will need translation, just as 3 floats, rotation as a 4 float quarternion, and scale as 3 floats.

- If you do not build the original skeleton, or the animation nodes, in local bone space for each bone all the way up to the world root, you'll have to transform the transforms of each bone by the inverse transform that exists between it and the root.

- Continuing on: just do the data for each bone in it's own local space: basically the parent's transform scope. So if you only have the world transform of each bone to go on, just do what I said before to get the local space transform for that bone. Basically: you REALLY need the local space transform for each bone.

- You end up using the world space transform of the bone to affect vertices anyway, but there's a reason you need local so keep reading.

- There are 2 reasons you need local space transforms for all of these. 1: You need to interpolate the transform between frames of a single animation, rotation you can do by interpolating a quaternion, it will spherically interpolate between one state and another as that's how they work. 2: You take what you get from number 1, for multiple active animations, and then you interpolate those values together to blend animations. You interpolate based of a animation weight that is between 1 and 0 for each animation with all the active animations weights adding up to equal 1.

- The interpolating of animation frames based off a time delta, and the interpolating of multiple animations based off an animation weight, are the reason you need to store the transforms in local bone space, so that you get the correct effect when blending, otherwise the effect will be wrong, and everything will look off.

- Using the resulting translation, rotation quaternion, and scale from all that interpolation, create a local transform matrix for the bone at that point in time.

- Using a local space transform for each bone, you generate the world transform of that bone that is calculated by multiplying the matrix that represents each bone in a chain (from child to parent to parent to parent to root) and then you send that off to your vertex shader or what have you to apply the skinning.

- Only worry about the transform of the current bone in the chain, thanks to matrices, the application of the hierarchy is so easy that it's actually the simplest bit in creating a skeletal animation system.

- use some debug drawing methods while you're building this.

So yeah, probably should read that a couple of times since it is a bit much.

More stuff:
- Additive animations are created by taking an animation that has the bones in local space, subtracting a base animation in bones local from that to generate what is a subtracted animation, then this animation is added on top of an animation to add stuff like breathing, etc. Additive animations do not have to interpolate with other animations.
- You can interpolate matrices with a m1 x (1-d) + m2 x (d) sort of equation. This simple math equation doesn't interpolate rotation correctly.

Ah well, hope it helps.

Share this post


Link to post
Share on other sites
Thanks for the thorough reply! Ill have to reread it a couple times when I sit down to do some work on it.

Share this post


Link to post
Share on other sites
So I have been doing a little bit of work here, setting up my data structure ect... I want to clear up the math part. If I transform a parent bone, I take the resulting world matrix and multiply it by the child's world matrix, and then take the resulting matrix of the child and multiply it by the child's world matrix of the child ect...? Im not individually multiplying my operation(rotation, translation, scale) by each bone but rather only the parent, then multiply the resulting matrices by the children matrices. Hope that makes sense!

Share this post


Link to post
Share on other sites
You multiply the child local space transformation matrix with the parent world space transformation matrix. This gives you the world space matrix of the child. You perform this recursively.

So like this:


void Node::CalcWorldMatrix()
{
mWorldMatrix = mLocalMatrix * mParent->mWorldMatrix;

for all child nodes of node
child->CalcWorldMatrix();
}



You might need to switch the multiply of the matrices, by placing the parent on the left, and local matrix on the right instead.
Also you can make this a single loop if you store your array of nodes with flattened hierarchy, so in the order in which this above function would recurse. That way you get rid of the recursion.

Also, nodes that are root nodes, so which have no parent, can use their local matrices as world matrices directly.

You build this mLocalMatrix before calculating the world matrices, by constructing the matrix from the position and rotation quaternion. You basically convert the quaternion to a matrix and set the translation component in the matrix to the position of the node. And you can apply scale too of course.

The recursive update of the hierarchy like this is called Forward Kinematics, because if you rotate a parent, all child nodes down the hierarchy will rotate with it automatically.

Share this post


Link to post
Share on other sites
Could I not just transform all of the bones using my translation, rotation and scale data(local matrix), and then simply use the resulting matrix, to adjust the vertex positions(pre- world transform), and then simply translate my vertex from local to world space?

Share this post


Link to post
Share on other sites
No, then your skin deformation won't work for example.
Also what is your world space then, you still need to know that :)
In a hierarchy the rotation of your parent has an effect on the child's world space position and rotation.
If you would have a single object without hierarchy that is basically what you would do, but when you have a hierarchy that doesn't really work, as you need to know the world space transform for that given object. You need to know how much the parent already moved the object, before you can move it to world space.

Share this post


Link to post
Share on other sites
Ok guys, I appreciate the answers to my question, but I still have need of a little explanation. I have been going through this with pen and paper, learning the math, ect, but I have run into an issue. Everything works great if the only rotation occuring is from the root bone, but if another bone has a rotation further down the line, the angle gets doubled.

So basically, root bone rotates 45 on the z axis, and 2 bones down, its local matrix is also a 45 rotation on the Z axis, but multiplying the parents world matrix by this bones 45 rotation local matrix, turns it into a 90 degree rotation on the z axis(instead of 45).

The root bones 45 z axis rotation, I am using its local as its world(because it has no parent)

Share this post


Link to post
Share on other sites
So basically, root bone rotates 45 on the z axis, and 2 bones down, its local matrix is also a 45 rotation on the Z axis, but multiplying the parents world matrix by this bones 45 rotation local matrix, turns it into a 90 degree rotation on the z axis(instead of 45).[/quote]

That is correct and expected behavior.

If you would want the child nodes to remain at a 45 degree angle in world space, their rotation will be identity.
If you have an arm, and you rotate the upper arm, the lower arm and hand automatically rotate with it.
Now if you want to rotate the lower arm 20 degrees, while the upper arm is already rotated 45 degrees, you end up with a world space rotation of 65 degrees.

You store your animation data also in local space. That means if your lower arm is never rotating, you don't need to store the animation data for it, because relative to the upper arm it never rotates. This can save you both calculations and memory.

This especially is useful for finger bones, because they are often not animated, or they do not animate a lot. In many motion capture files for example only the upper arm and lower arm would rotate, but not the fingers. Still, if you animate the arm bones, the finger location will rotate with it as explained above and as you indicated yourself.

Do you understand that? Or what exactly is not clear yet? If you have any specific questions just ask and we will try to answer and explain.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!