io_export_selected.py
Go to the documentation of this file.
00001 #  ***** BEGIN GPL LICENSE BLOCK *****
00002 #
00003 #  This program is free software: you can redistribute it and/or modify
00004 #  it under the terms of the GNU General Public License as published by
00005 #  the Free Software Foundation, either version 3 of the License, or
00006 #  (at your option) any later version.
00007 #
00008 #  This program is distributed in the hope that it will be useful,
00009 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011 #  GNU General Public License for more details.
00012 #
00013 #  You should have received a copy of the GNU General Public License
00014 #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00015 #
00016 #  ***** END GPL LICENSE BLOCK *****
00017 
00018 # <pep8 compliant>
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     #categories = dir(bpy.ops)
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     # Special case: Collada (built-in) -- has no explicitly defined Python properties
00168     apply_modifiers = bpy.props.BoolProperty(name="Apply Modifiers", description="Apply modifiers to exported mesh (non destructive)", default=False)
00169     #export_mesh_type=0 # couldn't find correspondence in the UI
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     #export_transformation_type=0 # couldn't find correspondence in the UI
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                     #"window_managers",
00412                     #"screens",
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         # Special case: export to .blend (the default)
00542         def_op("Blend")
00543         
00544         # Special case: Collada is built-in, resides
00545         # in an unconventional category, and has no
00546         # explicit ext/glob properties defined
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 


naoqi_tools
Author(s): Mikael Arguedas
autogenerated on Sat Jun 8 2019 20:30:21