This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/docs/re/structures/collisionset.md
Sylpheed RE agent 97d02f0092 re: confirm the cmesh<->model link via GameResourceID; world unit is oracle-blocked
Two follow-ups on yesterday's^Wthis morning's CollisionSet write-up.

1. The _cmesh <-> render-model link, which I recorded as UNTESTED because
   matching stems against .xbg object names covered 4 of 158.  The disc keeps
   only one build manifest, so that corpus was never going to answer it.  The
   right corpus is the GameResourceID field of the DefTables / GP_MAIN_GAME
   records -- 480 distinct values.  Against those, with a control that shuffles
   the characters of each stem:

       ship/mob stems prefixed by a real resource id   108/112 = 96.4%
       same stems, characters shuffled  (control)        0/112 =  0.0%
       asteroid stems prefixed (expected none)           0/46

   So a CollisionSet entry is <GameResourceID>[_<part>]_cmesh.  The 0/46 on
   asteroids matters as much as the 108/112: a test that fired on everything
   would be the bound-check hazard again.

2. The world unit.  Sweeping every pak for a name carrying a kilometre figure
   returns mapmesh_box_500km.col/.rgn and nothing else -- 162 references, all to
   that one pair.  The reading rests on a single filename with no corroborating
   instance anywhere in the data, so no static test can settle it; marking it
   blocked on the oracle rather than leaving it as an open static question.

   My objection's premise did survive: rou_e010 is a real GameResourceID and
   e010_ADAN_Attacker_S is in the stage tables, so the 133-unit mesh does belong
   to a craft the game calls an attacker.  Whether the trailing _S means "small"
   is a further guess (there are _EX4 / _HF / _HF_Wayne variants), so it stays
   suggestive rather than evidence.
2026-08-26 09:41:46 +00:00

143 lines
6.6 KiB
Markdown
Raw 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.
# `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 names *are* the game's own resource ids (upgraded from 🟡 the same day)
The previous write-up recorded this link as **untested**, because matching
`_cmesh` stems against `.xbg` object names covered only 4 of 158 — the disc keeps
just one build manifest (`machines\rou_e104\…`), so that test had no power in
either direction.
The right corpus is not the manifests, it is the **`GameResourceID` field** of
the `DefTables` / `GP_MAIN_GAME` records — 480 distinct values, e.g.
`GameResourceID = 'rou_e010'`. Against those, with a control that shuffles the
characters of each stem:
| | |
|---|---|
| ship / mob stems prefixed by a real `GameResourceID` | **108 / 112 = 96.4 %** (55 exact) |
| same stems, characters shuffled (control) | **0 / 112 = 0.0 %** |
| asteroid stems prefixed (expected none — they are not resources) | **0 / 46** |
So a `CollisionSet` entry is `<GameResourceID>[_<part>]_cmesh`: the collision
proxy for one part of one game resource. The four unmatched stems are
`rou_e303_wep`, `rou_f301_body`, `rou_f302`, `rou_f303_body`.
The 0 / 46 on asteroids is worth keeping: a test that fires on everything would
be the bound-check hazard again. It separates the two families correctly.
## 🟡 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.
**And there is no second instance of the convention to check against.** Sweeping
every `.pak` on the disc for a name carrying a kilometre figure returns
`mapmesh_box_500km.col` / `.rgn` and nothing else — 162 references, all to that
one pair. So the reading rests on a single filename, with no corroborating
example anywhere in the data. That is not a refutation; it is the absence of any
static way to settle it.
My objection's own premise did survive its check: `rou_e010` is a real
`GameResourceID`, and `e010_ADAN_Attacker_S` appears in the stage tables, so the
133-unit mesh does belong to a craft the game calls an attacker. (Whether the
trailing `_S` means "small" is a further guess — there are `_EX4`, `_HF` and
`_HF_Wayne` variants of the same unit — so it is suggestive, not evidence.)
🔴 **Blocked on the oracle.** An independent scale reference — the flight model's
speed values against a measured traversal time in the running game — is the only
route left, and it needs an emulator run rather than the disc.
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.