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)
else:
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;
}
else
{
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.identity();
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);
this.mat.rotate(quat);
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!
euler
orientation as quaternion, inverting it and converting to euler, and it seemed to workeuler = mat.to_euler()
as described here! I haven't tested it with different combinations yet of conversion matrices yet, though. $\endgroup$