From b8eb40f3e403f11fda906b8d689ce14f216fe2df Mon Sep 17 00:00:00 2001 From: ud18010 Date: Wed, 22 Jul 2026 17:26:49 +0800 Subject: [PATCH] ModelTranslator: QR use ExactQuadCount, authoritative QR_ENGINE override MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 真机验证发现 ExactQuadCount=0 自适应在高细节硬表面模型上面数暴涨 (well1500 目标2500->11500 quads)并连带 seam 展开重叠爆炸;改用 exact=True 尊重目标数后 well1500 得 5940 面/591 UV岛(优于 collapse 745)。 find_qr_engine 改为权威覆盖:QR_ENGINE 无效即 None,不再静默自动发现, 使强制回退可测、覆盖契约成立。 Co-Authored-By: Claude Opus 4.8 --- Tools/ModelTranslator/bl_decimate.py | 5 ++++- Tools/ModelTranslator/qr_bridge.py | 7 ++++--- Tools/ModelTranslator/tests/test_qr_bridge.py | 11 +++++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Tools/ModelTranslator/bl_decimate.py b/Tools/ModelTranslator/bl_decimate.py index 83caead0..f4a4ec3e 100644 --- a/Tools/ModelTranslator/bl_decimate.py +++ b/Tools/ModelTranslator/bl_decimate.py @@ -144,7 +144,10 @@ def _remesh_quad(obj, target_tris, warnings): bpy.ops.export_scene.fbx(filepath=in_fbx, use_selection=True) target_quads = qr_bridge.tris_to_target_quads(target_tris) with open(settings, "w") as fp: - fp.write(qr_bridge.build_settings(in_fbx, out_fbx, prog, target_quads)) + # exact=True:尊重目标四边形数。自适应(exact=False)在高细节硬表面模型上会 + # 把面数炸开数倍(实测 well1500 目标 2500→11500 quads),并连带 seam 展开重叠爆炸。 + fp.write(qr_bridge.build_settings(in_fbx, out_fbx, prog, target_quads, + exact=True)) proc = subprocess.Popen([engine, "-s", settings]) except Exception as e: warnings.append("QR 启动失败(%s),回退 collapse" % e) diff --git a/Tools/ModelTranslator/qr_bridge.py b/Tools/ModelTranslator/qr_bridge.py index 930b1aa6..975d44d8 100644 --- a/Tools/ModelTranslator/qr_bridge.py +++ b/Tools/ModelTranslator/qr_bridge.py @@ -8,10 +8,11 @@ QR_TIMEOUT = 120.0 # 引擎轮询超时(秒) def find_qr_engine(): """定位 QuadRemesher 的 xremesh.exe;找不到返回 None。 - 顺序:QR_ENGINE 环境变量 -> 标准 addon 路径(取字典序最大目录,当前 Blender 5.x 下即最高版本)。""" + 顺序:QR_ENGINE 环境变量(权威:显式设了就以它为准,无效即 None,不再自动发现) + -> 标准 addon 路径(取字典序最大目录,当前 Blender 5.x 下即最高版本)。""" env = os.environ.get("QR_ENGINE") - if env and os.path.isfile(env): - return env + if env: + return env if os.path.isfile(env) else None appdata = os.environ.get("APPDATA") if not appdata: return None diff --git a/Tools/ModelTranslator/tests/test_qr_bridge.py b/Tools/ModelTranslator/tests/test_qr_bridge.py index cdbdbfbe..01f463c2 100644 --- a/Tools/ModelTranslator/tests/test_qr_bridge.py +++ b/Tools/ModelTranslator/tests/test_qr_bridge.py @@ -83,6 +83,17 @@ class TestFindQrEngine(unittest.TestCase): os.environ["APPDATA"] = tempfile.mkdtemp() self.assertIsNone(qb.find_qr_engine()) + def test_invalid_override_does_not_fall_through(self): + # QR_ENGINE 权威:显式设了但无效,即便 APPDATA 下装了真引擎也返回 None + root = tempfile.mkdtemp() + os.environ["QR_ENGINE"] = os.path.join(root, "nope.exe") + os.environ["APPDATA"] = root + eng_dir = os.path.join(root, "Blender Foundation", "Blender", "5.0", + "scripts", "addons", "QuadRemesher", "Engine") + os.makedirs(eng_dir) + open(os.path.join(eng_dir, "xremesh.exe"), "w").close() + self.assertIsNone(qb.find_qr_engine()) + def test_globs_addon_path_picks_last(self): root = tempfile.mkdtemp() os.environ.pop("QR_ENGINE", None)