0
$\begingroup$

I have an armature in my .blend file found here:

I'm trying to create a mesh around it. I'm using the below script which generated a mesh around it but the mesh is created depending on Pose position not rest position, I want it depending on rest position instead. For some reason it's also mirrored around the Z axis (as if rotated 180 degrees) and also up high in the Z direction, not sure why?

This is my armature and the mesh after running the below script:

Pose position:

enter image description here

Rest position:

enter image description here

When disabling the armature modifier:

enter image description here

So as shown above the mesh is created based on the POSE position not the REST position, I want it to be created depending on the rest position. Also as you can see the mesh is kinda created on the mirrored Z. If anyone could please advise what is wrong and how to fix my script.

import bpy
import mathutils 
def CreateMesh():
    context = bpy.context
    bpy.ops.object.select_all(action='DESELECT')
    MyArmature = bpy.data.objects["MyRig"]  
    MyArmature.select = True
    bpy.context.scene.objects.active = MyArmature
    bpy.context.object.data.pose_position = 'REST'
    scene = context.scene
    selected = context.selected_objects
    processedAnything = False
    if len(selected) > 0:
        print ("selected objects present, processing selection")
        for obj in selected:
            processedAnything |= processObject(context, obj)
        pass
    else:
        print ("processing active object")
        obj = context.active_object
        processedAnything |= processObject(context, obj)

    if not processedAnything:
        print ("no objects processed")
    bpy.context.object.data.pose_position = 'POSE'


def decomposeMatrix(matrix):
    return [x.to_3d() for x in matrix.transposed()]

def processArmature(context, arm, genVertexGroups = True):
    print("processing armature {0}".format(arm.name))

    if genVertexGroups:
        #because setting pose_position ot 'REST' manually doesn't work for some reason.
        genVertexGroups = arm.data.pose_position == 'REST'

    meshName = arm.name + "_mesh"
    meshData = bpy.data.meshes.new(meshName + "Data")
    meshObj = bpy.data.objects.new(meshName, meshData)
    meshObj.location = arm.location

    scene = context.scene
    scene.objects.link(meshObj)

    armMatrix = arm.matrix_local.copy()

    verts = []
    edges = []
    faces = []
    vertexGroups = {}

    for bone in arm.pose.bones:
        if bone.bone.use_deform == True :
            poseBone = bone
            boneName = bone.name
            armBone = arm.data.bones[boneName]
            #print(poseBone.matrix)
            #print(armBone.matrix)
            #print(boneName)

            boneMatrix = poseBone.matrix
            boneStart = poseBone.head
            boneEnd = poseBone.tail

            decomposedMatrix = decomposeMatrix(boneMatrix)
            #print(decomposedMatrix)
            xSize = armBone.bbone_x
            zSize = armBone.bbone_z
            #print(xSize, zSize)
            xSizeAdd = bone.x_axis
            zSizeAdd = bone.z_axis
            xSizeAdd = decomposedMatrix[0]
            zSizeAdd = decomposedMatrix[2]
            ySizeAdd = decomposedMatrix[1]
            origin = mathutils.Vector((0.0, 0.0, 0.0)) * boneMatrix
            xSizeAdd *= xSize * 30
            zSizeAdd *= zSize * 30
            ySizeAdd *= bone.length * 30


            baseIndex = len(verts)

            verts.append((boneStart - xSizeAdd + zSizeAdd)*armMatrix)
            verts.append((boneStart + xSizeAdd + zSizeAdd)*armMatrix)
            verts.append((boneStart - xSizeAdd - zSizeAdd)*armMatrix)
            verts.append((boneStart + xSizeAdd - zSizeAdd)*armMatrix)
            verts.append((boneEnd - xSizeAdd + zSizeAdd)*armMatrix)
            verts.append((boneEnd + xSizeAdd + zSizeAdd)*armMatrix)
            verts.append((boneEnd - xSizeAdd - zSizeAdd)*armMatrix)
            verts.append((boneEnd + xSizeAdd - zSizeAdd)*armMatrix)

            base = baseIndex
            newFaces = [
                (base+0, base+1, base+3, base+2),
                (base+5, base+4, base+6, base+7),
                (base+1, base+0, base+4, base+5),
                (base+2, base+3, base+7, base+6),
                (base+3, base+1, base+5, base+7),
                (base+0, base+2, base+6, base+4)
                ]
            faces.extend(newFaces)

            if genVertexGroups:
                vertexGroups[boneName] = [(x, 1.0) for x in range(baseIndex, len(verts))]

    meshData.from_pydata(verts, edges, faces)

    if genVertexGroups:
        for name, vertexGroup in vertexGroups.items():
            groupObject = meshObj.vertex_groups.new(name)
            for (index, weight) in vertexGroup:
                groupObject.add([index], weight, 'REPLACE')

        modifier = meshObj.modifiers.new('ArmatureMod', 'ARMATURE')
        modifier.object = arm
        modifier.use_bone_envelopes = False
        modifier.use_vertex_groups = True

    meshData.update()

    return meshObj

def processObject(context, obj):
    if (obj == None):
        return False
    if (obj.type != "ARMATURE"):
        print ("invalid type {0} of object {1}: armature expected".format(obj.type, obj.name))
        return False
    processArmature(context, obj)
    return True

CreateMesh()
$\endgroup$
5
  • $\begingroup$ The calculation seems to be based on poseBone, instead of armBone $\endgroup$
    – lemon
    Commented Mar 1, 2017 at 11:54
  • $\begingroup$ @lemon thanks for your comment. I've updated it to boneMatrix = armBone.matrix boneStart = armBone.head boneEnd = armBone.tail but it gives weird result as shown here pasteall.org/pic/index.php?id=113141 $\endgroup$
    – Tak
    Commented Mar 1, 2017 at 12:00
  • $\begingroup$ Well, if the code is not good, this is another problem... $\endgroup$
    – lemon
    Commented Mar 1, 2017 at 12:01
  • $\begingroup$ @lemon the codes works really well if the armature rest position is the same as pose position except that it is mirrored not sure why. I don't know what is causing it to mess up when the pose and rest position are different that's why I'm asking this question. $\endgroup$
    – Tak
    Commented Mar 1, 2017 at 12:18
  • $\begingroup$ @lemon I just found out that if I applied the rotation of the armature which is 90 degrees around Z the mesh has the correct orientation, not sure why is that? I'd expect this to happen if the armature was rotated by 180 not 90. Still don't know how to create it depending on the rest position. $\endgroup$
    – Tak
    Commented Mar 1, 2017 at 12:24

1 Answer 1

2
$\begingroup$

Following some exchanges on the chat, here is the code from the same principle but using the armature in edit mode in order to get the rest pose information.

To avoid world orientations complexity, the mesh is created with same orientation and scale as the armature.

enter image description here

import bpy
import mathutils 
def CreateMesh():
    context = bpy.context
    bpy.ops.object.select_all(action='DESELECT')
    MyArmature = bpy.data.objects["MyRig"]  
    MyArmature.select = True
    bpy.context.scene.objects.active = MyArmature
    bpy.context.object.data.pose_position = 'REST'
    scene = context.scene
    selected = context.selected_objects
    processedAnything = False
    if len(selected) > 0:
        print ("selected objects present, processing selection")
        for obj in selected:
            processedAnything |= processObject(context, obj)
        pass
    else:
        print ("processing active object")
        obj = context.active_object
        processedAnything |= processObject(context, obj)

    if not processedAnything:
        print ("no objects processed")
    bpy.context.object.data.pose_position = 'POSE'


def decomposeMatrix(matrix):
    return [x.to_3d() for x in matrix.transposed()]

def meshFromArmature( arm ):
    name = arm.name + "_mesh"
    meshData = bpy.data.meshes.new( name + "Data" )
    meshObj = bpy.data.objects.new( name, meshData )
    meshObj.matrix_world = arm.matrix_world.copy()
    return meshObj

def newGeometry( l1, l2, x, z, baseSize, l1Size, l2Size, base ):
    x1 = x * baseSize * l1Size 
    z1 = z * baseSize * l1Size

    x2 = x * baseSize * l2Size 
    z2 = z * baseSize * l2Size

    verts = [
        l1 - x1 + z1,
        l1 + x1 + z1,
        l1 - x1 - z1,
        l1 + x1 - z1,
        l2 - x2 + z2,
        l2 + x2 + z2,
        l2 - x2 - z2,
        l2 + x2 - z2
        ] 

    faces = [
        (base+3, base+1, base+0, base+2),
        (base+6, base+4, base+5, base+7),
        (base+4, base+0, base+1, base+5),
        (base+7, base+3, base+2, base+6),
        (base+5, base+1, base+3, base+7),
        (base+6, base+2, base+0, base+4)
        ]

    return verts, faces

def processArmature(context, arm, genVertexGroups = True):
    print("processing armature {0}".format(arm.name))

    meshObj = meshFromArmature( arm )

    context.scene.objects.link( meshObj )

    armMatrix = arm.matrix_local.copy()

    verts = []
    edges = []
    faces = []
    vertexGroups = {}

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

    for editBone in [b for b in arm.data.edit_bones if b.use_deform]:
        boneName = editBone.name
        print( boneName )
        poseBone = arm.pose.bones[boneName]

        editBoneHead = editBone.head
        editBoneTail = editBone.tail
        editBoneRoll = editBone.roll
        editBoneX = editBone.x_axis
        editBoneZ = editBone.z_axis
        editBoneHeadRadius = editBone.head_radius
        editBoneTailRadius = editBone.tail_radius

        baseIndex = len(verts)
        baseSize = 20
        newVerts, newFaces = newGeometry( editBoneHead, editBoneTail, editBoneX, editBoneZ, baseSize, editBoneHeadRadius, editBoneTailRadius, baseIndex )

        verts.extend( newVerts )
        faces.extend( newFaces )

        vertexGroups[boneName] = [(x, 1.0) for x in range(baseIndex, len(verts))]

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

    meshObj.data.from_pydata(verts, edges, faces)

    if genVertexGroups:
        for name, vertexGroup in vertexGroups.items():
            groupObject = meshObj.vertex_groups.new(name)
            for (index, weight) in vertexGroup:
                groupObject.add([index], weight, 'REPLACE')

    modifier = meshObj.modifiers.new('ArmatureMod', 'ARMATURE')
    modifier.object = arm
    modifier.use_bone_envelopes = False
    modifier.use_vertex_groups = True

    meshObj.data.update()

    return meshObj

def processObject(context, obj):
    if (obj == None):
        return False
    if (obj.type != "ARMATURE"):
        print ("invalid type {0} of object {1}: armature expected".format(obj.type, obj.name))
        return False
    processArmature(context, obj)
    return True

CreateMesh()
$\endgroup$

You must log in to answer this question.

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