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$

2 Answers 2

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$
1
  • $\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
    Commented Oct 27, 2013 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$

You must log in to answer this question.

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