I have seen this post Convert bones to meshes but i need just box (or cylinder), not a piramyd at every bone, can someone help me please?
-
$\begingroup$ you can change the appearence of your armature in the Properties panel > Object Data > Viewport Display > Display As, and choose for example B-Bone to diplay the bones as rectangles, the in Edit mode ctrl alt S to fatten or shrink the rectangles, also try the other appearences $\endgroup$– moonbootsAug 8, 2020 at 11:36
-
$\begingroup$ I need to =convert= bone to mesh. $\endgroup$– Олег АнатольевичAug 8, 2020 at 11:42
-
$\begingroup$ yes, I thought you found a solution with the link you gave $\endgroup$– moonbootsAug 8, 2020 at 11:48
-
$\begingroup$ There the bone is replaced with pyramids, which, however, unfortunately, is not what I need. I suppose it needs to be changed from half a dozen lines of code, but I don't understand that. $\endgroup$– Олег АнатольевичAug 8, 2020 at 11:58
2 Answers
This is an update of lemon's code on the question Convert bones to meshes. Run the script and it will pop a menu asking if you want Pyramid, Tapered, or Box shapes. Pyramid is what the code did originally, tapered uses the bone envelope, and Box takes the average.
# from https://blender.stackexchange.com/a/75049/33589
import bpy
import mathutils
from mathutils import Vector
from math import *
class ArmatureMenu(bpy.types.Menu):
bl_label = "Mesh 2 Armature Menu"
bl_idname = "OBJECT_MT_Mesh_From_Armature"
def draw(self, context):
layout = self.layout
layout.operator("wm.mesh_from_armature", text="Pyramid").mesh_type = 'Pyramid' # from here
layout.operator("wm.mesh_from_armature", text="Tapered").mesh_type = 'Tapered' # from here
layout.operator("wm.mesh_from_armature", text="Box").mesh_type = 'Box' # from here
def CreateMesh(self, meshType):
obj = bpy.context.active_object
if obj == None:
self.report({"ERROR"}, "No selection" )
elif obj.type != 'ARMATURE':
self.report({"ERROR"}, "Armature expected" )
else:
processArmature( bpy.context, obj, meshType = meshType )
#Create the base object from the armature
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
#Create the bone geometry (vertices and faces)
def boneGeometry( l1, l2, x, z, baseSize, l1Size, l2Size, base, meshType ):
if meshType == 'Tapered':
print(meshType)
x1 = x * baseSize * l1Size
z1 = z * baseSize * l1Size
x2 = x * baseSize * l2Size
z2 = z * baseSize * l2Size
elif meshType == 'Box':
print(meshType)
lSize = (l1Size + l2Size) / 2
x1 = x * baseSize * lSize
z1 = z * baseSize * lSize
x2 = x * baseSize * lSize
z2 = z * baseSize * lSize
else: # default to Pyramid
print(meshType)
x1 = x * baseSize * l1Size
z1 = z * baseSize * l1Size
x2 = Vector( (0, 0, 0) )
z2 = Vector( (0, 0, 0) )
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
#Process the armature, goes through its bones and creates the mesh
def processArmature(context, arm, genVertexGroups = True, meshType = 'Pyramid'):
print("processing armature {0} {1}".format(arm.name, meshType) )
#Creates the mesh object
meshObj = meshFromArmature( arm )
context.collection.objects.link( meshObj )
verts = []
edges = []
faces = []
vertexGroups = {}
bpy.ops.object.mode_set(mode='EDIT')
try:
#Goes through each bone
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]
#Gets edit bone informations
editBoneHead = editBone.head
editBoneTail = editBone.tail
editBoneVector = editBoneTail - editBoneHead
editBoneSize = editBoneVector.dot( editBoneVector )
editBoneRoll = editBone.roll
editBoneX = editBone.x_axis
editBoneZ = editBone.z_axis
editBoneHeadRadius = editBone.head_radius
editBoneTailRadius = editBone.tail_radius
#Creates the mesh data for the bone
baseIndex = len(verts)
baseSize = sqrt( editBoneSize )
newVerts, newFaces = boneGeometry( editBoneHead, editBoneTail, editBoneX, editBoneZ, baseSize, editBoneHeadRadius, editBoneTailRadius, baseIndex, meshType )
verts.extend( newVerts )
faces.extend( newFaces )
#Creates the weights for the vertex groups
vertexGroups[boneName] = [(x, 1.0) for x in range(baseIndex, len(verts))]
#Assigns the geometry to the mesh
meshObj.data.from_pydata(verts, edges, faces)
except:
bpy.ops.object.mode_set(mode='OBJECT')
else:
bpy.ops.object.mode_set(mode='OBJECT')
#Assigns the vertex groups
if genVertexGroups:
for name, vertexGroup in vertexGroups.items():
groupObject = meshObj.vertex_groups.new(name=name)
for (index, weight) in vertexGroup:
groupObject.add([index], weight, 'REPLACE')
#Creates the armature modifier
modifier = meshObj.modifiers.new('ArmatureMod', 'ARMATURE')
modifier.object = arm
modifier.use_bone_envelopes = False
modifier.use_vertex_groups = True
meshObj.data.update()
return meshObj
class MeshFromArmatureOperator(bpy.types.Operator):
bl_idname = "wm.mesh_from_armature"
bl_label = "MeshFromArmatureOperator"
mesh_type : bpy.props.StringProperty(name="mesh_type")
def execute(self, context):
print('The mesh type is', self.mesh_type)
CreateMesh(self, self.mesh_type)
return {'FINISHED'}
def register():
bpy.utils.register_class( ArmatureMenu )
bpy.utils.register_class( MeshFromArmatureOperator )
def unregister():
bpy.utils.unregister_class( ArmatureMenu )
bpy.utils.unregister_class( MeshFromArmatureOperator )
if __name__ == "__main__":
register()
# The menu can also be called from scripts
bpy.ops.wm.call_menu(name='OBJECT_MT_Mesh_From_Armature')
-
$\begingroup$ I asked that question on meta a couple of hours ago, but I guess no one reads meta. :) $\endgroup$ Aug 8, 2020 at 19:54
-
$\begingroup$ I updated Lemon's answer (pending review). Only two lines actually changed, but I reflowed the code style from four spaces to triple back-ticks so it looks like all the lines changed. $\endgroup$ Aug 8, 2020 at 20:10
This is an update of Ron Jensen's answer (which is itself, as Ron points out, "an update of lemon's code on the question Convert bones to meshes").
This update was to rework some of the GUI code since I was having some issues on my end with using it to properly choose the mesh type (i.e., Pyramid, Tapered, or Box). I've changed the GUI to be a pop-up dialog that uses EnumProperty to pick which mesh type to use, and I've removed the ArmatureMenu class since I've changed the GUI to instead be called from the MeshFromArmatureOperator class when it is invoked (so now only 1 class has to be registered instead of 2).
Please note that I've only tested this in Blender 3.3.1 (which requires enabling "Developer Extras" in "Blender Preferences" -> "Interface" to be able to run "MeshFromArmatureOperator" from the F3 "Menu Search" window), so I can't vouch for compatibility with other versions.
# Built from:
# - https://blender.stackexchange.com/a/190354
# - https://blender.stackexchange.com/a/75049/33589
import bpy
import mathutils
from mathutils import Vector
from math import *
def CreateMesh(self, meshType):
obj = bpy.context.active_object
if obj == None:
self.report({"ERROR"}, "No selection" )
elif obj.type != 'ARMATURE':
self.report({"ERROR"}, "Armature expected" )
else:
processArmature( bpy.context, obj, meshType = meshType )
#Create the base object from the armature
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
#Create the bone geometry (vertices and faces)
def boneGeometry( l1, l2, x, z, baseSize, l1Size, l2Size, base, meshType ):
if meshType == 'Tapered':
print(meshType)
x1 = x * baseSize * l1Size
z1 = z * baseSize * l1Size
x2 = x * baseSize * l2Size
z2 = z * baseSize * l2Size
elif meshType == 'Box':
print(meshType)
lSize = (l1Size + l2Size) / 2
x1 = x * baseSize * lSize
z1 = z * baseSize * lSize
x2 = x * baseSize * lSize
z2 = z * baseSize * lSize
else: # default to Pyramid
print(meshType)
x1 = x * baseSize * l1Size
z1 = z * baseSize * l1Size
x2 = Vector( (0, 0, 0) )
z2 = Vector( (0, 0, 0) )
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
#Process the armature, goes through its bones and creates the mesh
def processArmature(context, arm, genVertexGroups = True, meshType = 'Pyramid'):
print("processing armature {0} {1}".format(arm.name, meshType) )
#Creates the mesh object
meshObj = meshFromArmature( arm )
context.collection.objects.link( meshObj )
verts = []
edges = []
faces = []
vertexGroups = {}
bpy.ops.object.mode_set(mode='EDIT')
try:
#Goes through each bone
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]
#Gets edit bone informations
editBoneHead = editBone.head
editBoneTail = editBone.tail
editBoneVector = editBoneTail - editBoneHead
editBoneSize = editBoneVector.dot( editBoneVector )
editBoneRoll = editBone.roll
editBoneX = editBone.x_axis
editBoneZ = editBone.z_axis
editBoneHeadRadius = editBone.head_radius
editBoneTailRadius = editBone.tail_radius
#Creates the mesh data for the bone
baseIndex = len(verts)
baseSize = sqrt( editBoneSize )
newVerts, newFaces = boneGeometry( editBoneHead, editBoneTail, editBoneX, editBoneZ, baseSize, editBoneHeadRadius, editBoneTailRadius, baseIndex, meshType )
verts.extend( newVerts )
faces.extend( newFaces )
#Creates the weights for the vertex groups
vertexGroups[boneName] = [(x, 1.0) for x in range(baseIndex, len(verts))]
#Assigns the geometry to the mesh
meshObj.data.from_pydata(verts, edges, faces)
except:
bpy.ops.object.mode_set(mode='OBJECT')
else:
bpy.ops.object.mode_set(mode='OBJECT')
#Assigns the vertex groups
if genVertexGroups:
for name, vertexGroup in vertexGroups.items():
groupObject = meshObj.vertex_groups.new(name=name)
for (index, weight) in vertexGroup:
groupObject.add([index], weight, 'REPLACE')
#Creates the armature modifier
modifier = meshObj.modifiers.new('ArmatureMod', 'ARMATURE')
modifier.object = arm
modifier.use_bone_envelopes = False
modifier.use_vertex_groups = True
meshObj.data.update()
return meshObj
class MeshFromArmatureOperator(bpy.types.Operator):
bl_idname = "wm.mesh_from_armature"
bl_label = "MeshFromArmatureOperator"
mesh_type_items = [
("Pyramid", "Pyramid", ""),
("Tapered", "Tapered", ""),
("Box", "Box", ""),
]
mesh_type: bpy.props.EnumProperty(items=mesh_type_items, name="Mesh Type")
def execute(self, context):
print('The mesh type is', self.mesh_type)
CreateMesh(self, self.mesh_type)
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
return wm.invoke_props_dialog(self)
def register():
bpy.utils.register_class( MeshFromArmatureOperator )
def unregister():
bpy.utils.unregister_class( MeshFromArmatureOperator )
if __name__ == "__main__":
register()
# The menu can also be called from scripts
bpy.ops.wm.mesh_from_armature('INVOKE_DEFAULT')
-
$\begingroup$ JPLC thanks! the script work fine BUT is there a way to get the meshed bones to look exactly like the armature, no scaling $\endgroup$ Nov 14 at 17:48