import { useEffect, useRef } from "react"; import { useThree } from "@react-three/fiber"; import * as THREE from "three"; interface SceneShadowWarmupProps { active: boolean; onReady: () => void; onStarted: () => void; } function markShadowLightForUpdate(object: THREE.Object3D): void { if ( !( object instanceof THREE.DirectionalLight || object instanceof THREE.PointLight || object instanceof THREE.SpotLight ) ) { return; } if (!object.castShadow) return; object.updateMatrixWorld(true); object.shadow.camera.updateProjectionMatrix(); object.shadow.needsUpdate = true; } function forceSceneShadowPass( gl: THREE.WebGLRenderer, scene: THREE.Scene, ): void { gl.shadowMap.enabled = true; gl.shadowMap.type = THREE.PCFShadowMap; gl.shadowMap.autoUpdate = true; gl.shadowMap.needsUpdate = true; scene.updateMatrixWorld(true); scene.traverse((object) => { if (object instanceof THREE.Mesh) { object.updateMatrixWorld(true); } markShadowLightForUpdate(object); }); } function restoreManualShadowUpdates(gl: THREE.WebGLRenderer): void { gl.shadowMap.autoUpdate = false; gl.shadowMap.needsUpdate = true; } export function SceneShadowWarmup({ active, onReady, onStarted, }: SceneShadowWarmupProps): null { const gl = useThree((state) => state.gl); const scene = useThree((state) => state.scene); const invalidate = useThree((state) => state.invalidate); const isRunningRef = useRef(false); useEffect(() => { if (!active) { isRunningRef.current = false; return undefined; } if (isRunningRef.current) return undefined; isRunningRef.current = true; onStarted(); forceSceneShadowPass(gl, scene); invalidate(); let firstFrame = 0; let secondFrame = 0; firstFrame = window.requestAnimationFrame(() => { forceSceneShadowPass(gl, scene); invalidate(); secondFrame = window.requestAnimationFrame(() => { forceSceneShadowPass(gl, scene); restoreManualShadowUpdates(gl); invalidate(); onReady(); }); }); return () => { window.cancelAnimationFrame(firstFrame); window.cancelAnimationFrame(secondFrame); }; }, [active, gl, invalidate, onReady, onStarted, scene]); return null; }