import bpy import os import sys import argparse class ArgumentParserForBlender(argparse.ArgumentParser): """ This class is identical to its superclass, except for the parse_args method (see docstring). It resolves the ambiguity generated when calling Blender from the CLI with a python script, and both Blender and the script have arguments. E.g., the following call will make Blender crash because it will try to process the script's -a and -b flags: >>> blender --python my_script.py -a 1 -b 2 To bypass this issue this class uses the fact that Blender will ignore all arguments given after a double-dash ('--'). The approach is that all arguments before '--' go to Blender, arguments after go to the script. The following calls work fine: >>> blender --python my_script.py -- -a 1 -b 2 >>> blender --python my_script.py -- """ def _get_argv_after_doubledash(self): """ Given the sys.argv as a list of strings, this method returns the sublist right after the '--' element (if present, otherwise returns an empty list). """ try: idx = sys.argv.index("--") return sys.argv[idx+1:] # the list after '--' except ValueError as e: # '--' not in the list: return [] # overrides superclass def parse_args(self): """ This method is expected to behave identically as in the superclass, except that the sys.argv list will be pre-processed using _get_argv_after_doubledash before. See the docstring of the class for usage examples and details. """ return super().parse_args(args=self._get_argv_after_doubledash()) def find_pid_objname(pid): for obj in bpy.data.objects: if obj.type == 'MESH': return obj.name def obj2glb(input_path: str, output_path: str, human_num, face_num, is_reduce_face) -> None: """ 将OBJ文件转换为GLB格式并保存。 参数: input_path (str): 输入OBJ文件的路径 output_path (str): 输出GLB文件的路径 human_num (int): 人物数量 face_num (int): 目标面数 is_reduce_face (str): 是否减面 img_quality (int): 图片质量 返回: None """ if is_reduce_face == "True": is_reduce_face = True else: is_reduce_face = False if not os.path.exists(input_path): raise FileNotFoundError(f"输入文件 {input_path} 不存在") bpy.ops.object.delete(use_global=False, confirm=False) bpy.ops.object.select_all(action="DESELECT") bpy.ops.object.select_by_type(type="MESH") bpy.ops.object.delete(use_global=False, confirm=False) bpy.ops.wm.obj_import(filepath=input_path) pid = os.path.splitext(os.path.basename(input_path))[0] print(f'人物数量:{human_num},目标面数:{face_num}') faces_dest = face_num * human_num total_faces: int = 0 for obj in bpy.data.objects: if obj.type == 'MESH': # 获取对象的面数并累加 total_faces += len(obj.data.polygons) print(f"所有对象的面数之和: {total_faces}") if not is_reduce_face: print("不减面") faces_dest = total_faces print(f"目标面数:{faces_dest} 当前面数:{total_faces}") for obj in bpy.data.objects: if obj.type == 'MESH': # 取消选中所有对象 bpy.ops.object.select_all(action='DESELECT') # 选中当前对象 obj.select_set(True) bpy.context.view_layer.objects.active = obj # 添加和应用修改器 modifier = obj.modifiers.new(name="Decimate", type='DECIMATE') modifier.ratio = faces_dest / total_faces # 应用修改器 bpy.ops.object.modifier_apply(modifier="Decimate") pid_objname = find_pid_objname(pid) obj = bpy.data.objects[pid_objname] # scale = 90 / bpy.data.objects[pid_objname].dimensions.y print(bpy.data.objects[pid_objname].dimensions) # bpy.data.objects[pid_objname].scale = (scale, scale, scale) # bpy.ops.object.transform_apply(scale=True) bpy.ops.outliner.orphans_purge(do_recursive=True) # 导出GLB文件 bpy.ops.export_scene.fbx(filepath=output_path, use_selection=False, # 导出所有对象,若只导出选中对象可设为 True object_types={'MESH'}, # 只导出网格对象 path_mode='RELATIVE', # 使用相对贴图路径 # embed_textures=False, # 不嵌入贴图(路径生效) # apply_unit_scale=True, # 应用单位缩放 # apply_scale_options='FBX_SCALE_ALL', # 应用缩放 bake_space_transform=False) # 保持原始空间变换 # 清理所有资源的优化版本 def clean_data_blocks(): """清理所有数据块""" # 首先删除所有对象 bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete() # 按特定顺序清理数据块以避免依赖问题 data_blocks = [ (bpy.data.actions, "动作"), (bpy.data.armatures, "骨骼"), (bpy.data.cameras, "相机"), (bpy.data.lights, "灯光"), (bpy.data.meshes, "网格"), (bpy.data.materials, "材质"), (bpy.data.textures, "纹理"), (bpy.data.images, "图片"), (bpy.data.curves, "曲线"), (bpy.data.lights, "灯光"), (bpy.data.worlds, "世界"), (bpy.data.collections, "集合") ] # 循环清理每种类型的数据 for data_block, block_name in data_blocks: try: for item in data_block: if item.users == 0: data_block.remove(item) else: item.user_clear() data_block.remove(item) except Exception as e: print(f"清理{block_name}时出错: {str(e)}") # 强制执行垃圾回收 bpy.ops.outliner.orphans_purge(do_recursive=True) import gc gc.collect() # 执行清理 clean_data_blocks() if __name__ == "__main__": parser = ArgumentParserForBlender() parser.add_argument("--input_path", type=str, required=True) parser.add_argument("--output_path", type=str, required=True) parser.add_argument("--human_num", type=int, required=True) parser.add_argument("--face_num", type=int, required=True) parser.add_argument("--is_reduce_face", type=str, required=True) args = parser.parse_args() obj2glb(args.input_path, args.output_path, args.human_num, args.face_num, args.is_reduce_face)