00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 bl_info = {
00021 "name": "Export Selected",
00022 "author": "dairin0d, rking",
00023 "version": (1, 4),
00024 "blender": (2, 6, 9),
00025 "location": "File > Export > Selected",
00026 "description": "Export selected objects to a chosen format",
00027 "warning": "",
00028 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"\
00029 "Scripts/Import-Export/Export_Selected",
00030 "tracker_url": "http://projects.blender.org/tracker/"\
00031 "?func=detail&aid=30942",
00032 "category": "Import-Export"}
00033
00034
00035 import bpy
00036
00037 from bpy_extras.io_utils import ExportHelper
00038
00039 join_before_export = {
00040 "export_mesh.ply",
00041 }
00042
00043 bpy_props = {
00044 bpy.props.BoolProperty,
00045 bpy.props.BoolVectorProperty,
00046 bpy.props.IntProperty,
00047 bpy.props.IntVectorProperty,
00048 bpy.props.FloatProperty,
00049 bpy.props.FloatVectorProperty,
00050 bpy.props.StringProperty,
00051 bpy.props.EnumProperty,
00052 bpy.props.PointerProperty,
00053 bpy.props.CollectionProperty,
00054 }
00055
00056 def is_bpy_prop(value):
00057 if isinstance(value, tuple) and (len(value) == 2):
00058 if (value[0] in bpy_props) and isinstance(value[1], dict):
00059 return True
00060 return False
00061
00062 def iter_public_bpy_props(cls, exclude_hidden=False):
00063 for key in dir(cls):
00064 if key.startswith("_"):
00065 continue
00066 value = getattr(cls, key)
00067 if is_bpy_prop(value):
00068 if exclude_hidden:
00069 options = value[1].get("options", "")
00070 if 'HIDDEN' in options:
00071 continue
00072 yield (key, value)
00073
00074 def get_op(idname):
00075 category_name, op_name = idname.split(".")
00076 category = getattr(bpy.ops, category_name)
00077 return getattr(category, op_name)
00078
00079 class ToggleObjectMode:
00080 def __init__(self, mode='OBJECT', undo=False):
00081 if not isinstance(mode, str):
00082 mode = ('OBJECT' if mode else None)
00083
00084 obj = bpy.context.object
00085 if obj and (obj.mode != mode):
00086 self.mode = mode
00087 else:
00088 self.mode = None
00089 self.undo = undo
00090
00091 def __enter__(self):
00092 if self.mode:
00093 edit_preferences = bpy.context.user_preferences.edit
00094
00095 self.global_undo = edit_preferences.use_global_undo
00096 self.prev_mode = bpy.context.object.mode
00097
00098 if self.prev_mode != self.mode:
00099 if self.undo is not None:
00100 edit_preferences.use_global_undo = self.undo
00101 bpy.ops.object.mode_set(mode=self.mode)
00102
00103 return self
00104
00105 def __exit__(self, type, value, traceback):
00106 if self.mode:
00107 edit_preferences = bpy.context.user_preferences.edit
00108
00109 if self.prev_mode != self.mode:
00110 bpy.ops.object.mode_set(mode=self.prev_mode)
00111 edit_preferences.use_global_undo = self.global_undo
00112
00113 def iter_exporters():
00114
00115 categories = ["export_anim", "export_mesh", "export_scene"]
00116 for category_name in categories:
00117 op_category = getattr(bpy.ops, category_name)
00118
00119 for name in dir(op_category):
00120 total_name = category_name + "." + name
00121
00122 if total_name == ExportSelected.bl_idname:
00123 continue
00124
00125 if "export" in total_name:
00126 op = getattr(op_category, name)
00127
00128 yield total_name, op
00129
00130 class CurrentFormatProperties(bpy.types.PropertyGroup):
00131 @classmethod
00132 def _clear_props(cls):
00133 keys_to_remove = list(cls._keys())
00134
00135 for key in keys_to_remove:
00136 delattr(cls, key)
00137
00138 CurrentFormatProperties.__dict = None
00139
00140 @classmethod
00141 def _add_props(cls, template):
00142 for key, value in iter_public_bpy_props(template):
00143 setattr(cls, key, value)
00144
00145 CurrentFormatProperties.__dict = {}
00146 for key in dir(template):
00147 value = getattr(template, key)
00148 if is_bpy_prop(value): continue
00149 CurrentFormatProperties.__dict[key] = value
00150
00151 @classmethod
00152 def _keys(cls, exclude_hidden=False):
00153 for kv in iter_public_bpy_props(cls, exclude_hidden):
00154 yield kv[0]
00155
00156 def __getattr__(self, name):
00157 return CurrentFormatProperties.__dict[name]
00158
00159 def __setattr__(self, name, value):
00160 if hasattr(self.__class__, name) and (not name.startswith("_")):
00161 supercls = super(CurrentFormatProperties, self.__class__)
00162 supercls.__setattr__(self, name, value)
00163 else:
00164 CurrentFormatProperties.__dict[name] = value
00165
00166 class ColladaEmulator:
00167
00168 apply_modifiers = bpy.props.BoolProperty(name="Apply Modifiers", description="Apply modifiers to exported mesh (non destructive)", default=False)
00169
00170 export_mesh_type_selection = bpy.props.EnumProperty(name="Type of modifiers", description="Modifier resolution for export", default='view', items=[('render', "Render", "Apply modifier's render settings"), ('view', "View", "Apply modifier's view settings")])
00171 selected = bpy.props.BoolProperty(name="Selection Only", description="Export only selected elements", default=False)
00172 include_children = bpy.props.BoolProperty(name="Include Children", description="Export all children of selected objects (even if not selected)", default=False)
00173 include_armatures = bpy.props.BoolProperty(name="Include Armatures", description="Export related armatures (even if not selected)", default=False)
00174 include_shapekeys = bpy.props.BoolProperty(name="Include Shape Keys", description="Export all Shape Keys from Mesh Objects", default=True)
00175 deform_bones_only = bpy.props.BoolProperty(name="Deform Bones only", description="Only export deforming bones with armatures", default=False)
00176 active_uv_only = bpy.props.BoolProperty(name="Only Active UV layer", description="Export textures assigned to the object UV maps", default=False)
00177 include_uv_textures = bpy.props.BoolProperty(name="Include UV Textures", description="Export textures assigned to the object UV maps", default=False)
00178 include_material_textures = bpy.props.BoolProperty(name="Include Material Textures", description="Export textures assigned to the object Materials", default=False)
00179 use_texture_copies = bpy.props.BoolProperty(name="Copy Textures", description="Copy textures to the same folder where .dae file is exported", default=True)
00180 triangulate = bpy.props.BoolProperty(name="Triangulate", description="Export Polygons (Quads & NGons) as Triangles", default=True)
00181 use_object_instantiation = bpy.props.BoolProperty(name="Use Object Instances", description="Instantiate multiple Objects from same Data", default=True)
00182 sort_by_name = bpy.props.BoolProperty(name="Sort by Object name", description="Sort exported data by Object name", default=False)
00183
00184 export_transformation_type_selection = bpy.props.EnumProperty(name="Transformation Type", description="Transformation type for translation, scale and rotation", default='matrix', items=[('both', "Both", "Use <matrix> AND <translate>, <rotate>, <scale> to specify transformations"), ('transrotloc', "TransLocRot", "Use <translate>, <rotate>, <scale> to specify transformations"), ('matrix', "Matrix", "Use <matrix> to specify transformations")])
00185 open_sim = bpy.props.BoolProperty(name="Export for OpenSim", description="Compatibility mode for OpenSim and compatible online worlds", default=False)
00186
00187 def draw(self, context):
00188 layout = self.layout
00189
00190 box = layout.box()
00191 box.label(text="Export Data Options", icon='MESH_DATA')
00192 row = box.split(0.6)
00193 row.prop(self, "apply_modifiers")
00194 row.prop(self, "export_mesh_type_selection", text="")
00195 box.prop(self, "selected")
00196 box.prop(self, "include_children")
00197 box.prop(self, "include_armatures")
00198 box.prop(self, "include_shapekeys")
00199
00200 box = layout.box()
00201 box.label(text="Texture Options", icon='TEXTURE')
00202 box.prop(self, "active_uv_only")
00203 box.prop(self, "include_uv_textures")
00204 box.prop(self, "include_material_textures")
00205 box.prop(self, "use_texture_copies", text="Copy")
00206
00207 box = layout.box()
00208 box.label(text="Armature Options", icon='ARMATURE_DATA')
00209 box.prop(self, "deform_bones_only")
00210 box.prop(self, "open_sim")
00211
00212 box = layout.box()
00213 box.label(text="Collada Options", icon='MODIFIER')
00214 box.prop(self, "triangulate")
00215 box.prop(self, "use_object_instantiation")
00216 row = box.split(0.6)
00217 row.label(text="Transformation Type")
00218 row.prop(self, "export_transformation_type_selection", text="")
00219 box.prop(self, "sort_by_name")
00220
00221 class ExportSelected(bpy.types.Operator, ExportHelper):
00222 '''Export selected objects to a chosen format'''
00223 bl_idname = "export_scene.selected"
00224 bl_label = "Export Selected"
00225
00226 filename_ext = bpy.props.StringProperty(
00227 default="",
00228 options={'HIDDEN'},
00229 )
00230
00231 filter_glob = bpy.props.StringProperty(
00232 default="*.*",
00233 options={'HIDDEN'},
00234 )
00235
00236 selection_mode = bpy.props.EnumProperty(
00237 name="Selection Mode",
00238 description="Limit/expand the selection",
00239 default='SELECTED',
00240 items=[
00241 ('SELECTED', "Selected", ""),
00242 ('VISIBLE', "Visible", ""),
00243 ('ALL', "All", ""),
00244 ],
00245 )
00246
00247 include_children = bpy.props.BoolProperty(
00248 name="Include Children",
00249 description="Keep children even if they're not selected",
00250 default=True,
00251 )
00252
00253 remove_orphans = bpy.props.BoolProperty(
00254 name="Remove Orphans",
00255 description="Remove datablocks that have no users",
00256 default=True,
00257 )
00258
00259 keep_materials = bpy.props.BoolProperty(
00260 name="Keep Materials",
00261 description="Keep Materials",
00262 default=True,
00263 )
00264
00265 keep_textures = bpy.props.BoolProperty(
00266 name="Keep Textures",
00267 description="Keep Textures",
00268 default=True,
00269 )
00270
00271 keep_world_textures = bpy.props.BoolProperty(
00272 name="Keep World Textures",
00273 description="Keep World Textures",
00274 default=False,
00275 )
00276
00277 object_types = bpy.props.EnumProperty(
00278 name="Object types",
00279 description="Object type(s) to export",
00280 default={'ALL'},
00281 items=[
00282 ('ALL', "All", ""),
00283 ('MESH', "Mesh", ""),
00284 ('CURVE', "Curve", ""),
00285 ('SURFACE', "Surface", ""),
00286 ('META', "Meta", ""),
00287 ('FONT', "Font", ""),
00288 ('ARMATURE', "Armature", ""),
00289 ('LATTICE', "Lattice", ""),
00290 ('EMPTY', "Empty", ""),
00291 ('CAMERA', "Camera", ""),
00292 ('LAMP', "Lamp", ""),
00293 ('SPEAKER', "Speaker", ""),
00294 ],
00295 options={'ENUM_FLAG'},
00296 )
00297
00298 visible_name = bpy.props.StringProperty(
00299 name="Visible name",
00300 description="Visible name",
00301 options={'HIDDEN'},
00302 )
00303
00304 format = bpy.props.StringProperty(
00305 name="Format",
00306 description="Export format",
00307 options={'HIDDEN'},
00308 )
00309
00310 format_props = bpy.props.PointerProperty(
00311 type=CurrentFormatProperties,
00312 options={'HIDDEN'},
00313 )
00314
00315 props_initialized = bpy.props.BoolProperty(
00316 options={'HIDDEN'},
00317 default=False,
00318 )
00319
00320 @classmethod
00321 def poll(cls, context):
00322 return len(context.scene.objects) != 0
00323
00324 def fill_props(self):
00325 if self.props_initialized: return
00326
00327 CurrentFormatProperties._clear_props()
00328
00329 if self.format:
00330 op = get_op(self.format)
00331 op_class = type(op.get_instance())
00332
00333 if self.format == "wm.collada_export":
00334 op_class = ColladaEmulator
00335
00336 CurrentFormatProperties._add_props(op_class)
00337 else:
00338 self.visible_name = "Blend"
00339 self.filename_ext = ".blend"
00340 self.filter_glob = "*.blend"
00341
00342 self.props_initialized = True
00343
00344 def invoke(self, context, event):
00345 self.fill_props()
00346 self.filepath = context.object.name + self.filename_ext
00347 return ExportHelper.invoke(self, context, event)
00348
00349 def clear_world(self, context):
00350 bpy.ops.ed.undo_push(message="Delete unselected")
00351
00352 for scene in bpy.data.scenes:
00353 if scene != context.scene:
00354 bpy.data.scenes.remove(scene)
00355
00356 scene = context.scene
00357
00358 objs = set()
00359
00360 def add_obj(obj):
00361 if self.object_types.intersection({'ALL', obj.type}):
00362 objs.add(obj)
00363
00364 if self.include_children:
00365 for child in obj.children:
00366 add_obj(child)
00367
00368 for obj in scene.objects:
00369 if (self.selection_mode == 'SELECTED') and obj.select:
00370 add_obj(obj)
00371 elif (self.selection_mode == 'VISIBLE') and obj.is_visible(scene):
00372 obj.hide_select = False
00373 add_obj(obj)
00374 elif (self.selection_mode == 'ALL'):
00375 obj.hide_select = False
00376 add_obj(obj)
00377
00378 for obj in scene.objects:
00379 if obj in objs:
00380 obj.select = True
00381 else:
00382 scene.objects.unlink(obj)
00383 bpy.data.objects.remove(obj)
00384 scene.update()
00385
00386 if not self.format:
00387 if not self.keep_materials:
00388 for material in bpy.data.materials:
00389 material.user_clear()
00390 bpy.data.materials.remove(material)
00391
00392 if not self.keep_textures:
00393 for world in bpy.data.worlds:
00394 for i in range(len(world.texture_slots)):
00395 world.texture_slots.clear(i)
00396 for material in bpy.data.materials:
00397 for i in range(len(material.texture_slots)):
00398 material.texture_slots.clear(i)
00399 for brush in bpy.data.brushes:
00400 brush.texture = None
00401 for texture in bpy.data.textures:
00402 texture.user_clear()
00403 bpy.data.textures.remove(texture)
00404 elif not self.keep_world_textures:
00405 for world in bpy.data.worlds:
00406 for i in range(len(world.texture_slots)):
00407 world.texture_slots.clear(i)
00408
00409 if self.remove_orphans:
00410 datablocks_cleanup_order = [
00411
00412
00413 "scenes",
00414 "worlds",
00415
00416 "grease_pencil",
00417 "fonts",
00418 "scripts",
00419 "texts",
00420 "movieclips",
00421 "actions",
00422 "speakers",
00423 "sounds",
00424 "brushes",
00425
00426 "node_groups",
00427 "groups",
00428 "objects",
00429
00430 "armatures",
00431 "cameras",
00432 "lamps",
00433 "lattices",
00434 "shape_keys",
00435 "meshes",
00436 "metaballs",
00437 "particles",
00438 "curves",
00439
00440 "materials",
00441 "textures",
00442 "images",
00443
00444 "libraries",
00445 ]
00446 for datablocks_name in datablocks_cleanup_order:
00447 datablocks = getattr(bpy.data, datablocks_name)
00448 if type(datablocks).__name__ == "bpy_prop_collection":
00449 for datablock in datablocks:
00450 if datablock.users == 0:
00451 datablocks.remove(datablock)
00452
00453 if self.format in join_before_export:
00454 bpy.ops.object.convert()
00455 bpy.ops.object.join()
00456
00457 def execute(self, context):
00458 with ToggleObjectMode(undo=None):
00459 self.clear_world(context)
00460
00461 if self.format:
00462 props = {}
00463 for key in CurrentFormatProperties._keys():
00464 props[key] = getattr(self.format_props, key)
00465 props["filepath"] = self.filepath
00466
00467 op = get_op(self.format)
00468
00469 op(**props)
00470 else:
00471 bpy.ops.wm.save_as_mainfile(
00472 filepath=self.filepath,
00473 copy=True,
00474 )
00475
00476 bpy.ops.ed.undo()
00477 bpy.ops.ed.undo_push(message="Export Selected")
00478
00479 return {'FINISHED'}
00480
00481 def draw(self, context):
00482 layout = self.layout
00483
00484 layout.label("Export " + self.visible_name)
00485
00486 layout.prop(self, "selection_mode", text="")
00487 layout.prop(self, "include_children")
00488 layout.prop_menu_enum(self, "object_types")
00489
00490 layout.box()
00491
00492 if not self.format:
00493 layout.prop(self, "remove_orphans")
00494 layout.prop(self, "keep_materials")
00495 layout.prop(self, "keep_textures")
00496 sublayout = layout.row()
00497 sublayout.enabled = self.keep_textures
00498 sublayout.prop(self, "keep_world_textures")
00499 return
00500
00501 op = get_op(self.format)
00502 op_class = type(op.get_instance())
00503
00504 if self.format == "wm.collada_export":
00505 op_class = ColladaEmulator
00506
00507 if hasattr(op_class, "draw"):
00508 self.format_props.layout = layout
00509 op_class.draw(self.format_props, context)
00510 else:
00511 for key in CurrentFormatProperties._keys(True):
00512 if key == 'filepath': continue
00513 layout.prop(self.format_props, key)
00514
00515 class OBJECT_MT_selected_export(bpy.types.Menu):
00516 bl_idname = "OBJECT_MT_selected_export"
00517 bl_label = "Selected"
00518
00519 def draw(self, context):
00520 layout = self.layout
00521
00522 def def_op(visible_name, total_name="", layout=layout):
00523 if visible_name.lower().startswith("export "):
00524 visible_name = visible_name[len("export "):]
00525
00526 if total_name:
00527 op = get_op(total_name)
00528 if not op.poll():
00529 layout = layout.row()
00530 layout.enabled = False
00531
00532 op_info = layout.operator(
00533 ExportSelected.bl_idname,
00534 text=visible_name,
00535 )
00536 op_info.format = total_name
00537 op_info.visible_name = visible_name
00538
00539 return op_info
00540
00541
00542 def_op("Blend")
00543
00544
00545
00546
00547 op_info = def_op("Collada", "wm.collada_export")
00548 op_info.filename_ext = ".dae"
00549 op_info.filter_glob = "*.dae"
00550
00551 for total_name, op in iter_exporters():
00552 op_class = type(op.get_instance())
00553 rna = op.get_rna()
00554
00555 op_info = def_op(rna.rna_type.name, total_name)
00556
00557 if hasattr(op_class, "filename_ext"):
00558 op_info.filename_ext = op_class.filename_ext
00559
00560 if hasattr(rna, "filter_glob"):
00561 op_info.filter_glob = rna.filter_glob
00562
00563 def menu_func_export(self, context):
00564 self.layout.menu("OBJECT_MT_selected_export", text="Selected")
00565
00566 def register():
00567 bpy.utils.register_class(CurrentFormatProperties)
00568 bpy.utils.register_class(ExportSelected)
00569 bpy.utils.register_class(OBJECT_MT_selected_export)
00570 bpy.types.INFO_MT_file_export.prepend(menu_func_export)
00571
00572 def unregister():
00573 bpy.types.INFO_MT_file_export.remove(menu_func_export)
00574 bpy.utils.unregister_class(OBJECT_MT_selected_export)
00575 bpy.utils.unregister_class(ExportSelected)
00576 bpy.utils.unregister_class(CurrentFormatProperties)
00577
00578 if __name__ == "__main__":
00579 register()
00580