docs(render): Phase A8.F — camera-collision root cause + handoff (session 2)
Root cause of the A8.F flap / missing-walls reframed (with the user's help):
the 3rd-person camera EYE passes through walls, and the A8.F renderer keys its
"am I inside?" (PointInCell) and portal side-tests (CameraOnInteriorSide) off
that eye position (camPos = invView translation, GameWindow.cs:7271). Eye clips
a wall -> those decisions flip frame-to-frame -> the flap.
Key finding from camera research (Opus agent + verified against the decomp):
retail's camera does NOT collide with walls either — it fades the player to
translucent (CameraSet::UpdateCamera @ 0x00458ae0 -> SetTranslucencyHierarchical),
which acdream already ports as RetailChaseCamera.ComputeTranslucency. So a
"spring arm that pulls the eye in on a wall hit" is a deliberate divergence from
retail, not a faithful port — needs user sign-off before coding.
Handoff documents: the eye->visibility coupling + flap mechanism, acdream's
current camera (the ported turn/jump input-lag = damping + velocity ring +
mouse filter; no collision), retail's camera (symbols+addresses), the reusable
swept-sphere collision machinery (BSPQuery.FindCollisions vs CellPhysics.BSP),
3 fix options (lead: modern spring arm), open design questions, apparatus, and a
pickup prompt.
Bug A (cellar terrain flood) already fixed + committed in 9417d3c; the
recursive-clip builder works (the prior "Bug B" framing was wrong).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
9417d3c4ce
commit
ce909ad0a8
1 changed files with 371 additions and 0 deletions
371
docs/research/2026-05-29-a8f-camera-collision-handoff.md
Normal file
371
docs/research/2026-05-29-a8f-camera-collision-handoff.md
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
# Phase A8.F — camera-collision root cause + handoff (2026-05-29, session 2)
|
||||
|
||||
## TL;DR
|
||||
|
||||
The A8.F "flap" (building walls / ground blinking in and out) and the
|
||||
missing/transparent-wall symptoms are **not** a builder or enforcement bug.
|
||||
Their root cause is the **3rd-person camera EYE passing through walls**: the
|
||||
A8.F renderer computes "am I inside a building?" and "which side of each
|
||||
doorway am I on?" from the camera eye position, so when the eye clips a wall
|
||||
those decisions flip frame-to-frame.
|
||||
|
||||
This session: (1) **fixed + committed** the worst symptom — the cellar terrain
|
||||
flood (commit `9417d3c`); (2) established the recursive-clip **builder actually
|
||||
works** for most cells (the prior "Bug B" framing was wrong); (3) **reframed the
|
||||
root cause** to the camera, with the user's help; (4) researched the camera
|
||||
system (Opus agent + my verification).
|
||||
|
||||
**The decision that blocks the next session** (brainstorm with the user FIRST):
|
||||
retail's camera does **NOT** collide with walls and pull the eye in — it lets
|
||||
the eye clip and **fades the player to translucent** instead (a mechanism
|
||||
acdream already ports). So "make the camera move closer on a wall hit" is a
|
||||
**deliberate divergence from retail**, not a faithful port. The next session
|
||||
must choose an approach (spring arm vs. decouple visibility from the eye) and
|
||||
get user sign-off before coding, per the CLAUDE.md no-redesign rule.
|
||||
|
||||
---
|
||||
|
||||
## What this session shipped / established
|
||||
|
||||
- **Bug A FIXED + committed (`9417d3c`):** an empty `OutsideView` while inside a
|
||||
building now draws **no** outdoor terrain/scenery (empty mask = "no outdoors
|
||||
visible," not "all outdoors"). Previously the `else` branch disabled the
|
||||
stencil and flooded ungated terrain over the cell interior. **Visual-confirmed
|
||||
by the user: cottage cellar walls are solid now, no terrain bleed-through.**
|
||||
App tests 108/108. All behind `ACDREAM_A8_INDOOR_BRANCH=1`; default play
|
||||
unaffected.
|
||||
- **"Bug B" (builder under-produces) is substantially NOT real.** The pv-dump
|
||||
census showed `PortalVisibilityBuilder` produces correctly *narrowed*
|
||||
`OutsideView` regions for most cells (`0172/0173/0162/015E/0165/016F` →
|
||||
`polys=1`). The empty cases are mostly legitimate (a windowless cellar can't
|
||||
see daylight) or driven by the camera being in an invalid position (see root
|
||||
cause). The handoff predecessor's Finding 2 ("never narrows") does not hold.
|
||||
- **Root cause reframed → the camera.** Confirmed below.
|
||||
- **Camera research done** (Opus agent, findings verified against the decomp +
|
||||
the code by this session).
|
||||
|
||||
### Diagnostics added (committed in `9417d3c`, all opt-in)
|
||||
|
||||
- `PortalVisibilityBuilder.Build` now emits a **`CAMPORTAL[i]` census** under
|
||||
`ACDREAM_A8_DUMP_PV=1`: per camera-cell portal, before the BFS guards, it logs
|
||||
`other=`, `polyLen=`, `hasPlane=`, `interiorSide=`, `planeN=`. This is what let
|
||||
us see that the cottage front-door exit portal was *culled by the side test*
|
||||
(`interiorSide=False`) rather than missing.
|
||||
- `[opaque]` probe (`ACDREAM_PROBE_ENVCELL=1`) — opaque cell-render stats
|
||||
(`cells`/`tris`) **before** the transparent loop overwrites them (the existing
|
||||
`[envcells]` line reads post-loop and is misleading — ignore its `cells=1
|
||||
tris=0`).
|
||||
- `tools/A8CellAudit` `portals <cellId>` now **replicates `BuildLoadedCell`'s
|
||||
polygon-vertex resolution** and prints `BUILDER_SEES=OK/EMPTY` per portal, so
|
||||
exit-portal validity is checkable offline without a launch.
|
||||
|
||||
---
|
||||
|
||||
## The visual symptoms (user-observed, 2026-05-29, with `ACDREAM_A8_INDOOR_BRANCH=1`)
|
||||
|
||||
1. Cellar walls **solid** (Bug A fix confirmed). ✓
|
||||
2. **Flap**: buildings/ground disappear when passing from inside to outside.
|
||||
3. Cellar entrance no longer covered by terrain (Bug A fix). ✓
|
||||
4. Looking into certain windows from **outside**: back walls missing.
|
||||
5. Inside **other** buildings: external + internal walls transparent (showing
|
||||
sky / NPCs / particles).
|
||||
|
||||
**The user's key correlation:** items 2/4/5 happen **when the camera passes
|
||||
through a door, or when standing inside and panning the camera through a wall**.
|
||||
They're intermittent — not always reproducible — which fits a camera-position
|
||||
trigger, not a static rendering bug.
|
||||
|
||||
---
|
||||
|
||||
## Root cause: the camera eye drives A8.F visibility, and it clips through walls
|
||||
|
||||
`camPos` is the **camera eye**, extracted from the inverse of the active
|
||||
camera's View matrix:
|
||||
|
||||
```
|
||||
GameWindow.cs:7270-7271
|
||||
Matrix4x4.Invert(camera.View, out var invView);
|
||||
var camPos = new Vector3(invView.M41, invView.M42, invView.M43); // = the eye
|
||||
```
|
||||
|
||||
(`camera.View` is `CreateLookAt(_dampedEye, …)`, so the translation is the
|
||||
chase-camera eye — **not** the player avatar position, which is tracked
|
||||
separately for interior lighting at `GameWindow.cs:7415-7417`.)
|
||||
|
||||
That eye then drives all three A8.F visibility decisions:
|
||||
|
||||
1. **Camera-cell + portal BFS** — `_cellVisibility.ComputeVisibility(camPos)`
|
||||
(`GameWindow.cs:7323`) → `FindCameraCell` via `PointInCell(camPos, …)`. The
|
||||
BFS portal side-test (`CellVisibility.cs:466-481`) culls portals the eye is on
|
||||
the wrong side of.
|
||||
2. **Strict inside-building gate** (`GameWindow.cs:7343-7346`):
|
||||
`cameraInsideBuilding = a8IndoorBranchEnabled && PointInCell(camPos,
|
||||
CameraCell) && CameraCell.BuildingId != null`. Picks the inside-out vs
|
||||
outside-in render branch.
|
||||
3. **Per-portal interior-side cull in the recursive-clip builder** —
|
||||
`PortalVisibilityBuilder.CameraOnInteriorSide(cell, i, cameraPos)`
|
||||
(`PortalVisibilityBuilder.cs:196-203`; cull site ~`:124`):
|
||||
transforms `cameraPos` to cell-local and dot-tests against the portal plane.
|
||||
|
||||
**The flap mechanism:** when the eye damps to a position outside the room (or in
|
||||
the next room), `PointInCell(eye)` flips and `CameraOnInteriorSide` inverts.
|
||||
The camera-cell ping-pongs, the inside/outside branch switches, and the exit
|
||||
portal through a doorway is culled-then-uncovered frame-to-frame → walls/ground
|
||||
blink. The 3-frame `CellSwitchGraceFrameCount` hysteresis (`CellVisibility.cs:167`)
|
||||
only masks single-frame blips; a sustained multi-frame clip defeats it.
|
||||
|
||||
**Hard evidence (this session's capture):** at cottage cell `0xA9B40170` the
|
||||
front-door exit portal was valid (`polyLen=4`) but the census showed
|
||||
`interiorSide=False` — i.e. the eye was on the *outdoor* side of the door plane
|
||||
`(0,-0.995,0.105)`. The plane math puts the cull threshold at eye Y < −8.50 with
|
||||
the door at Y≈−8.5: **the eye had poked out through the front wall while the
|
||||
player stood inside.** The same portal projected fine when the BFS reached
|
||||
`0170` from a deeper room (`0173`) where the eye was well inside.
|
||||
|
||||
---
|
||||
|
||||
## KEY FINDING: retail does NOT collide the camera — it fades the player
|
||||
|
||||
This **reframes the intended fix** and needs user attention.
|
||||
|
||||
- **Retail's camera does no eye-vs-wall collision.** `CameraManager::UpdateCamera`
|
||||
(0x00456660, decomp `:95505-95953`) computes the eye as
|
||||
`pivot + viewer_offset`, damps it (`Frame::interpolate_origin`, `:95922`), and
|
||||
writes it straight to the render viewer via
|
||||
`SmartBox::PlayerPhysicsUpdatedCallback` (0x00452d60, decomp `:91842`) — no
|
||||
raycast, no sphere sweep, no BSP query in between. The `CameraManager` struct
|
||||
(`acclient.h:35238-35263`) has **no** collision-radius / clipped-distance /
|
||||
obstruction field.
|
||||
- **Retail's anti-clip mechanism is to fade the *player***.
|
||||
`CameraSet::UpdateCamera` (0x00458ae0) calls
|
||||
`CPhysicsObj::SetTranslucencyHierarchical(player, …)` (decomp `:97679`, `:97698`,
|
||||
`:97725`, `:97737`) — opaque at ≥0.45 m camera-to-pivot, fully transparent at
|
||||
≤0.20 m. **acdream already ports this** as `RetailChaseCamera.ComputeTranslucency`
|
||||
(`RetailChaseCamera.cs:367-376`, far=0.45 / near=0.20).
|
||||
|
||||
**Verification (mine, this session):** I confirmed in the decomp that
|
||||
`SetTranslucencyHierarchical` is called from `CameraSet::UpdateCamera` (the four
|
||||
lines above), that `CameraManager::UpdateCamera`'s sole caller writes the eye
|
||||
with no intervening clip, that `RetailChaseCamera.cs` has zero
|
||||
collision/physics references (only `PlayerTranslucency`), and that `camPos` is
|
||||
the eye. (I relied on the Opus agent's full read of `CameraManager::UpdateCamera`
|
||||
for "zero collision calls in the whole function body"; I spot-checked the entry,
|
||||
the damping tail, the caller, and the struct — all consistent.)
|
||||
|
||||
**Implication:** "make the camera move closer when it hits a wall" is **not what
|
||||
retail does**. A spring arm would be a *modern divergence* from the retail
|
||||
behavior acdream faithfully ported. Per CLAUDE.md ("do not replace working
|
||||
retail-faithful logic with a modern redesign without explicit user approval"),
|
||||
the next session must get the user's sign-off on the approach before coding —
|
||||
*but note* that here "retail-faithful" literally means "no collision," which is
|
||||
the behavior that produces the A8.F flap. So some divergence or decoupling is
|
||||
unavoidable to fix the flap.
|
||||
|
||||
---
|
||||
|
||||
## acdream's current camera (file:line)
|
||||
|
||||
All under `src/AcDream.App/Rendering/` unless noted.
|
||||
|
||||
- **Two chase cameras, toggled per-frame:** legacy `ChaseCamera.cs` (rigid) and
|
||||
default `RetailChaseCamera.cs` (damped). Selected by
|
||||
`CameraController.Active` (`CameraController.cs:20-33`) via
|
||||
`CameraDiagnostics.UseRetailChaseCamera` (default ON,
|
||||
`src/AcDream.Core/Rendering/CameraDiagnostics.cs`).
|
||||
- **Eye computation (`RetailChaseCamera.Update`, `:86-142`):**
|
||||
`pivotWorld = playerPos + (0,0,1.5)`; `targetEye = pivotWorld +
|
||||
forward*(-Distance·cosPitch) + up*(Distance·sinPitch)` (`:113-117`); then
|
||||
exponential damping `_dampedEye = Lerp(_dampedEye, targetEye, alpha)` (`:121-133`);
|
||||
published as `Position` + `View = CreateLookAt(_dampedEye, …)` (`:136-137`).
|
||||
**No geometry test anywhere between target and publish.**
|
||||
- **The "input-lag for turning/jumping" port** = the RetailChaseCamera's three
|
||||
smoothing mechanisms, all verified faithful to the decomp:
|
||||
- Exponential damping (follow-lag): `:121-133` + `ComputeDampingAlpha:323-329`
|
||||
↔ retail `:95866-95923` (`stiffness*dt*10` clamped).
|
||||
- 5-frame velocity-averaged, slope-aligned heading (the jump/slope feel):
|
||||
`:94-107`, `:290-314`, `ComputeHeading:211-257` ↔ retail `old_velocities[5]`
|
||||
`:95644-95677`.
|
||||
- Mouse low-pass filter (input lag on flicks): `FilterMouseDelta:164-177` +
|
||||
`FilterMouseAxis:340-358` ↔ retail `CameraSet::FilterMouseInput` (0x00457530,
|
||||
`:96250-96279`); wired at `GameWindow.cs:1171-1199`.
|
||||
- **Constants** (from retail `CameraSet::SetDefaultOffsets` 0x00458F80,
|
||||
`:97916-97967`): `PivotHeight=1.5`, `Distance=2.61` (=|(0,−2.5,0.75)|),
|
||||
`Pitch=0.291 rad`, distance clamp `[2,40]`, pitch clamp `[−0.7,1.4]`. **No
|
||||
collision-radius constant exists** (no collision is done).
|
||||
- **No 1st-person mode** (retail `SetInHead` 0x00458CE0 unported).
|
||||
- Spec: `docs/superpowers/specs/2026-05-18-retail-chase-camera-design.md`
|
||||
explicitly scopes collision OUT (`:454-457`: "we don't attempt 'camera
|
||||
collides with wall' — same as retail").
|
||||
|
||||
---
|
||||
|
||||
## Integration: acdream already has the collision machinery
|
||||
|
||||
A camera "spring-arm" sweep can reuse the player's existing swept-sphere engine
|
||||
(`src/AcDream.Core/Physics/`):
|
||||
|
||||
- **`PhysicsEngine.ResolveWithTransition(curPos, targetPos, cellId, radius,
|
||||
height, stepUp, stepDown, isOnGround, body, moverFlags, entityId)`**
|
||||
(`PhysicsEngine.cs:589`) → `Transition.FindTransitionalPosition`
|
||||
(`TransitionTypes.cs:653`) → per sub-step `FindEnvCollisions`
|
||||
(`:1933`, indoor branch fetches `GetCellStruct(cellId)` → `cellPhysics.BSP.Root`
|
||||
→ `BSPQuery.FindCollisions`).
|
||||
- **`BSPQuery`** primitives (`BSPQuery.cs`): `PointInsideCellBsp` (`:1034`),
|
||||
`SphereIntersectsCellBsp` (`:1077`), `FindCollisions` (`:1637`),
|
||||
`SphereIntersectsPoly` (`:2085`). A purpose-built pivot→eye cast off these is
|
||||
likely a **better fit** than the full `Transition` (which carries unwanted
|
||||
step-up / walkable / gravity semantics).
|
||||
- **`CellPhysics`** (`PhysicsDataCache.cs:511-564`): per-cell `BSP` (collision),
|
||||
`CellBSP` (point-in-cell), polys/planes, transforms. Fetched via
|
||||
`GetCellStruct(cellId)`.
|
||||
- Player sweep params for reference: `radius 0.48`, `height 1.2`
|
||||
(`PlayerMovementController.cs:1107-1108`). A camera probe wants a **small**
|
||||
radius (e.g. `PhysicsGlobals.DummySphereRadius = 0.1`), `height 0` (single
|
||||
sphere), `isOnGround:false`, `body:null` (no contact-plane persistence).
|
||||
- **Slot-in point:** clamp `targetEye` toward `pivotWorld` **between
|
||||
`RetailChaseCamera.cs:117` (target) and `:131` (damp)** so the damping eases
|
||||
toward the clamped point. This requires injecting a physics/collision probe
|
||||
into `RetailChaseCamera` (currently GL-free, no engine ref); App→Core is the
|
||||
allowed dependency direction, so inject `PhysicsEngine` or a narrow
|
||||
`ICameraCollisionProbe` interface.
|
||||
|
||||
---
|
||||
|
||||
## The design decision (MUST brainstorm with the user before coding)
|
||||
|
||||
Fixing the flap requires the eye (or the point feeding visibility) to stop
|
||||
leaving the room. Three shapes, none purely retail-faithful:
|
||||
|
||||
- **Option A — modern spring arm (leading candidate; matches user's instinct).**
|
||||
Sweep pivot→targetEye, stop the eye at the first wall hit. Fixes *both* the
|
||||
render (no see-through) *and* the A8.F visibility (eye stays inside → stable
|
||||
camera-cell + side-tests). **Diverges from retail** (retail lets the eye clip +
|
||||
fades the player). The already-ported translucency fade still applies and will
|
||||
fade *less* (eye stays farther from the player when not clipping). Needs user
|
||||
sign-off for the divergence.
|
||||
- **Option B — decouple visibility from the rendered eye.** Leave the rendered
|
||||
eye retail-faithful (clips + fades), but feed `ComputeVisibility` /
|
||||
`PointInCell` / `CameraOnInteriorSide` a *clamped* point. Fixes the flap, but
|
||||
the render still shows through walls when the eye clips (you'd see exterior
|
||||
through the wall). Less satisfying visually.
|
||||
- **Option C — make A8.F robust to an outside eye.** When the eye is outside but
|
||||
the player is inside, use the *player's* cell for the camera-cell + side tests.
|
||||
Similar outcome to B (flap fixed, render still clips).
|
||||
|
||||
**Recommendation to put to the user:** Option A. It's the only one that fixes
|
||||
both the render and the visibility, it matches the user's stated expectation,
|
||||
and acdream already owns the collision machinery to do it. The cost is an
|
||||
explicit, documented divergence from retail's no-collision design. Confirm with
|
||||
the user, then (per roadmap discipline) file it as a phase before coding.
|
||||
|
||||
---
|
||||
|
||||
## Open design questions for the implementation session
|
||||
|
||||
1. **Approval for the retail divergence** (Option A spring arm) — the gating item.
|
||||
2. **Sweep radius:** small (≈0.1–0.2 m). Too small → eye hugs walls / near-plane
|
||||
clips; too large → eye yanks in aggressively in tight rooms.
|
||||
3. **Which primitive:** purpose-built `BSPQuery.FindCollisions` ray/sphere cast
|
||||
(recommended) vs. full `ResolveWithTransition` (wrong semantics for a camera).
|
||||
4. **Indoor vs outdoor geometry:** indoor walls are in `CellPhysics.BSP` (per
|
||||
cell). **Cottage exterior shells live in landblock-baked GfxObjs**, not cells
|
||||
(cf. issue #98/#101 — cottage floors/walls in GfxObj `0x01000A2B` etc.). A
|
||||
cell-BSP-only sweep fixes the indoor case but misses outdoor shells (the
|
||||
`FindObjCollisions` / ShadowEntry path would be needed for those). Decide scope.
|
||||
5. **1st-person fallback:** none today; a spring arm must no-op at distance 0 if
|
||||
1st-person is added.
|
||||
6. **Interaction with the ported translucency fade** (`ComputeTranslucency`) —
|
||||
verify the fade still behaves once the eye stops clipping.
|
||||
7. **Pivot reference:** sweep from `pivotWorld` (head, `:113`), not the feet.
|
||||
|
||||
---
|
||||
|
||||
## Apparatus / diagnostics (committed `9417d3c`; opt-in)
|
||||
|
||||
Launch (PowerShell), then walk `+Acdream` into a Holtburg cottage:
|
||||
|
||||
```powershell
|
||||
$env:ACDREAM_DAT_DIR="$env:USERPROFILE\Documents\Asheron's Call"; $env:ACDREAM_LIVE="1"
|
||||
$env:ACDREAM_TEST_HOST="127.0.0.1"; $env:ACDREAM_TEST_PORT="9000"
|
||||
$env:ACDREAM_TEST_USER="testaccount"; $env:ACDREAM_TEST_PASS="testpassword"
|
||||
$env:ACDREAM_A8_INDOOR_BRANCH="1"; $env:ACDREAM_A8_DUMP_PV="1"; $env:ACDREAM_PROBE_ENVCELL="1"
|
||||
dotnet run --project src\AcDream.App\AcDream.App.csproj --no-build -c Debug 2>&1 | Tee-Object -FilePath "a8f.log"
|
||||
```
|
||||
|
||||
- `ACDREAM_A8_DUMP_PV=1` — `[pv-dump]` per camera cell incl. the **`CAMPORTAL[i]`
|
||||
census** (polyLen + interiorSide per portal, before the guards) + the
|
||||
EXIT-CULLED/PROJ/CLIP trace + `OUTSIDEVIEW polys=N`.
|
||||
- `ACDREAM_PROBE_ENVCELL=1` — `[opaque]` cell-render stats (reliable; pre-loop).
|
||||
- `ACDREAM_PROBE_VIS=1` — `[buildings]`/`[draworder]`/`[stencil]`/`[envcells]`
|
||||
(heavy: ~17 GL queries × several calls per frame; keep captures short).
|
||||
- `tools/A8CellAudit portals <cellId>` — offline portal dump with
|
||||
`BUILDER_SEES=OK/EMPTY` per portal.
|
||||
|
||||
**A future "camera-cell ≠ player-cell" probe** would directly confirm the
|
||||
flap-by-frame: log when `PointInCell(eye)` disagrees with the player's cell.
|
||||
|
||||
---
|
||||
|
||||
## Current state / safety
|
||||
|
||||
- **Default game safe** — everything gated behind `ACDREAM_A8_INDOOR_BRANCH=1`.
|
||||
- Bug A fix + diagnostics committed (`9417d3c`); App tests 108/108.
|
||||
- Builder works (not the bug); camera-collision work **not started** (awaiting the
|
||||
design decision).
|
||||
- Tree clean as of `9417d3c` plus untracked launch logs (`a8f-*.log`).
|
||||
|
||||
---
|
||||
|
||||
## Pickup prompt
|
||||
|
||||
> Read `docs/research/2026-05-29-a8f-camera-collision-handoff.md`. The A8.F flap /
|
||||
> missing-walls are caused by the 3rd-person camera EYE clipping through walls and
|
||||
> destabilizing the camera-cell + portal side-tests (the eye drives
|
||||
> `PointInCell` / `CameraOnInteriorSide` via `camPos` at GameWindow.cs:7271).
|
||||
> Bug A (cellar terrain flood) is already fixed + committed (`9417d3c`); the
|
||||
> recursive-clip builder works. **Before coding, brainstorm the fix with the user**
|
||||
> (`superpowers:brainstorming`): retail does NOT collide the camera — it fades the
|
||||
> player (`SetTranslucencyHierarchical`, already ported as
|
||||
> `RetailChaseCamera.ComputeTranslucency`), so a "spring arm that pulls the eye in"
|
||||
> is a deliberate divergence needing sign-off. Leading option: a modern spring-arm
|
||||
> camera collision (Option A) sweeping pivot→targetEye via a small-radius
|
||||
> `BSPQuery.FindCollisions` against `CellPhysics.BSP`, clamping `targetEye` between
|
||||
> `RetailChaseCamera.cs:117` and `:131`. Watch the open questions (sweep radius,
|
||||
> outdoor shells live in GfxObjs not cells, 1st-person, the existing fade). File a
|
||||
> roadmap phase once the approach is agreed.
|
||||
|
||||
---
|
||||
|
||||
## Reference index
|
||||
|
||||
**acdream code** (`src/…`):
|
||||
- `AcDream.App/Rendering/RetailChaseCamera.cs` — eye `:113-137`, damping
|
||||
`:121-133`/`:323-329`, heading `:211-257`, mouse filter `:340-358`,
|
||||
translucency `:367-376`.
|
||||
- `AcDream.App/Rendering/ChaseCamera.cs` (legacy), `CameraController.cs:20-33`,
|
||||
`ICamera.cs`.
|
||||
- `AcDream.Core/Rendering/CameraDiagnostics.cs` — `UseRetailChaseCamera`, tunables.
|
||||
- `AcDream.App/Rendering/GameWindow.cs` — eye `:7270-7271`, visibility `:7323`,
|
||||
`cameraInsideBuilding` `:7343-7346`, camera updates `:6851`/`:6862`, mouse-filter
|
||||
`:1171-1199`.
|
||||
- `AcDream.App/Rendering/PortalVisibilityBuilder.cs` — `CameraOnInteriorSide`
|
||||
`:196-203`, cull `:124`, CAMPORTAL census in `Build`.
|
||||
- `AcDream.App/Rendering/CellVisibility.cs` — `ComputeVisibility` `:272`,
|
||||
`PointInCell` `:367`, BFS side-test `:466-481`, grace `:167`.
|
||||
- `AcDream.Core/Physics/` — `PhysicsEngine.cs:589`, `TransitionTypes.cs:653/1933`,
|
||||
`BSPQuery.cs:1034/1077/1637`, `PhysicsDataCache.cs:511-564`,
|
||||
`PlayerMovementController.cs:1107-1108`.
|
||||
- Spec: `docs/superpowers/specs/2026-05-18-retail-chase-camera-design.md` (collision
|
||||
out-of-scope `:454-457`).
|
||||
|
||||
**Retail decomp** (`docs/research/named-retail/acclient_2013_pseudo_c.txt`):
|
||||
- `CameraManager::UpdateCamera` @ 0x00456660 (`:95505-95953`) — NO collision;
|
||||
damping `:95866-95923`; velocity ring `:95644-95677`.
|
||||
- `CameraSet::UpdateCamera` @ 0x00458AE0 (`:97643-97742`) — player-fade
|
||||
`SetTranslucencyHierarchical` `:97679/97698/97725/97737`.
|
||||
- `CameraSet::FilterMouseInput` @ 0x00457530 (`:96250-96279`).
|
||||
- `CameraSet::SetDefaultOffsets` @ 0x00458F80 (`:97916-97967`) — pivot (0,0,1.5),
|
||||
viewer (0,−2.5,0.75).
|
||||
- `SmartBox::PlayerPhysicsUpdatedCallback` @ 0x00452d60 (`:91842`) — sole
|
||||
UpdateCamera caller; eye → render with no clip.
|
||||
- `CameraManager` struct `acclient.h:35238-35263` (no collision fields).
|
||||
Loading…
Add table
Add a link
Reference in a new issue