Round-2 integration: gameplay + flicker fixes land in main#5
Merged
Conversation
added 11 commits
July 4, 2026 09:49
… shafts, title Validated on the audit poses (region-luma vs the round-2 baselines): - water at grazing: clean-water rect now 1.00x sky luma (was 1.16-1.23x — the river measured BRIGHTER than the sky); the fat white band at the waterline is gone - backlit treeline: canopy/background 1.35 (audit) -> 1.26, and crowns now read as shaded volumes (lit tops, darker sides, saturated green) instead of flat pale-mint blobs; the remainder is sun-struck top facets water.wgsl (audit F2): - re-land the lost grazing calibration as min(schlick, 0.60) - lift reflected rays off the horizon (r.y >= 0.08): the GGX-prefiltered horizon mip is a pale wash that does not exist in the real sky - fade crest foam by view angle (measured ~5% of the milk stripe) - screen UV from the fragment position, not a per-vertex perspective divide (wrong snapshot texel at grazing distances) tree.wgsl (audit F3): - crown self-occlusion on the sky fill (sides 35%, tops 100%) — env irradiance by normal alone gave side facets half the sky dome - rim-gated, shadowed, cloud-modulated leaf transmission (a full-crown version brightens exactly the silhouettes the audit flagged) - direct term carries sun_dir.w; leaf shadow floor deepens with distance main.ts: - fog heightRef 0 -> -0.6, density 0.024 -> 0.016: the reference sat at water level, so grazing views integrated the densest slab down the whole valley — that WAS the visible milk band (it spilled over banks) - sun shafts 0.4/0.96 -> 0.18/0.90 (up to ~+0.14 HDR silhouette veil) - title centering via measureText (exact in the current engine; the 0.58 hand estimate was itself the off-centering) - stop engaging the drifted inline water fallback (stale lighting AND a mismatched params layout); SH-005 codegen is the durable fix Adds docs/audit-round2.md — the full round-2 audit with the evidence index (tools/.testout/audit2/) these numbers come from.
…of truth Round-2 audit F11 items — the gameplay-shaped findings: Waves: with one kind per wave and BODIES_PER_KIND=2 pool slots, the shipped game never showed more than 2 enemies at once — "19 enemies" was only ever a total. build-world.ts now accepts a comma-separated kind cycle per wave (an explicit spawn sequence when the list length equals count), arena_02 authors 3 mixed waves (22 enemies, dragoons + a tyrant finale that never appeared before), and MAX_CONCURRENT rises 4 -> 6 so the cap is the real limit again. Camera: the orbit-shortening raycast now hits STATIC geometry only. Enemy bodies (MOVING layer) used to shorten the orbit, pulling the camera inside the mob whenever the player got surrounded — near-plane clipping filled the screen with polygon soup (see the audit's combat captures). Enemies briefly occluding the camera reads far better. River: the world file authored six overlapping zig-zag water volumes (with a 2 m gap) that the runtime silently ignored in favour of hardcoded constants — two drifted sources of truth. The world file now carries the one real 80x5 band and main.ts reads extents from it. Boot-smoked 20 s at 4K on the round-2 engine lib; world generator output verified (1 water volume, 3 waves / 22 enemies, sequence matches the authored cycles).
Round-2 audit F8: every world material wrote velocity = (0,0), so TAA's motion-adaptive clamp never engaged for terrain / building / trees / grass — the primary mechanism behind TSR-0.5 shimmer on thin grass and sway ghosting. With the engine half (Bloom-Engine/engine PR #82: per-slot prev transforms + abi_motion_vector), the four world materials now output real motion vectors: - terrain.wgsl / building.wgsl: static geometry — previous clip = view.prev_view_proj x world (pure camera reprojection). - tree.wgsl: wind sway re-evaluated at frame.time - frame.delta_time, projected by the previous VP — sway finally moves the velocity buffer instead of ghosting through TAA at velocity 0. - grass_instanced.wgsl + the inline copy in main.ts (kept in sync, as the SH-005 note requires): per-blade sway one frame back. All velocity writes go through the ABI's abi_motion_vector (the core path's exact NDC-delta x 0.5 convention). Requires the engine lib from PR #82 (older libs compile these shaders fine — prev_view_proj and prev_mvp always existed in the ABI — but prev_mvp was filled with the current mvp there, so object motion would read zero; camera reprojection works either way). Smoked at 4K on the live game: all four materials compile and render clean with wind sway active — no smearing or warp, which a wrong velocity sign/scale would show immediately under TAA.
- CLAUDE.md: Windows-specifics build section (bun world build, --debug-symbols, engine rebuild + relink/perry-cache rules, self-reporting crashes, batch-run note), five quirks incl. the EN-020 string rule, inline-grass sync warning, real main.ts size. - perry-quirks.md: Impact section now describes the shipped build-time world generator (not the pre-generator hardcoded-world era); quirk-verification section updated; quirk #5 cross-refs. - tickets.md: round-2 PR status header; SH-012 shipped (round-2 tree shading, evolved form); SH-022 shipped (planar-reflection water + round-2 recalibration); SH-021 drift warning now cites the real EN-022 incident. - audit-round2.md: status addendum — PR roster, F1/EN-020 root-caused and fixed (toggle correlation was a red herring; per-frame string parsing was the trigger), decisions D1-D3 still open. - engine-notes.md: all three prerequisites are landed in the engine repo; doc kept as the record of why. - visual-quality.md: status header (tiers 1-4 + round-2 pass landed; numbers in tier tables are historical; where current truth lives). - README.md: stale engine-patch prerequisite replaced, F3 profiler row added, Windows --debug-symbols pointer.
…anded) Companion to engine 713e2a1 (fullscreen lag + flicker root causes). - SSGI remains enabled: the Lumen SW-GI camera-follow bakes that stalled the GPU 1-2.4s per ~5s of mouse-look on the 760M are now amortized engine-side (binned + sliced clipmap bake). Measured here: rotating gameplay locked at 59-60fps with GI on, startup freeze gone. - PERFTEST harness (dormant, PERFTEST=false): mode 0 staged feature bisect, mode 1 gameplay timeline with per-phase frame brackets (begin/A/B/C/D/E attribution; E=endDrawing catches GPU submit stalls), PERFSPIKE dumps, scripted enemy kills, camera-rotation load, optional swapchain screenshot schedule. This instrumentation found the clipmap stall, the stale shadow VPs, the AE hunting cycle, the EN-022 jitter velocity, and the GTAO phase pulse -- keep it for future regressions. - Notes-to-future-self in comments: injected keys never reach isAnyInputPressed (edge consumed by the pump) -- flip gameState directly; F3 profiler overlay costs ~5.5ms/frame at 4K, do not judge perf with it open.
- Enemies were steered in XZ only and kept their spawn Y forever — on any slope they walked INTO the hill and attacked the player from inside the ground. New terrainHeightAt() bilinearly samples the same heightfield grid the Jolt collider uses; spawn and the per-frame AI step snap enY to it. - Player model yaw offset was -pi/2, which faced the character INTO the camera (180 degrees off the aim direction). Verified empirically with windowed screenshots at yaws 0.00/0.31/0.78/4.29: +pi/2 lines the bsuit front up with camera forward at every heading.
…hell THE building flicker (user-visible gray lines/areas): the category-1 skip in the static-mesh loop only covered MODEL_IS_BOX placeholders, so the real building_floor.glb fell through to drawModel — coplanar with the procedural matBuilding shell. The two surfaces z-fought and the GLB window-slat texture rows flickered through the plaster whenever TAA jitter flipped the per-pixel depth winner. Root-caused from native F12 screenshots (4x zoom showed the slat rows, not shadow acne or text). The skip now covers every branch of the loop.
…ker) Root cause of the reported building flicker, found via native F12 swapchain screenshots: the mortar bands were pow(max(cos(y*f),0), band.w*8) — a razor-sharp procedural pulse with zero filtering. Re-evaluated each frame at TAA-jittered positions on the half-res buffer, its hard edges produced an aliasing beat pattern (~11 crawling lines out of 2-3 real bands) that flickered across the whole face, worst up close. Replaced with a distance-field band antialiased over one pixel via fwidth, with a sub-pixel coverage fade. The face is now stable plaster with a single clean band; verified by consecutive-frame diff (face went from bright-lines-everywhere to near-zero).
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (16)
📝 WalkthroughWalkthroughThis PR adds real motion vectors to building, grass, terrain, and tree materials, recalibrates water reflection/foam and tree lighting, simplifies world water config and multi-enemy wave spawning, adds terrain-following enemy movement, camera/model fixes, a PERFTEST harness, and expands documentation on Perry FFI quirks and build workflow. ChangesMotion vectors and material shading updates
Gameplay, world data, and main.ts systems
Documentation and repo hygiene updates
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Vertex as VertexShader
participant Frame as FrameUniforms
participant View as ViewUniforms
participant Fragment as FragmentShader
Vertex->>Frame: read frame.time - frame.delta_time
Vertex->>Vertex: recompute previous wind sway / world position
Vertex->>View: project prev position with view.prev_view_proj
Vertex->>Fragment: pass curr_clip, prev_clip
Fragment->>Fragment: velocity = abi_motion_vector(curr_clip, prev_clip)
sequenceDiagram
participant MainLoop
participant TerrainHeightAt
participant EnemyState
participant Camera
MainLoop->>TerrainHeightAt: query terrainHeightAt(enX, enZ)
TerrainHeightAt-->>EnemyState: bilinear-sampled Y
EnemyState-->>MainLoop: updated enY per tick
MainLoop->>Camera: raycast with Layer.NON_MOVING mask
Camera-->>MainLoop: orbit distance excluding enemies
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Consolidates all round-2 shooter work into main: the open round-2 PRs (already merged into this integration branch) plus the 2026-07-06/07 lag + flicker + gameplay fixes that are not in any individual PR.
Round-2 PRs subsumed (all already merged here)
New fixes landed on top
Depends on engine round2/integration (PR Bloom-Engine/engine#83). Verified via native F12 swapchain screenshots.
Summary by CodeRabbit
New Features
Bug Fixes