Skip to main content
GameDev.net gamedev.net
🔒 Locked

hierarchy animation

Started by 16bit_port Jun 8, 2010 at 10:59 PM 8 replies 1.7k views
Original Post
16bit_port
16bit_port
I'm having trouble animating the joints.

Given the following:
struct bindpose_joint{   int parent, sibling, child; //                               //   vec4 world_rotation;        //   vec4 world_position;        //                               //   vec4 local_rotation;        // <--- all of these were given from source file   vec4 local_position;        //   vec4 local_scale;           //                                  mat4x4 final_transform;   // <--- I added this};struct animation_joint{   vec4 local_rotation;   //   vec4 local_position;   // <--- all of these were given from source file   vec4 local_scale;      //};animation_joint frames[numframes][numjoints];



I create a chain of matrices from the bindpose_joints::local_position :
mat4x4 transl = mat4x4(childjoint.local_position);childjoint.final_transform = parentjoint.final_transform * transl;


and that looks fine when I render it (skeleton standing with arms out to the side with all of the joints in the right place in object space).

However, when I try to do the exact same thing with the joints in frame X (creating a chain of matrices out of animation_joint::local_position), all of frame X's joints are around the origin.

Is this the correct way to animate joints/bones? Do I need to multiply bindpose_joint's data with the animation_joint's? Or is the frame data just incorrect (all the animation_joint::local_position components are near zero)? Or am I just interpreting the frame joint data incorrectly?
Ohforf sake
Ohforf sake
I think your snipped is missing the rotation part, can you post that too?

Something else that stands out is that the hierarchy (parent-child relation) is redefined for every frame. Can you check that these are still valid for all frames > 0?

What is in the world_rotation/position fields? Can you dump them into the final_transform and check if things improve?
16bit_port
16bit_port
Quote:
Original post by Ohforf sake
I think your snipped is missing the rotation part, can you post that too?


So, I'm not all that familiar with animating. I only used the bindpose_joints::local_position when creating the chain and it looked fine. Why and where do I need the rotation part? http://www.cyberkreations.com/kreationsedge/?page_id=29 didn't mention anything about rotations(in the paragraph right after the bone struct/class definition)

Quote:
Original post by Ohforf sake
What is in the world_rotation/position fields? Can you dump them into the final_transform and check if things improve?

Same as the above question. Some of the sites I looked at didn't mention using the world_rotation and position when animating the joints (they did when talking about skinning). Why and where do I need to incorporate the world_rotation/position?

Quote:
Original post by Ohforf sake
Something else that stands out is that the hierarchy (parent-child relation) is redefined for every frame. Can you check that these are still valid for all frames > 0?

You mean if they're still indexed/positioned correctly in every frame? I believe so.
Ohforf sake
Ohforf sake
I'll check the link when I get home, but usually the rotation is the most interesting part. Imagine a simple walking animation. The displacement of the feet is not achieved by variations of the translation, but by changing the rotation of the bones. E.g. your upper leg rotates forward, your lower leg rotates backward, and as a result your foot rises into the air.
Not using the rotation will most likely be the problem in your case.
The initial pose might be unaffected because in some animations the initial rotation for all bones is zero.

About the world_location/rotation, I suspect them to contain what you are trying to compute, so it might be worth checking them out to see, if you are having the same trouble with them. But try to get the rotation stuff (the thing I mentioned above) working first.
Ohforf sake
Ohforf sake
Quote:
Original post by 16bit_port
Why and where do I need the rotation part? http://www.cyberkreations.com/kreationsedge/?page_id=29 didn't mention anything about rotations(in the paragraph right after the bone struct/class definition)


The paragraph right after the bone structure is somewhat misleading. However if you read on, you will notice that he uses quaternions for his rotations and that he also shows how to use them. That is, he posts the code but doesn't touch the topic in his text (at least in the parts I read).
16bit_port
16bit_port
I think it would help me out a lot if I understood what those different rotations/scales/translations mean. Like when would I use the bindpose::world rotation/position as opposed to the bindpose::local position/rotation. Do I used both of them to build the initial pose?

"The initial pose might be unaffected because in some animations the initial rotation for all bones is zero."
The bindpose::world_rotation and bindpose::local_rotation that I've looked at have values in them. I guess when I rendered them (the joints), it seemed like they were in the right position but are actually inaccurate/incorrect (since I'm not using the rotations).
NumberXaero
NumberXaero
What part is giving you trouble the skeletal animation or the skinning using the skeleton? Are you sure you have all the the skeleton/animation data needed from the file?
16bit_port
16bit_port
Quote:
Original post by NumberXaero
What part is giving you trouble the skeletal animation or the skinning using the skeleton? Are you sure you have all the the skeleton/animation data needed from the file?


Skeletal animation. I'm pretty sure I have all of the data. I'm just not really sure what I should be doing with some of the data (as addressed in the previous posts)
NumberXaero
NumberXaero
This may be different here or there, you may use matrices, or store things different, sometimes the words joints and bones are used interchangably, I call it joints, bone to me would be the part between two joints, I use joints because the bone idea confuses me. The main parts are

- Final Skeleton (used the generate the skinning matrix)
- Animation Skeleton (base skeleton pose for the specific animation)
- Animation Data (channel data sometimes called track data)

The general idea (not actual code)...

You have a Skeleton, this is a heirarchy of parent child joints. If it were a snake, you would have a head joint, it would have a child joint, and the child would have a child, the tail, etc. This would look like two bones (3 joints)

So, Skeleton
Skeleton {	SkeletonJoint joints[kMaxSkeletonJoints];	// update this from the active animation data	// these joints are the characters "rig", properly parented, indexable by the weight/joint index, 	// with root at index [0], and all children  at some index > 0}


Each of these joints has
SkeletonJoint {	SkeletonJoint* parent;		// parent == NULL, root joint, begin updating from there	SkeletonJoint* children[kMaxChildJoints]		// animation creates these from animating the skeleton, generating interpolated versions of these	vec3 local_pos;	vec3 local_scale;	quat local_rot;	// this.world_ = parent.world_ combined with  this.local_ 	vec3 world_pos;	vec3 world_scale;	quat world_rot;	Mat4 finalWorldMatrix;	// computed by combining world_ parts		Mat4 invBindPoseMatrix;	// from file, usually part of skinning info	Mat4 skinMatrix;	// updated each frame, skinMatrix = finalWorldMatrix * invBindMatrix	Update()	// make sure this is called from root joint down, parent == NULL	{		if(parent)	// should be updated first		{			vec3 parentWorldPos = parent->world_pos;						vec3 parentWorldScale = parent->world_scale;			vec3 parentWorldRot = parent->world_rot;			this->world_rot = parentWorldRot * this->local_rot;			this->world_pos = (parentWorldRot * (this->local_pos * parentWorldScale)) + parentWorldPos			this->world_scale = parentWorldScale * this->local_scale		}		else		{			world_pos = local_pos;			world_scale = local_scale;			world_rot = local_rot;				}		mat3 rm = world_rot.ToRotationMatrix();		mat3 sm = FromScale(world_scale)		finalWorld = Mat4FromMat3AndT(rm * sm, world_pos)		skinMatrix = finalWorld * invBindPoseMatrix	}}

You would use skeleton joints skinMatrix applied to the vertices, either on cpu or gpu to do the skinning using the weights/jointIndex pair.
Given a time along the animation generate an interpolated version of the animations base skeleton.
JointPose {	vec3 pos;	vec3 scale;	quat rot;}SkeletonPose {	Array<JointPose> joints;	// frozen animation start pose, from animation file, variable length, animations dont always change every joint}Animation {	float currentTime		// add deltaTime to this to advance the animation	SkeletonPose basePose;		// animate all or some of these to where it should be based on "currentTime"	AnimationChannel channels[?];	// sometimes called tracks}

So an animation has a base pose for the animation, for a duck idle animation, the character might have
their knees bent, and hands near the floor. This will be the starting point of the animation, think of it as
frame 0, depending on the current animation time, you generate a new result pose for this time. Depending
on the anmation channel data all or some of these joints have their pos, scale, or rot replaced by the
interpolation result. Animating to a result pose allows different animations to generate SkeletonResultPose's
which can then be blended or layered together
SkeletonResultPose {	JointPose jointPose[kMaxSkeletonJoints];	// result of animating}Skeleton::Update(float dt){	SkeletonResultPose result;	// the skeleton for this frame time of the active animation	activeAnimation->Update(dt, result)	{		result = basePose;	// copy base, start with the base pose, all or some of it is replaced by channel data		currentTime += dt;	// advance the animation		activeAnimation->ComputeFromChannels(currentTime, result)	// change parts of the result (base) or all		{			FOR EACH CHANNEL	// could be as much as num skeleton joints * 3 (rot, tran, scale) i suppose				Channel* ch = this->channels;				index = ch->targetIndex;	// channels data targets some joint				// change these				vec3& t = result.jointPose[index].pos;				quat& r = result.jointPose[index].rot;				vec3& s = result.jointPose[index].scale;								ch->Update(currentTime, r, t, s)				{					// channel generates interpoalted rotation, translation or scale, channel knows, we dont					// pass all 3, it modifies which ever it targets					// Example: determine interpolated "t" at "currentTime (0.5)"					{						// find previous key frame number							// from sample channel time data						FOR EACH TIME SAMPLE (i = 0; i < numKeyTime - 1; ++i)							if(time >= keyTime && time < keyTime[i + 1])								return i;	// prevFrame						// nextFrame index = prevFrame index + 1						// got frame before and after time (0.5), now interpolate						t =  // interpolated value between data[prevFrame] and data[nextFrame]						// channel may just change t.x or s.z, usually all of r, but replaces something					}										// or r					// or s				}						}	}	// now we have an resulting skeleton pose created by animation in "result"	// optional: gen another skeleton pose from another animation, blend them, creating a third pose	// update the local of the final skeleton using "result" from active animation	FOR EACH JOINT 	// simply copy result to final skeleton local		this->joints.local_pos = result.jointPose.pos;		this->joints.local_scale = result.jointPose.scale;		this->joints.local_rot = result.jointPose.rot;	// compute the matrices from the resulting pieces	FOR EACH JOINT  // see above		this->joints.Update()	// gen finalWorld, build new skinMatrix from it	// do skinning with joints.skinMatrix and wight}


thats the general idea/order of things, maybe it will help you organize what youve got.
16bit_port
16bit_port
I think what confused me was the fact that when I render the bindpose_world and bindpose_local data, they came out to be exactly the same, so I wasn't sure which one was used for what and why there was a need for the other. And that I thought that I need both the bindpose_world and bindpose_local to do skeletal animation (not skinning).

But it seems that the bindpose_world is for skinning only, while the bindpose_local is used as a base and when combined with the offset data in the frames, it produces a joint in world position.

Any corrections to what I've concluded is greatly appreciated!

Thanks, NumberXaero and Ohforf sake. Rate +.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.