Dev:IT/2.5/Py/Scripts/Cookbook/Code snippets/Armatures

提供: wiki
移動先: 案内検索

Armature

Armatura

Questo programma crea un'armatura. Code Snippets Armature.png

#---------------------------------------------------
# File armature.py
#---------------------------------------------------
import bpy, math
from mathutils import Vector, Matrix

def createRig(name, origin, boneTable):
    # Create armature and object
    bpy.ops.object.add(
        type='ARMATURE', 
        enter_editmode=True,
        location=origin)
    ob = bpy.context.object
    ob.show_x_ray = True
    ob.name = name
    amt = ob.data
    amt.name = name+'Amt'
    amt.show_axes = True
    
    # Create bones
    bpy.ops.object.mode_set(mode='EDIT')
    for (bname, pname, vector) in boneTable:        
        bone = amt.edit_bones.new(bname)
        if pname:
            parent = amt.edit_bones[pname]
            bone.parent = parent
            bone.head = parent.tail
            bone.use_connect = False
            (trans, rot, scale) = parent.matrix.decompose()
        else:
            bone.head = (0,0,0)
            rot = Matrix.Translation((0,0,0))	# identity matrix
        bone.tail = Vector(vector) * rot + bone.head
    bpy.ops.object.mode_set(mode='OBJECT')
    return ob
    
def poseRig(ob, poseTable):
    bpy.context.scene.objects.active = ob
    bpy.ops.object.mode_set(mode='POSE')
        
    for (bname, axis, angle) in poseTable:
        pbone = ob.pose.bones[bname]
        # Set rotation mode to Euler XYZ, easier to understand
        # than default quaternions
        pbone.rotation_mode = 'XYZ'
        # Documentation bug: Euler.rotate(angle,axis):
        # axis in ['x','y','z'] and not ['X','Y','Z']
        pbone.rotation_euler.rotate_axis(axis, math.radians(angle))
    bpy.ops.object.mode_set(mode='OBJECT')
    
def run(origo):
    origin = Vector(origo)
    # Table of bones in the form (bone, parent, vector)
    # The vector is given in local coordinates
    boneTable1 = [
        ('Base', None, (1,0,0)),
        ('Mid', 'Base', (1,0,0)),
        ('Tip', 'Mid', (0,0,1))
    ]
    bent = createRig('Bent', origin, boneTable1)

    # The second rig is a straight line, i.e. bones run along local Y axis
    boneTable2 = [
        ('Base', None, (1,0,0)),
        ('Mid', 'Base', (0,0.5,0)),
        ('Mid2', 'Mid', (0,0.5,0)),
        ('Tip', 'Mid2', (0,1,0))
    ]
    straight = createRig('Straight', origin+Vector((0,2,0)), boneTable2)
    
    # Pose second rig
    poseTable2 = [
        ('Base', 'X', 90),
        ('Mid2', 'Z', 45),
        ('Tip', 'Y', -45)
    ]
    poseRig(straight, poseTable2)
    
    # Pose first rig
    poseTable1 = [
        ('Tip', 'Y', 45),
        ('Mid', 'Y', 45),
        ('Base', 'Y', 45)
    ]
    poseRig(bent, poseTable1)
    
if __name__ == "__main__":
    run((0,5,0))

Rigged mesh

Questo programma aggiunge un'armatura a una mesh. L'armatura ha tre ossa (Base, Mid, Tip) e vincoli (constraints):

  1. Un vincolo IK. Mid -> Tip
  2. Un vincolo Stretch To. Mid -> Tip
  3. Un vincolo Copy Rotation. Base -> Tip

La mesh è deformata dall'armatura. Quindi un modificatore armatura e i corrispondenti vertex group sono creati. Code Snippets RiggedMesh.png

#----------------------------------------------------------
# File rigged_mesh.py
#----------------------------------------------------------
import bpy, mathutils

def createArmature(origin):
    # Create armature and object
    amt = bpy.data.armatures.new('MyRigData')
    rig = bpy.data.objects.new('MyRig', amt)
    rig.location = origin
    rig.show_x_ray = True
    amt.show_names = True
    # Link object to scene
    scn = bpy.context.scene
    scn.objects.link(rig)
    scn.objects.active = rig
    scn.update()

    # Create bones
#next two lines by PKHG SVN 36504 W32
    bpy.ops.object.editmode_toggle()
#    bpy.ops.object.mode_set(mode='EDIT')
#original does not work??!!    bpy.ops.object.mode_set(mode='EDIT')
    base = amt.edit_bones.new('Base')
    base.head = (0,0,0)
    base.tail = (0,0,1)

    mid = amt.edit_bones.new('Mid')
    mid.head = (0,0,1)
    mid.tail = (0,0,2)
    mid.parent = base
    mid.use_connect = True

    tip = amt.edit_bones.new('Tip')
    tip.head = (0,0,2)
    tip.tail = (0,0,3)

    # Bone constraints. Armature must be in pose mode.
    bpy.ops.object.mode_set(mode='POSE')

    # IK constraint Mid -> Tip
    pMid = rig.pose.bones['Mid']
    cns1 = pMid.constraints.new('IK')
    cns1.name = 'Ik'
    cns1.target = rig
    cns1.subtarget = 'Tip'
    cns1.chain_count = 1

    # StretchTo constraint Mid -> Tip with influence 0.5
    cns2 = pMid.constraints.new('STRETCH_TO')
    cns2.name = 'Stretchy'
    cns2.target = rig
    cns2.subtarget = 'Tip'
    cns2.influence = 0.5
    cns2.keep_axis = 'PLANE_X'
    cns2.volume = 'VOLUME_XZX'

    # Copy rotation constraints Base -> Tip
    pBase = rig.pose.bones['Base']
    cns3 = pBase.constraints.new('COPY_ROTATION')
    cns3.name = 'Copy_Rotation'
    cns3.target = rig
    cns3.subtarget = 'Tip'
    cns3.owner_space = 'WORLD'
    cns3.target_space = 'WORLD'

    bpy.ops.object.mode_set(mode='OBJECT')
    return rig

def createMesh(origin):
    # Create mesh and object
    me = bpy.data.meshes.new('Mesh')
    ob = bpy.data.objects.new('MeshObject', me)
    ob.location = origin
    # Link object to scene
    scn = bpy.context.scene
    scn.objects.link(ob)
    scn.objects.active = ob
    scn.update()

    # List of vertex coordinates
    verts = [
        (0.5, 0.5,0), (0.5,-0.5,0), (-0.5,-0.5,0), (-0.5,0.5,0),
        (0.5,0.5,1), (0.5,-0.5,1), (-0.5,-0.5,1), (-0.5,0.5,1),
        (-0.5,0.5,2), (-0.5,-0.5,2), (0.5,-0.5,2), (0.5,0.5,2),
        (0.5,0.5,3), (0.5,-0.5,3), (-0.5,-0.5,3), (-0.5, 0.5,3)
    ]
    # List of faces.
    faces = [
        (0, 1, 2, 3),
        (0, 4, 5, 1),
        (1, 5, 6, 2),
        (2, 6, 7, 3),
        (4, 0, 3, 7),
        (4, 7, 8, 11),
        (7, 6, 9, 8),
        (6, 5, 10, 9),
        (5, 4, 11, 10),
        (10, 11, 12, 13),
        (9, 10, 13, 14),
        (8, 9, 14, 15),
        (11, 8, 15, 12),
        (12, 15, 14, 13)
    ]

    # Create mesh from given verts, edges, faces. Either edges or
    # faces should be [], or you ask for problems
    me.from_pydata(verts, [], faces)

    # Update mesh with new data
    me.update(calc_edges=True)
    return ob

def skinMesh(ob, rig):
    # List of vertex groups, in the form (vertex, weight)
    vgroups = {}
    vgroups['Base'] = [
        (0, 1.0), (1, 1.0), (2, 1.0), (3, 1.0),
        (4, 0.5), (5, 0.5), (6, 0.5), (7, 0.5)]
    vgroups['Mid'] = [
        (4, 0.5), (5, 0.5), (6, 0.5), (7, 0.5),
        (8, 1.0), (9, 1.0), (10, 1.0), (11, 1.0)]
    vgroups['Tip'] = [(12, 1.0), (13, 1.0), (14, 1.0), (15, 1.0)]

    # Create vertex groups, and add verts and weights
    # First arg in assignment is a list, can assign several verts at once
    for name, vgroup in vgroups.items():
        grp = ob.vertex_groups.new(name)
        for (v, w) in vgroup:
            grp.add([v], w, 'REPLACE')

    # Give mesh object an armature modifier, using vertex groups but
    # not envelopes
    mod = ob.modifiers.new('MyRigModif', 'ARMATURE')
    mod.object = rig
    mod.use_bone_envelopes = False
    mod.use_vertex_groups = True

def run(origin):
    rig = createArmature(origin)
    ob = createMesh(origin)
    skinMesh(ob, rig)

    # Move and rotate the tip bone in pose mode
    bpy.context.scene.objects.active = rig
    bpy.ops.object.mode_set(mode='POSE')
    ptip = rig.pose.bones['Tip']
    ptip.location = (0.2,-0.5,0)
    rotMatrix = mathutils.Matrix.Rotation(0.6, 3, 'X')
    ptip.rotation_quaternion = rotMatrix.to_quaternion()

if __name__ == "__main__":
    run((0,0,0))

Edit mode versus pose mode

Gli attributi delle ossa che agiscono sulla posizione di riposo di un'armatura (head, tail, roll, parent, use connect, etx) sono disponibili solo in Edit Mode (usa un osso in ob.data.edit_bones), mentre gli attributi che coinvolgono il posizionamento richiedono che l'armatura sia in Pose Mode (usa un osso in ob.pose.bones). Per quanto ne so, l'unico modo per passare da Edit Mode a Pose Mode e viceversa, è con una chiamata a un operatore

bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.object.mode_set(mode='POSE')

Dal momento che gli operatori agiscono sull'oggetto attivo, ci dobbiamo assicurare che l'oggetto corretto è attivo impostando bpy.context.scene.objects.active.

Questo programma copia l'angolo roll da un rig (chiamato 'SrcRig') a un altro rig (chiamato 'TrgRig'). Entrambe le armature devono avere lo stesso numero di ossa con nomi identici. Code Snippets CopyRoll.png

#----------------------------------------------------------
# File copy_roll.py
#----------------------------------------------------------
import bpy

def copyRolls(src, trg):
    rolls = {}

    bpy.context.scene.objects.active = src
    bpy.ops.object.mode_set(mode='EDIT')
    for eb in src.data.edit_bones:
        rolls[eb.name] = eb.roll
    bpy.ops.object.mode_set(mode='POSE')

    bpy.context.scene.objects.active = trg
    bpy.ops.object.mode_set(mode='EDIT')
    for eb in trg.data.edit_bones:
        oldRoll = eb.roll
        eb.roll = rolls[eb.name]
        print(eb.name, oldRoll, eb.roll)
    bpy.ops.object.mode_set(mode='POSE')

objects = bpy.context.scene.objects
copyRolls(objects['SrcRig'], objects['TrgRig'])