From 687bc99aea3ddb575c3e772c4517c70715f4fa1e Mon Sep 17 00:00:00 2001 From: ud18010 Date: Wed, 22 Jul 2026 16:54:01 +0800 Subject: [PATCH] ModelTranslator: harden QR fallback (cleanup-in-try, wait after kill, keep engine error) Co-Authored-By: Claude Opus 4.8 --- Tools/ModelTranslator/bl_decimate.py | 29 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/Tools/ModelTranslator/bl_decimate.py b/Tools/ModelTranslator/bl_decimate.py index e6846be3..83caead0 100644 --- a/Tools/ModelTranslator/bl_decimate.py +++ b/Tools/ModelTranslator/bl_decimate.py @@ -126,7 +126,7 @@ def _remesh_quad(obj, target_tris, warnings): import time engine = qr_bridge.find_qr_engine() if not engine: - warnings.append("未找到 QuadRemesher 引擎(xremesh.exe),回退 collapse") + warnings.append("未找到 QuadRemesher 引擎(xremesh.exe),回退 collapse") return False work = os.path.join(tempfile.gettempdir(), "mt_qr") 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("\\", "/") prog = os.path.join(work, "progress.txt").replace("\\", "/") settings = os.path.join(work, "RetopoSettings.txt") - for f in (out_fbx, prog): - if os.path.isfile(f): - os.remove(f) try: + for f in (out_fbx, prog): + if os.path.isfile(f): + os.remove(f) bpy.ops.object.select_all(action='DESELECT') obj.select_set(True) 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)) proc = subprocess.Popen([engine, "-s", settings]) except Exception as e: - warnings.append("QR 启动失败(%s),回退 collapse" % e) + warnings.append("QR 启动失败(%s),回退 collapse" % e) return False t0 = time.time() @@ -158,26 +158,27 @@ def _remesh_quad(obj, target_tris, warnings): state, msg = qr_bridge.parse_progress(fp.read()) except IOError: state, msg = "running", "" - if state == "success": + if state in ("success", "error"): break - if state == "error": - warnings.append("QR 重拓扑失败(%s),回退 collapse" % msg) - return False if proc.poll() is not None: # 进程已退出:给 progress/输出一点落地时间后再判一次 time.sleep(QR_POLL_INTERVAL) try: with open(prog) as fp: - state, _ = qr_bridge.parse_progress(fp.read()) + state, msg = qr_bridge.parse_progress(fp.read()) except IOError: - state = "running" + state, msg = "running", "" break if time.time() - t0 > qr_bridge.QR_TIMEOUT: 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 time.sleep(QR_POLL_INTERVAL) + if state == "error": + warnings.append("QR 重拓扑失败(%s),回退 collapse" % msg) + return False if state != "success" or not ( os.path.isfile(out_fbx) and os.path.getsize(out_fbx) > 0): warnings.append("QR 无有效输出,回退 collapse") @@ -192,13 +193,13 @@ def _remesh_quad(obj, target_tris, warnings): warnings.append("QR 导回无 mesh,回退 collapse") return False retopo = new[0] - bpy.data.objects.remove(obj, do_unlink=True) bpy.ops.object.select_all(action='DESELECT') retopo.select_set(True) bpy.context.view_layer.objects.active = retopo + bpy.data.objects.remove(obj, do_unlink=True) return True except Exception as e: - warnings.append("QR 导回失败(%s),回退 collapse" % e) + warnings.append("QR 导回失败(%s),回退 collapse" % e) return False