collada_bmd_bdl_export.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. '''
  2. just a DAE exporter.
  3. it was supposed to only rotate the meshes around the X axis by -90 degrees
  4. but above that it now checks for armature and vertex group stuff
  5. only exports what is selected
  6. '''
  7. import bpy, math
  8. from .my_functions import *
  9. ####################################################
  10. # transform_apply_parent_child function
  11. # transform_apply the parent and its child meshes
  12. # selects the parent object at the end
  13. #
  14. # scene --> Blender scene in which the object exists
  15. # parent_object --> object that has child objects
  16. # to which the transform apply
  17. # operator will be applied
  18. # loc (bool) --> apply location
  19. # rot (bool) --> apply rotation
  20. # sca (bool) --> apply scaling
  21. ####################################################
  22. def transform_apply_parent_child(scene, parent_object, loc, rot, sca):
  23. #
  24. # parent object must be selected and active for the function to work
  25. bpy.ops.object.transform_apply(location = loc, rotation = rot, scale = sca)
  26. # armature child mesh scaling
  27. for child_mesh in parent_object.children:
  28. # empty selection and active object
  29. bpy.ops.object.select_all(action = 'DESELECT')
  30. scene.objects.active = None
  31. # select and activate child mesh
  32. child_mesh.select = True
  33. scene.objects.active = child_mesh
  34. # apply scale and rotation
  35. bpy.ops.object.transform_apply(location = loc, rotation = rot, scale = sca)
  36. # select parent_object
  37. bpy.ops.object.select_all(action='DESELECT')
  38. scene.objects.active = None
  39. parent_object.select = True
  40. scene.objects.active = parent_object
  41. #
  42. ################################################
  43. # write_bmd_bdl_collada function (MAIN FUNCTION)
  44. # function to write custom collada file for
  45. # SuperBMD conversion
  46. ################################################
  47. def write_bmd_bdl_collada(context, filepath, triangulate):
  48. #
  49. print("\nWriting Collada file for BMD/BDL conversion\n")
  50. # this thing is always needed for stuff
  51. scene = bpy.context.scene
  52. # if nothing is selected end the exporter
  53. if (scene.objects.active == None
  54. or
  55. scene.objects.active.type != 'ARMATURE'):
  56. error_string = "No Armature object selected. Select one and try again."
  57. print("\n### ERROR ###\n" + error_string + "\n### ERROR ###\n")
  58. show_message(error_string, "Error exporting collada file", 'ERROR')
  59. return {'FINISHED'}
  60. # get armature object
  61. armature = scene.objects.active
  62. print("Armature found: %s" % armature.name)
  63. print()
  64. # change to object view
  65. bpy.ops.object.mode_set(mode='OBJECT')
  66. # deselect everything (to be sure nothing weird happens)
  67. bpy.ops.object.select_all(action='DESELECT')
  68. scene.objects.active = None
  69. # re-select the armature object only
  70. armature.select = True
  71. scene.objects.active = armature
  72. # start mesh stuff
  73. print("\n### Mesh stuff ###")
  74. ##################################
  75. # check the meshes on the armature
  76. for object in armature.children:
  77. #
  78. if (object.type == "MESH"):
  79. #
  80. print()
  81. print("Mesh: \"" + object.name + "\"")
  82. print()
  83. ####################################
  84. # select the mesh and make it active
  85. object.select = True
  86. scene.objects.active = object
  87. # reset scale, rotation and position of the mesh
  88. bpy.ops.object.transform_apply(location = True, rotation = True, scale = True)
  89. ##############################
  90. # check mesh armature modifier
  91. has_arm_mod = False
  92. for modifiers in object.modifiers:
  93. if (modifiers.name == "Armature"):
  94. has_arm_mod = True
  95. if (has_arm_mod):
  96. print("Mesh \"" + object.name + "\" has an Armature Modifier")
  97. if (object.modifiers["Armature"].object != armature):
  98. print("But not assigned to the right Armature Object. Re-assigning...")
  99. object.modifiers["Armature"].object = armature
  100. else:
  101. print("And it is assigned to the right Armature Object: \"" + armature.name + "\"")
  102. else:
  103. print("Mesh \"" + object.name + "\" does not have an Armature Modifier")
  104. print("Creating Modifier...")
  105. bpy.ops.object.modifier_add(type='ARMATURE')
  106. print("Assigning Modifier object to: \"" + armature.name + "\"")
  107. object.modifiers["Armature"].object = armature
  108. # check if the mesh contains vertex groups
  109. # if not, assign one that links to the main armature bone
  110. # and weight all vertex in the mesh to it, otherwise leave
  111. # the mesh in peace (mesh might not be weighted)
  112. # probably make a weight checker for each mesh so the exporter is complete
  113. is_weighted = True # variable used later
  114. if (object.vertex_groups.active == None):
  115. print("Mesh \"" + object.name + "\" does not have any Vertex Group assigned.")
  116. # change to edit mode (for vertex selection stuff)
  117. bpy.ops.object.mode_set(mode='EDIT')
  118. # get vertex group name (first armature bone name)
  119. ver_group_name = armature.data.bones[0].name
  120. print("Creating Vertex Group: \"" + ver_group_name + "\"")
  121. # create new vertex group for mesh
  122. object.vertex_groups.new(name = ver_group_name)
  123. print("Assigning all vertex in mesh to Vertex Group...")
  124. # select all vertex in mesh
  125. bpy.ops.mesh.select_all(action='SELECT')
  126. #get vertex group created
  127. ver_group = object.vertex_groups[ver_group_name]
  128. # set active vertex group in mesh to new vertex group created
  129. object.vertex_groups.active = ver_group
  130. # assign all vertex of the mesh to said vertex group
  131. bpy.ops.object.vertex_group_assign()
  132. print("Vertex assigned.")
  133. # go back to object mode
  134. bpy.ops.object.mode_set(mode='OBJECT')
  135. else:
  136. # if the mesh contains vertex groups then they might not
  137. # be correctly linked to the bones on an armature,
  138. # in other words, the mesh might contain a vertex group which name
  139. # isn't the name of a bone in the armature object
  140. # (this will throw a SuperBMD error)
  141. # Assimp.AssimpException: Error importing file: Collada: [DAE filename].dae - Invalid contents in element "n".
  142. if (len(object.vertex_groups) > len(armature.data.bones)):
  143. error_string = "Mesh has a vertex group that isn't related to any bone of the Armature.\nCheck the meshes and try again."
  144. print("\n### ERROR ###\n" + error_string + "\n### ERROR ###\n")
  145. show_message(error_string, "Error exporting collada file", 'ERROR')
  146. return {'FINISHED'}
  147. else:
  148. for ver_group in object.vertex_groups:
  149. valid_vertex_group = False
  150. for bone in armature.data.bones:
  151. if (ver_group.name == bone.name):
  152. valid_vertex_group = True
  153. break
  154. if (valid_vertex_group == False):
  155. error_string = "Mesh has a vertex group that isn't related to any bone of the Armature.\nCheck the meshes and try again."
  156. print("\n### ERROR ###\n" + error_string + "\n### ERROR ###\n")
  157. show_message(error_string, "Error exporting collada file", 'ERROR')
  158. return {'FINISHED'}
  159. # also the group might be valid but the vertex in
  160. # the mesh might not be weigthed in the vertex group
  161. # print message saying that the mesh might not be weighted
  162. # even if it has a vertex group (set is_weighted to False)
  163. ########################################
  164. # make loop to check for weights (later)
  165. ########################################
  166. print("Mesh \"" + object.name + "\" has a Vertex Group assigned.")
  167. print("Mesh might not be weighted.")
  168. is_weighted = False
  169. # deselect mesh at the end of each loop
  170. object.select = False
  171. scene.objects.active = None
  172. #
  173. #
  174. print("\n### Mesh stuff done ###")
  175. print()
  176. #################################################################
  177. # final part of the collada export
  178. # print messages on vertex weight given by the is_weight variable
  179. if (is_weighted):
  180. print("All meshes are weighted")
  181. else:
  182. print("All meshes are probably weighted")
  183. print("Finalizing export...\n")
  184. # select armature object and make it active
  185. # do the -90 degree rotation on X axis and scale it to 100
  186. # then do transform_apply, do transform_apply on the meshes
  187. # inside the armature and finally export the result into a Collada file
  188. # the 100 scaling is done because SuperBMD isn't able
  189. # to take the scaling factor on the DAE model and apply it to
  190. # the mesh on the BMD/BDL conversion (it just ignores it)
  191. armature.select = True
  192. scene.objects.active = armature
  193. # somehow the 2 statements before this comment change
  194. # the armature object into pose mode (???)
  195. # bpy.ops.object.mode_set(mode='OBJECT')
  196. # armature scale/rotation
  197. print("Rotating Armature -90 degrees on the X axis...")
  198. armature.rotation_euler[0] = math.radians(-90)
  199. print("Scaling to 100...")
  200. armature.scale = (100, 100, 100)
  201. # transform_apply the armature and its meshes
  202. transform_apply_parent_child(scene, armature, False, True, True)
  203. # export model (selection only)
  204. print("Exporting Model...")
  205. bpy.ops.wm.collada_export(filepath = filepath,
  206. use_blender_profile = False,
  207. selected = True,
  208. include_children = True,
  209. triangulate = triangulate)
  210. print("Collada file (DAE) exported.")
  211. # after export reset object rotation and scaling to original state
  212. # i.e. apply a 90 degree rotation on X axis and scale to 0.01
  213. # then do transform_apply
  214. # armature scale/rotation
  215. print("Reversing the -90 degrees rotation on the X axis...")
  216. armature.rotation_euler[0] = math.radians(90)
  217. print("Reversing the 100 scaling...")
  218. armature.scale = (0.01, 0.01, 0.01)
  219. # transform_apply the armature and its meshes
  220. transform_apply_parent_child(scene, armature, False, True, True)
  221. print("\n### Done! ###\n")
  222. return {'FINISHED'}
  223. #
  224. #################################################
  225. # Stuff down is for the menu appending
  226. # of the exporter to work plus some setting stuff
  227. # comes from a Blender importer template
  228. #################################################
  229. from bpy_extras.io_utils import ExportHelper
  230. from bpy.props import StringProperty, BoolProperty, EnumProperty
  231. from bpy.types import Operator
  232. class Export_BMD_BDL_Collada(Operator, ExportHelper):
  233. #
  234. """Save a Collada file for BMD/BDL conversion with SuperBMD"""
  235. bl_idname = "export_scene.collada_bmd_bdl"
  236. bl_label = "Export Collada (for BMD/BDL)"
  237. filename_ext = ".dae"
  238. filter_glob = StringProperty( default="*.dae",
  239. options={'HIDDEN'},
  240. maxlen=255,
  241. )
  242. triangulate = BoolProperty( name = "Triangulate meshes",
  243. description = "Triangulate meshes inside armatures",
  244. default = False,
  245. )
  246. def execute(self, context):
  247. return write_bmd_bdl_collada(context, self.filepath, self.triangulate)
  248. #
  249. def menu_export_bmd_bdl_collada(self, context):
  250. self.layout.operator(Export_BMD_BDL_Collada.bl_idname, text="Collada (for BMD/BDL) (.dae)")
  251. bpy.utils.register_class(Export_BMD_BDL_Collada)
  252. bpy.types.INFO_MT_file_export.append(menu_export_bmd_bdl_collada)
  253. # test call
  254. bpy.ops.export_scene.collada_bmd_bdl('INVOKE_DEFAULT')