1
$\begingroup$

Consider the following armature:

Simple armature with two bones.

Two important notes: first, I'm using local space for posing, and two, the arm has zero bone roll. When I attempt to rotate the arm and restrict to the X, Y, and Z axes, the axes are as follows. Call this image Axes A (for easier reference later).

X, Y, and Z pose axes (in local space).

This all makes perfect sense so far. Next, I move the tail of the arm down, as shown here:

Same armature, but with the arm's tail lowered.

In doing so, Blender automatically rolls the arm (in this case, by 27 degrees). These are the resulting X, Y, and Z axes. As expected, X is unaffected, while Y and Z roll forward. Call this image Axes B.

Rolled axes after lowering the arm's tail position.

Finally, I reset the arm's roll to zero. Bizarrely, doing this appears to rotate the arm back around its local Y axis.

Tilted bone with zero roll appears skewed around its local Y axis.

These are the local XYZ axes for the arm now (shown from a different angle to emphasize that they've become skewed). Call this image Axes C.

Skewed axes from a sharp angle.

I'm using Blender to export meshes, armatures, and actions (animations) for video game development. As part of that process, my engine must be able to compute accurate rotation matrices per bone, where "accurate" means "matching Blender". That problem, then, is the crux of my question here: how does Blender compute local rotation axes for bones?

The approach I'm currently using is as follows:

  1. Compute each bone's axis (i.e. normalized vector from head to tail).
  2. Compute a quaternion rotation from unit Y (i.e. [0, 1, 0]) to that axis. This effectively rotates the XYZ coordinate space.
  3. Multiply that quaternion by another "roll" quaternion, computed with quat.FromAxisAngle(axis, roll).

This approach has worked correctly for many bones, but fails for angled bones like the arm shown in Axes B and Axes C. The reason is that, by the steps above, the angled arm with zero roll should result in Axes B (X axis unaffected, with Y and Z rolled forward). The only way to achieve the axes shown in Axes C would be to roll the coordinate space, which is why I'm baffled as to how Blender uses those axes while the arm's roll is supposedly zero.

I even investigated Blender's C source code directly. Specifically, I adapted the vec_roll_to_mat3_normalized function from armature.c. Using that function actually did exactly what I described in the previous paragraph: the arm's local axes matched Axes B, not Axes C. Of course it's possible I'm referencing the wrong function, but Blender's current behavior doesn't even make sense to me conceptually (why does Blender assign the arm in Axes B a roll 27° when it hasn't actually rolled around its local axis?).

Hoping for some clarification on this topic.

Edit:

It was suggested that I enable the "Axes" checkbox (under Object Data Properties -> Viewport Display). As shown below, this visualization shows the same axes from my examples above.

Repeated axes examples, but with the Axes viewport display option enabled.

$\endgroup$
2
  • 2
    $\begingroup$ (1) Turn on "Axes" under "Viewport Display" in the Properties editor to see the actual bone axes. I'm not sure "Local" does what you expect. (2) For export, just ask Blender what the bone-to-arma matrix ends up being with bone.matrix_local, don't try to redo it's calculation yourself. $\endgroup$
    – scurest
    Dec 18, 2021 at 10:11
  • $\begingroup$ Appreciate your response. I've added an additional screenshot. With "Axes" enabled, the displayed axes don't appear different. How does "Local" work if not in the way I've described? (rotating the XYZ coordinate space based on the bone's axis and roll) $\endgroup$
    – Grimelios
    Dec 18, 2021 at 18:42

3 Answers 3

2
+100
$\begingroup$

It's important that there's nothing that makes bones use some weird coordinate space where the base orientation is Y-up, unlike everything else. They exist in the exact same space as anything else. It's just that when you add new ones, they come out of the tin pre-rotated for you. I think you understand this already, but it's worth mentioning for others.

To find out the roll of a bone in Blender, without entering edit mode on that armature:

  1. The space that matters is pose space, not bone local space or world space. Reset or apply any rotation on the armature object so that you can use objects in world space that is the same as the armature's pose space.

  2. Create two empties or whatever at the world origin, without any transformations. Parent one to the other.

  3. With the parent, copy the location of your bone's head, then damped track the bone's tail.

  4. With your child, locked track (lock Y, track Z) a point at Z=1 in the bone's local space. In order to create this point, amidst changing rolls, I made a linked duplicate of the armature, with the bone posed with a z=1 transform.

  5. The rotational difference between parent and child is now equal to the roll of the bone.

Here is the file I used to test this hypothesis out:

I've created a custom property on the armature to display the roll of the bone, just to make it easier to update and check. I'm checking the roll of Bone.001. There are two linked armatures, for reasons described above.

There are two poles associated with this technique, when the bone is pointing in +- world Y. At these points, we need to say something arbitrary, which is that a roll of 0 is associated with the Z axis pointing in world Z.

This may seem like a weird way to do things. It has the disadvantage of needing poles. It has the advantage that it is a single axis measure that doesn't depend in any way on Euler order, or, in some orders, that it doesn't change the position of the tail when you adjust it. This measure of bone roll is probably the purest measure of roll, the closest measure to what we intuit as twist, roll, Y axis rotation (although our intuitions about this usually need to be developed a bit.)

In terms of steps you're going through, a damped track is an axis-angle rotation about the cross-product of the from and to vectors, and a locked track is an axis-angle rotation, in this case, in the Y axis of the corresponding object. If you want to find where Z+1 on some bone is, you run 0,0,1 (,1) through its matrix. If you want to find the angle between two vectors, you can take the arcos of the dot product of those two vectors. The angle is positive or negative depending on which way the cross-products of those two vectors point, and depending on the coordinate system (handedness) you are using.

$\endgroup$
1
  • $\begingroup$ I awarded the bounty to this answer because it lead me in the right direction to eventually solve my problem, but didn't accept the answer because it doesn't actually answer the question I asked directly. My intent in not accepting is to avoid confusing future readers. $\endgroup$
    – Grimelios
    Jan 5, 2022 at 4:59
0
$\begingroup$

Unfortunately the answer to your question is spread across several file in blenders/source/blender/editors/armatures; isn't trivial; and isn't documented in a single place.

The most interesting bits are in pose_transform.c starting around line 103 Perhaps this answer to a related question (How to make pose bone rotation toward a point?) might be helpful.

$\endgroup$
0
$\begingroup$

take a look at this answer. I've explained whatever you need about matrices in blender

$\endgroup$

You must log in to answer this question.

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