2
\$\begingroup\$

While I need to do this in a Blender3D modeler script, the math should be similar for other modelers or realtime engines.

Blender3D specific terminology:
Armature = skeleton
EditBone = rest pose bone (stores the rest pose matrix)
PoseBone = can store a different pose (animation matrix) for each frame of your animation

I need to share animations (Blender Actions) between Armatures which have EditBones with same names and which have the same positions, but can have different (rest pose) angles and scales. Plus the Armatures might have different bone hierarchy (bone parenting/ no bone parenting).

Why I need this:
I've made an importer/exporter for a 3d format for a game. The format doesn't store enough info to connect/parent the bones, which makes posing/animating character models in a 3d modeller nearly impossible (original model files for the 3d modeler don't exist, this is for modding).
As there are only 2 character skeleton types in the game, I decided to optionally allow to generate the bone from a hardcoded data in the model importer and undo that in the exporter. This allows to easily pose the model for checking weights, easily create weights, makes it easier for Blender to generate automatic weights and of course makes animating possible.

This worked perfectly: the importer optionally generated the Armature itself and the exporter removed those changes, so the exported model works with existing animations in the game. But now I'm writing an importer and exporter for the game's animation format and here come the problems of:

  1. Trying to make original animations work in Blender with my "custom" (modified) Armature
  2. Trying to make animations created by using the "custom" (modified) Armature work with the original models in the game (and Blender).

Constraints or bone snapping inside Blender won't work as they don't care that the bones have different angles in the rest pose, they will still face the same direction.

It seems I just need to get the "difference" between the EditBone matrices of all EditBones for the two Armatures somehow and apply that difference to PoseBone matrices of all PoseBones, for all frames of my animation. I need to know how to get that difference and how to apply it.

BTW, PoseBone matrices are relative to rest pose, they are by default

[1.000000, 0.000000, 0.000000, 0.000000](matrix [row 0])
[0.000000, 1.000000, 0.000000, 0.000000](matrix [row 1])
[0.000000, 0.000000, 1.000000, 0.000000](matrix [row 2])
[0.000000, 0.000000, 0.000000, 1.000000](matrix [row 3])

So the question is: How to get the difference between two bone (EditBone) matrices to apply that difference to the animation matrices (PoseBone matrices)?

Please be easy on the matrix math.

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

You don't mention how your source animation data is constructed, so I'm going to have to be a bit vague about the details.

From your input data you should be able to construct both the bind pose and animated pose matrices for each bone in two different coordinate spaces: world space (absolute position and orientation for the bone relative to the world origin), and local space (position and orientation relative to the coordinate space given by the parent bone).

Going from local space to world space is just a matter of concatenating the local matrix for a bone with its parent's world matrix (which can be computed the same way, recursively). A root bone's local and world matrices are identical.

bone.world_matrix = bone.local_matrix * bone.parent.world_matrix

Going the other way, from world space into local space, you concatenate the bone's world matrix with the inverse of the parent's world matrix:

bone.local_matrix = bone.world_matrix * bone.parent.world_matrix.inverted()

The pose bone matrix_basis is computed from the difference between the bind pose and the animated pose:

matrix_basis = bind_pose_matrix.inverted() * animated_bone_matrix

As you mentioned, Blender stores animations relative to the bind pose (in the pose bone matrix_basis). Both the edit bone and pose bone matrices are in local space in Blender. If your bones are all "flat" and not in a hierarchy you don't have to worry about this step, since then all matrices are in world space.

Let's assume you have two armatures, A and B, with different bind poses but otherwise compatible skeletons (same bone lengths and hierarchy, etc). You also have an animation X, given for armature A. Now you want to transfer poses from animation X onto armature B. The easiest way is to take the final position of each bone in armature A and use that exact same position for the corresponding bone in armature B.

Compute the world space matrices for each bone in armature A (you may have this from your input format already):

a.world_matrix = a.local_matrix * a.parent.world_matrix

Then pretend that these matrixes belong to armature B instead. Compute new local space matrices for armature B. These steps are important, since you can't reuse armature A's local matrices for armature B, if the hierarchies differ, but the absolute matrices should still be okay.

b.local_matrix = a.world_matrix * b.parent.world_matrix.inverted()

Now you can compute a new matrix_basis for each bone in armature B by comparing the local space bone matrix in its animated pose to the bind pose:

pose_bone_b.matrix_basis = edit_bone_b.matrix.inverted() * b.local_matrix.

\$\endgroup\$
6
  • \$\begingroup\$ What do you mean by "matrix_basis"? "The easiest way is to take the final position of each bone in armature A", what does "final position" mean here? \$\endgroup\$
    – user17402
    Jun 24, 2012 at 18:53
  • \$\begingroup\$ And BTW, edit bone matrix is in world space in Blender, at least in 2.49. You can get the "BONESPACE" matrix as well, but that value is read-only. And would like to know what to do with the "pose_bone_b.matrix_basis". Thanks. \$\endgroup\$
    – user17402
    Jun 25, 2012 at 10:28
  • 1
    \$\begingroup\$ The matrix_basis is the transform required to move a bone from the bind pose to the animated pose (animated_matrix = rest_pose_matrix * matrix_basis). \$\endgroup\$
    – ccxvii
    Jun 25, 2012 at 12:02
  • \$\begingroup\$ Edit bone matrices are difficult in Blender. I haven't found a way to directly set them. You have to create them with the head/tail/roll. To convert a matrix into a blender edit bone I have to duplicate the C code algorithm Blender uses to convert between head/tail/roll and matrices. If you know a better way, please let me know! \$\endgroup\$
    – ccxvii
    Jun 25, 2012 at 12:04
  • \$\begingroup\$ Excuse my noobiness, but what should we do with matrix_basis? You can set EditBone matrices by doing armature.makeEditable() and then armature.bone['name'].matrix = mymatrix. Though if you have a format which only uses matrices it can look weird if the modeller it was made with doesn't visualize bone angles. In that case bones can look wrong by 90 degrees on some axis. At least that's what I've heard. \$\endgroup\$
    – user17402
    Jun 25, 2012 at 12:26
0
\$\begingroup\$

I just tested the above answerer's solution and it works, but only if your new Armature doesn't have a bone hierarchy (parent/child bones).

Here's how exactly to do it in Blender (2.49):

matrix_basis = old_armature.bones[bone_name].copy().invert() * old_animation_matrix # old_animation_matrix should be the one from the file
new_animation_matrix = new_armature.bones[bone_name].matrix['ARMATURESPACE'].copy() * matrix_basis
pose.bones[bonename].localMatrix = new_animation_matrix * new_armature.bones[bonename].matrix['ARMATURESPACE'].copy().invert()

But if you do this for an Armature which has parent/child bones, then animations will screw up as assigning animations to parent bone will make the child bones transform as well.

There must be a solution for that case as well.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .