re: the format layer is NOT exonerated -- assemble_ship draws a non-renderable volume
Did the visual the backlog kept naming as the next step, and it overturns the
conclusion. Rendering e106 from the static assembly and from the baked runtime
capture: the capture has 8 placements, the static assembly 11 -- the same 8 plus
e303_wep_01 x2 and a second e106_eng_01. In the render the destroyer sits inside
a white slab that dwarfs it.
That slab is e303_wep_01: 172 verts, 110 tris, bounds X[-1000,600] Y[-1050,1050]
Z[-2400,2400] = 1600 x 2100 x 4800. A real e106 turret (wep_02_01) is 269x179x417
with 772 tris; the bridge is 105x76x305. So it is not a turret -- it reads as a
collision/trigger volume, and whatever it is the game does not draw it: the e106
capture contains no e303_wep_01 at all.
Why it was missed: assemble_ship treats every rou_* composite node as drawable,
the cross-id mount is documented as intended, and the test asserts
count("e303_wep_01") == 2. The capture's silence was explained as vbase dedup,
but dedup would show one instance, not zero -- and the test walks capture parts
looking them up in the static output, so EXTRA static placements cannot fail it.
Same shape as the earlier include_external gap: a test that can only fail one way.
Scope stated carefully: sweeping 335 ships for "ship-scale span with <400 tris"
flags 20 ships and 58 placements over 28 resources, but ONLY the e106 case is
proven (render + capture absence + geometry). Others may be legitimately large
low-poly parts and need the same three checks.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
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
|
`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.
|
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.
|
||||||
|
|||||||
BIN
docs/re/captures/e106-static-assembly-volume-bug.png
Normal file
BIN
docs/re/captures/e106-static-assembly-volume-bug.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Reference in New Issue
Block a user