Files
AIC-Project/Tools/ModelTranslator/qr_bridge.py
T
ud18010andClaude Opus 4.8 b8eb40f3e4 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>
2026-07-22 17:26:49 +08:00

65 lines
2.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""QR 桥纯逻辑:引擎发现、面数换算、settings 生成、progress 解析。不 import bpy。
减面可选后端 Quad Remesher 用——headless 直接调 Engine/xremesh.exe,绕开 modal 操作符。"""
import glob
import os
QR_TIMEOUT = 120.0 # 引擎轮询超时(秒)
def find_qr_engine():
"""定位 QuadRemesher 的 xremesh.exe;找不到返回 None。
顺序:QR_ENGINE 环境变量(权威:显式设了就以它为准,无效即 None,不再自动发现)
-> 标准 addon 路径(取字典序最大目录,当前 Blender 5.x 下即最高版本)。"""
env = os.environ.get("QR_ENGINE")
if env:
return env if os.path.isfile(env) else None
appdata = os.environ.get("APPDATA")
if not appdata:
return None
pattern = os.path.join(
appdata, "Blender Foundation", "Blender", "*",
"scripts", "addons", "QuadRemesher", "Engine", "xremesh.exe")
hits = sorted(glob.glob(pattern))
return hits[-1] if hits else None
def tris_to_target_quads(tris):
"""三角面数换算 QR 目标四边形数:1 quad ≈ 2 tris,至少 1。"""
return max(1, int(tris) // 2)
def build_settings(in_fbx, out_fbx, prog, target_quads,
adaptive=50, exact=False, hard_edges=True):
"""生成 RetopoSettings.txt 文本(xremesh.exe -s 读取)。"""
lines = [
"HostApp=Blender",
'FileIn="%s"' % in_fbx,
'FileOut="%s"' % out_fbx,
'ProgressFile="%s"' % prog,
"TargetQuadCount=%d" % target_quads,
"CurvatureAdaptivness=%d" % adaptive,
"ExactQuadCount=%d" % (1 if exact else 0),
"UseVertexColorMap=False",
"UseMaterialIds=0",
"UseIndexedNormals=0",
"AutoDetectHardEdges=%d" % (1 if hard_edges else 0),
]
return "\n".join(lines) + "\n"
def parse_progress(text):
"""解析 progress.txt -> (state, msg)。
首行 '2'=success<0=error(次行为文本);空/分数/非法=running。"""
lines = text.splitlines()
if not lines:
return ("running", "")
try:
v = float(lines[0])
except ValueError:
return ("running", "")
if v == 2:
return ("success", "")
if v < 0:
return ("error", lines[1] if len(lines) > 1 else "")
return ("running", "")