Conversation
Centralize lighting config into LightingConfig.ts. Replace tree's rawSunDir-based shade with dayIntensity-driven tint so trees and terrain (scene lights) transition to blue at the same rate. Remove unused applySunShade function and rawSunPosition plumbing. Made-with: Cursor
Replace hardcoded day cycle thresholds and sun tilt with centralized DAY_CYCLE and SUN_LIGHT config values from LightingConfig.ts. Made-with: Cursor
Extract dayIntensity-driven shade logic into a shared function so any future custom shader can import it for consistent blue tint timing. Made-with: Cursor
Use normalWorldGeometry instead of manual modelNormalMatrix * normalLocal to get the correct world-space sphere normal with per-instance rotation applied, without normal map or face-direction distortion. Made-with: Cursor
Add NIGHT.BRIGHTNESS master knob that controls both the tree shader nightDim floor and scene light intensity bases, so adjusting one value changes tree and terrain night brightness together. Made-with: Cursor
…rough Lobby floor, hospital floor, hospital cross, and banner cloths had emissive properties that overpowered the blue-shifted scene lights at night. Removing emissive lets them respond to ambient/hemisphere lighting like all other standard materials. Made-with: Cursor
…ital Inset corner pillars by PILLAR_BASE_SIZE/2 so they sit flush at the floor edge. Reduce hospital floor from 30x25 to 28x23 to avoid terrain clipping at the edges. Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
Made-with: Cursor
…e tuning Replace floor-based terrace quantization with ceil-based (raises terrain to plateaus, never lowers). Add global ELEVATION_OFFSET, ELEVATION_NOISE_AMOUNT, and ELEVATION_NOISE_SCALE constants for large-scale elevation variation. Update biome noise profiles (tundra, forest, canyon) and increase island radius to 2000. Mirror all changes in the worker JS string builder. Made-with: Cursor
Create arena-layout.ts as the single source of truth for all duel arena coordinates and dimensions. Update duel-manifest, world-areas, DuelArenaVisualsSystem, and ClientCameraSystem to import from it instead of using scattered hardcoded values. Shift arena Z by +50 from original. Made-with: Cursor
Reduce NOISE_SCALE (0.0008 → 0.0003) so dirt/grass patches don't visibly tile across the 4000-unit island. Reduce TERRAIN_TEX_TILE (0.08 → 0.025) to match. Shift shoreline height thresholds up to account for the global elevation offset (+8..+58 world units). Made-with: Cursor
Sample each biome texture at two non-harmonic UV scales (1x and 0.13x) and blend between them using Perlin noise. This prevents the tile grid from being visually detectable across the terrain. Made-with: Cursor
Terrain shader: slope drives dirt (low) vs cliff (high) on slopes, noise-driven dirt patches on flats, dual-scale texture blending, road overlay disabled. Move arena to (362, 434) and home to (444, 330). Disable skull markers and wilderness border bands. Made-with: Cursor
Lower ELEVATION_NOISE_SCALE from 0.0015 to 0.0005 for broader, more natural elevation undulation across the island. Made-with: Cursor
Increase WATER_THRESHOLD from 8.0 to 30.0 to account for the global elevation offset applied to the terrain. Made-with: Cursor
- Increase server chunk ranges (core 1→3, ring 1→5) so resource entities spawn up to ~1100m instead of 300m - Increase client chunk ranges to match (ring 3→5, horizon 5→7) - Decouple VegetationSystem fade from shadow maxFar so decorative trees render at distance without shadows instead of disappearing - Set GPU vegetation fade to 1000-1200m across all systems - Increase MAX_VEGETATION_TILE_RADIUS from 2 to 5 tiles - Increase LOD distances for all vegetation/resource categories - Note: VIEW_DISTANCE was dead code (unused checkPlayerMovement); the real tile loading uses coreChunkRange/ringChunkRange Made-with: Cursor
…aware initial LOD Boost Palm/Banana selection weight 20x near water so they dominate pond shores. Increase waterSearchRadius to 120m and waterMaxDistance to 100m for wider influence. Fix tree pop-in by computing correct initial LOD based on camera distance instead of always starting at LOD0. Made-with: Cursor
…cement rules Increase LOD distances for all vegetation/resource categories. Expand single shadow map frustum to 200m and size to 4096. Update fog config. Add waterSearchRadius and waterMaxDistance to TreePlacementRules. Made-with: Cursor
Remove Bamboo, ChinaPine, Yucca, Cactus, and WindSwept — none are referenced in any biome config. Made-with: Cursor
… load Workers take multiple frames to JIT-compile and return results on first page load, leaving terrain holes until they finish. Now the nearest 30 chunks (within 1200m) are generated synchronously on the main thread when workers haven't returned yet. Also adds burst-mode processing, distance- based priority sorting, and updates fog distances (300/1200). Made-with: Cursor
…nd fog tuning Water shader uses portfolio-style depth opacity curve (pow/saturate) so deep water fully hides underwater terrain. Reflection fades out beyond 500m to avoid artifacts at distance. Terrain texture tiling and slope thresholds adjusted. Fog range tightened for better atmosphere. Made-with: Cursor
…olor Replace the broken manual planar reflection system with the Three.js TSL reflector, positioned at the correct water level. The previous "sheet shape" artifact was caused by the reflector target being at Y=0 instead of Y=waterLevel. - Use TSL reflector() which handles camera mirroring, render target, and oblique clipping internally via updateBefore() - Position reflector target at waterLevel and sync on setWaterLevel() - Remove manual renderReflection(), reflection camera, texture matrix, and all associated temp vectors - Replace Beer-Lambert absorption with depth-fade color formula (pow(saturate(1 - depth/scale), falloff)) for smoother results - Add distance-based color fade so far water shows uniform deep color instead of depth-buffer noise artifacts - Increase sky dome radius to 5000 so the reflected camera stays inside Made-with: Cursor
Introduce WaterVisualManager that listens to the terrain quad-tree and generates flat water meshes per leaf node, replacing per-tile water when USE_QUADTREE_LOD is enabled. - Add WaterVisualManager: creates PlaneGeometry water chunks aligned with quad-tree nodes, with resolution scaling by tree depth - Add CompositeQuadTreeListener to forward events to both terrain and water visual managers from the same quad-tree - Gate per-tile generateWaterMeshes() behind !USE_QUADTREE_LOD - Clean up water visual manager in TerrainSystem.destroy() Made-with: Cursor
…heck Add enableSnow flag to BiomeTreeConfig and set it on tundra. Snow weight is now computed by summing all snow-enabled biome weights at each tree position, removing the hardcoded "tundra" string dependency. Made-with: Cursor
- Refactor tree placement to use Poisson disk sampling for natural spacing - Add clustering support with configurable radius and inter-cluster spacing - Make species zone allocation weight-proportional (reduce zone boost 10x→3x) - Fix tree LOD using "resource" distances (380m) instead of "tree" (800m) - Fix snow shader LOD issue where distant trees appeared all white - Lower vegetation alphaTest 0.5→0.1 to preserve sparse leaf texture edges - Add clusterRadius/clusterSpacing config options to BiomeTreeConfig - Update biome configs with clustering and corrected spacing values Made-with: Cursor
The hardcoded starter_area was superseded by central_haven from the world-areas.json manifest. Both were safe zones, producing two floating home icons. Remove the stale default so only the JSON-loaded area remains. Made-with: Cursor
Introduces GrassVisualManager, a QuadTreeListener that creates instanced grass meshes (GLB model) on max-depth terrain leaves. Includes per-chunk CPU-side frustum culling using the terrain chunk bounding box (same approach as portfolio project) to toggle mesh.visible, avoiding the InstancedMesh bounding-sphere bug with Three.js frustumCulled. Made-with: Cursor
… anime shading - Convert CPU terrain color output from sRGB to linear before passing to grass shader (float vertex attributes have no auto color space conversion) - Add half-lambert anime shade to grass material matching terrain shader so shadow tint is consistent at the grass/terrain boundary - Update CPU fallback color constants to match actual texture averages - Use darken() helper (×0.65) for dark variants matching GPU TEX_DARKEN - Sync sun direction uniform to grass from environment system - Remove COLOR_VARIATION, derive gradient purely from root ground color - Flip gradient: root is brighter (×1.5) to match terrain, tip fades darker - Add rebuildAllChunks/invalidateRegion for flat zone and road updates - Fix height data storage order (store after tile added to terrainTiles) - Invalidate quad-tree chunks when flat zones register/unregister Made-with: Cursor
Completes the flat zone invalidation support — terrain quad-tree chunks overlapping a registered/unregistered flat zone are destroyed and regenerated with correct heights, matching GrassVisualManager behavior. Made-with: Cursor
- Fix CPU color constants: non-texture constants (height gradient, variation, cliff tint, sand, shoreline) now use raw linear values matching GPU vec3() instead of incorrectly applying sRGB-to-linear conversion via lin() - Fix mismatched canyon constants (_CANYON_SAND_HIGH, _CANYON_VARIATION) - Remove fresnel from applyAnimeShade (caused view-dependent color shifts) - Add nightDim to applyCustomLighting matching tree shader pattern - Bypass PBR for both terrain and grass via shared applyCustomLighting - Add dayIntensity sync for terrain and grass day/night cycle - Refactor GrassVisualManager to be biome-agnostic via getTerrainColorAt - Add deferred chunk loading to reduce FPS hiccups on grass spawn - Re-enable terrain texture loading Made-with: Cursor
Made-with: Cursor
…D optimization - Add BiomeGrassConfig with per-biome density, slope, height, and patchiness - Blend grass configs by biome weight in TerrainSystem.getTerrainColorAt - Noise-based patchy grass distribution (uniform for forest, clustered for canyon/tundra) - Restore fresnel rim highlight to applyAnimeShade (always-on, shared by terrain and grass) - Add GPU distance fade: grass shrinks into ground from 350-500m - Prune far chunks beyond MAX_RENDER_DISTANCE to free GPU memory - Tighten LOD tiers: full detail 0-80m, medium 80-200m, low 200-500m - Remove global MIN_GRASS_WEIGHT in favor of per-biome minGrassWeight Made-with: Cursor
Move all CPU-intensive grass computation (terrain height, biome color, road influence, placement probability) into GrassWorker running off the main thread, eliminating FPS hiccups during chunk generation and LOD transitions. Main thread only creates InstancedMesh from pre-computed Float32Arrays returned via zero-copy transfer. - Rewrite GrassWorker with full terrain pipeline (shared builders) - GrassVisualManager dispatches to worker pool, sync fallback kept - LOD transitions keep old mesh visible until replacement arrives - Set player position before quad-tree update for correct initial LOD - TerrainSystem passes worker config, biome data, road segments Made-with: Cursor
Pass flat zone rectangles to the grass worker and add an isInFlatZone check in both the worker and sync fallback paths so grass is no longer placed inside artificially flattened areas. Made-with: Cursor
Sun and moon were rendering in front of clouds due to higher renderOrder. Move celestial bodies before clouds in the render pipeline and extract all render order values into a SKY_RENDER_ORDER constant object. Made-with: Cursor
- Add Rodrigues rotation to tilt grass blades along terrain slope - Override grass normalNode with terrain normal (world→view via cameraViewMatrix.transformDirection) so PBR N·L matches terrain - Enable receiveShadow on grass meshes for shadow map sampling - Apply applyAnimeShade as albedo tint on both terrain and grass; let PBR handle single Lambert N·L + shadow on top - Terrain colorNode feeds anime-shaded albedo + vertex lights into PBR; outputNode only applies fog to PBR result Made-with: Cursor
Add optional tintColor/tintStrength to BiomeGrassConfig so each biome can tint grass tips while keeping roots blended with the terrain. Tint data is passed as a separate per-instance vec4 attribute (instanceGrassTint) interleaved with instanceGroundColor into a single InstancedInterleavedBuffer to stay within WebGPU's 8 vertex buffer limit. Made-with: Cursor
Made-with: Cursor
…stems WorkerPool: track active tasks per worker and reject them on terminate() so in-flight promises no longer dangle. GLBTreeBatchedInstancer: guard against duplicate addInstance for same entityId; make addToPool atomic (pre-validate all geoIds, use fixed- length ids array aligned with batches). GLBTreeInstancer: add MAX_INSTANCES capacity check in addToPool; check target LOD pool instead of just LOD0; guard duplicate entityId; clone attributes in createSharedGeometry so disposal doesn't corrupt the model cache. ProcgenTreeInstancer: use tracked.preset in removeInstance instead of caller-provided preset; add capacity guard in showInMesh to prevent slot overwrite on wrap. GrassVisualManager: add destroyed flag to guard async callbacks; cancel workerInflight/pendingLodSwap on prune, destroy, invalidate, and rebuild; read latest pendingLodSwap in LOD swap completion for freshest target; dispose lodGeometries in destroy(); deduplicate pendingNodes pushes. Made-with: Cursor
Canyon's computeCanyonHeight had a hardcoded water carving (riverWidthVar/edge1/edge2) that was always active even with riverWidth: 0. Removed so canyon water features are controlled purely by the rivers/lakes/lakesFalloff config params like other biomes. Made-with: Cursor
Replace fixed 4-layer scrolling normals with two-phase flow crossfade (FlowUVW technique from cloud-sea shader) for organic, non-repeating water surface motion. Load waterNormal.png and noise28.png textures with procedural fallbacks. Shift cosine gradient palette from teal/cyan toward deep blue with subtle indigo undertone. Made-with: Cursor
Remove TreeId.Fir and TREE_TYPES.fir since Fir is not used in any biome config or woodcutting manifest. Bump water green offset from -0.30 to -0.22 for a blue-green tint. Made-with: Cursor
…der-overhaul Made-with: Cursor # Conflicts: # packages/shared/src/systems/shared/world/GLBTreeBatchedInstancer.ts # packages/shared/src/systems/shared/world/GLBTreeInstancer.ts
Main's dissolve system uses the B channel (1.0 - dissolveVal), which collided with our snow weight that was also in B. Reassign channels: R = highlight, G = snow weight, B = dissolve. Update shader reads in GPUMaterials to match (snow from batchColor.y, highlight from batchColor.x only). Made-with: Cursor
Code Review: Terrain Water Shader OverhaulThis is a massive PR (~7K additions, ~5K deletions, 80 files) that overhauls terrain generation, water rendering, sky/clouds, procedural grass, and vegetation. The overall direction is solid — per-biome terrain configs, centralized lighting, and the move to TSL-based anime shading are good architectural decisions. Below are the issues I found, ordered by severity. Critical1. 2. Widespread
Recommendation: Create a High3. No test coverage for major new features 4. Camera far plane / depth buffer precision 5. LOD distance inflation (5-20x) without perf validation Medium6. Dock rotation convention may be inverted ( 7. Code duplication in 8. GrassWorker terrain color sync is fragile 9. Dual water-level constants 10. Low11. Several features silently disabled
These should be tracked or removed entirely rather than left as dead code. 12. Magic numbers in GrassVisualManager
13. 14. 15. Positive Notes
SecurityNo hardcoded secrets, credentials, or security concerns found. |
sub pr: HyperscapeAI/assets#20
Summary
This PR is a major overhaul of the world rendering pipeline — terrain generation, shaders, water, sky/clouds, vegetation, and a brand-new procedural grass system. The goal is to make the world feel more like an open-world environment with fully procedural generation, unified anime-style lighting across all world objects, and dense vegetation coverage.
1. Terrain Generation & Shader Overhaul
Refactored the terrain from a shared-noise-with-weight-blending architecture to per-biome independent height generation. Each biome (Forest, Canyon, Tundra) now has its own
BiomeTerrainConfigwith full control over FBM octaves, erosion, altitude, rivers, terracing, and morehttps://www.youtube.com/watch?v=SwobuCOmJIU
(You might want to watch the video at 2× speed.)
2. Water Shader Overhaul
Refactored the water system with a new shader pipeline, quadtree-driven mesh generation, and proper reflections.
water.mp4
3. Procedural Grass System
Built a new chunk-based procedural grass system from scratch with per-biome configuration, multi-LOD rendering, and web worker offloading.
forest-grass.mp4
canyon-grass.mp4
tundra-grass.mp4
4. Sky & Cloud Shader
cloud2.mp4
5. Tree & Vegetation Improvements
enableSnowinBiomeTreeConfig)