godot_export_manager.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. # ##### BEGIN GPL LICENSE BLOCK #####
  2. #
  3. # This program is free software; you can redistribute it and/or
  4. # modify it under the terms of the GNU General Public License
  5. # as published by the Free Software Foundation; either version 2
  6. # of the License, or (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program; if not, write to the Free Software Foundation,
  15. # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. #
  17. # ##### END GPL LICENSE BLOCK #####
  18. # Script copyright (c) Andreas Esau
  19. bl_info = {
  20. "name": "Godot Export Manager",
  21. "author": "Andreas Esau",
  22. "version": (1, 0),
  23. "blender": (2, 7, 0),
  24. "location": "Scene Properties > Godot Export Manager",
  25. "description": "Godot Export Manager uses the Better Collada Exporter to manage Export Groups and automatically export the objects groups to Collada Files.",
  26. "warning": "",
  27. "wiki_url": ("http://www.godotengine.org"),
  28. "tracker_url": "",
  29. "category": "Import-Export"}
  30. import bpy
  31. from bpy.props import StringProperty, BoolProperty, EnumProperty, FloatProperty, FloatVectorProperty, IntProperty, CollectionProperty, PointerProperty
  32. import os
  33. from bpy.app.handlers import persistent
  34. from mathutils import Vector, Matrix
  35. class godot_export_manager(bpy.types.Panel):
  36. bl_label = "Godot Export Manager"
  37. bl_space_type = 'PROPERTIES'
  38. bl_region_type = 'WINDOW'
  39. bl_context = "scene"
  40. bpy.types.Scene.godot_export_on_save = BoolProperty(default=False)
  41. ### draw function for all ui elements
  42. def draw(self, context):
  43. layout = self.layout
  44. split = self.layout.split()
  45. scene = bpy.data.scenes[0]
  46. ob = context.object
  47. scene = context.scene
  48. row = layout.row()
  49. col = row.column()
  50. col.prop(scene,"godot_export_on_save",text="Export Groups on save")
  51. row = layout.row()
  52. col = row.column(align=True)
  53. op = col.operator("scene.godot_add_objects_to_group",text="Add selected objects to Group",icon="COPYDOWN")
  54. op = col.operator("scene.godot_delete_objects_from_group",text="Delete selected objects from Group",icon="PASTEDOWN")
  55. row = layout.row()
  56. col = row.column()
  57. col.label(text="Export Groups:")
  58. row = layout.row()
  59. col = row.column()
  60. col.template_list("UI_List_Godot","dummy",scene, "godot_export_groups", scene, "godot_export_groups_index",rows=1,maxrows=10,type='DEFAULT')
  61. col = row.column(align=True)
  62. col.operator("scene.godot_add_export_group",text="",icon="ZOOMIN")
  63. col.operator("scene.godot_delete_export_group",text="",icon="ZOOMOUT")
  64. col.operator("scene.godot_export_all_groups",text="",icon="EXPORT")
  65. if len(scene.godot_export_groups) > 0:
  66. row = layout.row()
  67. col = row.column()
  68. group = scene.godot_export_groups[scene.godot_export_groups_index]
  69. col.prop(group,"name",text="Group Name")
  70. col.prop(group,"export_name",text="Export Name")
  71. col.prop(group,"export_path",text="Export Filepath")
  72. row = layout.row()
  73. col = row.column()
  74. row = layout.row()
  75. col = row.column()
  76. col.label(text="Export Settings:")
  77. col = col.row(align=True)
  78. col.prop(group,"apply_loc",toggle=True,icon="MAN_TRANS")
  79. col.prop(group,"apply_rot",toggle=True,icon="MAN_ROT")
  80. col.prop(group,"apply_scale",toggle=True,icon="MAN_SCALE")
  81. row = layout.row()
  82. col = row.column()
  83. col.prop(group,"use_mesh_modifiers")
  84. col.prop(group,"use_tangent_arrays")
  85. col.prop(group,"use_triangles")
  86. col.prop(group,"use_copy_images")
  87. col.prop(group,"use_active_layers")
  88. col.prop(group,"use_exclude_ctrl_bones")
  89. col.prop(group,"use_anim")
  90. col.prop(group,"use_anim_action_all")
  91. col.prop(group,"use_anim_skip_noexp")
  92. col.prop(group,"use_anim_optimize")
  93. col.prop(group,"anim_optimize_precision")
  94. col.prop(group,"use_metadata")
  95. ### Custom template_list look
  96. class UI_List_Godot(bpy.types.UIList):
  97. def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
  98. ob = data
  99. slot = item
  100. col = layout.row(align=True)
  101. col.label(text=item.name,icon="GROUP")
  102. col.prop(item,"active",text="")
  103. op = col.operator("scene.godot_select_group_objects",text="",emboss=False,icon="RESTRICT_SELECT_OFF")
  104. op.idx = index
  105. op = col.operator("scene.godot_export_group",text="",emboss=False,icon="EXPORT")
  106. op.idx = index
  107. class add_objects_to_group(bpy.types.Operator):
  108. bl_idname = "scene.godot_add_objects_to_group"
  109. bl_label = "Add Objects to Group"
  110. bl_description = "Adds the selected Objects to the active group below."
  111. def execute(self,context):
  112. scene = context.scene
  113. objects_str = ""
  114. if len(scene.godot_export_groups) > 0:
  115. for i,object in enumerate(context.selected_objects):
  116. if object.name not in scene.godot_export_groups[scene.godot_export_groups_index].nodes:
  117. node = scene.godot_export_groups[scene.godot_export_groups_index].nodes.add()
  118. node.name = object.name
  119. if i == 0:
  120. objects_str += object.name
  121. else:
  122. objects_str += ", "+object.name
  123. self.report({'INFO'}, objects_str + " added to group." )
  124. bpy.ops.ed.undo_push(message="Objects added to group")
  125. else:
  126. self.report({'WARNING'}, "Create a group first." )
  127. return{'FINISHED'}
  128. class del_objects_from_group(bpy.types.Operator):
  129. bl_idname = "scene.godot_delete_objects_from_group"
  130. bl_label = "Delete Objects from Group"
  131. bl_description = "Delets the selected Objects from the active group below."
  132. def execute(self,context):
  133. scene = context.scene
  134. if len(scene.godot_export_groups) > 0:
  135. selected_objects = []
  136. for object in context.selected_objects:
  137. selected_objects.append(object.name)
  138. objects_str = ""
  139. j = 0
  140. for i,node in enumerate(scene.godot_export_groups[scene.godot_export_groups_index].nodes):
  141. if node.name in selected_objects:
  142. scene.godot_export_groups[scene.godot_export_groups_index].nodes.remove(i)
  143. if j == 0:
  144. objects_str += object.name
  145. else:
  146. objects_str += ", "+object.name
  147. j+=1
  148. self.report({'INFO'}, objects_str + " deleted from group." )
  149. bpy.ops.ed.undo_push(message="Objects deleted from group")
  150. else:
  151. self.report({'WARNING'}, "There is no group to delete from." )
  152. return{'FINISHED'}
  153. class select_group_objects(bpy.types.Operator):
  154. bl_idname = "scene.godot_select_group_objects"
  155. bl_label = "Select Group Objects"
  156. bl_description = "Will select all group Objects in the scene."
  157. idx = IntProperty()
  158. def execute(self,context):
  159. scene = context.scene
  160. for object in context.scene.objects:
  161. object.select = False
  162. for node in scene.godot_export_groups[self.idx].nodes:
  163. if node.name in bpy.data.objects:
  164. bpy.data.objects[node.name].select = True
  165. context.scene.objects.active = bpy.data.objects[node.name]
  166. return{'FINISHED'}
  167. class export_groups_autosave(bpy.types.Operator):
  168. bl_idname = "scene.godot_export_groups_autosave"
  169. bl_label = "Export All Groups"
  170. bl_description = "Exports all groups to Collada."
  171. def execute(self,context):
  172. scene = context.scene
  173. if scene.godot_export_on_save:
  174. for i in range(len(scene.godot_export_groups)):
  175. if scene.godot_export_groups[i].active:
  176. bpy.ops.scene.godot_export_group(idx=i)
  177. self.report({'INFO'}, "All Groups exported." )
  178. bpy.ops.ed.undo_push(message="Export all Groups")
  179. return{'FINISHED'}
  180. class export_all_groups(bpy.types.Operator):
  181. bl_idname = "scene.godot_export_all_groups"
  182. bl_label = "Export All Groups"
  183. bl_description = "Exports all groups to Collada."
  184. def execute(self,context):
  185. scene = context.scene
  186. for i in range(len(scene.godot_export_groups)):
  187. if scene.godot_export_groups[i].active:
  188. bpy.ops.scene.godot_export_group(idx=i)
  189. self.report({'INFO'}, "All Groups exported." )
  190. bpy.ops.ed.undo_push(message="Export all Groups")
  191. return{'FINISHED'}
  192. class export_group(bpy.types.Operator):
  193. bl_idname = "scene.godot_export_group"
  194. bl_label = "Export Group"
  195. bl_description = "Exports the active group to destination folder as Collada file."
  196. idx = IntProperty(default=0)
  197. def copy_object_recursive(self,ob,parent,single_user = True):
  198. new_ob = bpy.data.objects[ob.name].copy()
  199. if single_user or ob.type=="ARMATURE":
  200. new_mesh_data = new_ob.data.copy()
  201. new_ob.data = new_mesh_data
  202. bpy.context.scene.objects.link(new_ob)
  203. if ob != parent:
  204. new_ob.parent = parent
  205. else:
  206. new_ob.parent = None
  207. for child in ob.children:
  208. self.copy_object_recursive(child,new_ob,single_user)
  209. new_ob.select = True
  210. return new_ob
  211. def delete_object(self,ob):
  212. if ob != None:
  213. for child in ob.children:
  214. self.delete_object(child)
  215. bpy.context.scene.objects.unlink(ob)
  216. bpy.data.objects.remove(ob)
  217. def convert_group_to_node(self,group):
  218. if group.dupli_group != None:
  219. for object in group.dupli_group.objects:
  220. if object.parent == None:
  221. object = self.copy_object_recursive(object,object,True)
  222. matrix = Matrix(object.matrix_local)
  223. object.matrix_local = Matrix()
  224. object.matrix_local *= group.matrix_local
  225. object.matrix_local *= matrix
  226. self.delete_object(group)
  227. def execute(self,context):
  228. scene = context.scene
  229. group = context.scene.godot_export_groups
  230. path = group[self.idx].export_path
  231. if (path.find("//")==0 or path.find("\\\\")==0):
  232. #if relative, convert to absolute
  233. path = bpy.path.abspath(path)
  234. path = path.replace("\\","/")
  235. ### if path exists and group export name is set the group will be exported
  236. if os.path.exists(path) and group[self.idx].export_name != "":
  237. context.scene.layers = [True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True]
  238. if group[self.idx].export_name.endswith(".dae"):
  239. path = os.path.join(path,group[self.idx].export_name)
  240. else:
  241. path = os.path.join(path,group[self.idx].export_name+".dae")
  242. hide_select = []
  243. for object in context.scene.objects:
  244. hide_select.append(object.hide_select)
  245. object.hide_select = False
  246. object.select = False
  247. context.scene.objects.active = None
  248. for i,object in enumerate(group[self.idx].nodes):
  249. if object.name in bpy.data.objects:
  250. if bpy.data.objects[object.name].type == "EMPTY":
  251. self.convert_group_to_node(bpy.data.objects[object.name])
  252. else:
  253. bpy.data.objects[object.name].select = True
  254. else: # if object is not in the scene anymore it will be removed from the group
  255. group[self.idx].nodes.remove(i)
  256. bpy.ops.object.transform_apply(location=group[self.idx].apply_loc, rotation=group[self.idx].apply_rot, scale=group[self.idx].apply_scale)
  257. 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_exclude_ctrl_bones=group[self.idx].use_exclude_ctrl_bones, 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)
  258. self.report({'INFO'}, '"'+group[self.idx].name+'"' + " Group exported." )
  259. msg = "Export Group "+group[self.idx].name
  260. bpy.ops.ed.undo_push(message="")
  261. bpy.ops.ed.undo()
  262. bpy.ops.ed.undo_push(message=msg)
  263. else:
  264. self.report({'INFO'}, "Define Export Name and Export Path." )
  265. return{'FINISHED'}
  266. class add_export_group(bpy.types.Operator):
  267. bl_idname = "scene.godot_add_export_group"
  268. bl_label = "Adds a new export Group"
  269. bl_description = "Creates a new Export Group with the selected Objects assigned to it."
  270. def execute(self,context):
  271. scene = context.scene
  272. item = scene.godot_export_groups.add()
  273. item.name = "New Group"
  274. for object in context.selected_objects:
  275. node = item.nodes.add()
  276. node.name = object.name
  277. scene.godot_export_groups_index = len(scene.godot_export_groups)-1
  278. bpy.ops.ed.undo_push(message="Create New Export Group")
  279. return{'FINISHED'}
  280. class del_export_group(bpy.types.Operator):
  281. bl_idname = "scene.godot_delete_export_group"
  282. bl_label = "Delets the selected export Group"
  283. bl_description = "Delets the active Export Group."
  284. def invoke(self, context, event):
  285. wm = context.window_manager
  286. return wm.invoke_confirm(self,event)
  287. def execute(self,context):
  288. scene = context.scene
  289. scene.godot_export_groups.remove(scene.godot_export_groups_index)
  290. if scene.godot_export_groups_index > 0:
  291. scene.godot_export_groups_index -= 1
  292. bpy.ops.ed.undo_push(message="Delete Export Group")
  293. return{'FINISHED'}
  294. class godot_node_list(bpy.types.PropertyGroup):
  295. name = StringProperty()
  296. class godot_export_groups(bpy.types.PropertyGroup):
  297. name = StringProperty(name="Group Name")
  298. export_name = StringProperty(name="scene_name")
  299. nodes = CollectionProperty(type=godot_node_list)
  300. export_path = StringProperty(subtype="DIR_PATH")
  301. active = BoolProperty(default=True,description="Export Group")
  302. 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'})
  303. apply_scale = BoolProperty(name="Apply Scale",description="Apply Scale before export.",default=False)
  304. apply_rot = BoolProperty(name="Apply Rotation",description="Apply Rotation before export.",default=False)
  305. apply_loc = BoolProperty(name="Apply Location",description="Apply Location before export.",default=False)
  306. use_export_selected = BoolProperty(name="Selected Objects",description="Export only selected objects (and visible in active layers if that applies).",default=True)
  307. use_mesh_modifiers = BoolProperty(name="Apply Modifiers",description="Apply modifiers to mesh objects (on a copy!).",default=True)
  308. use_tangent_arrays = BoolProperty(name="Tangent Arrays",description="Export Tangent and Binormal arrays (for normalmapping).",default=False)
  309. use_triangles = BoolProperty(name="Triangulate",description="Export Triangles instead of Polygons.",default=False)
  310. use_copy_images = BoolProperty(name="Copy Images",description="Copy Images (create images/ subfolder)",default=False)
  311. use_active_layers = BoolProperty(name="Active Layers",description="Export only objects on the active layers.",default=True)
  312. use_exclude_ctrl_bones = BoolProperty(name="Exclude Control Bones",description="Exclude skeleton bones with names that begin with 'ctrl'.",default=True)
  313. use_anim = BoolProperty(name="Export Animation",description="Export keyframe animation",default=False)
  314. use_anim_action_all = BoolProperty(name="All Actions",description=("Export all actions for the first armature found in separate DAE files"),default=False)
  315. 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)
  316. use_anim_optimize = BoolProperty(name="Optimize Keyframes",description="Remove double keyframes",default=True)
  317. 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)
  318. use_metadata = BoolProperty(name="Use Metadata",default=True,options={'HIDDEN'})
  319. def register():
  320. bpy.utils.register_class(godot_export_manager)
  321. bpy.utils.register_class(godot_node_list)
  322. bpy.utils.register_class(godot_export_groups)
  323. bpy.utils.register_class(add_export_group)
  324. bpy.utils.register_class(del_export_group)
  325. bpy.utils.register_class(export_all_groups)
  326. bpy.utils.register_class(export_groups_autosave)
  327. bpy.utils.register_class(export_group)
  328. bpy.utils.register_class(add_objects_to_group)
  329. bpy.utils.register_class(del_objects_from_group)
  330. bpy.utils.register_class(select_group_objects)
  331. bpy.utils.register_class(UI_List_Godot)
  332. bpy.types.Scene.godot_export_groups = CollectionProperty(type=godot_export_groups)
  333. bpy.types.Scene.godot_export_groups_index = IntProperty(default=0,min=0)
  334. def unregister():
  335. bpy.utils.unregister_class(godot_export_manager)
  336. bpy.utils.unregister_class(godot_node_list)
  337. bpy.utils.unregister_class(godot_export_groups)
  338. bpy.utils.unregister_class(export_groups_autosave)
  339. bpy.utils.unregister_class(add_export_group)
  340. bpy.utils.unregister_class(del_export_group)
  341. bpy.utils.unregister_class(export_all_groups)
  342. bpy.utils.unregister_class(export_group)
  343. bpy.utils.unregister_class(add_objects_to_group)
  344. bpy.utils.unregister_class(del_objects_from_group)
  345. bpy.utils.unlregister_class(select_group_objects)
  346. bpy.utils.unregister_class(UI_List_Godot)
  347. @persistent
  348. def auto_export(dummy):
  349. bpy.ops.scene.godot_export_groups_autosave()
  350. bpy.app.handlers.save_post.append(auto_export)
  351. if __name__ == "__main__":
  352. register()