ModelTranslator: quad backend precollapse + fill-holes + keep-quads
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
bb6f32b1c1
commit
5fda5d6bef
@@ -1,6 +1,6 @@
|
||||
"""Blender 内运行:FBX 减面到指定三角面数 + UV 重展(锐边 seam 或 Smart UV Project)。
|
||||
调用:blender -b --factory-startup --python bl_decimate.py --
|
||||
<src.fbx> <out.fbx> <target_tris> [unwrap:seam|smart] [overlap_tol] [reducer:collapse|quad]
|
||||
<src.fbx> <out.fbx> <target_tris> [unwrap:seam|smart] [overlap_tol] [reducer:collapse|quad] [qr_input_cap]
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
@@ -129,6 +129,21 @@ def _edge_defects(obj):
|
||||
return boundary, nonman
|
||||
|
||||
|
||||
def _fill_small_holes(obj, max_sides, warnings):
|
||||
"""补掉 <=max_sides 边的小洞(QR 薄部位零星孔洞);异常降级不中断。"""
|
||||
import bpy
|
||||
try:
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
obj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = obj
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
bpy.ops.mesh.fill_holes(sides=max_sides)
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
except RuntimeError as e:
|
||||
warnings.append("fill_holes 失败(%s),跳过补洞" % e)
|
||||
|
||||
|
||||
def _remesh_quad(obj, target_tris, warnings):
|
||||
"""Quad Remesher 重拓扑 obj(headless 直调 xremesh.exe,绕开 modal 操作符)。
|
||||
成功返回 True,并把导回的 quad 网格设为新 active object(原 obj 已删);
|
||||
@@ -210,11 +225,14 @@ def _remesh_quad(obj, target_tris, warnings):
|
||||
warnings.append("QR 导回无 mesh,回退 collapse")
|
||||
return False
|
||||
retopo = new[0]
|
||||
# 水密门:QR 引擎非确定性地会打出孔洞/非流形,破损即弃用 QR 回退 collapse
|
||||
# 补小洞:QR 常在薄部位留零星小洞,先补掉再判软门
|
||||
_fill_small_holes(retopo, qr_bridge.QR_HOLE_MAX_SIDES, warnings)
|
||||
# 软水密门:补洞后仍比输入多出超容忍的破损才判灾难性,弃用 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"
|
||||
qr_boundary, qr_nonman,
|
||||
tol=qr_bridge.QR_CATASTROPHIC_TOL):
|
||||
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 与最终导出
|
||||
@@ -515,6 +533,7 @@ def main():
|
||||
# 直接调 bl_decimate 缺省用 collapse(安全确定性);model_decimate CLI 默认 quad,
|
||||
# 但总显式传第 6 位,故此兜底仅用于直接 Blender 调用,两者默认不同是有意为之
|
||||
reducer_arg = argv[5] if len(argv) > 5 else "collapse"
|
||||
qr_input_cap = int(argv[6]) if len(argv) > 6 else qr_bridge.QR_INPUT_CAP
|
||||
warnings = []
|
||||
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
@@ -536,9 +555,15 @@ def main():
|
||||
used_reducer = "collapse"
|
||||
did_quad = False
|
||||
if reducer_arg == "quad":
|
||||
# 预 collapse 到中等密度:给 QR 一个不炸洞的输入(高密度直接 QR 必破)
|
||||
pre = qr_bridge.precollapse_target(len(obj.data.polygons), qr_input_cap)
|
||||
if pre is not None:
|
||||
_decimate(obj, decimate_ratio(pre, len(obj.data.polygons)))
|
||||
_triangulate(obj)
|
||||
cur = len(obj.data.polygons) # 预 collapse 后更新,QR 失败时 collapse 从此续减
|
||||
if _remesh_quad(obj, target, warnings):
|
||||
obj = bpy.context.view_layer.objects.active # QR 导回的 quad 对象
|
||||
_triangulate(obj) # 三角化供导出/计数
|
||||
# 保四边:不三角化,直接导出四边网格(FBX/烘焙/Unity 均支持四边)
|
||||
cur = len(obj.data.polygons)
|
||||
used_reducer = "quad"
|
||||
did_quad = True
|
||||
|
||||
Reference in New Issue
Block a user