docs(physics): pin Slice I collision oracle
This commit is contained in:
parent
f1a8d36682
commit
624e1119ca
3 changed files with 827 additions and 0 deletions
276
docs/plans/2026-07-25-modern-runtime-slice-i.md
Normal file
276
docs/plans/2026-07-25-modern-runtime-slice-i.md
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
# Modern runtime Slice I — flat collision assets and zero-allocation physics
|
||||
|
||||
**Status:** I0 COMPLETE — retail oracle, representative graph fixtures, and
|
||||
four-mover allocation baselines fixed; I1 reusable scratch is next
|
||||
**Parent:** `2026-07-24-modern-runtime-architecture.md`, Slice I
|
||||
**Purpose:** remove parsed DAT object graphs and steady collision allocations
|
||||
without changing one retail collision decision, float, comparison, or traversal
|
||||
order.
|
||||
|
||||
## 1. Non-negotiable boundaries
|
||||
|
||||
1. Retail collision math is frozen. This slice changes storage and scratch
|
||||
ownership only.
|
||||
2. Every float read from DAT is copied bit-for-bit. The bake performs no
|
||||
normalization, quantization, coordinate conversion, `double` round-trip, or
|
||||
plane reconstruction.
|
||||
3. Positive/negative child order, polygon order within every leaf, early
|
||||
returns, epsilon comparisons, and contact-selection tie behavior remain
|
||||
exact.
|
||||
4. The current object-graph traversal remains an executable oracle until the
|
||||
flat traversal passes mass differential and connected gates.
|
||||
5. Physics `Transition` reuse lands only after reset-completeness,
|
||||
cross-mover-identity, failure, and reentrancy tests prove that no state can
|
||||
leak between resolves.
|
||||
6. No GL/App dependency enters Core. Flat collision records live in
|
||||
`AcDream.Core`; serialization and package access live in `AcDream.Content`;
|
||||
the bake tool produces them offline.
|
||||
7. The active G4 render cutover is independent. Its exact visual rollback
|
||||
remains:
|
||||
|
||||
```text
|
||||
git revert ef1d263337997bb030eadb7b8e71d73dc659907a
|
||||
```
|
||||
|
||||
## 2. Target data architecture
|
||||
|
||||
The runtime collision representation is immutable, array-backed, and addressed
|
||||
by integer index:
|
||||
|
||||
```text
|
||||
FlatCollisionAsset
|
||||
├── FlatPhysicsBsp
|
||||
│ ├── FlatBspNode[] child indices; -1 = no child
|
||||
│ ├── int[] leaf polygon-index stream
|
||||
│ ├── FlatPolygon[] plane, cull mode, id, vertex range
|
||||
│ └── Vector3[] verbatim polygon vertices
|
||||
├── FlatCellContainmentBsp split planes + positive/negative indices
|
||||
├── FlatSetupCollision cyl/sphere records + verbatim dimensions
|
||||
└── EnvCellTopology portals, visible cells, seen-outside
|
||||
```
|
||||
|
||||
Node order is deterministic pre-order. The flattener records the original
|
||||
positive child before the negative child and retains each source leaf's
|
||||
polygon list order. Traversal follows indices using the same recursive
|
||||
control flow as the current retail port; “flat” does not mean a new collision
|
||||
algorithm.
|
||||
|
||||
Per-EnvCell world transform remains publication state, not baked geometry.
|
||||
CellStruct collision geometry can therefore alias by exact source identity;
|
||||
cell-specific portals, visibility, `seen_outside`, and placement remain a
|
||||
separate small payload/publication record.
|
||||
|
||||
## 3. Execution slices
|
||||
|
||||
### I0 — oracle inventory and fixed evidence
|
||||
|
||||
1. Grep named retail before touching traversal:
|
||||
- `BSPTREE::find_collisions`
|
||||
- `BSPNODE::find_walkable`
|
||||
- `BSPLEAF::find_walkable`
|
||||
- `BSPNODE::sphere_intersects_*`
|
||||
- `BSPNODE::point_inside_cell_bsp`
|
||||
- `CTransition::find_transitional_position`
|
||||
- `CTransition::init_path`
|
||||
- `CPhysicsObj::transition`
|
||||
2. Cross-reference ACE and the existing acdream pseudocode/trajectory notes.
|
||||
3. Write one collision-layout pseudocode note that separates:
|
||||
source storage, traversal order, mutable query scratch, and returned
|
||||
side-effects.
|
||||
4. Inventory every production call into `BSPQuery`, every field read from
|
||||
`PhysicsBSPNode`, `CellBSPNode`, `ResolvedPolygon`, `CellPhysics`,
|
||||
`GfxObjPhysics`, and `SetupPhysics`, and every raw DAT graph retained by
|
||||
`LandblockBuild`/`PhysicsDatBundle`.
|
||||
5. Capture representative installed-DAT fixtures:
|
||||
- outdoor/no-BSP;
|
||||
- Facility Hub and cottage indoor cells;
|
||||
- staircase/ramp and cellar-lip cases;
|
||||
- a multipart door/building shell;
|
||||
- projectile/thin-wall geometry;
|
||||
- cells with asymmetric/null BSP children;
|
||||
- leaves with multiple polygons and equal-distance candidates.
|
||||
6. Record allocation baselines for player, remote, projectile, and camera
|
||||
resolve loops.
|
||||
|
||||
Gate: source inventory is complete, fixtures reproduce through the current
|
||||
graph path, and no behavior code changed.
|
||||
|
||||
### I1 — reusable transition and query scratch
|
||||
|
||||
1. Add explicit `ResetForReuse` methods to `ObjectInfo`, `CollisionInfo`,
|
||||
`SpherePath`, and `Transition`.
|
||||
2. Reset every scalar, nullable, property backing field, diagnostic counter,
|
||||
collision GUID list, sphere element, walkable reference, backup value, and
|
||||
transient flag. Retain only storage identity whose contents are completely
|
||||
reset.
|
||||
3. Give each `PhysicsEngine` one owned transition lease. The normal
|
||||
update-thread path rents, initializes, resolves, snapshots the value-only
|
||||
result, and returns in `finally`.
|
||||
4. Make reentrancy explicit and tested. Never hand one mutable transition to
|
||||
two concurrent/nested resolves and never silently share session scratch.
|
||||
5. Reuse walkable vertex arrays only when their exact logical length matches;
|
||||
allocate only on a true size change. No larger-capacity array may leak stale
|
||||
vertices into polygon math.
|
||||
6. Convert short-lived `BSPQuery.CollisionSphere` reference objects to
|
||||
value/ref scratch while preserving each mutation/copy boundary.
|
||||
7. Add structural reflection tests that poison every resettable member, reset,
|
||||
and compare its complete value graph with a fresh instance while asserting
|
||||
retained buffer identities.
|
||||
8. Differential-run fresh-transition and reused-transition engines across
|
||||
success, collision, failure, placement, grounded, airborne, sliding,
|
||||
step-up/down, two-sphere, projectile, and camera cases.
|
||||
|
||||
Gate: bit-identical `ResolveResult` and body side effects, no state leakage
|
||||
when alternating hostile fixtures between different mover IDs, and zero
|
||||
steady transition/query-scratch allocation. Close issue #237 only here.
|
||||
|
||||
### I2 — immutable flat collision schema and flattener
|
||||
|
||||
1. Add Core-only flat record types with explicit index/range contracts.
|
||||
2. Flatten physics and containment BSPs iteratively during preparation while
|
||||
emitting deterministic pre-order node arrays.
|
||||
3. Copy planes, spheres, vertices, setup dimensions, and collision metadata
|
||||
verbatim. Tests compare every float via `SingleToInt32Bits`.
|
||||
4. Resolve each leaf polygon ID to a direct polygon index at preparation time.
|
||||
Missing IDs are corruption, not a runtime fallback.
|
||||
5. Validate:
|
||||
- child indices/ranges;
|
||||
- acyclic/reachable tree shape;
|
||||
- polygon and vertex ranges;
|
||||
- source-vs-flat node, child, polygon, and float identity;
|
||||
- deterministic output independent of dictionary enumeration.
|
||||
6. Keep source graph and flat asset together only in test/shadow fixtures.
|
||||
|
||||
Gate: round-trip structural equality over synthetic edge cases and installed
|
||||
DAT samples; no traversal cutover yet.
|
||||
|
||||
### I3 — package serialization, bake, and prepared collision source
|
||||
|
||||
1. Append new `PakAssetType` values; never renumber the existing mesh values.
|
||||
2. Bump `CurrentBakeToolVersion` because a complete production package now
|
||||
includes collision assets.
|
||||
3. Add strict little-endian serializers with checked counts/ranges, exact float
|
||||
bits, cancellation, CRC coverage, corruption rejection, and trailing-byte
|
||||
rejection.
|
||||
4. Add a typed `IPreparedCollisionSource` over the existing memory-mapped pak.
|
||||
It shares package/catalog lifetime but does not overload the render-only
|
||||
`ObjectMeshData` API.
|
||||
5. Bake:
|
||||
- GfxObj physics BSP + polygons + visual bounds;
|
||||
- Setup cylinder/sphere/dimension records;
|
||||
- CellStruct physics BSP + containment BSP + physics/portal polygons;
|
||||
- EnvCell topology records.
|
||||
6. Alias only byte-identical immutable collision payloads. Cell-specific
|
||||
topology and world placement cannot be aliased merely because geometry is.
|
||||
7. Extend full-bake determinism, two-thread equivalence, corruption,
|
||||
cancellation, publish-transaction, and catalog-completeness tests.
|
||||
|
||||
Gate: two independent bakes are byte-identical; every collision key required
|
||||
by the canonical route is present; a failed/cancelled bake cannot replace the
|
||||
last good pak.
|
||||
|
||||
### I4 — flat traversal shadow implementation
|
||||
|
||||
1. Port each current entry point line-for-line against integer-index nodes:
|
||||
- point-in-cell;
|
||||
- sphere/cell overlap;
|
||||
- walkable search;
|
||||
- six-path moving collision dispatcher;
|
||||
- static sphere/polygon overlap;
|
||||
- time-of-impact overlap.
|
||||
2. Share only polygon-level math that is storage-independent. Do not
|
||||
“simplify” recursion, branch ordering, or early returns.
|
||||
3. Preserve mutation semantics for valid-position spheres, hit polygon, path,
|
||||
contact plane, slide normal, walk interpolation, and transition state.
|
||||
4. Add a differential harness that executes old and flat queries from cloned
|
||||
transition/body inputs and compares:
|
||||
- bool/enum result;
|
||||
- all output vectors/planes by float bits;
|
||||
- selected polygon ID;
|
||||
- path/collision/object side effects;
|
||||
- cell membership and final `ResolveResult`.
|
||||
5. Sweep a large installed-DAT sample with randomized points, spheres,
|
||||
movement vectors, insert modes, orientations, scales, and boundary values.
|
||||
6. Require explicit fixtures for null children, on-plane equality, radius
|
||||
equality, equal candidate distances, multi-polygon leaf order, and deep
|
||||
trees.
|
||||
|
||||
Gate: zero differential mismatch. Any mismatch blocks cutover; no tolerance
|
||||
band is permitted for deterministic scalar results.
|
||||
|
||||
### I5 — dual publication and connected shadow gate
|
||||
|
||||
1. Extend collision preparation so a `LandblockBuild` carries immutable flat
|
||||
assets and small placement/topology records rather than reparsing them on
|
||||
the update thread.
|
||||
2. During the shadow phase, publish both current and flat views under one
|
||||
landblock generation/receipt.
|
||||
3. Keep the current graph path authoritative. Run sampled flat queries from
|
||||
cloned inputs and emit a deterministic mismatch artifact without affecting
|
||||
gameplay.
|
||||
4. Verify load, pending-to-loaded, demotion, rehydrate, same-location revisit,
|
||||
removal, cancellation, and session reset release both views exactly once.
|
||||
5. Run connected login, indoor/outdoor portal churn, doors, ramps, jumping,
|
||||
projectiles, camera collision, and graceful reconnect.
|
||||
|
||||
Gate: zero shadow mismatch and no retained collision owner after landblock or
|
||||
session teardown.
|
||||
|
||||
### I6 — production cutover and graph removal
|
||||
|
||||
1. Flip canonical `PhysicsDataCache` records and every `BSPQuery` production
|
||||
call to flat assets in one bisectable commit.
|
||||
2. Keep the graph route as an automated referee for the first cutover gate,
|
||||
with an exact revert command recorded in this plan and project memory.
|
||||
3. After automated and user correctness acceptance, remove:
|
||||
- production `PhysicsBSPTree`/`CellBSPTree` retention;
|
||||
- production polygon dictionaries and vertex DBObj graphs;
|
||||
- raw collision graphs from streaming build/publication payloads;
|
||||
- obsolete graph adapters and referee code.
|
||||
4. Live-DAT tooling may parse source graphs only to produce/compare flat
|
||||
assets; gameplay production never falls back from a missing/corrupt prepared
|
||||
collision asset.
|
||||
|
||||
Gate: trajectories and collision fixtures remain bit-identical, package-only
|
||||
startup succeeds, and retained-memory accounting shows no parsed collision
|
||||
DBObj graph after stable world reveal.
|
||||
|
||||
### I7 — allocation, soak, and documentation closeout
|
||||
|
||||
1. Measure capped and uncapped resolve allocation separately for player,
|
||||
remotes, projectiles, and camera.
|
||||
2. Run the canonical connected lifecycle/reconnect route and the nine-stop R6
|
||||
portal soak.
|
||||
3. Repeat indoor door/ramp/cellar, jump/fall, combat/projectile, and camera-wall
|
||||
gates.
|
||||
4. Under RDP, accept correctness and lifecycle evidence only; defer absolute
|
||||
GPU/frame-time acceptance to a physical-display run.
|
||||
5. Update architecture, WorldBuilder inventory, milestones, roadmap, issue
|
||||
ledger, divergence register, and both physics/render memory digests.
|
||||
|
||||
Final gate:
|
||||
|
||||
- full Release build and solution tests;
|
||||
- zero deterministic old-vs-flat mismatch;
|
||||
- zero steady transition/query-scratch allocation;
|
||||
- no raw collision graph in production stable state;
|
||||
- connected portal/reconnect/interaction routes pass;
|
||||
- exact cutover rollback recorded before user verification.
|
||||
|
||||
## 4. Commit order
|
||||
|
||||
Each unit is independently buildable and reversible:
|
||||
|
||||
1. `docs(physics): pin Slice I collision oracle and fixtures`
|
||||
2. `perf(physics): reuse reset-complete transition scratch`
|
||||
3. `feat(physics): define deterministic flat collision assets`
|
||||
4. `feat(content): bake and read flat collision assets`
|
||||
5. `feat(physics): add differential flat BSP traversal`
|
||||
6. `feat(streaming): shadow-publish flat collision assets`
|
||||
7. `perf(physics): cut production traversal to flat assets`
|
||||
8. `refactor(physics): remove parsed collision graphs`
|
||||
9. `docs(physics): close Slice I evidence and roadmap`
|
||||
|
||||
The exact production-cutover commit and its `git revert <sha>` command are
|
||||
added to this plan and `claude-memory/project_physics_collision_digest.md`
|
||||
immediately when step 7 lands, before any visual/correctness gate.
|
||||
Loading…
Add table
Add a link
Reference in a new issue