diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index b81f0bf..f50fdcd 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -150,3 +150,61 @@ comparison is no longer merely the next step — it is the only remaining one.** Render `e106` in the viewer beside `ship_render`'s output of the same `assemble_ship` result; if they agree, the bug is in neither and the original report needs re-grounding against a specific ship and a specific expectation. + +--- + +## ⚠️ The format layer is NOT exonerated — `assemble_ship` draws a non-renderable volume + +**Found 2026-08-11 by finally doing the visual**, which the notes above kept +naming as the next step. It overturns their conclusion. + +Render `e106` from the static assembly and from the baked runtime capture and +compare — `ship_render` does both: + +| | placements | parts | +|---|---|---| +| runtime capture (ground truth) | **8** | `bdy_01…04`, `brg_01`, `eng_01`, `eng_02`, `wep_02_01` | +| `assemble_ship(--static)` | **11** | the same 8, **plus `e303_wep_01` ×2** and a second `e106_eng_01` | + +The render makes it obvious: the destroyer sits inside a white slab that dwarfs +it ([capture](captures/e106-static-assembly-volume-bug.png)). That slab is +`e303_wep_01`, and its own geometry is: + +``` +e303_wep_01 172 verts, 110 tris bounds X[-1000, 600] Y[-1050, 1050] Z[-2400, 2400] 1600 x 2100 x 4800 +e106_wep_02_01 1002 verts, 772 tris 269 x 179 x 417 ← what a real e106 turret looks like +e106_brg_01 202 verts, 202 tris 105 x 76 x 305 +``` + +**110 triangles, perfectly round axis-aligned bounds, and bigger than the ship it +is mounted on.** That is not a turret. 🟡 It reads as a collision / trigger +volume, and ✅ whatever it is, **the game does not draw it**: the runtime capture +of e106 contains no `e303_wep_01` at all. + +### Why this was missed + +`assemble_ship` treats **every** `rou_*` node in the composite as a drawable +part, and the doc comment states the cross-id mount as intended behaviour — +`"INCLUDING repeated instances and cross-id turret mounts (rou_e303_wep_01_root +×2 on the e106 hull)"` — with +`ship::tests::static_assembly_matches_runtime_capture` asserting +`count("e303_wep_01") == 2`. The absence from the capture was explained away as +vbase dedup, but **dedup would show one instance, not zero**. + +The test cannot catch it either: it walks the capture's parts and looks each up +in the static output, so **extra** static placements are invisible to it. That is +the same shape of gap as the earlier `include_external` hypothesis — a test that +can only fail one way. + +### Scope, stated carefully + +Sweeping all 335 assembled ships for the signature *ship-scale span with under +400 triangles* flags **20 ships and 58 placements** over 28 distinct resources +(`e005_ant_*`, `f001_ant_*`, `f002_bdy_*`, `f301_barrel`, `f303_body`, +`e303_wep_01`, …). **Only the `e106`/`e303_wep_01` case is proven** — by render, +by capture absence, and by geometry. Some of the others may be legitimately large +low-poly parts, and each needs the same three checks before being called a bug. + +**Next:** decide the rule that separates drawable parts from volumes (candidates: +the node-name suffix, a descriptor flag, or the triangle-density heuristic), then +re-point `static_assembly_matches_runtime_capture` so it also fails on extras. diff --git a/docs/re/captures/e106-static-assembly-volume-bug.png b/docs/re/captures/e106-static-assembly-volume-bug.png new file mode 100644 index 0000000..e44453c Binary files /dev/null and b/docs/re/captures/e106-static-assembly-volume-bug.png differ