I'm trying to export armature bone matrices (relative to parent), but fail miserably, because sometimes rotations and translations are getting applied to different axes, in comparison to Blender's preview.

I know there are already questions on Blender SE about exporting bone matrices (like this one), but most of these questions are dealing with exporting frame-by-frame matrices based on pose bone data (PoseBone structure), meanwhile I'm trying to export those matrices from Bone (because I'm using keyframes for animation).

Here is the code I'm using to export the matrices, at the moment:

# Write armature
def write_armature(fw, armature, global_matrix):
    fw('# Armature data\n')
    fw('arm_name %s\n' % armature.data.name)

    if armature.animation_data is not None and armature.animation_data.action is not None:
        fw ('arm_action %s\n' % name_compat(armature.animation_data.action.name))

    for bone in armature.data.bones:
        if bone.parent is not None:
            parent = name_compat(bone.parent.name)
            parent = ''

        mat = calc_matrix(bone, armature, global_matrix)
        euler = mat.to_euler()

        tail = bone.matrix_local.copy()
        tail.translation = bone.tail_local
        tail = global_matrix * armature.matrix_world * tail

        x, y, z = (mat.translation[0], mat.translation[1], mat.translation[2])
        vx, vy, vz = (tail.translation[0], tail.translation[1], tail.translation[2])

        ox = euler.x
        oy = euler.y
        oz = euler.z

        fw('arm_bone %s %s %f %f %f %f %f %f %f %f %f\n' % (name_compat(bone.name), parent, x, y, z, vx, vy, vz, ox, oy, oz))

def calc_matrix(bone, armature, global_matrix):
    return global_matrix * armature.matrix_world * bone.matrix_local

Where global_matrix is a matrix responsible for converting to different coordinates system (like from XZY to XYZ).

Currently, I export bone's head and orientation in euler angles (plus bone's tail, but that's for debug rendering). I know it's not relative-to-parent approach, but I wasn't been able to make it work otherwise.

Here is how I multiply this information in the game:

public Matrix4f computeMatrix(Matrix4f m)
    float x = this.x;
    float y = this.y;
    float z = this.z;

    if (this.parentBone == null)
        x += this.head.x;
        y += this.head.y;
        z += this.head.z;
        x += this.head.x - this.parentBone.head.x;
        y += this.head.y - this.parentBone.head.y;
        z += this.head.z - this.parentBone.head.z;

    this.mat.translate(x, y, z);
    this.mat.scale(this.scaleX, this.scaleY, this.scaleZ);

    Quaternionf quat = new Quaternionf();
    Vector3f orient = this.orient;

    quat.rotateLocal(orient.x, orient.y, orient.z);
    quat.rotateLocal(this.rotateX, this.rotateY, this.rotateZ);
    quat.rotateLocal(-orient.x, -orient.y, -orient.z);


    if (this.parentBone != null)
        m = this.parentBone.computeMatrix(m);

    return m.mul(this.mat);

I hope you could help me with this, because I've been bashing my head with this for half of month, haha. I'm using Blender 2.78, by the way.

Thank you for attention!

  • $\begingroup$ I tried doing exporting euler orientation as quaternion, inverting it and converting to euler, and it seemed to work euler = mat.to_euler() as described here! I haven't tested it with different combinations yet of conversion matrices yet, though. $\endgroup$
    – McHorse
    Feb 1, 2018 at 20:30

1 Answer 1


After another few days of frustration, I finally was able to make it work. Basically, I gave up on exporting euler angles, head and tail, instead, I just exported the bone matrix itself like this:

mat = global_matrix * armature.matrix_world * bone.matrix_local

And in the game's code, I use this exported matrix for computing inverse and for relative to parent matrices (by multiplying bone's matrix by inverse of parent's).


You must log in to answer this question.

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