EDIT:
I’ve found the solution.
Quickly what this topic was about:
In new Blender API edit_bone.matrix is read-only, unlike before. So creating bones from transform matrices is not possible, unless writing complex math yourself. We tried porting Blender’s C code for doing that to Python, but it didn’t work 100%. Using the Blender 2.4 code instead worked.
The solution seems to be to do what the old Blender 2.4 API did. So again seems like a bug in Blender 2.6. I’ve tried letting the blender devs know about this enough, my script works and I’m happy.
Code:
def vec_roll_to_mat3(vec, roll):
target = mathutils.Vector((0,1,0))
nor = vec.normalized()
axis = target.cross(nor)
if axis.dot(axis) > 0.0000000001: # this seems to be the problem for some bones, no idea how to fix
axis.normalize()
theta = target.angle(nor)
bMatrix = mathutils.Matrix.Rotation(theta, 3, axis)
else:
updown = 1 if target.dot(nor) > 0 else -1
bMatrix = mathutils.Matrix.Scale(updown, 3)
# C code:
#bMatrix[0][0]=updown; bMatrix[1][0]=0.0; bMatrix[2][0]=0.0;
#bMatrix[0][1]=0.0; bMatrix[1][1]=updown; bMatrix[2][1]=0.0;
#bMatrix[0][2]=0.0; bMatrix[1][2]=0.0; bMatrix[2][2]=1.0;
bMatrix[2][2] = 1.0
rMatrix = mathutils.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
usage:
pos = mymatrix.to_translation()
axis, roll = mat3_to_vec_roll(mymatrix.to_3x3())
bone = armature.edit_bones.new('name')
bone.head = pos
bone.tail = pos + axis
bone.roll = roll
================================================
orig post:
The new Blender API doesn’t have a way to create correct bones from transform matrices, to my surprise.
After days of digging, I found the only way is to use complex matrix math yourself, for example port the Blender C functions to Python.
The functions are:
def vec_roll_to_mat3(vec, roll):
target = mathutils.Vector((0,1,0))
nor = vec.normalized()
axis = target.cross(nor)
if axis.dot(axis) > 0.0000000001: # this seems to be the problem for some bones, no idea how to fix
axis.normalize()
theta = target.angle(nor)
bMatrix = mathutils.Matrix.Rotation(theta, 3, axis)
else:
updown = 1 if target.dot(nor) > 0 else -1
bMatrix = mathutils.Matrix.Scale(updown, 3)
rMatrix = mathutils.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
usage:
pos = mymatrix.to_translation()
axis, roll = mat3_to_vec_roll(mymatrix.to_3x3())
bone = armature.edit_bones.new('name')
bone.head = pos
bone.tail = pos + axis
bone.roll = roll
This is still not a 100% correct solution, some bones are still wrong compared to Blender 2.4. The problem seems to come from this line:
if axis.dot(axis) > 0.0000000001:
How to solve it? I don’t know and looks like even Ton isn’t sure:
https://svn.blender.org/svnroot/bf-blender/trunk/blender/source/blender/blenkernel/intern/armature.c
But there must be solution, otherwise how does Blender 2.4 do it correctly, when it uses the same bone base, tip and roll thing?
So help me get this fixed, it will make my importer work properly, as well as Blender itself.