feat(physics): S2 chunk 4 - movement publishes from the transition's cells; children inherit at registration
Movement: CommitSetPosition's RefreshPositionRows/ReplacePositionRows and the staged apply publish the retail render product from the exact cell list collision just used, the transition's cell_array retail feeds add_shadows_to_cells in CPhysicsObj::SetPositionInternal @0x00515330 (pseudo-C 283526-283539); the separate move-path bbox recompute is deleted. calc_cross_cells @0x00515230 stays the distinct full-recompute path (PhysicsShadowCommitAction.Recalculate). Children (Contract B recursion): ShadowObjectRegistry.AttachChild/DetachChild give an attached object the root's current cells as part entries only, republished whenever the root's array changes, detached at withdrawal and cascaded from the root's Deregister; nested attachment resolves to the root with a bounded, cycle-safe chain. EquippedChildRenderController attaches at realization (FromSetupRenderParts over the child's Setup) and detaches at its single removal funnel. WalkProductionWorldData's dynamic sweep reads TryGetRetailCellArray directly; the 64-hop parent-chain walk and its FindParentLocalId plumbing are deleted. CollisionWorldState.Clear now also clears the retail products. Gates (implementer's isolated worktree at identical content): Release build 0/0; Core 4,970/4,970; App hermetic 6,761/6,761; targeted walk/child/live-entity/placement/comparator 166/166; Runtime 1,884/1,884. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
706fc49397
commit
75ea269d35
14 changed files with 1072 additions and 179 deletions
|
|
@ -212,27 +212,58 @@ walk. The Facility Hub stair Setup `0x02000623` is the installed-DAT
|
|||
regression: its authored parent is `0x8A02015F`, while its visual parts also
|
||||
register in `0x8A02015E` and the adjoining vertical cells.
|
||||
|
||||
**Moving multipart objects and equipped children (S2 chunk 4, 2026-09-02).**
|
||||
Moving multipart objects follow the sibling retail route rather than their
|
||||
authored/feet cell alone: `CPhysicsObj::add_shadows_to_cells` installs every
|
||||
`CPartArray` part in every cell of the object's retained `CELLARRAY`, and
|
||||
`CPhysicsPart::Draw` stamps each part only after that part passes a cell's
|
||||
portal test. `WalkProductionWorldData` therefore buckets live projections by
|
||||
all `ShadowObjectRegistry.GetOwnerCells` rows, while the walk classifier owns
|
||||
a projection+part drawn-pass stamp. Retail advances that stamp after
|
||||
portal test. `WalkProductionWorldData.ResolveDynamicRenderCells` therefore
|
||||
reads `ShadowObjectRegistry.TryGetRetailCellArray` directly for every dynamic
|
||||
record — the SAME borrow-not-recompute recipe the static sweeps already use
|
||||
— falling back to `GetOwnerCells` (counted by the shared
|
||||
`UnregisteredStaticRenderFallbackCount`) only when the registry has not
|
||||
registered an array yet, while the walk classifier owns a separate
|
||||
projection+part drawn-pass stamp. Retail advances that stamp after
|
||||
`LScape::draw` + `FlushAlphaList` and before the interior depth clear
|
||||
(`PView::DrawCells` @0x005A4886), so a part may draw once in the landscape
|
||||
pass and once again in the post-clear interior-cell pass; acdream re-arms the
|
||||
classifier at that exact walk boundary. `add_shadows_to_cells` recursively
|
||||
passes the root's same `CELLARRAY` to every attached child, so equipped-child render
|
||||
projections resolve their accepted parent chain to the root's owner cells;
|
||||
they do not fall back to one authored cell. Never replace the per-part stamp
|
||||
with a whole-projection drawn-once gate: at a stair portal the torso may pass
|
||||
in the first cell while head/hair fails and must retry through another crossed
|
||||
classifier at that exact walk boundary. Never replace the per-part stamp with
|
||||
a whole-projection drawn-once gate: at a stair portal the torso may pass in
|
||||
the first cell while head/hair fails and must retry through another crossed
|
||||
cell. One binding exception is the local player: retail
|
||||
`RenderDeviceD3D::DrawMeshInternal` bypasses `Get/SetDrawnThisFrame` when
|
||||
`CPhysicsPart::IsPartOfPlayerObj` is true, repainting the player's parts at
|
||||
every crossed-cell turn so later wall/depth ordering remains correct.
|
||||
|
||||
Retail's move-path product is the SAME `add_shadows_to_cells` call the
|
||||
registration path uses, not a second independent flood:
|
||||
`CPhysicsObj::SetPositionInternal` (0x00515330) takes the transition's own
|
||||
`cell_array` on a successful move and calls
|
||||
`remove_shadows_from_cells`/`add_shadows_to_cells` with it directly — the
|
||||
`calc_cross_cells_static` cylsphere/bbox dispatch never re-runs on a move.
|
||||
`ShadowObjectRegistry.ReplacePositionRows` mirrors this exactly:
|
||||
`PublishRetailProductFromExactCells` publishes the retail render product from
|
||||
the SAME exact cell list collision just republished into, replacing the
|
||||
independent Contract A recompute an earlier chunk ran on every move; retail's
|
||||
`num_cells > 0` keep-when-empty gate covers both products identically, so a
|
||||
move with no resolvable transition array leaves both untouched.
|
||||
|
||||
`add_shadows_to_cells` also recursively passes the root's same `CELLARRAY` to
|
||||
every object in `children` (Contract B); acdream's attached projections
|
||||
(equipped weapons/shields/ammunition) own no independent collision shapes, so
|
||||
`ShadowObjectRegistry.AttachChild`/`DetachChild` publish PART ENTRIES only —
|
||||
never a collision row — into every cell of the resolved root's current retail
|
||||
CELLARRAY, re-publishing automatically whenever that array changes
|
||||
(registration, move, staged apply, `ReplaceMultiPartPayload`).
|
||||
`EquippedChildRenderController.TryRealize` calls `AttachChild` at the exact
|
||||
moment an attached projection's `WorldEntity` is registered against its
|
||||
accepted parent; `CommitProjectionRemoval` (the withdrawal/unparent/teardown
|
||||
funnel) calls `DetachChild`. A nested attachment (a child of a child)
|
||||
resolves to the ultimate root by walking the existing attach chain at the
|
||||
registry, bounded and cycle-safe — `WalkProductionWorldData` no longer walks
|
||||
an accepted-parent chain of its own; it reads the child's own id straight off
|
||||
`TryGetRetailCellArray`, identically to any other dynamic record.
|
||||
|
||||
**Slice I3 prepared collision extension (2026-07-25).** At its introduction,
|
||||
the package remained format 1 and retained mesh type values 1–3; bake-tool 4
|
||||
appended typed GfxObj,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue