ModelTranslator: QR use ExactQuadCount, authoritative QR_ENGINE override
真机验证发现 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 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
11036877fc
commit
b8eb40f3e4
@@ -144,7 +144,10 @@ def _remesh_quad(obj, target_tris, warnings):
|
|||||||
bpy.ops.export_scene.fbx(filepath=in_fbx, use_selection=True)
|
bpy.ops.export_scene.fbx(filepath=in_fbx, use_selection=True)
|
||||||
target_quads = qr_bridge.tris_to_target_quads(target_tris)
|
target_quads = qr_bridge.tris_to_target_quads(target_tris)
|
||||||
with open(settings, "w") as fp:
|
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])
|
proc = subprocess.Popen([engine, "-s", settings])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
warnings.append("QR 启动失败(%s),回退 collapse" % e)
|
warnings.append("QR 启动失败(%s),回退 collapse" % e)
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ QR_TIMEOUT = 120.0 # 引擎轮询超时(秒)
|
|||||||
|
|
||||||
def find_qr_engine():
|
def find_qr_engine():
|
||||||
"""定位 QuadRemesher 的 xremesh.exe;找不到返回 None。
|
"""定位 QuadRemesher 的 xremesh.exe;找不到返回 None。
|
||||||
顺序:QR_ENGINE 环境变量 -> 标准 addon 路径(取字典序最大目录,当前 Blender 5.x 下即最高版本)。"""
|
顺序:QR_ENGINE 环境变量(权威:显式设了就以它为准,无效即 None,不再自动发现)
|
||||||
|
-> 标准 addon 路径(取字典序最大目录,当前 Blender 5.x 下即最高版本)。"""
|
||||||
env = os.environ.get("QR_ENGINE")
|
env = os.environ.get("QR_ENGINE")
|
||||||
if env and os.path.isfile(env):
|
if env:
|
||||||
return env
|
return env if os.path.isfile(env) else None
|
||||||
appdata = os.environ.get("APPDATA")
|
appdata = os.environ.get("APPDATA")
|
||||||
if not appdata:
|
if not appdata:
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -83,6 +83,17 @@ class TestFindQrEngine(unittest.TestCase):
|
|||||||
os.environ["APPDATA"] = tempfile.mkdtemp()
|
os.environ["APPDATA"] = tempfile.mkdtemp()
|
||||||
self.assertIsNone(qb.find_qr_engine())
|
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):
|
def test_globs_addon_path_picks_last(self):
|
||||||
root = tempfile.mkdtemp()
|
root = tempfile.mkdtemp()
|
||||||
os.environ.pop("QR_ENGINE", None)
|
os.environ.pop("QR_ENGINE", None)
|
||||||
|
|||||||
Reference in New Issue
Block a user