re: decode CollisionSet_*.bin -- the per-object collision-mesh library
All 18 blobs are byte-identical: the per-stage naming is nominal, and every
stage points at one shared 1675148-byte library stored eighteen times. That
identical size was the reason to open the item, and it turned out to be the
answer to it.
Record layout: {u32 size, u32 name_len, char name[name_len], u32 nv, u32 nt,
f32[3] x nv, u32[3] x nt}, next record at off + 8 + size. The indices are u32
here where MCOL uses u16 -- two different serialisers in one archive.
What makes this a decode rather than a plausible reading: the walk consumes the
file to the byte over 158 variable-length records, with the size word predicted
from the two counts 158/158. A wrong field would desynchronise within a few
records and could not land exactly on the end. All indices in range 158/158;
98.24% of edges shared by exactly two triangles; 147/158 fully manifold.
158 meshes, 90 836 triangles: per-part ship proxies (_bdy/_brg/_eng/_wep/_sld,
the XBG7 sub-part vocabulary) plus 46 stage asteroid meshes whose prefixes are
exactly the stages that have an _AsteroidVolume_wp MCOL.
Two things this file makes me walk back:
* The "1 unit = 1 metre" reading from mapmesh_box_500km is downgraded to
amber. The 500000 arithmetic stands, but it implies that a craft the game's
own tables call "small" is 133 m and that rob_f002 is 447 km -- 89% of the
arena width. The format check survives; the interpretation has no
independent support.
* The _cmesh <-> render-model name link is recorded as UNTESTED, not
confirmed: only one .xbg build manifest survives on the disc, so matching
stems against object names covers 4 of 158, which is no coverage at all.
This commit is contained in:
109
docs/re/structures/collisionset.md
Normal file
109
docs/re/structures/collisionset.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# `CollisionSet_*.bin` — the per-object collision-mesh library
|
||||
|
||||
**✅ Decoded 2026-08-26.** The 18 remaining `hidden/MiscBin.pak` entries, named
|
||||
in the previous iteration as `CollisionSet_S01…S16.bin`,
|
||||
`CollisionSet_Tutorial.bin` and `CollisionSet_test.bin` from the `StageResource`
|
||||
objects' `CollisionMeshes` field. See
|
||||
[`mcol-collision.md`](mcol-collision.md) for the naming and for `MCOL`, which is
|
||||
the *map* hull; this is the per-*object* library.
|
||||
|
||||
## ✅ All 18 are byte-identical
|
||||
|
||||
CollisionSet blobs: 18 distinct contents: 1
|
||||
|
||||
The per-stage naming is nominal. Every stage points at the same 1 675 148-byte
|
||||
library, stored eighteen times — which is why the sizes were identical, the
|
||||
observation that made this worth opening.
|
||||
|
||||
## ✅ The record layout
|
||||
|
||||
u32 size bytes following this word's own 8-byte prefix
|
||||
u32 name_len 16, 24 or 32 — the name field, padded to a multiple of 8
|
||||
char name[name_len]
|
||||
u32 vertex_count
|
||||
u32 triangle_count
|
||||
f32 vertex[3] × vertex_count
|
||||
u32 index[3] × triangle_count
|
||||
|
||||
and the next record starts at `offset + 8 + size`. Note the indices are **`u32`**
|
||||
here, where `MCOL` uses `u16` — these are two different serialisers.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| records | **158** |
|
||||
| bytes consumed | **1 675 148 / 1 675 148 — exact** |
|
||||
| `size == 16 + name_len + 12·nv + 12·nt − 8` | **158 / 158** |
|
||||
| every triangle index `<` vertex count | **158 / 158** |
|
||||
|
||||
The walk consuming the file to the byte over 158 variable-length records, with
|
||||
the size word predicted from the two counts every time, is what makes this a
|
||||
decode rather than a plausible reading — a wrong field would desynchronise the
|
||||
walk within a few records and could not land exactly on the end.
|
||||
|
||||
## ✅ And they are meshes
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| edges shared by exactly two triangles | **132 167 / 134 529 = 98.24 %** |
|
||||
| fully manifold meshes (every edge shared by two) | **147 / 158** |
|
||||
|
||||
92 of the 158 have Euler characteristic **2** — a single closed surface. The
|
||||
remainder are multi-component (χ = 4, 6, 8) or open shells, which is expected
|
||||
for parts like `rou_e901_wing_01_L` that are one piece of a larger hull.
|
||||
|
||||
## ✅ What is in it
|
||||
|
||||
158 meshes, **48 241 vertices and 90 836 triangles**, in four families:
|
||||
|
||||
| prefix | count | what the names look like |
|
||||
|---|---|---|
|
||||
| `rou_` | 78 | `rou_e101_bdy`, `rou_e101_brg_01`, `rou_e106_eng`, `rou_f104_sld_02` |
|
||||
| `mob_` | 30 | `mob_n040`, `mob_n110_bdy`, `mob_n900_gear` |
|
||||
| `rob_` | 4 | `rob_e001`, `rob_e005_bdy`, `rob_f001_bdy`, `rob_f002` |
|
||||
| stage asteroids | 46 | `S01_Asteroid_cmesh_01a`, `S14_p1_Asteroid_cmesh_05` |
|
||||
|
||||
The `_bdy` / `_brg` / `_eng` / `_wep` / `_sld` suffixes are body, bridge, engine,
|
||||
weapon and shield — the same sub-part vocabulary as the `XBG7` render models, so
|
||||
these are per-part collision proxies. The asteroid meshes carry exactly the
|
||||
stage prefixes that have an `_AsteroidVolume_wp` `MCOL`.
|
||||
|
||||
🟡 The name correspondence to the render models is **not demonstrated**. Only one
|
||||
`XBG2` build manifest survives on the disc (`machines\rou_e104\…`), so matching
|
||||
`_cmesh` stems against `.xbg` object names covers 4 of 158 — no coverage, and
|
||||
therefore no evidence either way. It is not a refutation; it is an untested claim.
|
||||
|
||||
## 🟡 The world unit — the metre reading does not survive this file
|
||||
|
||||
[`mcol-collision.md`](mcol-collision.md) inferred **1 unit = 1 metre** from
|
||||
`mapmesh_box_500km.col` spanning exactly 500 000 units. The arithmetic is exact,
|
||||
but this library is the first chance to check what that scale *implies*, and the
|
||||
implications are large:
|
||||
|
||||
| mesh | bbox diagonal, if 1 unit = 1 m |
|
||||
|---|---|
|
||||
| `rou_e010` (`e010_ADAN_Attacker_S` — an "attacker, small") | 133 m |
|
||||
| `rou_e106_bdy` | 2 368 m |
|
||||
| `rou_e101_bdy` | 9 371 m |
|
||||
| `rob_f001_bdy` | 50 179 m |
|
||||
| `S04_Asteroid_cmesh_04` | 84 714 m |
|
||||
| `rob_f002` | 447 522 m — 89 % of the arena's width |
|
||||
|
||||
A craft the game's own tables call *small* would be 133 m, and the largest object
|
||||
would nearly span the arena. That is not impossible for the genre, but it is not
|
||||
the corroboration I claimed. **Downgrading the scale claim to 🟡**: either the
|
||||
game's units really are metres and its ships are kilometre-scale, or `500km` in
|
||||
that filename names something other than the box's span. The 500 000 arithmetic
|
||||
stands; the *interpretation* does not have independent support.
|
||||
|
||||
An independent scale reference — the flight model's speed values against a
|
||||
measured traversal time in the running game — would settle it, and has not been
|
||||
run.
|
||||
|
||||
Reproduce with `tools/re-capture/collisionset.py verify` / `list`
|
||||
([`../data/collisionset.txt`](../data/collisionset.txt)); `collisionset.py obj`
|
||||
exports any named mesh.
|
||||
|
||||
❔ Still open: the meshes carry no material, flag or surface-type field — the
|
||||
record is exactly counts, positions and indices — so whatever distinguishes a
|
||||
hull hit from a shield hit is not stored here. And the library is not indexed:
|
||||
lookup is presumably by name from the unit tables, which has not been traced.
|
||||
@@ -540,9 +540,16 @@ triangles spanning exactly ±250 000** — a cube. Its name says that cube is
|
||||
|
||||
> **one world unit is one metre.**
|
||||
|
||||
This is a name-based inference, but the arithmetic is exact and it runs the
|
||||
other way as a check on the format work: a wrong stride or index width could not
|
||||
have produced a box whose measured size matches its own filename.
|
||||
The arithmetic is exact and it runs the other way as a check on the format work:
|
||||
a wrong stride or index width could not have produced a box whose measured size
|
||||
matches its own filename.
|
||||
|
||||
> 🟡 **Downgraded 2026-08-26, same day.** The *format* check stands. The
|
||||
> *interpretation* does not: decoding `CollisionSet_*.bin` gave the first chance
|
||||
> to see what a metre implies, and it makes a craft the game's own tables call
|
||||
> "small" 133 m long and the largest object 447 km — 89 % of the arena. Either
|
||||
> the ships really are kilometre-scale or `500km` names something other than the
|
||||
> span. See [`collisionset.md`](collisionset.md).
|
||||
|
||||
Reproduce with `tools/re-capture/miscbin_names.py <extract root> [--pairs]`
|
||||
(recorded in [`../data/miscbin-names.txt`](../data/miscbin-names.txt)).
|
||||
|
||||
Reference in New Issue
Block a user