I need this for a Blender3d script, but you don't really need to know Blender to answer this.

I need to get bone head and tip positions from a transform matrix read from a file. The position of base is the location part of the matrix, length of the bone (distance from base to tip) is the scale, position of the tip is calculated from the scale (distance from bone base) and rotation part of the matrix.

So how to calculate these?

bone.base([x,y,z]) # x,y,z - floats

You can find the base by multiplying the origin vector (0, 0, 0) by the transform matrix.

The tip question is slightly more in-depth: it really depends on what the canonical direction is that all the bones are defined from - in other words, where does the bone originally rotate from?

One way or another, to get the tip position you multiply a unit vector (for example, in Z this would be (0, 0, 1)) by the transformation matrix.

Bear in mind as well that your bones are probably hierarchical and the position will be relative to its parent joint - you will need to multiply one particular joint's matrix by its parent (and the parent joint by its parent, and so on) to get a global, world position.

  • \$\begingroup\$ No worries about hierarchy - isn't any. So what about the tip position? I think for the lenght you assumed bone's original rotation is facing the z-axis. Let's assume the same when calculating tip position. Now what is the actual formula? \$\endgroup\$ – ddos Jul 10 '12 at 5:33
  • \$\begingroup\$ I guess I don't need to get the size because by setting tip and base position that is set automatically. \$\endgroup\$ – ddos Jul 10 '12 at 5:44
  • 1
    \$\begingroup\$ That should be the answer. If the scale denotes the length, and rotation the direction, then multiplying a unit vector (Z if all bones originally faced Z, X if X, and so on) then the resulting position will be the tip's position. I'll modify the answer to correct that. \$\endgroup\$ – Matt Kemp Jul 10 '12 at 8:10
  • \$\begingroup\$ I just tried this with Blender 2.62 and it doesn't seem to work. I mean it works, the math is correct, but when inspecting a skeleton imported with my importer for Blender 2.4 and then Blender 2.6, you can see the bone roll (rotation relative to itself) is different. The same happens if you do bone.tail = mathutils.Vector((0,1,0)) and then bone.transform(mymatrix). \$\endgroup\$ – user17402 Jul 10 '12 at 16:11
  • \$\begingroup\$ I don't have Blender to hand, but how does it store the initial roll of the bone? If I understand you correctly the bone tip is in the correct place but the orientation is wrong. How does it store this orientation? If it's a normal vector, the solution is simple: multiply the normal by the matrix too (with the W component as zero, so (nX, nY, nZ, 0)). This will apply the rotation (and scale, so you'll probably have to re-normalize) without applying the translation. \$\endgroup\$ – Matt Kemp Jul 10 '12 at 20:13

If you want to preserve the original transform so that Blender's bones recreate the same transform, you will also need to calculate the bone roll.

Blender has a function to calculate the bone head, tail and roll from a transform matrix in C code. I haven't found a convenient way to access this functionality from the Python API, but fortunately the functions are short and can easily be translated into Python.

Below is a direct translation of the functions you need, for Blender 2.62 or later:

def vec_roll_to_mat3(vec, roll):
    target = Vector((0,1,0))
    nor = vec.normalized()
    axis = target.cross(nor)
    if axis.dot(axis) > 0.000001:
        theta = target.angle(nor)
        bMatrix = Matrix.Rotation(theta, 3, axis)
        updown = 1 if target.dot(nor) > 0 else -1
        bMatrix = Matrix.Scale(updown, 3)
    rMatrix = Matrix.Rotation(roll, 3, nor)
    mat = rMatrix * bMatrix
    return mat

def mat3_to_vec_roll(mat):
    vec = mat.col[1]
    vecmat = vec_roll_to_mat3(mat.col[1], 0)
    vecmatinv = vecmat.inverted()
    rollmat = vecmatinv * mat
    roll = math.atan2(rollmat[0][2], rollmat[2][2])
    return vec, roll

pos = transform.to_translation()
axis, roll = mat3_to_vec_roll(transform.to_3x3())

bone.head = pos
bone.tail = pos + axis
bone.roll = roll
  • \$\begingroup\$ Shame it's not part of the Python API. Weird as I didn't need to do this in 2.4. Anyway, will try this right away. Thanks! \$\endgroup\$ – user17402 Jul 10 '12 at 21:04
  • \$\begingroup\$ OK, it works 99%. My characters left hand palm is facing the opposite direction as well as two fingers on the right hand. Weird that only few bones are like this. BTW, do I need to do something similar for animation matrices? I'm having problem with some PoseBone deformations. \$\endgroup\$ – user17402 Jul 10 '12 at 21:11
  • 1
    \$\begingroup\$ For the pose bones you can calculate and set the matrix_basis directly from your animation and rest pose matrices. \$\endgroup\$ – ccxvii Jul 10 '12 at 21:18
  • \$\begingroup\$ Yeah OK. I'm also having trouble settings poseMatrices when bones are parented. But that's a separate question I'll post tomorrow. Thanks for your help. BTW, I fixed those few bones by increasing the floating point precision here: if axis.dot(axis) > 0.0000000001 \$\endgroup\$ – user17402 Jul 10 '12 at 21:21
  • \$\begingroup\$ OK, I was wrong! That didn't solve it, still have problems (wrong angles in-game) with some bones. Help. BTW, please link me to the original C code if you can, I always get lost in program sourcecodes. \$\endgroup\$ – user17402 Jul 11 '12 at 7:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.