acdream/docs/plans/2026-04-13-rendering-rebuild.md
Erik 157ed9d974 fix(movement): jump works locally (airborne velocity preserved)
Two fixes for jump physics:
- Skip ground-snap when velocity Z > 0 (prevents immediate re-landing
  at high framerates where per-frame Z delta < 0.05 snap threshold)
- Guard apply_current_movement velocity write behind OnWalkable check
  (prevents MotionInterpreter.DoMotion from zeroing jump velocity on
  every frame while airborne)
- Guard PlayerMovementController velocity replacement behind OnWalkable
  (preserves momentum during airborne flight)

Jump works locally but server packet not yet sent (BUG-002).
Facing direction mismatch logged as BUG-003.
RunRate not verified as BUG-004.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 00:12:11 +02:00

98 lines
3.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Rendering Rebuild from ACME — COMPLETE
Port ACME's rendering pipeline to acdream. Each step produces a
visually testable result. The animation system stays unchanged (ACME
has none — ours is ported from the decompiled client).
**Status:** All 5 steps shipped 2026-04-13.
## Step 1: Port StaticObject shader + instanced rendering
The biggest performance win. Replace per-entity DrawElements with
instanced rendering using a shared instance VBO.
**From ACME:**
- StaticObject.vert: aInstanceMatrix (mat4 at locations 3-6, divisor=1)
- StaticObject.frag: sampler2DArray + alpha cutout
- Instance buffer pattern: single float[] upload per frame
**Changes:**
- New shader: mesh_instanced.vert/frag (from ACME StaticObject.vert/frag)
- Rewrite StaticMeshRenderer to use instance buffer pattern
- Group entities by (GfxObjId, textureAtlas) → one DrawElementsInstanced per group
- Per-GfxObj TextureAtlasManager (grow-on-demand, starts at 32 slots)
- ushort indices (not uint32) for objects
**Test:** Same visual output, fewer draw calls (check perf overlay).
## Step 2: Port Landscape shader + terrain chunks
Replace per-landblock terrain draws with chunk batching.
**From ACME:**
- Landscape.vert/frag: 8 packed uvec4 attributes, sampler2DArray terrain + alpha
- TerrainChunk: N×N landblocks baked into one VBO/IBO
- TerrainGPUResourceManager: buffer creation + partial updates
**Changes:**
- New shader: terrain_acme.vert/frag (from ACME Landscape.vert/frag)
- New TerrainChunkRenderer (replaces TerrainRenderer)
- LandblockMesh.Build outputs VertexLandscape-compatible structs
- Single DrawElements per chunk (multiple landblocks)
**Test:** Same terrain appearance, one draw call per chunk.
## Step 3: Port AdjustPlanes lighting
Replace guessed sun direction with decompiled retail values.
**From decompiled:**
- FUN_00532440 (AdjustPlanes): face-normal accumulation + per-vertex lighting
- DAT constants: sun direction, ambient, diffuse
**Changes:**
- LandblockMesh: face-normal accumulation (replaces central differences)
- Shader uniforms: xLightDirection, xAmbient from decompiled constants
- Static objects: same lighting model
**Test:** Side-by-side with retail client shows matching lighting.
## Step 4: Port EnvCell portal visibility
Render only visible interior cells.
**From ACME:**
- EnvCellManager: portal visibility BFS from camera cell
- Portal occluder pass: depth-only draw of portal polygons
- Conditional depth clear when camera is inside a cell
**Changes:**
- New CellVisibility system (BFS through CellPortals)
- Portal occluder depth pass before EnvCell geometry
- Conditional depth clear in render order
**Test:** Enter a building — only visible rooms render.
## Step 5: Wire animation into instanced pipeline
The AnimationSequencer outputs per-part transforms. These need to flow
into the instance buffer alongside static transforms.
**Changes:**
- Animated entities write their per-part instance matrices into the
shared instance buffer every frame (same buffer as static objects)
- AnimationSequencer.Advance(dt) is called BEFORE the instance buffer
upload so the latest frame's transforms are included
**Test:** NPCs breathe, player walks — all through the instanced pipeline.
## Render Order (target)
```
1. Terrain (one DrawElements per chunk, PolygonOffset on)
2. Conditional depth clear (if camera inside EnvCell)
3. EnvCell geometry (DrawElementsInstanced, portal visibility culled)
4. Static objects opaque (DrawElementsInstanced, alpha cutout)
5. Static objects translucent (DrawElementsInstanced, blend on)
6. Particles (additive blend — future)
```