Files
AIC-Project/docs/superpowers/specs/2026-07-23-quad-reducer-retopo-upgrade-design.md
2026-07-23 15:09:24 +08:00

127 lines
6.7 KiB
Markdown
Raw Permalink 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.
# `--reducer quad` 升级为可靠四边重拓扑 设计
日期:2026-07-23
## 背景与动机
真机排查(gargoyle 500k、supergirl 1M)确认了现有 `--reducer quad` 的三个问题:
1. **高密度输入必破洞**:QR 引擎从很高密度网格(500k~1M 面)激进减面时,非确定性地打出
孔洞/非流形边,水密门(零容忍)几乎总判破损 → 回退 collapse。实测把高模先 collapse 到
**中等密度(~50k)** 再喂 QR,输出即干净。
2. **导出前被三角化**`bl_decimate.main` 在 quad 成功路径也调 `_triangulate`,导致"quad 后端"
的 FBX 里其实是三角面,四边拓扑被毁——与"要四边"矛盾。
3. **零容忍门太严**:有机角色(supergirl)QR 常只留 1 个薄部位小洞(约 3 开边),门把这个
近乎干净的 QR 一票否决。补掉小洞即净。
用户决定:**改造现有 `--reducer quad`**,把"预 collapse + QR + 补小洞 + 保四边"整套配方内建,
让 quad 后端真正可用。`collapse` 后端与默认值(quad)不变。
## 约束
- 不加外部依赖。
- `collapse` 后端行为**零变化**`--reducer` 默认仍为 `quad`
- QR 任何灾难性失败仍**自动回退 collapse**,绝不中断(保留现有回退契约)。
- 不 stage 用户脏工作区(Unity/src/产物)。
- 纯策略(cap 判断、软门)与 Blender 操作分离,纯逻辑 TDD。
## 新流程(`--reducer quad`
`bl_decimate.main()` 的 quad 分支改为:
1. **预 collapse**`_clean_mesh` + `_triangulate` 后,若面数 > `qr_input_cap`(默认 50000)
`_decimate` 到 cap + `_triangulate`;≤ cap 则跳过。给 QR 一个不炸洞的中等密度输入。
2. **QR 重拓扑**`_remesh_quad` 导出预处理网格 → `xremesh.exe`(`ExactQuadCount=1`
目标 `tris_to_target_quads(--tris)` 四边)→ 导回。
3. **补小洞**QR 导回后 `fill_holes(sides <= QR_HOLE_MAX_SIDES=6)`,补掉薄部位零星小洞。
4. **软水密门**:补洞后比对输入基线——`topology_acceptable(src_b, src_nm, out_b, out_nm,
tol=QR_CATASTROPHIC_TOL)``out_b <= src_b + tol and out_nm <= src_nm + tol` 则接受
(保留四边);否则判灾难性破碎,弃用 QR **回退 collapse**(标 `quad->collapse`)。
5. **保四边导出**quad 成功路径**不再 `_triangulate`**`_do_unwrap` 与导出直接作用于四边网格
(FBX/Cycles 烘焙/Unity 均支持四边;Unity 导入自动三角化)。
collapse 路径与 quad->collapse 回退路径**仍三角化**(collapse 本就产三角)。
## 架构与接口
### `qr_bridge.py`(纯逻辑,不 import bpy
- 常量:`QR_INPUT_CAP = 50000`、`QR_HOLE_MAX_SIDES = 6`、`QR_CATASTROPHIC_TOL = 12`。
- 修改 `topology_acceptable(src_boundary, src_nonmanifold, out_boundary, out_nonmanifold,
tol=0)`:加 `tol` 参数(默认 0,向后兼容现有调用/测试),判定改为
`out_boundary <= src_boundary + tol and out_nonmanifold <= src_nonmanifold + tol`。
- 新增 `precollapse_target(face_count, cap=QR_INPUT_CAP) -> int | None``face_count > cap`
返回 `cap`,否则 `None`(跳过预 collapse)。
### `bl_decimate.py`Blender 内)
- 新增 `_fill_small_holes(obj, max_sides)`:编辑模式全选 → `bpy.ops.mesh.fill_holes(sides=max_sides)`
→ 回物体模式。异常降级不中断。
- 修改 `_remesh_quad(obj, target_tris, warnings)`:导回 retopo 后
(a) `_fill_small_holes(retopo, qr_bridge.QR_HOLE_MAX_SIDES)`
(b) 用软门 `topology_acceptable(..., tol=QR_CATASTROPHIC_TOL)` 判定;
(c) 接受则删原 obj、retopo 设为 active、return True(**不三角化**);拒绝则弃 retopo、
恢复 obj 选中态、return False。其余(引擎发现/子进程/轮询/选择态恢复)不变。
- 修改 `main()`
- 解析 `qr_input_cap = int(argv[6]) if len(argv) > 6 else QR_INPUT_CAP`。
- quad 分支:`_remesh_quad` 前按 `precollapse_target` 预 collapse;成功后**去掉**
`_triangulate(obj)`(保四边)。`orig`(tris_before)仍取预 collapse 前的高模面数。
### `model_decimate.py`CLI
- 加 `--qr-input-cap`(type intdefault `QR_INPUT_CAP`),透传为 Blender argv 第 7 位(index 6)。
- 校验 `> 0`。摘要打印沿用(`后端` 已有)。
## 数据流
```
FBX --import--> join --clean--> triangulate --orig
reducer=quad:
面数 > cap? --yes--> collapse 到 cap --triangulate
_remesh_quad: export --xremesh(exact)--> retopo --import
fill_holes(<=6) --> 软门(tol) 过? --yes--> 保留四边(不三角化)
--no--> 弃用, 回退 collapse(三角)
reducer=collapse 或 quad->collapse: decimate 循环(三角)
--> _do_unwrap(smart/seam) --> export(四边或三角) + MT_SUMMARY{reducer,...}
```
## 错误处理
| 情况 | 处理 |
|---|---|
| 引擎缺失/超时/报错/无输出/导回无 mesh | warning,回退 collapse(现有) |
| fill_holes 异常 | warning,跳过补洞,继续走软门 |
| 补洞后仍灾难性破损(超 tol) | warning,弃 QR 回退 collapse |
| 预 collapse 后 QR 仍失败 | 回退 collapse(在已预collapse的网格上继续减到目标) |
回退后 `reducer` 标 `quad->collapse`,绝不中断。
## 已知取舍
- 预 collapse 会在 QR 前损失部分细节 → 低模形状比"直接 QR"略有偏差(实测形变从 ~0.015%
升到 ~0.06%,量级仍很小)。**烘焙细节来自原始高模**(model_bake 用 high FBX),故低模这点
偏差对最终贴图影响小;换来的是 QR 不破洞、能保四边。取舍值得。
- 补洞产生的少量 n-gon 补丁(≤6 边)与 QR 偶发三角使输出"以四边为主",非 100% 纯四边。
## 测试
- **`tests/test_qr_bridge.py`**
- `topology_acceptable` 加 `tol``(0,0,3,0,tol=12)->True`、`(0,0,13,0,tol=12)->False`、
默认 `tol=0` 时 `(0,0,1,0)->False`(现有语义不变)。
- `precollapse_target``(1000000,50000)->50000`、`(40000,50000)->None`、边界 `(50000,50000)->None`。
- **回归**`python -m unittest discover -s tests` 全绿(现有测试含 `topology_acceptable` 旧调用
仍按 tol=0 通过)。
- **真机 smoke**
- `model_decimate supergirl.fbx --tris 10000 --reducer quad`backend=`quad`、输出以四边为主
(quad 数 >> tri)、开边/非流形 ≤ 输入基线+tol、有 UV。
- `gargoyle.fbx --tris 10000 --reducer quad`:同上或合理 `quad->collapse`。
- `--reducer collapse` 回归:面数、拓扑与升级前一致(零变化)。
- 强制回退(`QR_ENGINE` 指错)`quad->collapse`、输出正常。
## 明确不做(YAGNI
- 不做硬边引导/直边保真(实测无效,另属课题)。
- 不暴露 fill-holes sides、catastrophic tol 为 CLI(内置常量足够;只暴露 `--qr-input-cap`)。
- 不改 model_bake/model_translator(四边低模它们已支持)。
- 不追求 100% 纯四边(少量 n-gon/tri 可接受)。