- Bump high to 30m chunk / HD<20m and ultra to HD<30m so HD models
persist further before swapping.
- Render the 5 graphics preset cards on a single row.
- Vegetation LOD selection now uses the distance to the nearest instance
in each chunk instead of the chunk centre, matching MapInstancingSystem
and avoiding premature LOD swaps when the camera enters a chunk.
Register the three new vegetation LOD models in MAP_LOD_MODEL_PATHS and
extend VegetationSystem with per-chunk distance-based LOD selection
(mirroring MapInstancingSystem). Chunk model paths are re-evaluated on
the existing CHUNK_CONFIG.updateInterval cadence and Suspense keys
include the resolved path so a swap unmounts the previous instanced mesh
cleanly.
Switching alphaMode from BLEND to MASK with a 0.5 cutoff lets the lampe
material participate in instanced shadow rendering correctly. BLEND is
known to interact badly with InstancedMesh shadow casting, producing
incorrect or missing shadows on the pylone lampe.
Restore ultra to its original behaviour (50m chunk streaming, HD within
20m, no fog) and introduce a new max preset that disables chunk streaming
entirely (loads all chunks unconditionally) and pushes the HD/LOD swap
distance to 50m. Add chunkStreamingEnabled flag to GraphicsPresetConfig
so the streaming gate honours the preset. The settings card label shows
'All' when streaming is off.
Shadows occasionally failed to render on initial load and the Fabrik
doorway sometimes blocked the player. Both issues are tracked down to
geometry that mounts after Lighting:
- Shadows: GLTFs and the merged static map mount imperatively after
Lighting, so materials get compiled against a renderer state that
pre-dates the final scene and bake a 'no shadow map' permutation,
silently dropping shadows. A WebGL context-restore cycle fixes it,
but is too invasive. New 'useShadowMapWarmup' hook replays it
cheaply: once the scene mesh count has been stable for ~1s, it
disposes the directional shadow map (three.js reallocates it on
the next render) and marks every material 'needsUpdate' so shaders
rebind to the freshly created shadow sampler.
- Doorway: the door slab + its Solidify-modifier frame (children of
the 'Thicken' parent in the LaFabrik GLTF) sat inside the doorway
AABB and prevented the player from walking through. Stripped from
the collision octree alongside the existing 'porte' slab; visual
rendering is unaffected.
Also: extract sun-relative-to-camera placement into a small helper,
remove the temporary diagnostic logs, and document the shadow warmup
in three-debugging.md.
- Lighting: replace single-frame needsUpdate with a 3-rAF warmup that forces
scene.updateMatrixWorld + sun.shadow.needsUpdate + gl.shadowMap.needsUpdate.
This restores the SceneShadowWarmup behaviour (deleted in 777e51e) inline,
so shadows survive Physics Suspense remounts and webglcontextrestored.
- octreeCollisionConfig: remove (comment out) the thin LA_FABRIK interior box
at x=-6.93 that was sealing the doorway despite the mesh hole; fabrik mesh
octree already provides surrounding wall collision.
- DebugOctreeVisualization: add Fabrik-only filter to inspect interior
collisions/non-collisions in isolation.
The previous talkie-overlay refactor lost the rest/active behaviour and
left the radio-shake CSS animation running constantly. Restore the
intended polish:
- Position lerp between TALKIE_REST_Y (idle) and TALKIE_ACTIVE_Y
(raised when narrator is speaking) with a subtle floating bob.
- Subtle z-axis float at idle, faster shake when active.
- Gate the CSS radio-shake on the new --active modifier so the talkie
is calm when no narrator dialogue is playing.
- Keep the face-camera rotation [0.18, PI, -0.08] from the original
overlay version.
Visibility still kicks in at the reveal step (no regression on the
recent fix).
Shadows still go missing intermittently despite the per-frame
needsUpdate fix. Add temporary console logs to narrow down the cause:
- [shadow:mount]: one-shot snapshot of renderer shadow flags and a
scene.traverse count of meshes with castShadow / receiveShadow at
Lighting mount time.
- [shadow:tick]: every 2s during useFrame, log shadow map enabled flag,
autoUpdate, sun.castShadow, sun intensity, shadow map texture
presence, sun and target world positions, and renderer draw calls.
To be removed once the root cause is identified.
Default visualization was unreadable because every node from depth 0 to
maxDepth was rendered with rainbow-coloured edges. Add three filters
exposed in the Debug folder:
- Octree Leaves Only (default true): skip internal nodes
- Octree Min Depth (default 4): hide the largest enclosing boxes
- Octree Opacity (default 0.35): tone down line density
Also skip nodes without triangles, drop the per-depth HSL palette in
favour of a uniform cyan, and bump default Octree Max Depth to 8.
Regression introduced by the narrator-video revert (1ad0c4d): the talkie
overlay was hidden whenever no narrator subtitle was active. Restore the
prior behaviour where the talkie stays visible from the reveal step
onward and only the --raised modifier and signal lines depend on the
active narrator dialogue.
Update three-debugging.md to reflect that the shadow intermittence
is resolved by explicit sun.shadow.needsUpdate = true at mount and
in useFrame after updateMatrixWorld.
Restore sun.shadow.needsUpdate = true at mount and in useFrame
after updateMatrixWorld. Lost during SHADOW_CONFIG centralization.
Matches develop's belt-and-suspenders pattern; autoUpdate alone
is insufficient because the sun follows the camera (matrix dirty
every frame) and three.js can skip shadow map re-render.
- Drop SceneShadowWarmup section, document centralized shadow config.
- Document the localized Suspense boundaries in World.tsx.
- Document the new player model and octree debug visualizations.
- Open note about intermittent first-load shadow rendering.
Late asset loads inside GameStageContent (e.g. EbikeSpeedometer's
useTexture) and the spawn-player block were bubbling Suspense up to the
root boundary in pages/page.tsx, which unmounted World mid-load and
triggered a redundant octree rebuild + shadow re-config. Localize the
suspension by wrapping each block in its own Suspense fallback.
Also mount DebugOctreeVisualization conditionally on the new debug
toggle.
- Show Player Model: render the main character GLTF in camera-local
space so it stays visible at any pitch.
- Show Octree: overlay the collision octree as colored line segments,
one wireframe per spatial cell, colored by depth.
- Octree Max Depth: cap recursion to keep the scene readable.
- Extract SHADOW_CONFIG into lightingConfig.ts (bias=0, normalBias=0,
cameraSize=95) matching the historically working values from develop.
- Drop SceneShadowWarmup; rely on sun.shadow.autoUpdate=true for
steady-state refresh.
- Enable cloud castShadow and traverse Ebike meshes for cast/receive.
Strip the 'porte' mesh from the cloned scene used to build the la fabrik
collision octree. The wall geometry already has a doorway cutout, so
removing the door slab leaves the opening passable. The visual model is
rendered separately by MergedStaticMapModel and is unaffected.
Drops the stop-gap LA_FABRIK_COLLISION_Y_OFFSET added during debugging.
Reverts the manual shadow refresh throttle introduced in 6d58b90 which
prevented shadows from rendering. Renderer and sun shadow now use
autoUpdate=true and a per-frame needsUpdate=true pulse, matching the
behaviour that produced visible shadows before that commit.