single tools scripts
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

192 lines
6.9 KiB

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)