feat(physics): S2 chunk 1b - callers supply the visual part array; installed-DAT comparator
Every production registration now hands ShadowObjectRegistry the object's whole visual part array beside its collision dispatch: static publication (App LandblockPhysicsPublisher and the headless Content twin) through ShadowShapeBuilder.FromStaticRenderParts, live entities (Runtime LiveEntityCollisionBuilder) through the new FromSetupRenderParts, which walks every Setup part with the same physics-sphere-else-drawing-sphere and part-box rule from the PhysicsDataCache Runtime already reaches. Nothing consumes the retail products yet; the App hermetic lane still passes 6,758/6,758. The Lane=InstalledDat comparator registers five real fixtures through the real publication inputs and prints retail CELLARRAY, collision cells, and the old render cells side by side: Facility Hub stair Setup 0x02000623 (7 cells incl. 0x8A02015F/015E), cathedral ramp 0x020009A2 (3 cells, the genuine multi-part case), the #334 Neftet formation (25 cells), and a landblock-edge crosser (6 cells, 2 in the neighbor block). All three answers agree for BSP-bearing objects, as the shared primitive predicts; the divergence chunk 3 expects appears only for decorative non-BSP parts. Core Physics 2,202/2,202; Runtime 1,884/1,884; App hermetic 6,758/6,758; comparator + stair pin 5/5; solution Release build 0 warnings / 0 errors. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
059b8883ab
commit
5a2792d689
7 changed files with 908 additions and 11 deletions
|
|
@ -111,3 +111,116 @@ FacilityStairAssembly_RegisterAcross015FTo015EWithoutCollisionRows`, the
|
|||
and `ShadowShapeBuilder*`, `LiveEntityCollisionBuilderTests`,
|
||||
`LandblockPhysicsPublisherTests`, `Issue334*`, the flood replay fixtures, and
|
||||
the Runtime placement/physics suites (full list in the 2026-09-02 trace).
|
||||
|
||||
## 5. Chunk 1 evidence (2026-09-02, chunk 1b)
|
||||
|
||||
Chunk 1b threaded the whole visual part array through every production
|
||||
`RegisterMultiPart`/`ReplaceMultiPartPayload` caller as a zero-pixel-change
|
||||
side product:
|
||||
|
||||
- `LandblockPhysicsPublisher.PublishStaticEntity` — builds
|
||||
`ShadowShapeBuilder.FromStaticRenderParts(entity.MeshRefs,
|
||||
publication.StagingCache.GetGfxObj, publication.StagingCache.GetVisualBounds,
|
||||
out _)` once per static and passes it as `partArray:` to BOTH the
|
||||
BSP-branch and the Setup cylinder/sphere fallback-branch `RegisterMultiPart`
|
||||
calls. The visual-bounds source is `publication.StagingCache`, the SAME
|
||||
`PhysicsDataCache` `WalkProductionWorldData.ResolveStaticRenderCells`
|
||||
already reads (`cache.GetGfxObj`/`cache.GetVisualBounds`) — no new source
|
||||
was threaded, none was needed.
|
||||
- `LandblockPhysicsContentBuilder.PublishStaticCollision` (the no-window/
|
||||
Headless twin, driven from `HeadlessSessionWorldProjection`) — same
|
||||
treatment, same `cache.GetGfxObj`/`cache.GetVisualBounds` pair.
|
||||
- `LiveEntityCollisionBuilder` — added `ShadowShapeBuilder.FromSetupRenderParts`
|
||||
(Core, mirrors `FromStaticRenderParts`'s per-part dispatch but walks a
|
||||
Setup's parts directly instead of `MeshRef`s, since a live entity has no
|
||||
meshRef list). `LiveEntityCollisionRegistration` gained a `RenderParts`
|
||||
field; `Build()` computes it via two new resolvers
|
||||
(`Func<uint, GfxObjPhysics?> _getGfxObj`, `Func<uint, GfxObjVisualBounds?>
|
||||
_getVisualBounds`) that the public constructor wires to
|
||||
`PhysicsDataCache.GetGfxObj`/`GetVisualBounds` — the identical resolver
|
||||
SHAPE `FromStaticRenderParts` takes, so a static and a live Setup sharing a
|
||||
GfxObj id read the same render geometry. Both new constructor parameters
|
||||
default to a no-op resolver, so every existing 2-arg test fixture keeps its
|
||||
exact prior behavior (`RenderParts` resolves to an empty list, entering
|
||||
`RecomputeRetailCellArray`'s `partArray.Count == 0` early return — a no-op
|
||||
on every dictionary chunk 1a introduced). `Register`/`ReconcileAppearance`
|
||||
pass `partArray: registration.RenderParts` into `RegisterMultiPart`/
|
||||
`ReplaceMultiPartPayload`.
|
||||
- No other production caller exists: a full-source grep for
|
||||
`.RegisterMultiPart(`/`.ReplaceMultiPartPayload(` after this change finds
|
||||
exactly these four call sites (App publisher ×2 branches, Content builder
|
||||
×2 branches, Runtime builder ×2 methods) plus the four in this list — every
|
||||
one now supplies `partArray:`.
|
||||
- Zero-behavior-change proof: App.Tests filtered to the non-InstalledDat
|
||||
lanes still passes exactly 6,758/6,758 (unchanged count), Core.Tests
|
||||
Physics 2,202/2,202, Runtime.Tests 1,884/1,884 — chunk 1b changed no
|
||||
observable production output.
|
||||
|
||||
### Installed-DAT comparator results
|
||||
|
||||
`tests/AcDream.App.Tests/Physics/RetailCellArrayComparatorInstalledDatTests.cs`
|
||||
registers each fixture through the real production dispatch
|
||||
(`ShadowShapeBuilder.FromLandblockBspParts` first, else the Setup
|
||||
CylSphere/Sphere fallback — `LandblockPhysicsPublisher.PublishStaticEntity`'s
|
||||
exact branch order — plus the `FromStaticRenderParts` part array) against a
|
||||
registry backed by the real `PhysicsDataCache`, then reads back retail's
|
||||
CELLARRAY (`TryGetRetailCellArray`), today's COLLISION cells
|
||||
(`GetOwnerCells`), and today's OLD RENDER cells (`ComputeStaticRenderCells`
|
||||
over the identical part array and seed).
|
||||
|
||||
| Fixture | Route | Parts | Retail cells | Collision cells | Old-render cells | Retail == old-render |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 1: Facility Hub stair Setup `0x02000623` (parent `0x8A02015F`) | BoundingBox | 1 (single wide part, NOT multi-part — see below) | 7: `0x8A02015F,0x8A0201C1,0x8A02015E,0x8A0201C0,0x8A0201C7,0x8A020141,0x8A020163` | identical | identical | yes (exact set) |
|
||||
| 2: Cathedral ramp Setup `0x020009A2` (resolved parent `0xF4180112`) | BoundingBox | 7 (`0x01001FE8` + six `0x01001FE6` slabs) | 3: `0xF4180112,0xF4180113,0xF4180009` | identical | identical | yes (exact set) |
|
||||
| 3: #334 Neftet formation GfxObj `0x010046D8` (landblock `0x8764`) | BoundingBox | 1 | 25 (5×5 rectangle about landcell (2,2), entirely within `0x8764`) | identical | identical | yes (exact set) |
|
||||
| 4: outdoor landblock-edge crosser, GfxObj `0x010046EB` (landblock `0x8764`, origin (133.38, 183.52)) | BoundingBox | 1 | 6: `0x87640030,0x8764002F,0x87650029,0x87640037,0x87640038,0x87650031` — 2 of 6 in neighbor landblock `0x8765` | identical | identical | yes (exact set) |
|
||||
| 5: multi-part Setup, parts in different cells | — | — | — see note | — | — | — |
|
||||
|
||||
Every fixture reports retail == collision == old-render exactly (same
|
||||
ordered set), zero mismatches. This is a structural consequence of chunk 1a's
|
||||
route logic, not incidental: every one of these statics is BSP-bearing
|
||||
(`hasPhysicsBsp=True`), so `RecomputeRetailCellArray`'s dispatch always takes
|
||||
the `BoundingBox` branch (`CPhysicsObj::find_bbox_cell_list` over the SAME
|
||||
part array, same seed, same `PhysicsDataCache` — the identical primitive
|
||||
`ComputeStaticRenderCells` already calls). A retail-vs-collision difference
|
||||
never appeared for these five fixtures either — the collision dispatch for a
|
||||
BSP-bearing object also flows through the box-walk over the same shapes, so
|
||||
there is no decorative-vs-collision part split to observe here (that
|
||||
divergence needs a fixture with a non-colliding decorative part alongside a
|
||||
colliding one — none of the fixtures required by the plan packet happen to
|
||||
be that shape; it remains open evidence for chunk 3, not a gap in this
|
||||
chunk's coverage).
|
||||
|
||||
Per-cell part-entry counts (`GetRetailPartEntriesInCell`) confirm retail's
|
||||
CELLARRAY-then-part-array insertion order: fixture 1's single part appears
|
||||
once in each of its 7 cells (7 entries total); fixture 2's 7 parts each
|
||||
appear in each of its 3 cells (21 entries total — every part registers in
|
||||
every cell of the entity's CELLARRAY, not a per-part cell subset, matching
|
||||
`CPartArray::AddPartsShadow`'s exact semantics); fixtures 3 and 4's single
|
||||
part appears once per cell.
|
||||
|
||||
**Fixture-1 correction against the plan packet's assumption:** Facility Hub
|
||||
stair Setup `0x02000623` resolves via `SetupMesh.Flatten` to exactly ONE
|
||||
visual part, not several — its own wide vertex-array box spans all 7 crossed
|
||||
cells. It is a wide SINGLE-part static. The genuinely multi-part case (item
|
||||
5, "parts in different cells") is fixture 2, the Cathedral ramp: 7 real
|
||||
parts, whose combined CELLARRAY spans 3 cells, with every part entered in
|
||||
every one of those 3 cells per retail's `AddPartsShadow` semantics — "parts
|
||||
in different cells" is best read as "the entity's CELLARRAY spans multiple
|
||||
cells," not "each part is confined to its own cell" (retail's render index
|
||||
has no such per-part cell subsetting; S3 owns per-part draw admission
|
||||
separately). No installed-DAT fixture with genuinely per-part-confined
|
||||
membership was found among the test files this chunk was pointed at.
|
||||
|
||||
**Fixture-4 note:** the first candidate a bounding-SPHERE-based edge scan
|
||||
picked was fixture 3's own Neftet formation GfxObj `0x010046D8` (69 m sphere
|
||||
radius crosses the landblock's 0 m edge at its 63.78 m origin) — but the
|
||||
outdoor flood's governing geometry is the vertex-array BOX
|
||||
(`CLandCell::add_all_outside_cells` divides the BOX by `square_length`, #334),
|
||||
and that object's real 96 m box (48 m half-width) does not reach the edge
|
||||
from its centered position. The scan was corrected to key on
|
||||
`GfxObjPhysics.VisualBounds` (the box) instead, and to explicitly exclude
|
||||
`0x010046D8` so fixture 4 stays independent of fixture 3; it now resolves to
|
||||
a different Neftet-landblock GfxObj (`0x010046EB`) genuinely near the block's
|
||||
Y=192 edge, producing 2 of 6 retail cells in the neighboring landblock
|
||||
`0x8765`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue