Creating an Animation File Format

Started by
10 comments, last by gzboli 14 years, 1 month ago
Animated COLLADA files provide the following info Skin - bind shape matrix - list of joint/weight index pairs - list of weights - list of joint nodes used during skinning - list of inverse bind matrices, one per joint and the animation channel (track) data. Does the entire contents of this file make for a single animation? (lets assume there is only one animation in the file) is the skin info unique to this specific animation data, or is it unique to the model data? I read you can pre transform the model by the bind shape matrix because this will be the base pose for the entire animation. So does that mean for every animation you will have unique skin data (bind pose matrix, weights, inv bind matrix), animation data and a pre transformed mesh copy?
Advertisement
Quote:So does that mean for every animation you will have unique skin data..?

I'm not sure which terms you're applying "unique" to, just the skin data, or animation data and "pre-transformed" mesh. I'm not particularly familiar with Collada so I'll have to guess at what some of your terms mean.

A single set of skin data can be animated with any number of animations. However, the joints in each animation set must match the joints in the skin data.

Yes, you can pre-transform the mesh, but that requires multiple sets of mesh data (which can be a lot of memory depending on the number of vertices), equal in number to the maximum weights-per-vertex. It's a memory versus speed choice.

Is that what you're asking about?

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Yeah thats what I was wondering about, if i pre transform using the bind shape matrix for every animation (this was assuming each animation got its own set of skin data, with a different bind shape matrix) the copies of that mesh would balloon every time I need a new animation.
Quote:A single set of skin data can be animated with any number of animations.

This is what I figured at first its also what Im hoping. What Im trying to do is pull all the animation and character geometry from animated COLLADA files, and create a mesh and animation file from them, but Im trying to figure what data is constant across all animations and put that in the mesh file, and the animation data that changes per animation (walk, idle, run, etc) pull that out and drop it in an animation file, then bring it all together. One animated model file, and a bunch of animation files that target it. So any unique data that doesnt change, skeleton node hierarchy, mesh, joint name list, ends up in the mesh. Any data related to making him do an animation, goes into the animation file. So I have

Mesh File
------------------
Skeleton node hierarchy (joints)
Joint name list (referred to by index)containing joint transform data)
Mesh data (that get skinned/rendered)

Animation file
------------------
Single Animation (time data, name)
- containing channels
Multiple channels (track data? xform info for joints)
- target joint index
- interpolation type
- type of output values (quat, translation, scale)
- output values (xform data)

and the skin data which im unsure of where to place, because Im not an animator and Im unsure if this stuff would change per animation, or change partially

Skin data
- bind shape matrix (places him in T pose, is there one of these per animation?)
- list of nodes that are targeted
- inv bind matrix list (one per target node)
- weight list
- per vertex, joint/weight index pairs, or the number of influences per vertex

I guess when youre animating do you "set" a new bind pose at the start before animating? this would change the bind shape matrix, and the inv bind list and at least make those two unique to the animation. Meaning part of the skin info at least would end up in the animation file.
Quote:I guess when youre animating do you "set" a new bind pose at the start before animating?

Hmm. No. You should use the same rest pose for all animations. EDIT: For each animation set (walk, run, etc.), start with the rest pose, orient each joint (which moves the mesh) for frame 0 of the animation. Don't set a "new" rest pose. Animate the rest of the frames.

The inverse bind matrices move the rest pose into joint space. The animation matrices move the vertices from joint space back into world space. The animation data doesn't "know" anything about the rest pose in the Mesh data. It's just data that you've created, based on moving the rest pose around.

The skinning data is (should be) a list of joints (each of which must correspond to a node in the mesh hierarchy), list of vertices/weights that the joint influences, and info to transform the mesh into joint space (the inverse bind matrices).

If you're careful with your bookkeeping and you want to, there's no reason (except wasting some memory) not to include skinning info for a joint in its node in the hierarchy.

Mesh rest pose vertices + skinning info = Mesh Data (can be used for any animation that has the same hierarchy and is based on the same rest pose)

[Edited by - Buckeye on February 20, 2010 12:12:32 PM]

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Im basing this all off what COLLADA throws at me so I get the feeling there is something I should be calculating/deriving from this data that isnt explicitly provided with respect to "poses". What Im doing right now is

- after loading the mesh, create a copy of it and transform each vertex position by the <bind_shape_matrix> (BSM) a 4x4 matrix (transform normal later when its working)

loaded mesh data (skinned and render)
loaded mesh data copy (pre transformed copy of loaded mesh by BSM)

Each frame with a running animation...
1) Update the Animation: Interpolate/build the joint animation xform data, apply it to the joint
2a) Update the joint hierarchy: creates jointWorld
2b) Update skinMatrix = jointWorld * jointInvBind
3) Update skin mesh: Using the pre transformed bind shape copy
For each vertex (from BSM copy)
- start with bsm_vertex
- For each vertex influence (joint/weight)
-- get joints skin matrix, get weight
-- out_vertex += (jointSkinMatrix * bsm_vertex) * weight
- store skinned out_vertex for rendering
Next vertex (from BSM copy)

this does perform the animation, but because the bind shape matrix is always the same T pose (character terminology, rest pose?) for every animation (walk, run idle), it seems that its influencing or biasing every animation towards this T pose, which would make sense seeing as its being used that way (start point for skinning), but it seems like it shouldnt. Which is where this idea of some sort of start "pose" per animation comes in, which I feel Im missing, and should be calculating some how. Or, a bind shape matrix per animation, something. Although it could be related to the way it was animated.

For ref sake, the weight data looks odd but that because his parts are not joined, Rayman like
Quote:transform each vertex position by the <bind_shape_matrix> (BSM)

I'm not familiar with Collada, I'm afraid, and I'm not sure what you mean by BSM. If your statement has anything to do with using some sort of matrix related to joints which influence the vertex, that's not good.

Animation is usually done:
v1 = original_v*inverseBindMatrix[joint1]*animMatrix[joint1]*weight[joint1]v2 = original_v*inverseBindMatrix[joint2]*animMatrix[joint2]*weight[joint2]finalV = v1 + v2

where animMatrix[jointx] is the lerped/slerped matrix for the animation time
If you're doing something like:
v_used_for_animation = original_v*inverseBindMatrix[joint1]*weight[joint1]+original_v*inverseBindMatrix[joint2]*weight[joint2]...

and then using v_used_for_animation for animating the mesh, that's not the same.

EDIT: I may be making some invalid assumptions regarding what Collada is giving you. I'm afraid I can't follow the terms you use (skinMatrix, jointWorld, etc.).

In general, I would expect (after you load the mesh and animation data):
vertex_array - vertex positions, normals, array of joint indices & weights, etc.
offset_array - an array of matrices, one for each joint (inverseBindMatrix??), once created, never changes
animation_matrix_array - matrices to be updated during animation.

[Edited by - Buckeye on February 20, 2010 2:13:03 PM]

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

In your first example where would "animMatrix" come from? What would it be? How is it calculated?

From the COLLADA spec BSM is... which I assumed to be the first matrix you apply to the mesh vertices every frame, regardless, so do it once and store a copy of the original mesh with it applied



where:
n: number of joints that influence vertex v
BSM: bind shape matrix
IBMi: inverse bind matrix of joint i
JMi: joint matrix of joint i
JW: joint weight/influence of joint i on vertex v
Common optimizations include:
(v * BSM) is calculated and stored at load time.
Okay. That's (sort of) the algorithm for generating the animated vertex (outv) that I mentioned above. JMi is the animation matrix. I'm guessing that BSM may just be a transform for the base pose mesh.

So, taking it one step at a time:
Quote:1) Update the Animation: Interpolate/build the joint animation xform data, apply it to the joint

What do you mean by "apply it to the joint"?

Just for clarification, the animation matrix calculated for each joint from the animation data is a local transform and needs to be combined with it's parent's animated transform and it's parent-parent's xform, etc. Perhaps that's what your step "2a) Update the joint hierarchy: creates jointWorld" is doing?

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Yes. The result of the interpolation is stored as the joints local xform, once all interpolating is done and the local is set for each joint, the world matrix is calculated from the parents of each joint (JM from the img, which i have referred to has jointWorld, which I think is your animMatrix).

Quote:I'm guessing that BSM may just be a transform for the base pose mesh.


Yes. I must have the Bind shape matrix applied to my vertices first or my geometry explodes and arms and legs end up everywhere.
Quote:skinMatrix = jointWorld * jointInvBind

Then you multiply the jointWorld and jointInvBind and store it in array which you call jointSkinMatrix?

If so, then it appears your general algorithm is correct.

I kinda lost track, are you still having a problem? [looksaround]

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

This topic is closed to new replies.

Advertisement