collada_superbmd_export.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  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. # export the object
  93. bpy.ops.wm.collada_export(filepath = filepath, use_blender_profile = False,
  94. selected = True, include_children = True,
  95. triangulate = triangulate)
  96. # delete the duplicate object
  97. blender_funcs.select_obj(scene, armature, True)
  98. bpy.ops.object.delete(use_global = False)
  99. # re-assign the original object names
  100. armature = old_armature
  101. armature.name = obj_name
  102. armature.data.name = obj_name
  103. for i in range(0, len(armature.children)):
  104. armature.children[i].name = child_names[i]
  105. armature.children[i].data.name = child_names[i]
  106. # done!
  107. blender_funcs.select_obj(scene, armature, False)
  108. blender_funcs.disp_msg("Armature \"%s\" exported!" % (armature.name))
  109. return {'FINISHED'}
  110. # Stuff down is for the menu appending
  111. # of the exporter to work plus some setting stuff
  112. # comes from a Blender importer template
  113. from bpy_extras.io_utils import ExportHelper
  114. from bpy.props import StringProperty, BoolProperty, EnumProperty
  115. from bpy.types import Operator
  116. class export_superbmd_collada(Operator, ExportHelper):
  117. """Export a Collada file for SuperBMD (SuperBMD only)"""
  118. bl_idname = "export_scene.superbmd_collada"
  119. bl_label = "Export SuperBMD Collada (.DAE)"
  120. filename_ext = ".dae"
  121. filter_glob = StringProperty(default = "*.dae", options = {'HIDDEN'}, maxlen = 255)
  122. triangulate = BoolProperty(name = "Triangulate meshes",
  123. description = "Triangulate meshes inside armatures",
  124. default = False)
  125. def execute(self, context):
  126. return write_bmd_bdl_collada(context, self.filepath, self.triangulate)
  127. # Only needed if you want to add into a dynamic menu
  128. def menu_export_superbmd_collada(self, context):
  129. self.layout.operator(export_superbmd_collada.bl_idname, text="SuperBMD Collada (.dae)")
  130. bpy.utils.register_class(export_superbmd_collada)
  131. bpy.types.INFO_MT_file_export.append(menu_export_superbmd_collada)
  132. # test call
  133. bpy.ops.export_scene.superbmd_collada('INVOKE_DEFAULT')