optimize lighting loop

This commit is contained in:
tresabhi committed 2026-06-26 13:22:24 -05:00
1 parent 05572918b1
commit b2eb74914f
1 file changed
+80 -52
@@ -1,9 +1,9 @@
import { useFrame, useThree } from "@react-three/fiber"; import { useFrame } from "@react-three/fiber";
import { times } from "lodash-es"; import { clamp, times } from "lodash-es";
import { Quicklime } from "quicklime"; import { Quicklime } from "quicklime";
import { useEffect, useRef } from "react"; import { useEffect, useRef, useState, type RefObject } from "react";
import { HemisphereLight, SpotLight, type Group } from "three"; import { HemisphereLight, SpotLight, type Group } from "three";
import { clamp, degToRad, lerp } from "three/src/math/MathUtils.js"; import { degToRad, lerp } from "three/src/math/MathUtils.js";
import { Tankopedia } from "../../../../../../stores/tankopedia"; import { Tankopedia } from "../../../../../../stores/tankopedia";
import { TankopediaPersistent } from "../../../../../../stores/tankopediaPersistent"; import { TankopediaPersistent } from "../../../../../../stores/tankopediaPersistent";
import { HelpingSpotLight } from "../../../../../HelpingSpotLight"; import { HelpingSpotLight } from "../../../../../HelpingSpotLight";
@@ -25,67 +25,49 @@ export const transitionEvent = new Quicklime<number>(0);
export function Lighting() { export function Lighting() {
const wrapper = useRef<Group>(null!); const wrapper = useRef<Group>(null!);
const t0 = useRef(0);
const transition = useRef(false);
const clock = useThree((state) => state.clock);
const display = Tankopedia.use((state) => state.display);
const requestedDisplay = Tankopedia.use((state) => state.requestedDisplay);
const animationTime = useRef(REVEAL_ANIMATION_TIME);
const highGraphics = TankopediaPersistent.use((state) => state.highGraphics); const highGraphics = TankopediaPersistent.use((state) => state.highGraphics);
const requestedDisplay = Tankopedia.use((state) => state.requestedDisplay);
const animationTime = useRef(REVEAL_ANIMATION_TIME);
const t0 = useRef(performance.now() / 1e3 - animationTime.current);
const isRevealing = useRef(true);
const [animate, setAnimate] = useState(true);
console.log(animate);
useEffect(() => { useEffect(() => {
Tankopedia.mutate((draft) => { Tankopedia.mutate((draft) => {
draft.revealed = true; draft.revealed = true;
}); });
t0.current = performance.now() / 1000 - REVEAL_ANIMATION_TIME;
}, []); }, []);
useFrame(({ invalidate }) => {
if (transition.current) {
transition.current = false;
t0.current = performance.now() / 1000;
}
const x = clamp(
(performance.now() / 1000 - t0.current) / animationTime.current,
0,
2,
);
const t = (0.5 * Math.sin(Math.PI * (x + 0.5)) + 0.5) ** 2;
if (t !== transitionEvent.last) {
transitionEvent.dispatch(t);
}
for (const child of wrapper.current.children) {
if (child instanceof SpotLight) {
child.angle = ANGLE * t;
} else if (child instanceof HemisphereLight) {
child.intensity = HEMISPHERE_INTENSITY * t;
}
}
if (x < 2) invalidate();
if (
x > 1 &&
Tankopedia.state.display !== Tankopedia.state.requestedDisplay
) {
Tankopedia.mutate((draft) => {
draft.display = requestedDisplay;
});
}
});
useEffect(() => { useEffect(() => {
if (requestedDisplay === display) return; if (isRevealing.current) {
isRevealing.current = false;
transition.current = true; } else {
animationTime.current = TRANSITION_ANIMATION_TIME; animationTime.current = TRANSITION_ANIMATION_TIME;
t0.current = performance.now() / 1e3;
}
setAnimate(true);
}, [requestedDisplay]); }, [requestedDisplay]);
return ( return (
<>
{animate && (
<Animator
t0={t0}
animationTime={animationTime}
wrapper={wrapper}
stop={() => {
setAnimate(false);
}}
/>
)}
<group ref={wrapper}> <group ref={wrapper}>
<hemisphereLight intensity={0} color="#ffffff" groundColor="#afafaf" /> <hemisphereLight intensity={0} color="#ffffff" groundColor="#afafaf" />
@@ -114,5 +96,51 @@ export function Lighting() {
); );
})} })}
</group> </group>
</>
); );
} }
interface AnimatorProps {
stop: () => void;
t0: RefObject<number>;
animationTime: RefObject<number>;
wrapper: RefObject<Group>;
}
function Animator({ stop, t0, animationTime, wrapper }: AnimatorProps) {
const requestedDisplay = Tankopedia.use((state) => state.requestedDisplay);
useFrame(({ invalidate }) => {
const dt = performance.now() / 1e3 - t0.current;
const x = clamp(dt / animationTime.current, 0, 2);
console.log(x);
if (x === 2) {
stop();
return;
}
const t = (0.5 * Math.sin(Math.PI * (x + 0.5)) + 0.5) ** 2;
for (const child of wrapper.current.children) {
if (child instanceof SpotLight) {
child.angle = ANGLE * t;
} else if (child instanceof HemisphereLight) {
child.intensity = HEMISPHERE_INTENSITY * t;
}
}
transitionEvent.dispatch(t);
invalidate();
if (x >= 1 && Tankopedia.state.display !== requestedDisplay) {
Tankopedia.mutate((draft) => {
draft.display = requestedDisplay;
});
}
});
return null;
}