''' just a DAE exporter. it was supposed to only rotate the meshes around the X axis by -90 degrees but above that it now checks for armature and vertex group stuff only exports an armature object selection ''' import bpy, math from .my_functions import * #################################################### # transform_apply_parent_child function # transform_apply the parent and its child meshes # selects the parent object at the end # # scene --> Blender scene in which the object exists # parent_object --> object that has child objects # to which the transform apply # operator will be applied # loc (bool) --> apply location # rot (bool) --> apply rotation # sca (bool) --> apply scaling #################################################### def transform_apply_parent_child(scene, parent_object, loc, rot, sca): # # parent object must be selected and active for the function to work bpy.ops.object.transform_apply(location = loc, rotation = rot, scale = sca) # armature child mesh scaling for child_mesh in parent_object.children: # empty selection and active object bpy.ops.object.select_all(action = 'DESELECT') scene.objects.active = None # select and activate child mesh child_mesh.select = True scene.objects.active = child_mesh # apply scale and rotation bpy.ops.object.transform_apply(location = loc, rotation = rot, scale = sca) # select parent_object bpy.ops.object.select_all(action='DESELECT') scene.objects.active = None parent_object.select = True scene.objects.active = parent_object # ################################################ # write_bmd_bdl_collada function (MAIN FUNCTION) # function to write custom collada file for # SuperBMD conversion ################################################ def write_bmd_bdl_collada(context, filepath, triangulate): # print("\nWriting Collada file for BMD/BDL conversion\n") # this thing is always needed for stuff scene = bpy.context.scene # if nothing is selected end the exporter if (scene.objects.active == None or scene.objects.active.type != 'ARMATURE'): error_string = "No Armature object selected. Select one and try again." print("\n### ERROR ###\n" + error_string + "\n### ERROR ###\n") show_message(error_string, "Error exporting collada file", 'ERROR') return {'FINISHED'} # get armature object armature = scene.objects.active print("Armature found: %s" % armature.name) print() # change to object view bpy.ops.object.mode_set(mode='OBJECT') # deselect everything (to be sure nothing weird happens) bpy.ops.object.select_all(action='DESELECT') scene.objects.active = None # re-select the armature object only armature.select = True scene.objects.active = armature # start mesh stuff print("\n### Mesh stuff ###") ################################## # check the meshes on the armature for object in armature.children: # if (object.type == "MESH"): # print() print("Mesh: \"" + object.name + "\"") print() #################################### # select the mesh and make it active object.select = True scene.objects.active = object # change mesh to object view (in case it isn't) bpy.ops.object.mode_set(mode='OBJECT') # reset scale, rotation and position of the mesh bpy.ops.object.transform_apply(location = True, rotation = True, scale = True) ############################## # check mesh armature modifier has_arm_mod = False for modifiers in object.modifiers: if (modifiers.name == "Armature"): has_arm_mod = True if (has_arm_mod): print("Mesh \"" + object.name + "\" has an Armature Modifier") if (object.modifiers["Armature"].object != armature): print("But not assigned to the right Armature Object. Re-assigning...") object.modifiers["Armature"].object = armature else: print("And it is assigned to the right Armature Object: \"" + armature.name + "\"") else: print("Mesh \"" + object.name + "\" does not have an Armature Modifier") print("Creating Modifier...") bpy.ops.object.modifier_add(type='ARMATURE') print("Assigning Modifier object to: \"" + armature.name + "\"") object.modifiers["Armature"].object = armature # check if the mesh contains vertex groups # if not, assign one that links to the main armature bone # and weight all vertex in the mesh to it, otherwise leave # the mesh in peace (mesh might not be weighted) # probably make a weight checker for each mesh so the exporter is complete is_weighted = True # variable used later if (object.vertex_groups.active == None): print("Mesh \"" + object.name + "\" does not have any Vertex Group assigned.") # change to edit mode (for vertex selection stuff) bpy.ops.object.mode_set(mode='EDIT') # get vertex group name (first armature bone name) ver_group_name = armature.data.bones[0].name print("Creating Vertex Group: \"" + ver_group_name + "\"") # create new vertex group for mesh object.vertex_groups.new(name = ver_group_name) print("Assigning all vertex in mesh to Vertex Group...") # select all vertex in mesh bpy.ops.mesh.select_all(action='SELECT') #get vertex group created ver_group = object.vertex_groups[ver_group_name] # set active vertex group in mesh to new vertex group created object.vertex_groups.active = ver_group # assign all vertex of the mesh to said vertex group bpy.ops.object.vertex_group_assign() print("Vertex assigned.") # go back to object mode bpy.ops.object.mode_set(mode='OBJECT') else: # if the mesh contains vertex groups then they might not # be correctly linked to the bones on an armature, # in other words, the mesh might contain a vertex group which name # isn't the name of a bone in the armature object # (this will throw a SuperBMD error) # Assimp.AssimpException: Error importing file: Collada: [DAE filename].dae - Invalid contents in element "n". if (len(object.vertex_groups) > len(armature.data.bones)): error_string = "Mesh has a vertex group that isn't related to any bone of the Armature.\nCheck the meshes and try again." print("\n### ERROR ###\n" + error_string + "\n### ERROR ###\n") show_message(error_string, "Error exporting collada file", 'ERROR') return {'FINISHED'} else: for ver_group in object.vertex_groups: valid_vertex_group = False for bone in armature.data.bones: if (ver_group.name == bone.name): valid_vertex_group = True break if (valid_vertex_group == False): error_string = "Mesh has a vertex group that isn't related to any bone of the Armature.\nCheck the meshes and try again." print("\n### ERROR ###\n" + error_string + "\n### ERROR ###\n") show_message(error_string, "Error exporting collada file", 'ERROR') return {'FINISHED'} # also the group might be valid but the vertex in # the mesh might not be weigthed in the vertex group # print message saying that the mesh might not be weighted # even if it has a vertex group (set is_weighted to False) ######################################## # make loop to check for weights (later) ######################################## print("Mesh \"" + object.name + "\" has a Vertex Group assigned.") print("Mesh might not be weighted.") is_weighted = False # deselect mesh at the end of each loop object.select = False scene.objects.active = None # # print("\n### Mesh stuff done ###") print() ################################################################# # final part of the collada export # print messages on vertex weight given by the is_weight variable if (is_weighted): print("All meshes are weighted") else: print("All meshes are probably weighted") print("Finalizing export...\n") # select armature object and make it active # do the -90 degree rotation on X axis and scale it to 100 # then do transform_apply, do transform_apply on the meshes # inside the armature and finally export the result into a Collada file # the 100 scaling is done because SuperBMD isn't able # to take the scaling factor on the DAE model and apply it to # the mesh on the BMD/BDL conversion (it just ignores it) armature.select = True scene.objects.active = armature # somehow the 2 statements before this comment change # the armature object into pose mode (???) # bpy.ops.object.mode_set(mode='OBJECT') # armature scale/rotation print("Rotating Armature -90 degrees on the X axis...") armature.rotation_euler[0] = math.radians(-90) print("Scaling to 100...") armature.scale = (100, 100, 100) # transform_apply the armature and its meshes transform_apply_parent_child(scene, armature, False, True, True) # export model (selection only) print("Exporting Model...") bpy.ops.wm.collada_export(filepath = filepath, use_blender_profile = False, selected = True, include_children = True, triangulate = triangulate) print("Collada file (DAE) exported.") # after export reset object rotation and scaling to original state # i.e. apply a 90 degree rotation on X axis and scale to 0.01 # then do transform_apply # armature scale/rotation print("Reversing the -90 degrees rotation on the X axis...") armature.rotation_euler[0] = math.radians(90) print("Reversing the 100 scaling...") armature.scale = (0.01, 0.01, 0.01) # transform_apply the armature and its meshes transform_apply_parent_child(scene, armature, False, True, True) print("\n### Done! ###\n") return {'FINISHED'} # ################################################# # Stuff down is for the menu appending # of the exporter to work plus some setting stuff # comes from a Blender importer template ################################################# from bpy_extras.io_utils import ExportHelper from bpy.props import StringProperty, BoolProperty, EnumProperty from bpy.types import Operator class Export_BMD_BDL_Collada(Operator, ExportHelper): # """Save a Collada file for BMD/BDL conversion with SuperBMD""" bl_idname = "export_scene.collada_bmd_bdl" bl_label = "Export Collada (for BMD/BDL)" filename_ext = ".dae" filter_glob = StringProperty( default="*.dae", options={'HIDDEN'}, maxlen=255, ) triangulate = BoolProperty( name = "Triangulate meshes", description = "Triangulate meshes inside armatures", default = False, ) def execute(self, context): return write_bmd_bdl_collada(context, self.filepath, self.triangulate) # def menu_export_bmd_bdl_collada(self, context): self.layout.operator(Export_BMD_BDL_Collada.bl_idname, text="Collada (for BMD/BDL) (.dae)") bpy.utils.register_class(Export_BMD_BDL_Collada) bpy.types.INFO_MT_file_export.append(menu_export_bmd_bdl_collada) # test call bpy.ops.export_scene.collada_bmd_bdl('INVOKE_DEFAULT')