acdream/docs/research/2026-07-25-slice-i2-flat-collision-schema.md
Erik d9300c7854 feat(physics): define deterministic flat collision assets
Add immutable indexed physics and containment BSP records, exact-bit polygon and Setup payloads, separated CellStruct/topology ownership, and iterative positive-before-negative flattening. Reject malformed graphs and ranges, and prove source identity over synthetic edge cases and installed retail DAT samples without cutting production traversal over.
2026-07-25 14:52:47 +02:00

4.3 KiB

Slice I2 — deterministic immutable collision assets

Status: COMPLETE Parent: docs/plans/2026-07-25-modern-runtime-slice-i.md Scope: Core collision storage and preparation only. Production movement, BSP traversal, streaming publication, and package format are unchanged.

Result

Slice I2 defines the immutable representation that will replace parsed collision DBObj graphs after the later shadow and differential gates:

  • physics BSP nodes in deterministic positive-before-negative pre-order;
  • explicit signed child indices, where -1 means absent;
  • one ordered leaf polygon-index stream;
  • polygon rows with checked vertex ranges and one contiguous vertex stream;
  • a separate, smaller cell-containment BSP schema;
  • ordered Setup cylinder/sphere rows and verbatim dimensions;
  • separate reusable CellStruct geometry and per-EnvCell topology;
  • direct prepared indices from portals and BSP leaves to polygon rows;
  • optional GfxObj root bounds and visual bounds.

All collections are immutable arrays. Per-EnvCell transforms remain publication state and are deliberately absent from reusable CellStruct geometry.

Retail and source ordering

The storage contract is pinned to the existing retail traversal oracle:

  • BSPTREE::find_collisions 0x0053A440
  • BSPNODE::find_walkable 0x0053CC80
  • BSPLEAF::find_walkable 0x0053D6F0

The source graph is flattened iteratively. A stack pushes the negative child before the positive child, so the output records the current node, then its complete positive subtree, then its complete negative subtree. Leaf polygon references stay in their stored order. Polygon-table rows are sorted by ID, which makes output independent of dictionary insertion and bucket order; leaves and portals resolve IDs to direct indices during preparation.

No plane is reconstructed and no vector is normalized. Plane, Vector3, sphere, cylinder, Setup-dimension, and visual-bound floats are copied directly. Tests compare all components through BitConverter.SingleToInt32Bits.

Corruption contracts

Construction rejects:

  • unsupported BSP node types or child presence inconsistent with the encoded node type;
  • source cycles and shared child nodes;
  • flat cycles, shared children, unreachable nodes, and non-pre-order layouts;
  • child, polygon-index, polygon, or vertex ranges outside their arrays;
  • missing leaf or portal polygon IDs;
  • duplicate/unsorted polygon IDs or visible-cell IDs;
  • polygon key/row-ID disagreement or point-count disagreement;
  • default immutable arrays and null source rows.

An empty tree uses root index -1. A populated deterministic tree always uses root index 0.

Evidence

The synthetic suite covers:

  • positive/negative child ordering;
  • multi-polygon leaf order;
  • dictionary-order independence;
  • negative-zero and nontrivial float payload preservation;
  • source mutation after preparation;
  • missing IDs, invalid ranges, malformed child patterns, cycles, shared children, and unreachable/non-pre-order shapes;
  • asymmetric containment trees;
  • a 20,000-node containment tree without call-stack recursion;
  • Setup row order and exact dimensions;
  • portal direct-index resolution and deterministic visible-cell ordering.

Installed retail DAT coverage:

  • Facility Hub cells 0x8A02016E and 0x8A02017A;
  • Holtburg cottage cell 0xA9B4013F;
  • cottage, stair/ramp, and multipart-door GfxObjs 0x01000A2B, 0x01000AC5, and 0x010044B5;
  • player, wide-low collision-family, and multipart-door Setups 0x02000001, 0x020019E3, and 0x020019FF.

For every installed sample, tests compare source node type, plane bits, positive/negative child identity, leaf index, solid marker, bounds, polygon order, polygon metadata, vertex bits, Setup shapes, dimensions, and visual bounds.

Release gates:

  • I2 focused structural/installed-DAT tests: 13 passed
  • expanded Slice I graph + flat oracle: 131 passed
  • complete Core tests: 3,263 passed / 2 skipped
  • dotnet build AcDream.slnx -c Release --no-restore: passed
  • complete solution tests: 8,355 passed / 5 skipped

There is intentionally no traversal cutover and no new retail divergence in this commit. Slice I3 serializes these records into the package, adds typed prepared collision access, and bakes the complete installed catalog before any flat query can affect gameplay.