From a2a491bd5c9a4de4eddec28b038b0e358979a4b6 Mon Sep 17 00:00:00 2001 From: Tom Boullay Date: Mon, 1 Jun 2026 16:50:21 +0200 Subject: [PATCH] chore(world): add temporary shadow pipeline diagnostics 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. --- src/world/Lighting.tsx | 56 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/src/world/Lighting.tsx b/src/world/Lighting.tsx index 4912046..d3625ea 100644 --- a/src/world/Lighting.tsx +++ b/src/world/Lighting.tsx @@ -1,10 +1,12 @@ import { useEffect, useRef } from "react"; import { useFrame, useThree } from "@react-three/fiber"; import { + Mesh, PCFShadowMap, type AmbientLight, type DirectionalLight, type Object3D, + type Scene, type WebGLRenderer, } from "three"; import { @@ -51,12 +53,33 @@ function configureSunShadow(sun: DirectionalLight, sunTarget: Object3D): void { sun.shadow.camera.updateProjectionMatrix(); } +// [diag] temporary helper: count shadow-casting/receiving meshes in the scene +function snapshotShadowMeshes(scene: Scene): { + meshCount: number; + castShadowCount: number; + receiveShadowCount: number; +} { + let meshCount = 0; + let castShadowCount = 0; + let receiveShadowCount = 0; + scene.traverse((obj) => { + if (obj instanceof Mesh) { + meshCount += 1; + if (obj.castShadow) castShadowCount += 1; + if (obj.receiveShadow) receiveShadowCount += 1; + } + }); + return { meshCount, castShadowCount, receiveShadowCount }; +} + export function Lighting(): React.JSX.Element { const camera = useThree((state) => state.camera); const gl = useThree((state) => state.gl); + const scene = useThree((state) => state.scene); const ambient = useRef(null); const sun = useRef(null); const sunTarget = useRef(null); + const lastDiagAtRef = useRef(0); useEffect(() => { if (!sun.current || !sunTarget.current) return; @@ -64,7 +87,18 @@ export function Lighting(): React.JSX.Element { configureSunShadow(sun.current, sunTarget.current); configureRendererShadows(gl); sun.current.shadow.needsUpdate = true; - }, [gl]); + + // [diag] one-shot scene snapshot to count shadow casters/receivers + const counts = snapshotShadowMeshes(scene); + console.log("[shadow:mount]", { + shadowMapEnabled: gl.shadowMap.enabled, + shadowMapType: gl.shadowMap.type, + shadowAutoUpdate: gl.shadowMap.autoUpdate, + sunCastShadow: sun.current.castShadow, + hasShadowMap: !!sun.current.shadow.map, + ...counts, + }); + }, [gl, scene]); useDebugFolder("Lighting", (folder) => { folder.addColor(LIGHTING_STATE, "ambientColor").name("Ambient Color"); @@ -98,7 +132,7 @@ export function Lighting(): React.JSX.Element { .name("Sun Z"); }); - useFrame(() => { + useFrame(({ clock }) => { if (ambient.current) { ambient.current.color.set(LIGHTING_STATE.ambientColor); ambient.current.intensity = LIGHTING_STATE.ambientIntensity; @@ -117,6 +151,24 @@ export function Lighting(): React.JSX.Element { sun.current.updateMatrixWorld(); sun.current.shadow.needsUpdate = true; } + + // [diag] periodic shadow pipeline check (every 2s) + const now = clock.getElapsedTime(); + if (now - lastDiagAtRef.current > 2 && sun.current) { + lastDiagAtRef.current = now; + console.log("[shadow:tick]", { + shadowMapEnabled: gl.shadowMap.enabled, + shadowAutoUpdate: gl.shadowMap.autoUpdate, + sunCastShadow: sun.current.castShadow, + sunIntensity: sun.current.intensity, + hasShadowMapTexture: !!sun.current.shadow.map?.texture, + sunPos: sun.current.position.toArray().map((n) => Number(n.toFixed(2))), + targetPos: sunTarget.current?.position + .toArray() + .map((n) => Number(n.toFixed(2))), + renderCalls: gl.info.render.calls, + }); + } }); return (