5
$\begingroup$

I want to calculate a rotation matrix from two vectors, in order to check the steps are correct I have setup 3 armatures.

  1. A reference bone (arm_org),
  2. One that is directly rotated by applying a rotation matrix (arm_rot) and
  3. Another (arm_mat) that should be rotated by a matrix that has been calculated from the rotational difference of the 1st and 2nd.

Why are the bones tails not updated? Unfortunatly ArmatureEditBones isn't well documented. I also tried to set the active bone. But the changes are not reflected in the 3D view. Only the currently selected bone can manipulated.

def setBone( name, v ):
    arm = bpy.data.armatures[ name ]
    print("set arm=" + name + " v=" + str( v ))
    for bone in arm.edit_bones: 
        print( bone.tail )
        bone.tail= v

def getBone( name ):
    #bpy.context.scene.objects.active = bpy.data.objects[ name ] 
    arm = bpy.data.armatures[ name ]
    print("get arm=" + name + " arm=" + str( arm ))
    v = None
    for bone in arm.edit_bones: 
        print( bone.tail )
        v = bone.tail
    return v

def vec():
    mx = mathutils.Matrix.Rotation(math.radians(15.0), 4, 'X')
    my = mathutils.Matrix.Rotation(math.radians(30.0), 4, 'Y')
    mz = mathutils.Matrix.Rotation(math.radians(45.0), 4, 'Z')
    m = mx * my * mz
    print( "m : " + str (m ))

    # get and duplicate original tail pos of reference bone    
    vo = getBone("arm_org")
    if vo == None:
        print("arm_org not found")
        return
    setBone("arm_mat", vo.copy() )
    setBone("arm_rot", vo.copy() )
    vr = vo * m
    setBone("arm_rot", vr )

    # recalc and apply matrix from rotational difference
    q = vo.rotation_difference( vr )
    mr = q.to_matrix()
    mr.resize_4x4()
    vm = vo * mr
    setBone( "arm_mat" , vm )
    print("mr: " + str(mr))
    print( "-" * 20 )

vec()

Blend file (works only in edit mode with arm_org selected)

$\endgroup$
5
$\begingroup$

To change bones in an Armature, you need to use the EditBones, and the Armature has to be in editmode. As only a single object can be in editmode at a time in Blender, you need to change mode if you wanna access different Armature's EditBones.

Your script doesn't change mode and the .edit_bones property will be None if the owning Armature is in Object or Pose Mode. Thus for bone in arm.edit_bones will end immediately (nothing to loop over) and v remains None (the initial value).

I tried to make your script working, and it will have a visual effect now, not sure if it's what you want though:

import bpy
from bpy import context
import mathutils
from mathutils import Vector,Quaternion
import math

def dump2(obj, level=0):
   for attr in dir(obj):
       print( "obj.%s = %s" % (attr, getattr(obj, attr)))

def setBone(name, v):
    arm = bpy.data.objects.get(name)
    if armature_prepare(arm):
        print("set arm=" + name + " v=" + str(v))
        for bone in arm.data.edit_bones: 
            print( bone.tail )
            bone.tail= v

def armature_prepare(arm):
    if arm is not None and bpy.ops.object.mode_set.poll():
        bpy.ops.object.mode_set(mode='OBJECT')
        bpy.ops.object.select_all(action='DESELECT')
        bpy.context.scene.objects.active = arm
        arm.select = True
        bpy.ops.object.mode_set(mode='EDIT')
        return True
    return False

def getBone(name):
    v = None
    arm = bpy.data.objects.get(name)

    if armature_prepare(arm):
        print("get arm=" + name + " arm=" + str(arm))
        for bone in arm.data.edit_bones: 
            print(bone.tail)
            v = bone.tail
    return v

def vec():
    mx = mathutils.Matrix.Rotation(math.radians(15.0), 4, 'X')
    my = mathutils.Matrix.Rotation(math.radians(30.0), 4, 'Y')
    mz = mathutils.Matrix.Rotation(math.radians(45.0), 4, 'Z')
    m = mx * my * mz
    print( "m : " + str (m ))

    # get and duplicate original tail pos of reference bone    
    vo = getBone("arm_org")
    if vo == None:
        print("arm_org not found")
        return
    setBone("arm_mat", vo.copy())
    setBone("arm_rot", vo.copy())
    vr = vo * m
    setBone("arm_rot", vr )

    # recalc and apply matrix from rotational difference
    q = vo.rotation_difference( vr )
    mr = q.to_matrix()
    mr.resize_4x4()
    vm = vo * mr
    setBone("arm_mat" , vm)
    print("mr: " + str(mr))
    print("-" * 20)

vec()
$\endgroup$
  • $\begingroup$ Thank so much, do you have an idea why q = vo.rotation_difference( vr ) and therefore the matrix mr shows no difference in rotation (identity matrix) ? $\endgroup$ – stacker Oct 27 '13 at 21:47
1
$\begingroup$

Based on CoDEmanX answer I added a few minor changes to make it fully work:

  • copy() of vectors in order to not share references

  • reverse order of vectors in rotational_difference() because the resulting quaternion was mirrored.

-

import bpy
from bpy import context
import mathutils
from mathutils import Vector, Quaternion
import math

def setBone( name, v ):
    arm = bpy.data.objects.get(name)
    if armature_prepare(arm):
        print("set arm=" + name + " v=" + str( v ))
        for bone in arm.data.edit_bones: 
            print( bone.tail )
            bone.tail= v.copy()

def armature_prepare(arm):
    if arm is not None and bpy.ops.object.mode_set.poll():
        bpy.ops.object.mode_set(mode='OBJECT')
        bpy.ops.object.select_all(action='DESELECT')
        bpy.context.scene.objects.active = arm
        arm.select = True
        bpy.ops.object.mode_set(mode='EDIT')
        return True
    return False

def getBone(name):
    v = None
    arm = bpy.data.objects.get(name)
    if armature_prepare(arm):
        print("get arm=" + name + " arm=" + str( arm ))
        for bone in arm.data.edit_bones: 
            print( bone.tail )
            v = bone.tail.copy()
    return v

def vec():
    mx = mathutils.Matrix.Rotation(math.radians(15.0), 4, 'X')
    my = mathutils.Matrix.Rotation(math.radians(30.0), 4, 'Y')
    mz = mathutils.Matrix.Rotation(math.radians(45.0), 4, 'Z')
    #m = mx * my * mz
    m = mz * my * mx
    print( "m : " + str (m ))

    # get and duplicate original tail pos of reference bone    
    vo = getBone("arm_org")
    vr = vo * m
    setBone("arm_rot", vr )

    # recalc and apply matrix from rotational difference
    print("." * 20 )
    print( "vo=" + str( vo ) + repr( vo ) )
    print( "vr=" + str( vr ) + repr( vr ))
    #q = vo.rotation_difference( vr )
    q = vr.rotation_difference( vo )
    print("q " + str(q))
    mr = q.to_matrix()
    mr.resize_4x4()
    vm = vo * mr
    setBone( "arm_mat" , vm )
    print("mr: " + str(mr))
    print( "-" * 20 )

vec()
$\endgroup$

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.