ModelTranslator: QR watertightness gate + quad default
根因(真机排查):QuadRemesher 引擎在激进减面下非确定性地从封闭流形 输入打出孔洞/非流形边(实测同一输入约 85% 次数破碎),即"破碎面"。 修复:QR 导回后比对输入拓扑,若比输入新增开边(孔洞)/非流形边即判破损、 弃用 QR 回退 collapse(topology_acceptable 纯策略+单测;_edge_defects 计数)。 顺带修复水密门在导入后拒绝时未恢复 obj 选中态,导致 collapse 回退时 export use_selection 选不到对象、只导出 4KB 空 FBX 的 bug。 含用户改动:--reducer 默认改为 quad(配合水密门,破碎自动回退,安全), 及其 CLI 默认值测试。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
44060ac3cb
commit
0481f10b8c
@@ -117,10 +117,22 @@ def _decimate(obj, ratio):
|
||||
_apply_modifier(obj, mod)
|
||||
|
||||
|
||||
def _edge_defects(obj):
|
||||
"""(开边数, 非流形边数):开边=只挂 1 面(孔洞边界),非流形=挂 >2 面。
|
||||
用于 QR 输出水密门——QR 引擎在激进减面下会非确定性地打出孔洞/非流形。"""
|
||||
import bmesh
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
boundary = sum(1 for e in bm.edges if len(e.link_faces) == 1)
|
||||
nonman = sum(1 for e in bm.edges if len(e.link_faces) > 2)
|
||||
bm.free()
|
||||
return boundary, nonman
|
||||
|
||||
|
||||
def _remesh_quad(obj, target_tris, warnings):
|
||||
"""Quad Remesher 重拓扑 obj(headless 直调 xremesh.exe,绕开 modal 操作符)。
|
||||
成功返回 True,并把导回的 quad 网格设为新 active object(原 obj 已删);
|
||||
任何失败记 warning 返回 False,交上层回退 collapse。"""
|
||||
任何失败——含 QR 输出破损未过水密门——记 warning 返回 False,交上层回退 collapse。"""
|
||||
import bpy
|
||||
import subprocess
|
||||
import tempfile
|
||||
@@ -142,6 +154,7 @@ def _remesh_quad(obj, target_tris, warnings):
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
src_boundary, src_nonman = _edge_defects(obj) # QR 输入拓扑基线
|
||||
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:
|
||||
@@ -197,6 +210,18 @@ def _remesh_quad(obj, target_tris, warnings):
|
||||
warnings.append("QR 导回无 mesh,回退 collapse")
|
||||
return False
|
||||
retopo = new[0]
|
||||
# 水密门:QR 引擎非确定性地会打出孔洞/非流形,破损即弃用 QR 回退 collapse
|
||||
qr_boundary, qr_nonman = _edge_defects(retopo)
|
||||
if not qr_bridge.topology_acceptable(src_boundary, src_nonman,
|
||||
qr_boundary, qr_nonman):
|
||||
warnings.append("QR 输出拓扑破损(开边 %d→%d,非流形 %d→%d),回退 collapse"
|
||||
% (src_boundary, qr_boundary, src_nonman, qr_nonman))
|
||||
bpy.data.objects.remove(retopo, do_unlink=True) # 弃坏网格,原 obj 完好
|
||||
# FBX 导入把选中挪给了 retopo;恢复 obj 选中+active,供 collapse 与最终导出
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
return False
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
retopo.select_set(True)
|
||||
bpy.context.view_layer.objects.active = retopo
|
||||
|
||||
Reference in New Issue
Block a user