collada_superbmd_export.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import bpy, math
  2. from . import blender_funcs
  3. # just a DAE exporter.
  4. # it was supposed to only rotate the meshes around the X axis by -90 degrees
  5. # but above that it now checks for armature and vertex group stuff
  6. # only exports the armature object selected
  7. # write_bmd_bdl_collada function
  8. # function to write custom collada file for
  9. # SuperBMD conversion
  10. def write_bmd_bdl_collada(context, filepath, triangulate):
  11. # this thing is always needed for stuff
  12. scene = bpy.context.scene
  13. # if nothing is selected end the exporter
  14. if (scene.objects.active == None or scene.objects.active.type != 'ARMATURE'):
  15. if (scene.objects.active == None):
  16. blender_funcs.disp_msg("No Armature selected. Select one and try again.")
  17. else:
  18. blender_funcs.disp_msg("No Armature selected. Currently selecting: \"%s\""
  19. % (scene.objects.active.name))
  20. return {'FINISHED'}
  21. # get armature object
  22. armature = scene.objects.active
  23. print("\nArmature found: \"%s\"" % (armature.name))
  24. # check if the armature contains only mesh objects inside
  25. for obj in armature.children:
  26. if (obj.type != 'MESH'):
  27. blender_funcs.disp_msg("\"%s\": contains non-mesh object (%s)."
  28. % (armature.name, obj.name))
  29. return {'FINISHED'}
  30. # check if all meshes have an armature modifier that is
  31. # assigned to the armature object and it is binded to
  32. # vertex groups if none of those are just create them,
  33. # I don't think it is bad to automate that
  34. for mesh in armature.children:
  35. if("Armature" not in mesh.modifiers):
  36. blender_funcs.disp_msg("\"%s\": has no armature modifier. Adding one..." % (mesh.name))
  37. blender_funcs.select_obj(scene, mesh, False)
  38. bpy.ops.object.modifier_add(type = 'ARMATURE')
  39. mesh.modifiers["Armature"].object = armature
  40. mesh.modifiers["Armature"].use_vertex_groups = True
  41. else: # ensure bind is to a vertex group
  42. if (mesh.modifiers["Armature"].use_vertex_groups == False):
  43. blender_funcs.disp_msg("\"%s\": armature modifier wasn't binded to vertex groups"
  44. % (mesh.name))
  45. mesh.modifiers["Armature"].use_vertex_groups = True
  46. # check if all the vertex groups in each mesh correspond to the name of a skeleton bone
  47. bone_name_list = []
  48. for bone in armature.data.bones:
  49. bone_name_list.append(bone.name)
  50. # vertex group check
  51. for mesh in armature.children:
  52. for v_group in mesh.vertex_groups:
  53. if (v_group.name not in bone_name_list):
  54. blender_funcs.disp_msg(("\"%s\": contains non-valid vert group \"%s\"."
  55. % (mesh.name, v_group.name)) + " Unable to continue.")
  56. return {'FINISHED'}
  57. # vertex weight check
  58. for mesh in armature.children:
  59. for vertex in mesh.data.vertices:
  60. if (len(vertex.groups) == 0):
  61. blender_funcs.disp_msg(("\"%s\": contains unweighted vertices."
  62. % (mesh.name)) + " Unable to continue.")
  63. return {'FINISHED'}
  64. # get the object names
  65. obj_name = armature.name
  66. child_names = []
  67. for child in armature.children:
  68. child_names.append(child.name)
  69. # change to object view and make a copy of the armature object (with its children)
  70. bpy.ops.object.mode_set(mode='OBJECT')
  71. blender_funcs.select_obj(scene, armature, True)
  72. bpy.ops.object.duplicate(linked = True)
  73. bpy.ops.object.make_single_user(type = 'SELECTED_OBJECTS', object = True, obdata = True)
  74. old_armature = armature
  75. armature = scene.objects.active
  76. blender_funcs.select_obj(scene, armature, False)
  77. # apply the transformations to the armature
  78. armature.rotation_euler[0] = math.radians(-90)
  79. armature.scale = 100 * armature.scale
  80. bpy.ops.object.transform_apply(location = True, rotation = True, scale = True)
  81. # apply the transformations to the meshes inside the armature
  82. for i in range(0, len(armature.children)):
  83. blender_funcs.select_obj(scene, armature.children[i], False)
  84. bpy.ops.object.transform_apply(location = True, rotation = True, scale = True)
  85. blender_funcs.select_obj(scene, armature, False)
  86. # handle the object names so that they match the original model names
  87. armature.name = obj_name
  88. armature.data.name = obj_name
  89. for i in range(0, len(armature.children)):
  90. armature.children[i].name = child_names[i]
  91. armature.children[i].data.name = child_names[i]
  92. # store the new bind_mat and rest_mat (the user might want to update them)
  93. for data_bone in armature.data.bones:
  94. # related pose bone
  95. pose_bone = armature.pose.bones[data_bone.name]
  96. # bind matrix
  97. bpy.data.armatures[armature.data.name].pose_position = 'REST'
  98. bpy.context.scene.update() # update scene
  99. blender_funcs.set_bone_bind_mat(data_bone, pose_bone.matrix)
  100. # rest matrix
  101. bpy.data.armatures[armature.data.name].pose_position = 'POSE'
  102. bpy.context.scene.update() # update scene
  103. mat = None
  104. if (pose_bone.parent != None):
  105. mat = pose_bone.parent.matrix.inverted() * pose_bone.matrix
  106. else:
  107. mat = pose_bone.matrix
  108. blender_funcs.set_bone_rest_mat(data_bone, mat)
  109. # export the object
  110. bpy.ops.wm.collada_export(filepath = filepath, use_blender_profile = False,
  111. selected = True, include_children = True,
  112. triangulate = triangulate)
  113. # delete the duplicate object
  114. blender_funcs.select_obj(scene, armature, True)
  115. bpy.ops.object.delete(use_global = False)
  116. # re-assign the original object names
  117. armature = old_armature
  118. armature.name = obj_name
  119. armature.data.name = obj_name
  120. for i in range(0, len(armature.children)):
  121. armature.children[i].name = child_names[i]
  122. armature.children[i].data.name = child_names[i]
  123. # done!
  124. blender_funcs.select_obj(scene, armature, False)
  125. blender_funcs.disp_msg("Armature \"%s\" exported!" % (armature.name))
  126. return {'FINISHED'}
  127. # Stuff down is for the menu appending
  128. # of the exporter to work plus some setting stuff
  129. # comes from a Blender importer template
  130. from bpy_extras.io_utils import ExportHelper
  131. from bpy.props import StringProperty, BoolProperty, EnumProperty
  132. from bpy.types import Operator
  133. class export_superbmd_collada(Operator, ExportHelper):
  134. """Export a Collada file for SuperBMD (SuperBMD only)"""
  135. bl_idname = "export_scene.superbmd_collada"
  136. bl_label = "Export SuperBMD Collada (.DAE)"
  137. filename_ext = ".dae"
  138. filter_glob = StringProperty(default = "*.dae", options = {'HIDDEN'}, maxlen = 255)
  139. triangulate = BoolProperty(name = "Triangulate meshes",
  140. description = "Triangulate meshes inside armatures",
  141. default = False)
  142. def execute(self, context):
  143. return write_bmd_bdl_collada(context, self.filepath, self.triangulate)
  144. # Only needed if you want to add into a dynamic menu
  145. def menu_export_superbmd_collada(self, context):
  146. self.layout.operator(export_superbmd_collada.bl_idname, text="SuperBMD Collada (.dae)")
  147. bpy.utils.register_class(export_superbmd_collada)
  148. bpy.types.INFO_MT_file_export.append(menu_export_superbmd_collada)
  149. # test call
  150. bpy.ops.export_scene.superbmd_collada('INVOKE_DEFAULT')