Extract rendering controllers, add static obstacles + skyscrapers#1
Open
holoduke wants to merge 1 commit into
Open
Extract rendering controllers, add static obstacles + skyscrapers#1holoduke wants to merge 1 commit into
holoduke wants to merge 1 commit into
Conversation
…skyscrapers
Structural refactor of the 2334-line god-object in src/app.js (now 1272).
Extracted three focused modules, an obstacle system, and a skyscraper city.
## Architecture
- `src/world/HeightmapPipeline.js` owns the procedural heightmap state and
erosion/effect pipeline (thermal, hydraulic, smooth, terracing, ridged,
cellular, billow, warped). TerrainApp delegates via `applyTerrainEffect`.
- `src/rendering/ShadowController.js` owns CSM state: 3 cascade cameras,
render targets, light-space bounds, terrain uniform sync. Per-frame
Vector3 allocations pre-allocated as class fields (~30 alloc/frame
eliminated from updateCascadeCamera).
- `src/rendering/PostProcessingController.js` owns bloom pipeline, FXAA,
color grading. `setup()` is idempotent; `dispose()` removes its resize
listener.
- `src/world/SunController.js` owns sun/ambient/sky-tint state previously
scattered across TerrainApp fields.
- TerrainApp exposes getter/setter proxies for all controller state so
ControlPanel's existing `app.shadowsEnabled`, `app.bloomStrength` etc.
continue to work unchanged. Derived-state setters throw to catch
accidental mutation.
## Decoupling
- Removed `window.game` global (18 reach-throughs in Player/CollisionDetector/
InputManager); now passed via constructor injection.
- Removed late-set `inputManager.game = this` back-pointer.
- Moved InputManager's `game.app.controlPanel.panel` deep reach into
Game.toggleMenu() / Game.isMenuVisible().
## Correctness / perf fixes
- Pre-allocate 4 per-frame Vector3 in Player.updateFlightDynamics/Camera
- Pre-allocate testPoint in CollisionDetector
- Replace velocity.clone().multiplyScalar() in laser/bomb update loops
- Fix death-explosion rAF leak: store handle, cancel in Player.dispose
- Strip 4 hot-path console.logs from CollisionDetector's laser march
- Collapse duplicated effectParameters init in app.js
- Fresh `_fadeTo()` helper unifies AudioManager fadeIn/fadeOut
- Extract FLIGHT_CONSTANTS (speeds, turn rates, weapon timings, camera)
- Fix InputManager.toggleMenu race: clear intentionalUnlock + cancel pending
re-lock timeout on repeated M-key presses
- Reconcile ShadowController resolution default (4192) with setter clamp
(was 2048; now both 4096)
- Kill dead updateEngineGlow (per-frame mesh.traverse looking for a flag
that was never set)
## Static obstacles
- New `src/world/StaticObstacleIndex.js` — 2D spatial hash with AABB slab
sweep for laser/bomb segments and AABB containment for player/bomb
position checks. Register once, query O(1).
- CollisionDetector wires obstacles into the existing laser-march so a
fast laser can't tunnel through thin obstacles between simulation steps.
- Player crash detection calls checkPointObstacle each frame while alive;
hit triggers existing takeDamage → death-explosion pipeline.
## Skyscrapers on flat terrain
- New `src/assets/shaders/skyscraper.{vert,frag}` — procedural window grid
in local space (yaw-invariant), per-window lit emission (~18%, seeded
per building), mirrored sky reflection using `reflect(-viewDir, normal)`
plus a fresnel mix factor, and a specular sun glint on glass panes.
Minimum-light floor prevents pitch-black shadows.
- `setBuildingDensity(0..25)` control: disposes existing buildings +
obstacle entries, regenerates templates, re-runs flat-terrain placement.
- Flat-terrain placement scans a 35×35 grid of candidate positions,
measures height variation across each 180-unit footprint, sorts by
flatness, and picks the top N with a 260-unit minimum spacing.
- Procedural template pool: 25 skyscrapers with varied sizes (55-99 wide,
160-399 tall) and a 10-color wall/glass palette cycle, each with a
stable index-based seed.
- ControlPanel adds a "Scenery" section with a density slider.
## Cleanup
- Added .gitignore (node_modules/, dist/, .DS_Store, .vite/, *.log); untracked
1,375 previously-committed node_modules + dist files.
- Removed unused assets: 5 Skybox PNGs, 3 standalone textures,
JetstreamArcade.mp3, shift.glsl, src/assets/screenshots/.
- Removed dead exports from noise/index.js (noise snapshot, setTerrainTexture,
getNoiseConfig, HeightmapGenerator + TerrainTexture re-exports).
- Removed geometry.cube (unused).
- Removed orphaned HeightmapPipeline methods (storeOriginalHeightmap,
storeRawHeightmap, reapplyTerrainSettings — no callers).
- Removed TerrainApp.toggleRenderer/showRendererNotification (only referenced
from a disabled KeyG handler).
Net src diff: 26 files, -577 lines. app.js: 2334 -> 1272 (-45%).
Verified build clean; runtime-verified via Chrome:
- Menu loads, canvas 1708x898, WebGL active
- ENGAGE -> play mode: Collision detector initialized, Player loaded, Input
enabled, Game started in play mode; no errors from refactored paths
- Shadow/bloom/post-fx toggles round-trip correctly through controller proxies
- Generate Ridged Terrain exercises HeightmapPipeline end-to-end
- Player teleport into obelisk/building triggers crash -> takeDamage ->
death explosion
- Laser segment-sweep returns kind:"obstacle" at correct hit point
- Building density slider transitions 10 -> 20 -> 3 -> 0 -> 10 cleanly, with
correct obstacle-count tracking at each step
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.
Summary
Structural refactor of
app.js(2334 → 1272 LOC, −45%) extracting three rendering controllers and a sun state owner, plus two new gameplay/scenery systems built on top of the cleaner architecture.HeightmapPipeline,ShadowController,PostProcessingController,SunController— each owns its state + public API; TerrainApp holds the composition root and exposes getter/setter proxies soControlPanelcontinues to work unchanged.StaticObstacleIndex2D spatial hash with AABB slab sweep.CollisionDetectorwires it into the laser-march (tunnel-proof) and into per-frame player-position checks. Player crash into an obstacle triggers the existing death-explosion pipeline.reflect(-viewDir, normal)+ fresnel mix, specular sun glint on glass, lit-window emission (~18%, per-building seed).setBuildingDensity(0..25)slider in ControlPanel; placement scans the real heightmap for flat sites (Δh ≤ 28 over a 180-unit footprint, 260-unit spacing) and drops the rest.Perf / correctness fixes along the way
ShadowController.updateCascadeCamera(previouslynew Vector3per cascade per frame).testPointinCollisionDetector.Player.dispose.window.gameglobal (18 reach-throughs) → constructor injection.inputManager.game = thisback-pointer.InputManager.toggleMenu'sgame.app.controlPanel.paneldeep reach intoGame.toggleMenu()/Game.isMenuVisible().InputManager.toggleMenunow clearsintentionalUnlockand cancels any pending re-lock setTimeout on rapid M-key toggling.PostProcessingController.setupis idempotent (callsdisposefirst).ShadowControllerresolution default (4192) with its setter clamp (was 2048; now both 4096; slider range matches).updateEngineGlow(mesh.traverse every frame for a flag that was never set).AudioManagerfadeIn/fadeOut behind_fadeTo()helper.FLIGHT_CONSTANTS(speeds, turn rates, weapon timings, camera tunables).effectParametersinitialisation inapp.js.console.logs fromCollisionDetector's laser march.Cleanup
.gitignorefornode_modules/,dist/,.DS_Store,.vite/,*.log; untracked 1375 previously-committed build files.JetstreamArcade.mp3,shift.glsl,src/assets/screenshots/.geometry.cube, orphanedHeightmapPipelinemethods with no callers,toggleRenderer/showRendererNotification(only referenced from a disabled KeyG handler).TerrainAppthrow when written, to catch accidental external mutation of moved fields.File structure
Test plan
npm run buildclean (verified: 2.69s, no warnings)🎮 Game system initialized→✅ Player loaded→🎮 Input enabled→✅ Game started in play modeHeightmapPipelineend-to-end:📋 Initialized base & master heightmap→🏔️ Generating ridged→✅ Rebuilt master heightmap→🔄 Updated terrain from masterkind: "obstacle"Net diff
26 source files modified/added/deleted. −577 source LOC after cleanup.