2
0

godot_export_manager.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  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_include_particle_duplicates")
  84. col.prop(group,"use_mesh_modifiers")
  85. col.prop(group,"use_tangent_arrays")
  86. col.prop(group,"use_triangles")
  87. col.prop(group,"use_copy_images")
  88. col.prop(group,"use_active_layers")
  89. col.prop(group,"use_exclude_ctrl_bones")
  90. col.prop(group,"use_anim")
  91. col.prop(group,"use_anim_action_all")
  92. col.prop(group,"use_anim_skip_noexp")
  93. col.prop(group,"use_anim_optimize")
  94. col.prop(group,"anim_optimize_precision")
  95. col.prop(group,"use_metadata")
  96. ### Custom template_list look
  97. class UI_List_Godot(bpy.types.UIList):
  98. def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
  99. ob = data
  100. slot = item
  101. col = layout.row(align=True)
  102. col.label(text=item.name,icon="GROUP")
  103. col.prop(item,"active",text="")
  104. op = col.operator("scene.godot_select_group_objects",text="",emboss=False,icon="RESTRICT_SELECT_OFF")
  105. op.idx = index
  106. op = col.operator("scene.godot_export_group",text="",emboss=False,icon="EXPORT")
  107. op.idx = index
  108. class add_objects_to_group(bpy.types.Operator):
  109. bl_idname = "scene.godot_add_objects_to_group"
  110. bl_label = "Add Objects to Group"
  111. bl_description = "Adds the selected Objects to the active group below."
  112. undo = BoolProperty(default=True)
  113. def execute(self,context):
  114. scene = context.scene
  115. objects_str = ""
  116. if len(scene.godot_export_groups) > 0:
  117. for i,object in enumerate(context.selected_objects):
  118. if object.name not in scene.godot_export_groups[scene.godot_export_groups_index].nodes:
  119. node = scene.godot_export_groups[scene.godot_export_groups_index].nodes.add()
  120. node.name = object.name
  121. if i == 0:
  122. objects_str += object.name
  123. else:
  124. objects_str += ", "+object.name
  125. self.report({'INFO'}, objects_str + " added to group." )
  126. if self.undo:
  127. bpy.ops.ed.undo_push(message="Objects added to group")
  128. else:
  129. self.report({'WARNING'}, "Create a group first." )
  130. return{'FINISHED'}
  131. class del_objects_from_group(bpy.types.Operator):
  132. bl_idname = "scene.godot_delete_objects_from_group"
  133. bl_label = "Delete Objects from Group"
  134. bl_description = "Delets the selected Objects from the active group below."
  135. def execute(self,context):
  136. scene = context.scene
  137. if len(scene.godot_export_groups) > 0:
  138. selected_objects = []
  139. for object in context.selected_objects:
  140. selected_objects.append(object.name)
  141. objects_str = ""
  142. j = 0
  143. for i,node in enumerate(scene.godot_export_groups[scene.godot_export_groups_index].nodes):
  144. if node.name in selected_objects:
  145. scene.godot_export_groups[scene.godot_export_groups_index].nodes.remove(i)
  146. if j == 0:
  147. objects_str += object.name
  148. else:
  149. objects_str += ", "+object.name
  150. j+=1
  151. self.report({'INFO'}, objects_str + " deleted from group." )
  152. bpy.ops.ed.undo_push(message="Objects deleted from group")
  153. else:
  154. self.report({'WARNING'}, "There is no group to delete from." )
  155. return{'FINISHED'}
  156. class select_group_objects(bpy.types.Operator):
  157. bl_idname = "scene.godot_select_group_objects"
  158. bl_label = "Select Group Objects"
  159. bl_description = "Will select all group Objects in the scene."
  160. idx = IntProperty()
  161. def execute(self,context):
  162. scene = context.scene
  163. for object in context.scene.objects:
  164. object.select = False
  165. for node in scene.godot_export_groups[self.idx].nodes:
  166. if node.name in bpy.data.objects:
  167. bpy.data.objects[node.name].select = True
  168. context.scene.objects.active = bpy.data.objects[node.name]
  169. return{'FINISHED'}
  170. class export_groups_autosave(bpy.types.Operator):
  171. bl_idname = "scene.godot_export_groups_autosave"
  172. bl_label = "Export All Groups"
  173. bl_description = "Exports all groups to Collada."
  174. def execute(self,context):
  175. scene = context.scene
  176. if scene.godot_export_on_save:
  177. for i in range(len(scene.godot_export_groups)):
  178. if scene.godot_export_groups[i].active:
  179. bpy.ops.scene.godot_export_group(idx=i)
  180. self.report({'INFO'}, "All Groups exported." )
  181. bpy.ops.ed.undo_push(message="Export all Groups")
  182. return{'FINISHED'}
  183. class export_all_groups(bpy.types.Operator):
  184. bl_idname = "scene.godot_export_all_groups"
  185. bl_label = "Export All Groups"
  186. bl_description = "Exports all groups to Collada."
  187. def execute(self,context):
  188. scene = context.scene
  189. for i in range(0,len(scene.godot_export_groups)):
  190. bpy.ops.scene.godot_export_group(idx=i,export_all=True)
  191. self.report({'INFO'}, "All Groups exported." )
  192. return{'FINISHED'}
  193. class export_group(bpy.types.Operator):
  194. bl_idname = "scene.godot_export_group"
  195. bl_label = "Export Group"
  196. bl_description = "Exports the active group to destination folder as Collada file."
  197. idx = IntProperty(default=0)
  198. export_all = BoolProperty(default=False)
  199. def copy_object_recursive(self,ob,parent,single_user = True):
  200. new_ob = bpy.data.objects[ob.name].copy()
  201. if single_user or ob.type=="ARMATURE":
  202. new_mesh_data = new_ob.data.copy()
  203. new_ob.data = new_mesh_data
  204. bpy.context.scene.objects.link(new_ob)
  205. if ob != parent:
  206. new_ob.parent = parent
  207. else:
  208. new_ob.parent = None
  209. for child in ob.children:
  210. self.copy_object_recursive(child,new_ob,single_user)
  211. new_ob.select = True
  212. return new_ob
  213. def delete_object(self,ob):
  214. if ob != None:
  215. for child in ob.children:
  216. self.delete_object(child)
  217. bpy.context.scene.objects.unlink(ob)
  218. bpy.data.objects.remove(ob)
  219. def convert_group_to_node(self,group):
  220. if group.dupli_group != None:
  221. for object in group.dupli_group.objects:
  222. if object.parent == None:
  223. object = self.copy_object_recursive(object,object,True)
  224. matrix = Matrix(object.matrix_local)
  225. object.matrix_local = Matrix()
  226. object.matrix_local *= group.matrix_local
  227. object.matrix_local *= matrix
  228. self.delete_object(group)
  229. def execute(self,context):
  230. scene = context.scene
  231. group = context.scene.godot_export_groups
  232. if not group[self.idx].active and self.export_all:
  233. return{'FINISHED'}
  234. for i,object in enumerate(group[self.idx].nodes):
  235. if object.name in bpy.data.objects:
  236. pass
  237. else:
  238. group[self.idx].nodes.remove(i)
  239. bpy.ops.ed.undo_push(message="Clear not existent Group Nodes.")
  240. path = group[self.idx].export_path
  241. if (path.find("//")==0 or path.find("\\\\")==0):
  242. #if relative, convert to absolute
  243. path = bpy.path.abspath(path)
  244. path = path.replace("\\","/")
  245. ### if path exists and group export name is set the group will be exported
  246. if os.path.exists(path) and group[self.idx].export_name != "":
  247. context.scene.layers = [True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True,True]
  248. if group[self.idx].export_name.endswith(".dae"):
  249. path = os.path.join(path,group[self.idx].export_name)
  250. else:
  251. path = os.path.join(path,group[self.idx].export_name+".dae")
  252. hide_select = []
  253. for object in context.scene.objects:
  254. hide_select.append(object.hide_select)
  255. object.hide_select = False
  256. object.select = False
  257. context.scene.objects.active = None
  258. ### make particle duplicates, parent and select them
  259. nodes_to_be_added = []
  260. if group[self.idx].use_include_particle_duplicates:
  261. for i,object in enumerate(group[self.idx].nodes):
  262. if bpy.data.objects[object.name].type != "EMPTY":
  263. context.scene.objects.active = bpy.data.objects[object.name]
  264. bpy.data.objects[object.name].select = True
  265. bpy.ops.object.duplicates_make_real()
  266. for object in context.selected_objects:
  267. nodes_to_be_added.append(object)
  268. bpy.ops.object.parent_set(type="OBJECT", keep_transform=False)
  269. for object in context.selected_objects:
  270. object.select = False
  271. bpy.data.objects[object.name].select = False
  272. context.scene.objects.active = None
  273. for object in nodes_to_be_added:
  274. object.select = True
  275. ### select all other nodes from the group
  276. for i,object in enumerate(group[self.idx].nodes):
  277. if bpy.data.objects[object.name].type == "EMPTY":
  278. self.convert_group_to_node(bpy.data.objects[object.name])
  279. else:
  280. bpy.data.objects[object.name].select = True
  281. bpy.ops.object.transform_apply(location=group[self.idx].apply_loc, rotation=group[self.idx].apply_rot, scale=group[self.idx].apply_scale)
  282. 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)
  283. self.report({'INFO'}, '"'+group[self.idx].name+'"' + " Group exported." )
  284. msg = "Export Group "+group[self.idx].name
  285. bpy.ops.ed.undo_push(message="")
  286. bpy.ops.ed.undo()
  287. bpy.ops.ed.undo_push(message=msg)
  288. else:
  289. self.report({'INFO'}, "Define Export Name and Export Path." )
  290. return{'FINISHED'}
  291. class add_export_group(bpy.types.Operator):
  292. bl_idname = "scene.godot_add_export_group"
  293. bl_label = "Adds a new export Group"
  294. bl_description = "Creates a new Export Group with the selected Objects assigned to it."
  295. def execute(self,context):
  296. scene = context.scene
  297. item = scene.godot_export_groups.add()
  298. item.name = "New Group"
  299. for object in context.selected_objects:
  300. node = item.nodes.add()
  301. node.name = object.name
  302. scene.godot_export_groups_index = len(scene.godot_export_groups)-1
  303. bpy.ops.ed.undo_push(message="Create New Export Group")
  304. return{'FINISHED'}
  305. class del_export_group(bpy.types.Operator):
  306. bl_idname = "scene.godot_delete_export_group"
  307. bl_label = "Delets the selected export Group"
  308. bl_description = "Delets the active Export Group."
  309. def invoke(self, context, event):
  310. wm = context.window_manager
  311. return wm.invoke_confirm(self,event)
  312. def execute(self,context):
  313. scene = context.scene
  314. scene.godot_export_groups.remove(scene.godot_export_groups_index)
  315. if scene.godot_export_groups_index > 0:
  316. scene.godot_export_groups_index -= 1
  317. bpy.ops.ed.undo_push(message="Delete Export Group")
  318. return{'FINISHED'}
  319. class godot_node_list(bpy.types.PropertyGroup):
  320. name = StringProperty()
  321. class godot_export_groups(bpy.types.PropertyGroup):
  322. name = StringProperty(name="Group Name")
  323. export_name = StringProperty(name="scene_name")
  324. nodes = CollectionProperty(type=godot_node_list)
  325. export_path = StringProperty(subtype="DIR_PATH")
  326. active = BoolProperty(default=True,description="Export Group")
  327. 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'})
  328. apply_scale = BoolProperty(name="Apply Scale",description="Apply Scale before export.",default=False)
  329. apply_rot = BoolProperty(name="Apply Rotation",description="Apply Rotation before export.",default=False)
  330. apply_loc = BoolProperty(name="Apply Location",description="Apply Location before export.",default=False)
  331. use_export_selected = BoolProperty(name="Selected Objects",description="Export only selected objects (and visible in active layers if that applies).",default=True)
  332. use_mesh_modifiers = BoolProperty(name="Apply Modifiers",description="Apply modifiers to mesh objects (on a copy!).",default=True)
  333. use_tangent_arrays = BoolProperty(name="Tangent Arrays",description="Export Tangent and Binormal arrays (for normalmapping).",default=False)
  334. use_triangles = BoolProperty(name="Triangulate",description="Export Triangles instead of Polygons.",default=False)
  335. use_copy_images = BoolProperty(name="Copy Images",description="Copy Images (create images/ subfolder)",default=False)
  336. use_active_layers = BoolProperty(name="Active Layers",description="Export only objects on the active layers.",default=True)
  337. use_exclude_ctrl_bones = BoolProperty(name="Exclude Control Bones",description="Exclude skeleton bones with names that begin with 'ctrl'.",default=True)
  338. use_anim = BoolProperty(name="Export Animation",description="Export keyframe animation",default=False)
  339. use_anim_action_all = BoolProperty(name="All Actions",description=("Export all actions for the first armature found in separate DAE files"),default=False)
  340. 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)
  341. use_anim_optimize = BoolProperty(name="Optimize Keyframes",description="Remove double keyframes",default=True)
  342. 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)
  343. use_metadata = BoolProperty(name="Use Metadata",default=True,options={'HIDDEN'})
  344. use_include_particle_duplicates = BoolProperty(name="Include Particle Duplicates",default=True)
  345. def register():
  346. bpy.utils.register_class(godot_export_manager)
  347. bpy.utils.register_class(godot_node_list)
  348. bpy.utils.register_class(godot_export_groups)
  349. bpy.utils.register_class(add_export_group)
  350. bpy.utils.register_class(del_export_group)
  351. bpy.utils.register_class(export_all_groups)
  352. bpy.utils.register_class(export_groups_autosave)
  353. bpy.utils.register_class(export_group)
  354. bpy.utils.register_class(add_objects_to_group)
  355. bpy.utils.register_class(del_objects_from_group)
  356. bpy.utils.register_class(select_group_objects)
  357. bpy.utils.register_class(UI_List_Godot)
  358. bpy.types.Scene.godot_export_groups = CollectionProperty(type=godot_export_groups)
  359. bpy.types.Scene.godot_export_groups_index = IntProperty(default=0,min=0)
  360. def unregister():
  361. bpy.utils.unregister_class(godot_export_manager)
  362. bpy.utils.unregister_class(godot_node_list)
  363. bpy.utils.unregister_class(godot_export_groups)
  364. bpy.utils.unregister_class(export_groups_autosave)
  365. bpy.utils.unregister_class(add_export_group)
  366. bpy.utils.unregister_class(del_export_group)
  367. bpy.utils.unregister_class(export_all_groups)
  368. bpy.utils.unregister_class(export_group)
  369. bpy.utils.unregister_class(add_objects_to_group)
  370. bpy.utils.unregister_class(del_objects_from_group)
  371. bpy.utils.unregister_class(select_group_objects)
  372. bpy.utils.unregister_class(UI_List_Godot)
  373. @persistent
  374. def auto_export(dummy):
  375. bpy.ops.scene.godot_export_groups_autosave()
  376. bpy.app.handlers.save_post.append(auto_export)
  377. if __name__ == "__main__":
  378. register()