diff options
author | Isaac <isaclien9752@gmail.com> | 2025-08-08 15:03:57 -0400 |
---|---|---|
committer | Isaac <isaclien9752@gmail.com> | 2025-08-08 15:03:57 -0400 |
commit | dc612a18b60341b233fcac00f99405d98b61c01a (patch) | |
tree | 0a914f90bb69ed665287ca8701d000aa8c470da8 | |
parent | 46fb63ea8d3c33d5cb5914a05998c8f6ff74abea (diff) | |
download | blenxy-dc612a18b60341b233fcac00f99405d98b61c01a.tar.gz blenxy-dc612a18b60341b233fcac00f99405d98b61c01a.zip |
small stuff on collada / OBJ exporter for KCL / importer for NeoKCLCreate OBJs
-rw-r--r-- | __init__.py | 16 | ||||
-rw-r--r-- | collada_superbmd_export.py | 10 | ||||
-rw-r--r-- | collada_superbmd_import.py | 2 | ||||
-rw-r--r-- | obj_kcl_export.py | 73 | ||||
-rw-r--r-- | obj_neokclcreate_import.py | 87 |
5 files changed, 178 insertions, 10 deletions
diff --git a/__init__.py b/__init__.py index b360c57..5f5b5fc 100644 --- a/__init__.py +++ b/__init__.py @@ -19,13 +19,15 @@ from bpy.app.handlers import persistent # custom CSV animation file (for BCK) exporter/importer @persistent -def set_blenxy_env(dummy): # "dummy" is a variable that is somehow - # passed to set_blenxy_env when called by - # bpy.app.handlers.load_post.append - from . import required_modules # install needed modules for bundled python (awful) - from . import basic_settings # settings blenxy has - from . import collada_superbmd_import # "custom" importer for SuperBMD collada files - from . import collada_superbmd_export # "custom" exporter for SuperBMD collada files +def set_blenxy_env(dummy): # "dummy" is a variable that is somehow + # passed to set_blenxy_env when called by + # bpy.app.handlers.load_post.append + from . import required_modules # install needed modules for bundled python (awful) + from . import basic_settings # settings blenxy has + from . import collada_superbmd_import # "custom" importer for SuperBMD collada files + from . import collada_superbmd_export # "custom" exporter for SuperBMD collada files + from . import obj_kcl_export # "custom" exporter for OBJ files (Collision) + from . import obj_neokclcreate_import # "custom" importer for OBJ files (Colllision, NeoKCLCreate) # ~ from . import csv_anim_bck_export # exporter for CSV files for BCK conversion # ~ from . import csv_anim_bck_import # importer for CSV files from BCK animation diff --git a/collada_superbmd_export.py b/collada_superbmd_export.py index 5d6f83f..ad1363b 100644 --- a/collada_superbmd_export.py +++ b/collada_superbmd_export.py @@ -26,6 +26,8 @@ def write_bmd_bdl_collada(context, filepath, triangulate): # get armature object armature = scene.objects.active print("\nArmature found: \"%s\"" % (armature.name)) + # change to object mode + bpy.ops.object.mode_set(mode='OBJECT') # check if the armature contains only mesh objects inside for obj in armature.children: @@ -45,11 +47,15 @@ def write_bmd_bdl_collada(context, filepath, triangulate): bpy.ops.object.modifier_add(type = 'ARMATURE') mesh.modifiers["Armature"].object = armature mesh.modifiers["Armature"].use_vertex_groups = True - else: # ensure bind is to a vertex group + else: # ensure bind is to a vertex group and that the bind is to the actual parent armature if (mesh.modifiers["Armature"].use_vertex_groups == False): blender_funcs.disp_msg("\"%s\": armature modifier wasn't binded to vertex groups" % (mesh.name)) mesh.modifiers["Armature"].use_vertex_groups = True + if (mesh.modifiers["Armature"].object != armature): + blender_funcs.disp_msg("\"%s\": armature modifier was binded to another armature object" + % (mesh.name)) + mesh.modifiers["Armature"].object = armature; # check if all the vertex groups in each mesh correspond to the name of a skeleton bone bone_name_list = [] @@ -140,7 +146,7 @@ class export_superbmd_collada(Operator, ExportHelper): filter_glob = StringProperty(default = "*.dae", options = {'HIDDEN'}, maxlen = 255) triangulate = BoolProperty(name = "Triangulate meshes", - description = "Triangulate meshes inside armatures", + description = "Triangulate meshes inside the armature", default = False) def execute(self, context): return write_bmd_bdl_collada(context, self.filepath, self.triangulate) diff --git a/collada_superbmd_import.py b/collada_superbmd_import.py index 8187fb9..39e7ce6 100644 --- a/collada_superbmd_import.py +++ b/collada_superbmd_import.py @@ -40,7 +40,7 @@ def import_collada_superbmd(context, filepath): root = xml.getroot() # texture files path - if (os.name == "posix"): + if (os.name == "posix" and root.find("library_images") != None): for image in root.find("library_images"): tmp = image.find("init_from").text while (tmp[0] != '\\'): diff --git a/obj_kcl_export.py b/obj_kcl_export.py new file mode 100644 index 0000000..b106869 --- /dev/null +++ b/obj_kcl_export.py @@ -0,0 +1,73 @@ +import bpy, math +from . import blender_funcs + +# just a simple OBJ exporter wrapper +# it only selects all the meshes inside an armature and sets some OBJ exporter variables + +# write_kcl_obj function +# function to write custom obj file for kcl conversion +def write_kcl_obj(context, filepath): + + # 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'): + if (scene.objects.active == None): + blender_funcs.disp_msg("No Armature selected. Select one and try again.") + else: + blender_funcs.disp_msg("No Armature selected. Currently selecting: \"%s\"" + % (scene.objects.active.name)) + return {'FINISHED'} + + # get armature object + armature = scene.objects.active + print("\nArmature found: \"%s\"" % (armature.name)) + # change to object mode + bpy.ops.object.mode_set(mode='OBJECT') + + # check if the armature contains only mesh objects inside + for obj in armature.children: + if (obj.type != 'MESH'): + blender_funcs.disp_msg("\"%s\": contains non-mesh object (%s)." + % (armature.name, obj.name)) + return {'FINISHED'} + + # select all the meshes inside the armature + blender_funcs.select_obj(scene, armature, True) + + # export the object + bpy.ops.export_scene.obj(filepath = filepath, axis_up = "Y", axis_forward = "-Z", + use_selection = True, use_triangles = True, global_scale = 100, + use_materials = True, use_normals = True) + + # done! + blender_funcs.select_obj(scene, armature, False) + blender_funcs.disp_msg("Meshes from \"%s\" exported!" % (armature.name)) + 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_obj_kcl(Operator, ExportHelper): + """Export a OBJ file for KCL conversion""" + bl_idname = "export_scene.obj_kcl" + bl_label = "Export OBJ for KCL (.OBJ)" + filename_ext = ".obj" + filter_glob = StringProperty(default = "*.obj", options = {'HIDDEN'}, maxlen = 255) + def execute(self, context): + return write_kcl_obj(context, self.filepath) + +# Only needed if you want to add into a dynamic menu +def menu_export_obj_kcl(self, context): + self.layout.operator(export_obj_kcl.bl_idname, text="OBJ for KCL (.obj)") + +bpy.utils.register_class(export_obj_kcl) +bpy.types.INFO_MT_file_export.append(menu_export_obj_kcl) + +# test call +bpy.ops.export_scene.obj_kcl('INVOKE_DEFAULT') diff --git a/obj_neokclcreate_import.py b/obj_neokclcreate_import.py new file mode 100644 index 0000000..1b63633 --- /dev/null +++ b/obj_neokclcreate_import.py @@ -0,0 +1,87 @@ +import bpy, re +from . import blender_funcs + +# simple OBJ importer specific for NeoKCLCreate + +# import_obj_neokclcreate() function +# read superbmd/blenxy collada file +# used to import the collada file into Blender +def import_obj_neokclcreate(context, filepath): + + # scene variable is always needed for something + scene = bpy.context.scene + + # load the OBJ file into blender + bpy.ops.import_scene.obj(filepath = filepath, axis_forward = '-Z', axis_up = 'Y') + + # select the single OBJ mesh and reduce its size to match blenxy's coordinate system + obj = None + for o in bpy.data.objects: + # selection in blender api is confusing + if (o.select == True): + obj = o + break + blender_funcs.select_obj(scene, obj, False) + obj.scale = obj.scale / 100 + bpy.ops.object.transform_apply(location = True, rotation = True, scale = True) + + # deal with the infinite materials (merge those that are similar) + # slow as a turtle, but works like a charm + for i in range(len(obj.material_slots)): + # i will not be bound to len(obj.material_slots) once a material is deleted + if (i >= len(obj.material_slots)): + break + mat = obj.material_slots[i] + cur_mat_name = re.sub("^(.*?)\| ", "", mat.name) + # iterate over all the mesh faces + for face in obj.data.polygons: + face_mat = obj.material_slots[face.material_index] + face_mat_name = re.sub("^(.*?)\| ", "", face_mat.name) + # material with the same name found + if (cur_mat_name == face_mat_name and i != face.material_index): + bpy.data.materials.remove(face_mat.material) # remove the material data and the slot + obj.active_material_index = face.material_index + bpy.ops.object.material_slot_remove() + face.material_index = i # change the index to be the material of the current loop + + # put the OBJ inside an armature object + bpy.ops.object.armature_add() + obj.parent = scene.objects.active + + # done! + blender_funcs.disp_msg("NeoKCLCreate OBJ file imported!") + return {'FINISHED'} + + +################################################# +# Stuff down is for the menu appending +# of the importer to work plus some setting stuff +# comes from a Blender importer template + +# ExportHelper is a helper class, defines filename and +# invoke() function which calls the file selector. +from bpy_extras.io_utils import ExportHelper +from bpy.props import StringProperty, BoolProperty, EnumProperty +from bpy.types import Operator + +class import_neokclcreate_obj(Operator, ExportHelper): + """Import a Collada file from SuperBMD (SuperBMD only)""" + bl_idname = "import_scene.neokclcreate_obj" + bl_label = "Import NeoKCLCreate OBJ (.OBJ)" + + # ExportHelper mixin class uses this + filename_ext = ".obj" + filter_glob = StringProperty(default = "*.obj", options = {'HIDDEN'}, maxlen = 255) + # execute function + def execute(self, context): + return import_obj_neokclcreate(context, self.filepath) + +# Only needed if you want to add into a dynamic menu +def menu_import_neokclcreate_obj(self, context): + self.layout.operator(import_neokclcreate_obj.bl_idname, text="NeoKCLCreate OBJ (.obj)") + +bpy.utils.register_class(import_neokclcreate_obj) +bpy.types.INFO_MT_file_import.append(menu_import_neokclcreate_obj) + +# test call +bpy.ops.import_scene.neokclcreate_obj('INVOKE_DEFAULT') |