For my modeling work, I found that I often need to do the following things before exporting:
select objects
for each object apply the modifiers
join the objects
triangulate the objects
Now I created an operator that does exactly that. It creates a copy of the selected objects, applies the modifiers for each, joins the objects and triangulates them.
Here it is:
import bpy
bl_info = {
"name": "Merge objects and apply modifiers",
"description": "Creates a copy of the selected objects, applies their modifiers, and joins them into a single mesh",
"author": "Jonatan Bijl",
"version": (0, 1, 0),
"blender": (2, 6, 4),
"api": 44136,
"location": "context menu",
"category": "Object"}
def main(context):
#make a list of the selected objects of type 'mesh'
objs = [obj for obj in context.selected_objects if obj.type == 'MESH']
active_obj = context.object
new_active_obj = None
bpy.ops.object.select_all(action='DESELECT')
new_objs = []
for obj in objs:
new_mesh = obj.to_mesh(context.scene, True, 'PREVIEW')
new_obj = bpy.data.objects.new('{}_tmp'.format(new_mesh.name), new_mesh)
context.scene.objects.link(new_obj)
#triangulate
context.scene.objects.active = new_obj
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris()
bpy.ops.object.mode_set(mode='OBJECT')
new_obj.location = obj.location
new_obj.rotation_euler = obj.rotation_euler
new_obj.scale = obj.scale
new_objs.append(new_obj)
new_obj.select = True
if obj == active_obj:
new_active_obj = new_obj
#make sure we have a new active object selected
if new_active_obj:
context.scene.objects.active = new_active_obj
else:
new_mesh = bpy.data.meshes.new('joined')
new_obj = bpy.data.objects.new('joined', new_mesh)
context.scene.objects.link(new_obj)
new_obj.select = True
context.scene.objects.active = new_obj
bpy.ops.object.join()
class MergeApplyOperator(bpy.types.Operator):
"""Create a joined copy of selected meshes, with modifiers applied if needed"""
bl_idname = "object.merge_apply"
bl_label = "Merge objects and apply modifiers"
@classmethod
def poll(cls, context):
return len(context.selected_objects) > 0
def execute(self, context):
main(context)
return {'FINISHED'}
def register():
bpy.utils.register_class(MergeApplyOperator)
def unregister():
bpy.utils.unregister_class(MergeApplyOperator)
if __name__ == "__main__":
register()
# test call
bpy.ops.object.merge_apply()
I added an extra feature. If the input objects contain group instances, they are also made real and joined into the result.
import bpy
bl_info = {
"name": "Merge objects and apply modifiers",
"description": "Creates a copy of the selected objects, applies their modifiers, and joins them into a single mesh",
"author": "Jonatan Bijl",
"version": (0, 1, 2),
"blender": (2, 6, 4),
"api": 44136,
"location": "context menu",
"category": "Object"}
import bmesh, bpy, mathutils
from bpy.props import StringProperty
def main(context, name='joined'):
#First store references to the objects we want to work on, because in the process we will do a deselect
objs = [obj for obj in context.selected_objects if obj.type == 'MESH']
group_instances = [obj for obj in context.selected_objects if obj.dupli_group]
bpy.ops.object.select_all(action='DESELECT')
#list of objects that we will need to delete afterwards
group_duplicates = []
for group_instance in group_instances:
clone = group_instance.copy()
context.scene.objects.link(clone)
clone.select = True
#by now we have only the clones of the dupligroups selected
bpy.ops.object.duplicates_make_real()
#the newly created duplicate objects are all selected
for obj in context.selected_objects:
print('{} {}'.format(group_instance, obj))
group_duplicates.append(obj)
if obj.type == 'MESH':
objs.append(obj)
bpy.ops.object.select_all(action='DESELECT')
new_objs = []
for obj in objs:
#create a mesh with the operators applied
new_mesh = obj.to_mesh(context.scene, True, 'PREVIEW')
#Create a new object which has the same parent
new_obj = bpy.data.objects.new('{}_tmp'.format(new_mesh.name), new_mesh)
new_obj.parent = obj.parent
context.scene.objects.link(new_obj)
#apply object-linked materials to the mesh itself
for mat_slot in new_obj.material_slots.values():
if mat_slot.link == 'OBJECT':
mat = mat_slot.material = material
mat_slot.link = 'DATA'
mat_slot.material = mat
#triangulate
context.scene.objects.active = new_obj
bpy.ops.object.parent_clear(type='CLEAR_KEEP_TRANSFORM')
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris()
bpy.ops.object.mode_set(mode='OBJECT')
new_obj.location = obj.location
new_obj.rotation_euler = obj.rotation_euler
new_obj.scale = obj.scale
new_objs.append(new_obj)
new_obj.select = True
new_mesh = bpy.data.meshes.new(name)
new_obj = bpy.data.objects.new(name, new_mesh)
context.scene.objects.link(new_obj)
new_obj.select = True
context.scene.objects.active = new_obj
bpy.ops.object.join()
#delete the temporary objects we generated when handling the dupligroups
bpy.ops.object.select_all(action='DESELECT')
for obj in group_duplicates:
obj.select = True
bpy.ops.object.delete()
#now select the result
new_obj.select = True
context.scene.objects.active = new_obj
new_obj.data.show_double_sided = False
from bpy.props import StringProperty
class MergeApplyOperator(bpy.types.Operator):
"""Create a joined copy of selected meshes, with modifiers applied if needed"""
bl_idname = "object.merge_apply"
bl_label = "Merge objects and apply modifiers"
bl_options = {'REGISTER', 'UNDO'}
object_name = StringProperty(name='object_name', description='The name for the new mesh and object', default='joined')
@classmethod
def poll(cls, context):
return len(context.selected_objects) > 0
def execute(self, context):
main(context, self.object_name)
return {'FINISHED'}
def register():
bpy.utils.register_class(MergeApplyOperator)
def unregister():
bpy.utils.unregister_class(MergeApplyOperator)
if __name__ == "__main__":
register()
# test call
bpy.ops.object.merge_apply()
i change it a bit to have the appility to let the mesh as quads:
class MergeApplyOperator(bpy.types.Operator):
"""Create a joined copy of selected meshes, with modifiers applied if needed"""
bl_idname = "object.merge_apply"
bl_label = "Merge objects and apply modifiers"
bl_options = {'REGISTER', 'UNDO'}
object_name = StringProperty(name='object_name', description='The name for the new mesh and object', default='joined')
tri = bpy.props.BoolProperty(name="Triangulation", description="Triangulation", default=False)
@classmethod
def poll(cls, context):
return len(context.selected_objects) > 0
def execute(self, context):
main(context, self.object_name)
for i in range(self.tri):
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris()
bpy.ops.object.editmode_toggle()
return {'FINISHED'}