ModelTranslator: harden QR fallback (cleanup-in-try, wait after kill, keep engine error)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ud18010
2026-07-22 16:54:01 +08:00
co-authored by Claude Opus 4.8
parent be09397609
commit 687bc99aea
+15 -14
View File
@@ -126,7 +126,7 @@ def _remesh_quad(obj, target_tris, warnings):
import time import time
engine = qr_bridge.find_qr_engine() engine = qr_bridge.find_qr_engine()
if not engine: if not engine:
warnings.append("未找到 QuadRemesher 引擎(xremesh.exe),回退 collapse") warnings.append("未找到 QuadRemesher 引擎xremesh.exe,回退 collapse")
return False return False
work = os.path.join(tempfile.gettempdir(), "mt_qr") work = os.path.join(tempfile.gettempdir(), "mt_qr")
os.makedirs(work, exist_ok=True) os.makedirs(work, exist_ok=True)
@@ -134,10 +134,10 @@ def _remesh_quad(obj, target_tris, warnings):
out_fbx = os.path.join(work, "retopo.fbx").replace("\\", "/") out_fbx = os.path.join(work, "retopo.fbx").replace("\\", "/")
prog = os.path.join(work, "progress.txt").replace("\\", "/") prog = os.path.join(work, "progress.txt").replace("\\", "/")
settings = os.path.join(work, "RetopoSettings.txt") settings = os.path.join(work, "RetopoSettings.txt")
for f in (out_fbx, prog):
if os.path.isfile(f):
os.remove(f)
try: try:
for f in (out_fbx, prog):
if os.path.isfile(f):
os.remove(f)
bpy.ops.object.select_all(action='DESELECT') bpy.ops.object.select_all(action='DESELECT')
obj.select_set(True) obj.select_set(True)
bpy.context.view_layer.objects.active = obj bpy.context.view_layer.objects.active = obj
@@ -147,7 +147,7 @@ def _remesh_quad(obj, target_tris, warnings):
fp.write(qr_bridge.build_settings(in_fbx, out_fbx, prog, target_quads)) fp.write(qr_bridge.build_settings(in_fbx, out_fbx, prog, target_quads))
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)
return False return False
t0 = time.time() t0 = time.time()
@@ -158,26 +158,27 @@ def _remesh_quad(obj, target_tris, warnings):
state, msg = qr_bridge.parse_progress(fp.read()) state, msg = qr_bridge.parse_progress(fp.read())
except IOError: except IOError:
state, msg = "running", "" state, msg = "running", ""
if state == "success": if state in ("success", "error"):
break break
if state == "error":
warnings.append("QR 重拓扑失败(%s),回退 collapse" % msg)
return False
if proc.poll() is not None: if proc.poll() is not None:
# 进程已退出:给 progress/输出一点落地时间后再判一次 # 进程已退出:给 progress/输出一点落地时间后再判一次
time.sleep(QR_POLL_INTERVAL) time.sleep(QR_POLL_INTERVAL)
try: try:
with open(prog) as fp: with open(prog) as fp:
state, _ = qr_bridge.parse_progress(fp.read()) state, msg = qr_bridge.parse_progress(fp.read())
except IOError: except IOError:
state = "running" state, msg = "running", ""
break break
if time.time() - t0 > qr_bridge.QR_TIMEOUT: if time.time() - t0 > qr_bridge.QR_TIMEOUT:
proc.kill() proc.kill()
warnings.append("QR 超时(%ds),回退 collapse" % int(qr_bridge.QR_TIMEOUT)) proc.wait()
warnings.append("QR 超时(%ds),回退 collapse" % int(qr_bridge.QR_TIMEOUT))
return False return False
time.sleep(QR_POLL_INTERVAL) time.sleep(QR_POLL_INTERVAL)
if state == "error":
warnings.append("QR 重拓扑失败(%s),回退 collapse" % msg)
return False
if state != "success" or not ( if state != "success" or not (
os.path.isfile(out_fbx) and os.path.getsize(out_fbx) > 0): os.path.isfile(out_fbx) and os.path.getsize(out_fbx) > 0):
warnings.append("QR 无有效输出,回退 collapse") warnings.append("QR 无有效输出,回退 collapse")
@@ -192,13 +193,13 @@ def _remesh_quad(obj, target_tris, warnings):
warnings.append("QR 导回无 mesh,回退 collapse") warnings.append("QR 导回无 mesh,回退 collapse")
return False return False
retopo = new[0] retopo = new[0]
bpy.data.objects.remove(obj, do_unlink=True)
bpy.ops.object.select_all(action='DESELECT') bpy.ops.object.select_all(action='DESELECT')
retopo.select_set(True) retopo.select_set(True)
bpy.context.view_layer.objects.active = retopo bpy.context.view_layer.objects.active = retopo
bpy.data.objects.remove(obj, do_unlink=True)
return True return True
except Exception as e: except Exception as e:
warnings.append("QR 导回失败(%s),回退 collapse" % e) warnings.append("QR 导回失败%s,回退 collapse" % e)
return False return False