"""Blender 内运行:校验低模 FBX 的 UV 质量门;无 UV 或不达标就地 seam 重展并覆盖导出。 调用:blender -b --factory-startup --python bl_uvgate.py -- [mode_label] mode_label 仅用于日志/回退警告中标注上游展开方式(默认 rizom)。""" import os import sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) def main(): import bpy import json from bl_decimate import (join_meshes, uv_gate_ok, _collect_uv_metrics, _do_unwrap) argv = sys.argv[sys.argv.index("--") + 1:] fbx = argv[0] mode_label = argv[1] if len(argv) > 1 else "rizom" warnings = [] bpy.ops.wm.read_factory_settings(use_empty=True) bpy.ops.import_scene.fbx(filepath=fbx) meshes = [o for o in bpy.data.objects if o.type == 'MESH'] if not meshes: print("MT_SUMMARY " + json.dumps({"error": "FBX 中没有 mesh"}, ensure_ascii=False)) return obj = join_meshes(meshes) if obj.data.uv_layers: m = _collect_uv_metrics(obj) if uv_gate_ok(m["flipped"], m["overlap"]): m["mode"] = mode_label print("MT_SUMMARY " + json.dumps( {"uv": m, "reexported": False, "warnings": warnings}, ensure_ascii=False)) return warnings.append("%s UV 质量不达标(翻转 %.1f%% 重叠 %s),回退 seam" % (mode_label, m["flipped"] * 100, "%.1f%%" % (m["overlap"] * 100) if m["overlap"] is not None else "未知")) else: warnings.append("%s 输出无 UV,回退 seam" % mode_label) m = _do_unwrap(obj, "seam", warnings) # 内部自带 smart 回退 if m["mode"] == "seam": m["mode"] = "seam_fallback" bpy.ops.object.select_all(action='DESELECT') obj.select_set(True) bpy.ops.export_scene.fbx(filepath=os.path.abspath(fbx), use_selection=True, path_mode='STRIP', embed_textures=False) print("MT_SUMMARY " + json.dumps( {"uv": m, "reexported": True, "warnings": warnings}, ensure_ascii=False)) if __name__ == "__main__" and "--" in sys.argv: main()