assigning matrices to EditBones in 2.5/2.6 [solution]

I hit the same problem with read only matrix for edit bone.
So far this works for me:


        boneObj.head = Vector([0,0,0])       
        boneObj.tail = Vector([0,tailVector,0])
                
        boneObj.transform( boneRotMatrix)
        boneObj.translate( boneTranslationVectorFromMatrix)

Keep bone space rotation as separate matrix. Armature space to bone space is rotation of 90 about x axis if I remember correctly. Set up the space conversion matrix and use it in your your final calculation, roll_matrix*bone_space_matrix (since column major).

This should eliminate the need for the cross and the dot check. As mentioned your cross is zero if your vector is parallel to your bone space up vector (0,1,0). Your dot of normalized vectors represents a projection which is also zero if vectors are perpendicular.

Save your bonespace conversion until all other calculations are done. Your bonespace conversion is just a rotation of 90 degrees about the x axis. So calculate your roll matrix then do the bonespace conversion. roll_matrix*bonespace_matrix (since column major).

This should eliminate the need for the cross operation and the dot check. As mention before your cross will be zero if your bonespace up vector (0,1,0) (target) is parallel to the given vector. Your normalized dot operation represents a projection and will be zero if the vectors are perpendicular.

For anyone looking for a solution to this problem, I’m posting this wherever I found it referred to. I have ported this newer upated internal C code of blender into python: https://developer.blender.org/T39470 I have not extensively tested it, but it seems correct, produces good results and it was super easy to port. It is used in the exact same way as before (see the above question), should be more accurate and get all (?) bone orientations right!

    def vec_roll_to_mat3(vec, roll):
        #port of the updated C function from armature.c
        #https://developer.blender.org/T39470
        #note that C accesses columns first, so all matrix indices are swapped compared to the C version
        
        nor = vec.normalized()
        THETA_THRESHOLD_NEGY = 1.0e-9
        THETA_THRESHOLD_NEGY_CLOSE = 1.0e-5
        
        #create a 3x3 matrix
        bMatrix = mathutils.Matrix().to_3x3()

        theta = 1.0 + nor[1];

        if (theta > THETA_THRESHOLD_NEGY_CLOSE) or ((nor[0] or nor[2]) and theta > THETA_THRESHOLD_NEGY):

            bMatrix[1][0] = -nor[0];
            bMatrix[0][1] = nor[0];
            bMatrix[1][1] = nor[1];
            bMatrix[2][1] = nor[2];
            bMatrix[1][2] = -nor[2];
            if theta > THETA_THRESHOLD_NEGY_CLOSE:
                #If nor is far enough from -Y, apply the general case.
                bMatrix[0][0] = 1 - nor[0] * nor[0] / theta;
                bMatrix[2][2] = 1 - nor[2] * nor[2] / theta;
                bMatrix[0][2] = bMatrix[2][0] = -nor[0] * nor[2] / theta;
            
            else:
                #If nor is too close to -Y, apply the special case.
                theta = nor[0] * nor[0] + nor[2] * nor[2];
                bMatrix[0][0] = (nor[0] + nor[2]) * (nor[0] - nor[2]) / -theta;
                bMatrix[2][2] = -bMatrix[0][0];
                bMatrix[0][2] = bMatrix[2][0] = 2.0 * nor[0] * nor[2] / theta;

        else:
            #If nor is -Y, simple symmetry by Z axis.
            bMatrix = mathutils.Matrix().to_3x3()
            bMatrix[0][0] = bMatrix[1][1] = -1.0;

        #Make Roll matrix
        rMatrix = mathutils.Matrix.Rotation(roll, 3, nor)
        
        #Combine and output result
        mat = rMatrix * bMatrix
        return mat

    def mat3_to_vec_roll(mat):
        #this hasn't changed
        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