# ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### # Script copyright (c) Andreas Esau bl_info = { "name": "Godot Export Manager", "author": "Andreas Esau", "version": (1, 0), "blender": (2, 7, 0), "location": "Scene Properties > Godot Export Manager", "description": "Godot Export Manager uses the Better Collada Exporter to manage Export Groups and automatically export the objects groups to Collada Files.", "warning": "", "wiki_url": ("http://www.godotengine.org"), "tracker_url": "", "category": "Import-Export"} import bpy from bpy.props import StringProperty, BoolProperty, EnumProperty, FloatProperty, FloatVectorProperty, IntProperty, CollectionProperty, PointerProperty import os from bpy.app.handlers import persistent from mathutils import Vector, Matrix class godot_export_manager(bpy.types.Panel): bl_label = "Godot Export Manager" bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = "scene" bpy.types.Scene.godot_export_on_save = BoolProperty(default=False) ### draw function for all ui elements def draw(self, context): layout = self.layout split = self.layout.split() scene = bpy.data.scenes[0] ob = context.object scene = context.scene row = layout.row() col = row.column() col.prop(scene,"godot_export_on_save",text="Export Groups on save") row = layout.row() col = row.column(align=True) op = col.operator("scene.godot_add_objects_to_group",text="Add selected objects to Group",icon="COPYDOWN") op = col.operator("scene.godot_delete_objects_from_group",text="Delete selected objects from Group",icon="PASTEDOWN") row = layout.row() col = row.column() col.label(text="Export Groups:") row = layout.row() col = row.column() col.template_list("UI_List_Godot","dummy",scene, "godot_export_groups", scene, "godot_export_groups_index",rows=1,maxrows=10,type='DEFAULT') col = row.column(align=True) col.operator("scene.godot_add_export_group",text="",icon="ZOOMIN") col.operator("scene.godot_delete_export_group",text="",icon="ZOOMOUT") col.operator("scene.godot_export_all_groups",text="",icon="EXPORT") if len(scene.godot_export_groups) > 0: row = layout.row() col = row.column() group = scene.godot_export_groups[scene.godot_export_groups_index] col.prop(group,"name",text="Group Name") col.prop(group,"export_name",text="Export Name") col.prop(group,"export_path",text="Export Filepath") row = layout.row() col = row.column() row = layout.row() col = row.column() col.label(text="Export Settings:") col = col.row(align=True) col.prop(group,"apply_loc",toggle=True,icon="MAN_TRANS") col.prop(group,"apply_rot",toggle=True,icon="MAN_ROT") col.prop(group,"apply_scale",toggle=True,icon="MAN_SCALE") row = layout.row() col = row.column() col.prop(group,"use_include_particle_duplicates") col.prop(group,"use_mesh_modifiers") col.prop(group,"use_tangent_arrays") col.prop(group,"use_triangles") col.prop(group,"use_copy_images") col.prop(group,"use_active_layers") col.prop(group,"use_anim") col.prop(group,"use_anim_action_all") col.prop(group,"use_anim_skip_noexp") col.prop(group,"use_anim_optimize") col.prop(group,"anim_optimize_precision") col.prop(group,"use_metadata") ### Custom template_list look class UI_List_Godot(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): ob = data slot = item col = layout.row(align=True) col.label(text=item.name,icon="GROUP") col.prop(item,"active",text="") op = col.operator("scene.godot_select_group_objects",text="",emboss=False,icon="RESTRICT_SELECT_OFF") op.idx = index op = col.operator("scene.godot_export_group",text="",emboss=False,icon="EXPORT") op.idx = index class add_objects_to_group(bpy.types.Operator): bl_idname = "scene.godot_add_objects_to_group" bl_label = "Add Objects to Group" bl_description = "Adds the selected Objects to the active group below." undo = BoolProperty(default=True) def execute(self,context): scene = context.scene objects_str = "" if len(scene.godot_export_groups) > 0: for i,object in enumerate(context.selected_objects): if object.name not in scene.godot_export_groups[scene.godot_export_groups_index].nodes: node = scene.godot_export_groups[scene.godot_export_groups_index].nodes.add() node.name = object.name if i == 0: objects_str += object.name else: objects_str += ", "+object.name self.report({'INFO'}, objects_str + " added to group." ) if self.undo: bpy.ops.ed.undo_push(message="Objects added to group") else: self.report({'WARNING'}, "Create a group first." ) return{'FINISHED'} class del_objects_from_group(bpy.types.Operator): bl_idname = "scene.godot_delete_objects_from_group" bl_label = "Delete Objects from Group" bl_description = "Delets the selected Objects from the active group below." def execute(self,context): scene = context.scene if len(scene.godot_export_groups) > 0: selected_objects = [] for object in context.selected_objects: selected_objects.append(object.name) objects_str = "" j = 0 for i,node in enumerate(scene.godot_export_groups[scene.godot_export_groups_index].nodes): if node.name in selected_objects: scene.godot_export_groups[scene.godot_export_groups_index].nodes.remove(i) if j == 0: objects_str += object.name else: objects_str += ", "+object.name j+=1 self.report({'INFO'}, objects_str + " deleted from group." ) bpy.ops.ed.undo_push(message="Objects deleted from group") else: self.report({'WARNING'}, "There is no group to delete from." ) return{'FINISHED'} class select_group_objects(bpy.types.Operator): bl_idname = "scene.godot_select_group_objects" bl_label = "Select Group Objects" bl_description = "Will select all group Objects in the scene." idx = IntProperty() def execute(self,context): scene = context.scene for object in context.scene.objects: object.select = False for node in scene.godot_export_groups[self.idx].nodes: if node.name in bpy.data.objects: bpy.data.objects[node.name].select = True context.scene.objects.active = bpy.data.objects[node.name] return{'FINISHED'} class export_groups_autosave(bpy.types.Operator): bl_idname = "scene.godot_export_groups_autosave" bl_label = "Export All Groups" bl_description = "Exports all groups to Collada." def execute(self,context): scene = context.scene if scene.godot_export_on_save: for i in range(len(scene.godot_export_groups)): if scene.godot_export_groups[i].active: bpy.ops.scene.godot_export_group(idx=i) self.report({'INFO'}, "All Groups exported." ) bpy.ops.ed.undo_push(message="Export all Groups") return{'FINISHED'} class export_all_groups(bpy.types.Operator): bl_idname = "scene.godot_export_all_groups" bl_label = "Export All Groups" bl_description = "Exports all groups to Collada." def execute(self,context): scene = context.scene for i in range(0,len(scene.godot_export_groups)): bpy.ops.scene.godot_export_group(idx=i,export_all=True) self.report({'INFO'}, "All Groups exported." ) return{'FINISHED'} class export_group(bpy.types.Operator): bl_idname = "scene.godot_export_group" bl_label = "Export Group" bl_description = "Exports the active group to destination folder as Collada file." idx = IntProperty(default=0) export_all = BoolProperty(default=False) def copy_object_recursive(self,ob,parent,single_user = True): new_ob = bpy.data.objects[ob.name].copy() if single_user or ob.type=="ARMATURE": new_mesh_data = new_ob.data.copy() new_ob.data = new_mesh_data bpy.context.scene.objects.link(new_ob) if ob != parent: new_ob.parent = parent else: new_ob.parent = None for child in ob.children: self.copy_object_recursive(child,new_ob,single_user) new_ob.select = True return new_ob def delete_object(self,ob): if ob != None: for child in ob.children: self.delete_object(child) bpy.context.scene.objects.unlink(ob) bpy.data.objects.remove(ob) def convert_group_to_node(self,group): if group.dupli_group != None: for object in group.dupli_group.objects: if object.parent == None: object = self.copy_object_recursive(object,object,True) matrix = Matrix(object.matrix_local) object.matrix_local = Matrix() object.matrix_local *= group.matrix_local object.matrix_local *= matrix self.delete_object(group) def execute(self,context): scene = context.scene group = context.scene.godot_export_groups if not group[self.idx].active and self.export_all: return{'FINISHED'} for i,object in enumerate(group[self.idx].nodes): if object.name in bpy.data.objects: pass else: group[self.idx].nodes.remove(i) bpy.ops.ed.undo_push(message="Clear not existent Group Nodes.") path = group[self.idx].export_path if (path.find("//")==0 or path.find("\\\\")==0): #if relative, convert to absolute path = bpy.path.abspath(path) path = path.replace("\\","/") ### if path exists and group export name is set the group will be exported if os.path.exists(path) and group[self.idx].export_name != "": context.scene.layers = [True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True] if group[self.idx].export_name.endswith(".dae"): path = os.path.join(path,group[self.idx].export_name) else: path = os.path.join(path,group[self.idx].export_name+".dae") hide_select = [] for object in context.scene.objects: hide_select.append(object.hide_select) object.hide_select = False object.select = False context.scene.objects.active = None ### make particle duplicates, parent and select them nodes_to_be_added = [] if group[self.idx].use_include_particle_duplicates: for i,object in enumerate(group[self.idx].nodes): if bpy.data.objects[object.name].type != "EMPTY": context.scene.objects.active = bpy.data.objects[object.name] bpy.data.objects[object.name].select = True bpy.ops.object.duplicates_make_real() for object in context.selected_objects: nodes_to_be_added.append(object) bpy.ops.object.parent_set(type="OBJECT", keep_transform=False) for object in context.selected_objects: object.select = False bpy.data.objects[object.name].select = False context.scene.objects.active = None for object in nodes_to_be_added: object.select = True ### select all other nodes from the group for i,object in enumerate(group[self.idx].nodes): if bpy.data.objects[object.name].type == "EMPTY": self.convert_group_to_node(bpy.data.objects[object.name]) else: bpy.data.objects[object.name].select = True bpy.ops.object.transform_apply(location=group[self.idx].apply_loc, rotation=group[self.idx].apply_rot, scale=group[self.idx].apply_scale) bpy.ops.export_scene.dae(check_existing=True, filepath=path, filter_glob="*.dae", object_types=group[self.idx].object_types, use_export_selected=group[self.idx].use_export_selected, use_mesh_modifiers=group[self.idx].use_mesh_modifiers, use_tangent_arrays=group[self.idx].use_tangent_arrays, use_triangles=group[self.idx].use_triangles, use_copy_images=group[self.idx].use_copy_images, use_active_layers=group[self.idx].use_active_layers, use_anim=group[self.idx].use_anim, use_anim_action_all=group[self.idx].use_anim_action_all, use_anim_skip_noexp=group[self.idx].use_anim_skip_noexp, use_anim_optimize=group[self.idx].use_anim_optimize, anim_optimize_precision=group[self.idx].anim_optimize_precision, use_metadata=group[self.idx].use_metadata) self.report({'INFO'}, '"'+group[self.idx].name+'"' + " Group exported." ) msg = "Export Group "+group[self.idx].name bpy.ops.ed.undo_push(message="") bpy.ops.ed.undo() bpy.ops.ed.undo_push(message=msg) else: self.report({'INFO'}, "Define Export Name and Export Path." ) return{'FINISHED'} class add_export_group(bpy.types.Operator): bl_idname = "scene.godot_add_export_group" bl_label = "Adds a new export Group" bl_description = "Creates a new Export Group with the selected Objects assigned to it." def execute(self,context): scene = context.scene item = scene.godot_export_groups.add() item.name = "New Group" for object in context.selected_objects: node = item.nodes.add() node.name = object.name scene.godot_export_groups_index = len(scene.godot_export_groups)-1 bpy.ops.ed.undo_push(message="Create New Export Group") return{'FINISHED'} class del_export_group(bpy.types.Operator): bl_idname = "scene.godot_delete_export_group" bl_label = "Delets the selected export Group" bl_description = "Delets the active Export Group." def invoke(self, context, event): wm = context.window_manager return wm.invoke_confirm(self,event) def execute(self,context): scene = context.scene scene.godot_export_groups.remove(scene.godot_export_groups_index) if scene.godot_export_groups_index > 0: scene.godot_export_groups_index -= 1 bpy.ops.ed.undo_push(message="Delete Export Group") return{'FINISHED'} class godot_node_list(bpy.types.PropertyGroup): name = StringProperty() class godot_export_groups(bpy.types.PropertyGroup): name = StringProperty(name="Group Name") export_name = StringProperty(name="scene_name") nodes = CollectionProperty(type=godot_node_list) export_path = StringProperty(subtype="DIR_PATH") active = BoolProperty(default=True,description="Export Group") object_types = EnumProperty(name="Object Types",options={'ENUM_FLAG'},items=(('EMPTY', "Empty", ""),('CAMERA', "Camera", ""),('LAMP', "Lamp", ""),('ARMATURE', "Armature", ""),('MESH', "Mesh", ""),('CURVE', "Curve", ""),),default={'EMPTY', 'CAMERA', 'LAMP', 'ARMATURE', 'MESH','CURVE'}) apply_scale = BoolProperty(name="Apply Scale",description="Apply Scale before export.",default=False) apply_rot = BoolProperty(name="Apply Rotation",description="Apply Rotation before export.",default=False) apply_loc = BoolProperty(name="Apply Location",description="Apply Location before export.",default=False) use_export_selected = BoolProperty(name="Selected Objects",description="Export only selected objects (and visible in active layers if that applies).",default=True) use_mesh_modifiers = BoolProperty(name="Apply Modifiers",description="Apply modifiers to mesh objects (on a copy!).",default=True) use_tangent_arrays = BoolProperty(name="Tangent Arrays",description="Export Tangent and Binormal arrays (for normalmapping).",default=False) use_triangles = BoolProperty(name="Triangulate",description="Export Triangles instead of Polygons.",default=False) use_copy_images = BoolProperty(name="Copy Images",description="Copy Images (create images/ subfolder)",default=False) use_active_layers = BoolProperty(name="Active Layers",description="Export only objects on the active layers.",default=True) use_anim = BoolProperty(name="Export Animation",description="Export keyframe animation",default=False) use_anim_action_all = BoolProperty(name="All Actions",description=("Export all actions for the first armature found in separate DAE files"),default=False) use_anim_skip_noexp = BoolProperty(name="Skip (-noexp) Actions",description="Skip exporting of actions whose name end in (-noexp). Useful to skip control animations.",default=True) use_anim_optimize = BoolProperty(name="Optimize Keyframes",description="Remove double keyframes",default=True) anim_optimize_precision = FloatProperty(name="Precision",description=("Tolerence for comparing double keyframes (higher for greater accuracy)"),min=1, max=16,soft_min=1, soft_max=16,default=6.0) use_metadata = BoolProperty(name="Use Metadata",default=True,options={'HIDDEN'}) use_include_particle_duplicates = BoolProperty(name="Include Particle Duplicates",default=True) def register(): bpy.utils.register_class(godot_export_manager) bpy.utils.register_class(godot_node_list) bpy.utils.register_class(godot_export_groups) bpy.utils.register_class(add_export_group) bpy.utils.register_class(del_export_group) bpy.utils.register_class(export_all_groups) bpy.utils.register_class(export_groups_autosave) bpy.utils.register_class(export_group) bpy.utils.register_class(add_objects_to_group) bpy.utils.register_class(del_objects_from_group) bpy.utils.register_class(select_group_objects) bpy.utils.register_class(UI_List_Godot) bpy.types.Scene.godot_export_groups = CollectionProperty(type=godot_export_groups) bpy.types.Scene.godot_export_groups_index = IntProperty(default=0,min=0) def unregister(): bpy.utils.unregister_class(godot_export_manager) bpy.utils.unregister_class(godot_node_list) bpy.utils.unregister_class(godot_export_groups) bpy.utils.unregister_class(export_groups_autosave) bpy.utils.unregister_class(add_export_group) bpy.utils.unregister_class(del_export_group) bpy.utils.unregister_class(export_all_groups) bpy.utils.unregister_class(export_group) bpy.utils.unregister_class(add_objects_to_group) bpy.utils.unregister_class(del_objects_from_group) bpy.utils.unregister_class(select_group_objects) bpy.utils.unregister_class(UI_List_Godot) @persistent def auto_export(dummy): bpy.ops.scene.godot_export_groups_autosave() bpy.app.handlers.save_post.append(auto_export) if __name__ == "__main__": register()