import { React, Suspense, useState, useRef, useEffect } from "react";
import * as THREE from "three";
import { useFrame } from "@react-three/fiber";
import { Environment, useProgress } from "@react-three/drei";
import First from "./models/First";
import Second from "./models/Second";
import Third from "./models/Third";
import Fourth from "./models/Fourth";
import Fifth from "./models/Fifth";
import Sixth from "./models/Sixth";
import Seventh from "./models/Seventh";
import Eighth from "./models/Eighth";
import Ninth from "./models/Ninth";
import Tenth from "./models/Tenth";
import Eleventh from "./models/Eleventh";
import Bird from "./models/Bird";
import Background from "./models/Background";
import {
cameraCurve,
DebugCurve,
CameraHelper,
rotationTargets,
} from "./utils/curve";
import Fire from "./components/Fire";
import WaterFall from "./components/WaterFall";
import { useExperienceStore } from "../stores/experienceStore";
const LoadingManager = () => {
const { active, progress } = useProgress();
const { setIsExperienceLoading } = useExperienceStore();
useEffect(() => {
setIsExperienceLoading(active);
}, [active, progress, setIsExperienceLoading]);
return null;
};
const useChunkedLoading = () => {
const [loadingStage, setLoadingStage] = useState(0);
const { active } = useProgress();
const prevActiveRef = useRef(true);
const { incrementLoadedChunks } = useExperienceStore();
useEffect(() => {
if (prevActiveRef.current && !active) {
setLoadingStage((prev) => prev + 1);
incrementLoadedChunks();
}
prevActiveRef.current = active;
}, [active, incrementLoadedChunks]);
return {
shouldRenderChunk: (chunkIndex) => loadingStage >= chunkIndex,
};
};
const Scene = ({
cameraGroup,
camera,
scrollProgress,
setscrollProgress,
targetScrollProgress,
lerpFactor,
mousePositionOffset,
mouseRotationOffset,
}) => {
const [pulseIntensity, setPulseIntensity] = useState(0);
const [rotationBufferQuat] = useState(
new THREE.Quaternion().setFromEuler(rotationTargets[0].rotation)
);
const timeRef = useRef(0);
const { shouldRenderChunk } = useChunkedLoading();
const getLerpedRotation = (progress) => {
for (let i = 0; i < rotationTargets.length - 1; i++) {
const start = rotationTargets[i];
const end = rotationTargets[i + 1];
if (progress >= start.progress && progress <= end.progress) {
const lerpFactor =
(progress - start.progress) / (end.progress - start.progress);
const startQuaternion = new THREE.Quaternion().setFromEuler(
start.rotation
);
const endQuaternion = new THREE.Quaternion().setFromEuler(end.rotation);
const lerpingQuaternion = new THREE.Quaternion();
lerpingQuaternion.slerpQuaternions(
startQuaternion,
endQuaternion,
lerpFactor
);
return lerpingQuaternion;
}
}
return new THREE.Quaternion().setFromEuler(
rotationTargets[rotationTargets.length - 1].rotation
);
};
useFrame((state) => {
if (camera) {
// Pulsating Time
timeRef.current = state.clock.getElapsedTime();
const newPulseIntensity = (Math.sin(state.clock.elapsedTime * 3) + 1) / 2;
setPulseIntensity(newPulseIntensity);
// Lerp to new position
let newProgress = THREE.MathUtils.lerp(
scrollProgress,
targetScrollProgress.current,
lerpFactor
);
// So the camera can loop
if (newProgress > 1) {
newProgress = 0;
targetScrollProgress.current = 0;
} else if (newProgress < 0) {
newProgress = 1;
targetScrollProgress.current = 1;
}
setscrollProgress(newProgress);
// Lerp to new camera offset position
const basePoint = cameraCurve.getPoint(newProgress);
cameraGroup.current.position.x = THREE.MathUtils.lerp(
cameraGroup.current.position.x,
basePoint.x,
0.1
);
cameraGroup.current.position.y = THREE.MathUtils.lerp(
cameraGroup.current.position.y,
basePoint.y,
0.1
);
cameraGroup.current.position.z = THREE.MathUtils.lerp(
cameraGroup.current.position.z,
basePoint.z,
0.1
);
camera.current.position.x = THREE.MathUtils.lerp(
camera.current.position.x,
mousePositionOffset.current.x,
0.1
);
camera.current.position.y = THREE.MathUtils.lerp(
camera.current.position.y,
-mousePositionOffset.current.y,
0.1
);
camera.current.position.z = 0;
const targetRotation = getLerpedRotation(newProgress);
// Use slerp to smoothly interpolate between our target rotations
rotationBufferQuat.slerp(targetRotation, 0.05);
cameraGroup.current.quaternion.copy(rotationBufferQuat);
// Direct camera rotation, NOT the group
camera.current.rotation.x = THREE.MathUtils.lerp(
camera.current.rotation.x,
-mouseRotationOffset.current.x,
0.1
);
camera.current.rotation.y = THREE.MathUtils.lerp(
camera.current.rotation.y,
-mouseRotationOffset.current.y,
0.1
);
}
});
return (
<>
{/* */}
{shouldRenderChunk(1) && (
)}
{shouldRenderChunk(2) && (
)}
{shouldRenderChunk(3) && (
)}
>
);
};
export default Scene;