feat(ui): debug overlay + refined input controls
Adds the first on-screen HUD for the dev client plus today's mouse-control refinements. Also lands yesterday's scenery-alignment changes that were left uncommitted in the working tree. Overlay: - BitmapFont rasterizes a system TTF via StbTrueTypeSharp into a 512x512 R8 atlas at startup (Consolas on Windows, DejaVu/Menlo fallbacks) - TextRenderer batches 2D quads in screen-space with ortho projection; one shader + two draw calls (rect then text) for panel backgrounds under glyphs - DebugOverlay composes info / stats / compass / help panels on top of the 3D scene; toggles via F1/F4/F5/F6; transient toasts for key events - DebugLineRenderer and its shaders (carried over from the scenery work) are properly committed in this commit Controls: - Per-mode mouse sensitivity (Chase 0.15, Fly 1.0, Orbit 1.0); F8/F9 to adjust the active mode multiplicatively (x1.2) - Hold RMB to free-orbit the chase camera around the player; release stays at the new angle (no snap-back) - Mouse-wheel zooms chase distance between 2m and 40m - Chase pitch widened to [-0.7, 1.4] so mouse-Y tilts both ways from the default neutral angle Scenery alignment (carried from yesterday's session): - ShadowObjectRegistry AllEntriesForDebug + Scale field - SceneryGenerator uses ACViewer's OnRoad polygon test + baseLoc + set_heading rotation - BSPQuery dispatchers accept localToWorld so normals/offsets transform correctly per part - TransitionTypes.CylinderCollision rewritten with wall-slide + push-out - PhysicsDataCache caches visual-mesh AABB for scenery that lacks physics Setup bounds
This commit is contained in:
parent
6b4e7569a3
commit
ff325abd7b
20 changed files with 2734 additions and 268 deletions
169
memory/project_session_2026_04_16.md
Normal file
169
memory/project_session_2026_04_16.md
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
# Session 2026-04-16 — Scenery Alignment + Collision Registration
|
||||
|
||||
## Headline result
|
||||
|
||||
**Scenery alignment + collision finally in a working state.**
|
||||
- All outdoor entities get collision cylinders
|
||||
- Trees in roads mostly fixed
|
||||
- Trees snapped to terrain (triangle-aware Z sample matching player physics)
|
||||
- Visual-to-collision position alignment correct
|
||||
|
||||
## Critical root causes found this session
|
||||
|
||||
### 1. Scenery entity ID collision across landblocks
|
||||
**File:** `src/AcDream.App/Rendering/GameWindow.cs` `BuildSceneryEntitiesForStreaming`
|
||||
|
||||
Old: `sceneryIdBase = 0x80000000 | (landblockId & 0x00FFFF00)` — the mask only
|
||||
captured bits 8-23 which always has `0xFF` in the low byte (from the lb id
|
||||
suffix). Result: every landblock with the same Y coord produced scenery with
|
||||
**identical IDs**. When streaming loaded the next landblock, `ShadowObjects
|
||||
.Register` called `Deregister(entityId)` first, wiping out the previous
|
||||
landblock's collision. Only the LAST-loaded landblock had collision for any
|
||||
given ID.
|
||||
|
||||
Fix: `0x80000000 | (lbXByte << 16) | (lbYByte << 8)` — each landblock gets a
|
||||
unique ID namespace. Every scenery entity now uniquely identified.
|
||||
|
||||
### 2. Scenery generator using wrong terrain-Z formula
|
||||
**File:** `src/AcDream.App/Rendering/GameWindow.cs` `SampleTerrainZ`
|
||||
|
||||
My earlier "triangle-aware" terrain sample had wrong barycentric math.
|
||||
Physics's `TerrainSurface.SampleZ` used different (correct) math derived
|
||||
from the WorldBuilder mesh index buffer. So player walked at one Z,
|
||||
scenery placed at another → trees hovered above ground.
|
||||
|
||||
Fix: Ported WorldBuilder `TerrainUtils.GetHeight` exactly — same split
|
||||
direction formula, same barycentric triangle pair, same interpolation.
|
||||
Both scenery and player now use identical height sampling.
|
||||
|
||||
### 3. Collision cylinder position using AABB center, not entity root
|
||||
Multi-part scenery meshes can have parts offset from the mesh origin. The
|
||||
visual mesh AABB center is NOT the entity's rendered position. Using
|
||||
AABB center for the collision cylinder put collision meters away from the
|
||||
visible mesh.
|
||||
|
||||
Fix: Collision cylinder center = `entity.Position` (the rendered origin).
|
||||
Radius from the retail Setup CylSphere when available, clamped to
|
||||
[0.3, 1.5] m.
|
||||
|
||||
### 4. Scenery iterated 81 vertices, retail iterates 64 cells
|
||||
**File:** `src/AcDream.Core/World/SceneryGenerator.cs`
|
||||
|
||||
Decompiled FUN_005311a0 iterates `local_8c < 8 && local_94 < 8` — 64 cells.
|
||||
We iterated 0..8 on both axes (81 vertices). Extra scenery at landblock
|
||||
seams.
|
||||
|
||||
Fix: `for (int x = 0; x < CellsPerSide; x++)` where `CellsPerSide = 8`.
|
||||
|
||||
Note: WorldBuilder iterates 81, ACViewer iterates 81. Retail iterates 64.
|
||||
Currently matching retail decompiled — may need revisiting if we see seam
|
||||
gaps.
|
||||
|
||||
### 5. Road check was per-vertex nearest-neighbor, should be 4-corner polygonal
|
||||
**File:** `src/AcDream.Core/World/SceneryGenerator.cs` `IsOnRoad`
|
||||
|
||||
ACE-server's simplified road test uses single-vertex `(terrain & 0x3)` check.
|
||||
That's wrong — retail uses a 4-corner polygonal test with 5m road ribbon
|
||||
(`FUN_00530d30`).
|
||||
|
||||
Fix: Direct port of ACViewer's `Landblock.OnRoad` (lines 300-398) with
|
||||
16-case corner-configuration dispatch and 5m `RoadHalfWidth`. Also kept an
|
||||
extra "origin-cell has road vertex" guard since retail's exact threshold
|
||||
constants (`_DAT_007c97cc/...`) weren't in dumped chunks.
|
||||
|
||||
### 6. Pre-displacement road check was wrong
|
||||
Retail doesn't skip a vertex based on its own road bit — it rolls displacement
|
||||
then tests the final position against OnRoad. My code had `if (IsRoadVertex(raw))
|
||||
continue;` which silently dropped scenery retail would have kept.
|
||||
|
||||
Fix: Removed. The post-displacement OnRoad test is the only road check.
|
||||
|
||||
### 7. Rotation missing baseLoc + (450-heading)%360 flip
|
||||
**File:** `src/AcDream.Core/World/SceneryGenerator.cs`
|
||||
|
||||
Retail's `AFrame::set_heading(degrees)` transforms `yaw = -(450 - heading) % 360`
|
||||
before building the quaternion. It also composes with `BaseLoc.Orientation`.
|
||||
|
||||
Fix: Applied both. Rotations now match retail for asymmetric scenery.
|
||||
|
||||
### 8. Stabs in buildingCells
|
||||
**File:** `src/AcDream.App/Rendering/GameWindow.cs`
|
||||
|
||||
Was treating `lbInfo.Objects` (stabs) as buildings, over-suppressing scenery
|
||||
in town landblocks. Retail only suppresses scenery in cells with actual
|
||||
`lbInfo.Buildings`.
|
||||
|
||||
Fix: Only `Buildings` contribute to `buildingCells`.
|
||||
|
||||
## Still imperfect (for tomorrow)
|
||||
|
||||
- Some scenery still hovers slightly above ground in certain cells.
|
||||
Probably a minor split-direction edge case or BaseLoc.Z handling quirk.
|
||||
- User wants a real debug overlay (on-screen text/markers with positions,
|
||||
directions, distances, etc.). Currently has F3 console dump + cyan debug
|
||||
wireframes from the DebugLineRenderer.
|
||||
- Collision is now consistent but "feel" needs tuning against retail.
|
||||
- Large trees with BSP on canopy still rely on our CylSphere fallback
|
||||
rather than a retail-faithful dual-test (BSP + CylSphere).
|
||||
|
||||
## Pickup for next session
|
||||
|
||||
User explicitly said next session:
|
||||
1. **Debug overlay UI** — on-screen text for player pos, direction, heading
|
||||
in degrees, nearest object distances, "am I colliding" indicator. The
|
||||
current approach (F3 console dump, wireframes in 3D world) is insufficient
|
||||
because the user doesn't know "what 40m looks like" visually.
|
||||
2. **Controls** — make working with the client easier. Presumably: keybind
|
||||
list, camera controls, debug toggles, maybe a tool palette.
|
||||
|
||||
## Files changed this session
|
||||
|
||||
- `src/AcDream.App/Rendering/GameWindow.cs` — scenery ID namespace, OnRoad,
|
||||
visual-mesh-AABB collision, triangle-aware SampleTerrainZ, debug wireframes,
|
||||
F3 dump, building/stab separation, DebugLineRenderer wiring.
|
||||
- `src/AcDream.App/Rendering/DebugLineRenderer.cs` (new) — minimal GL line
|
||||
renderer for wireframe debug.
|
||||
- `src/AcDream.App/Rendering/Shaders/debug_line.{vert,frag}` (new) — debug
|
||||
line shader pair.
|
||||
- `src/AcDream.Core/World/SceneryGenerator.cs` — 64-cell iteration, ACViewer
|
||||
OnRoad, baseLoc+heading rotation, BaseLoc.Z passthrough, removed
|
||||
pre-displacement road check.
|
||||
- `src/AcDream.Core/World/WorldEntity.cs` — added `Scale` field for scenery.
|
||||
- `src/AcDream.Core/Physics/PhysicsDataCache.cs` — `GfxObjVisualBounds` +
|
||||
`GetVisualBounds` for mesh AABB fallback.
|
||||
- `src/AcDream.Core/Physics/ShadowObjectRegistry.cs` — `AllEntriesForDebug()`
|
||||
for the debug overlay, `Scale` on ShadowEntry.
|
||||
- `src/AcDream.Core/Physics/BSPQuery.cs` — `localToWorld` rotation parameter
|
||||
on all dispatcher methods so collision normals/offsets transform correctly.
|
||||
- `src/AcDream.Core/Physics/TransitionTypes.cs` — `CylinderCollision` rewritten
|
||||
with wall-slide + push-out, `FindObjCollisions` rewritten per-object with
|
||||
retail 6-path dispatcher, TransitionalInsert retry loop.
|
||||
|
||||
## Key reference pointers (for tomorrow)
|
||||
|
||||
- WorldBuilder scenery: `references/WorldBuilder/Chorizite.OpenGLSDLBackend/
|
||||
Lib/SceneryRenderManager.cs` + `SceneryHelpers.cs`.
|
||||
- WorldBuilder terrain: `references/WorldBuilder/WorldBuilder.Shared/
|
||||
Modules/Landscape/Lib/TerrainUtils.cs` (GetHeight, GetNormal, OnRoad).
|
||||
- Retail decompiled collision: `docs/research/decompiled/chunk_00530000.c`
|
||||
FUN_005311a0 (scenery loop) and FUN_00530d30 (OnRoad).
|
||||
- Setup field layout: `docs/research/decompiled/chunk_00510000.c` getter
|
||||
thunks ~line 7563-7662 (CylSpheres at +0x48, Radius +0x64, etc.).
|
||||
|
||||
## Collision pipeline overview (current state)
|
||||
|
||||
1. **Scenery generation** (`SceneryGenerator.Generate`) — 64 cells per lb,
|
||||
LCG-deterministic displacement, OnRoad + building cell filter, slope
|
||||
filter, returns `ScenerySpawn{ObjectId, LocalPosition, Rotation, Scale}`.
|
||||
2. **Scenery hydration** (`BuildSceneryEntitiesForStreaming`) — samples
|
||||
terrain Z via `_physicsEngine.SampleTerrainZ`, builds `WorldEntity`.
|
||||
3. **Collision registration** (end of `ApplyLoadedTerrain`) — for every
|
||||
outdoor mesh entity: pick radius from Setup.CylSphere → Setup.Radius →
|
||||
mesh AABB fallback. Height from Setup.Height or mesh. Register cylinder
|
||||
at `entity.Position` in `ShadowObjectRegistry`.
|
||||
4. **Collision query** (`Transition.FindObjCollisions`) — player sphere
|
||||
sweeps via `GetNearbyObjects` which searches player's lb + 8 neighbors.
|
||||
Per-object dispatch: BSP → `BSPQuery.FindCollisions` (retail 6-path),
|
||||
Cylinder → `CylinderCollision` (wall-slide + push-out).
|
||||
|
||||
Good night.
|
||||
113
memory/project_session_2026_04_17.md
Normal file
113
memory/project_session_2026_04_17.md
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
# Session 2026-04-17 — Debug Overlay + Control Tuning
|
||||
|
||||
## Headline result
|
||||
|
||||
**On-screen HUD overlay and refined input controls for the dev client.**
|
||||
- TTF-based font atlas rendered via stb_truetype
|
||||
- Screen-space text + rect batcher (`TextRenderer`)
|
||||
- Composite overlay with panels for position / heading / collision /
|
||||
FPS / compass / keybind help
|
||||
- Per-mode mouse sensitivity (Chase, Fly, Orbit)
|
||||
- RMB-held free-orbit around the player (no snap-back on release)
|
||||
- Mouse-wheel zoom in chase mode
|
||||
- Extended chase pitch range so mouse-Y moves both ways
|
||||
|
||||
## Files added
|
||||
|
||||
- `src/AcDream.App/Rendering/BitmapFont.cs` — TTF atlas (stb_truetype)
|
||||
- `src/AcDream.App/Rendering/TextRenderer.cs` — 2D quad batcher for text + rects
|
||||
- `src/AcDream.App/Rendering/DebugOverlay.cs` — composed HUD panels
|
||||
- `src/AcDream.App/Rendering/Shaders/ui_text.{vert,frag}` — ortho-proj text shader
|
||||
- `src/AcDream.App/Rendering/Shaders/debug_line.{vert,frag}` — wireframe shader
|
||||
(carried from yesterday's scenery-alignment session but not committed then)
|
||||
|
||||
## Files modified
|
||||
|
||||
- `src/AcDream.App/AcDream.App.csproj` — added `StbTrueTypeSharp` 1.26.12
|
||||
- `src/AcDream.App/Rendering/Shader.cs` — added `SetVec2` / `SetVec4`
|
||||
- `src/AcDream.App/Rendering/ChaseCamera.cs`
|
||||
- Added `YawOffset` for RMB free-orbit
|
||||
- Added `AdjustDistance` for mouse-wheel zoom
|
||||
- Widened pitch clamp from `[0.05, 1.4]` to `[-0.7, 1.4]`
|
||||
(mouse-Y now moves camera in both directions from neutral)
|
||||
- `DistanceMin=2f`, `DistanceMax=40f` zoom envelope
|
||||
- `src/AcDream.App/Rendering/GameWindow.cs`
|
||||
- Field block: `_textRenderer`, `_debugFont`, `_debugOverlay`,
|
||||
`_sensChase/_sensFly/_sensOrbit`, `_rmbHeld`, `_lastFps`, `_lastFrameMs`
|
||||
- OnLoad: load Consolas → BitmapFont → TextRenderer → DebugOverlay
|
||||
(silently skips if no system font)
|
||||
- Keyboard: F1/F2/F4/F5/F6 panel toggles; F8/F9 sensitivity
|
||||
(multiplicative ×1.2 steps, per-mode)
|
||||
- Mouse: MouseDown / MouseUp track RMB; MouseMove routes to the
|
||||
active mode's sensitivity; RMB release does NOT snap YawOffset
|
||||
- OnRender: snapshot builder (player pos, heading, nearest-obj dist,
|
||||
colliding flag) passed to `DebugOverlay.Draw`
|
||||
|
||||
## Yesterday's scenery work (finally committed in this session's commit)
|
||||
|
||||
The untracked files show that yesterday's scenery-alignment fixes lived
|
||||
on disk but hadn't been committed. This session's commit includes:
|
||||
- `src/AcDream.Core/Physics/BSPQuery.cs` — `localToWorld` rotation params
|
||||
- `src/AcDream.Core/Physics/PhysicsDataCache.cs` — `GfxObjVisualBounds`
|
||||
- `src/AcDream.Core/Physics/ShadowObjectRegistry.cs` — `Scale` + debug iter
|
||||
- `src/AcDream.Core/Physics/TransitionTypes.cs` — rewritten cylinder / BSP
|
||||
- `src/AcDream.Core/World/SceneryGenerator.cs` — 64-cell, ACViewer OnRoad,
|
||||
baseLoc + set_heading rotation
|
||||
- `src/AcDream.Core/World/WorldEntity.cs` — `Scale` field
|
||||
|
||||
## Sensitivity defaults (current)
|
||||
|
||||
| Mode | Default | F8 step | F9 step |
|
||||
|-------|---------|-------------|-----------|
|
||||
| Chase | 0.15x | ÷ 1.2 | × 1.2 |
|
||||
| Fly | 1.0x | ÷ 1.2 | × 1.2 |
|
||||
| Orbit | 1.0x | ÷ 1.2 | × 1.2 |
|
||||
|
||||
Effective rate at chase 0.15x: `0.15 × 0.003 rad/px = 0.00045 rad/px`
|
||||
≈ 0.026°/pixel. 1000 pixels → 26° rotation.
|
||||
|
||||
Fly at 1.0x is `0.003 rad/px` ≈ 0.172°/px.
|
||||
|
||||
## Keybinds (full, current)
|
||||
|
||||
| Key | Action |
|
||||
|-----------|--------|
|
||||
| F1 | Toggle keybind help panel |
|
||||
| F2 | Toggle collision wireframes |
|
||||
| F3 | Console dump (pos + nearby objects) |
|
||||
| F4 | Toggle HUD info panel |
|
||||
| F5 | Toggle HUD stats panel |
|
||||
| F6 | Toggle compass |
|
||||
| F8 / F9 | Active-mode mouse sensitivity slower / faster |
|
||||
| F | Toggle fly camera |
|
||||
| Tab | Toggle player mode (live session only) |
|
||||
| WASD | Move (player mode) / fly |
|
||||
| Space | Jump (hold to charge, release to fire) |
|
||||
| Shift | Run |
|
||||
| Mouse | Turn character / look |
|
||||
| Hold RMB | Free-orbit camera around player (stays on release) |
|
||||
| Wheel | Zoom chase distance |
|
||||
| Escape | Exit fly / player / close window |
|
||||
|
||||
## Open issue (parked for follow-up)
|
||||
|
||||
User reports mouse "feels like you can only move one way" at low
|
||||
sensitivity. Diagnosed + fixed: chase `PitchMin` was clamping at
|
||||
`0.05f`, preventing any upward tilt. Widened to `-0.7f`. Needs
|
||||
visual verification next session.
|
||||
|
||||
## Pickup for next session
|
||||
|
||||
**MAJOR TASK PARKED HERE**: user has asked for a deep investigation
|
||||
+ port of the retail AC client's GUI subsystem. User explicitly
|
||||
directed **Opus 4.7 with extra-high effort** for this work. The
|
||||
agents are dispatched in this session and their output lives in
|
||||
`docs/research/2026-04-17-retail-ui-*.md`. See that set of files
|
||||
for the in-depth UI research + C# scaffold.
|
||||
|
||||
After the retail-UI port is in place:
|
||||
1. Hook the retail chat window to the existing WorldSession message
|
||||
stream
|
||||
2. Port the health/stamina/mana globes to real player stats (need
|
||||
`CharacterCreate`/`InqStats` wire parsing first)
|
||||
3. Port the inventory panel (needs CreateObject item parsing)
|
||||
Loading…
Add table
Add a link
Reference in a new issue