98 lines
2.4 KiB
TypeScript
98 lines
2.4 KiB
TypeScript
import { useRef, useState, type ComponentRef } from "react";
|
|
import {
|
|
Canvas,
|
|
useFrame,
|
|
useThree,
|
|
type ThreeEvent,
|
|
useLoader,
|
|
} from "@react-three/fiber";
|
|
import { OrbitControls } from "@react-three/drei";
|
|
import { FBXLoader } from "three/examples/jsm/Addons.js";
|
|
import { Vector3 } from "three";
|
|
|
|
import "./App.css";
|
|
|
|
const Room = () => {
|
|
const controls = useRef<ComponentRef<typeof OrbitControls>>(null);
|
|
const room = useLoader(FBXLoader, "/room.fbx");
|
|
const [focus, setFocus] = useState<string | null>(null);
|
|
|
|
const cameraPosition = useRef<Vector3 | null>(null);
|
|
const cameraTarget = useRef<Vector3>(new Vector3(0, 1, 0));
|
|
|
|
const { camera } = useThree();
|
|
|
|
useFrame(() => {
|
|
if (!controls.current) return;
|
|
|
|
if (cameraPosition.current) {
|
|
camera.position.lerp(cameraPosition.current, 0.09);
|
|
if (camera.position.distanceTo(cameraPosition.current) < 0.012) {
|
|
if (!focus) {
|
|
controls.current.enableZoom = true;
|
|
controls.current.enableRotate = true;
|
|
}
|
|
|
|
cameraPosition.current = null;
|
|
}
|
|
}
|
|
|
|
controls.current.target.lerp(cameraTarget.current, 0.1);
|
|
});
|
|
|
|
const handleClick = (e: ThreeEvent<MouseEvent>) => {
|
|
if (!controls.current) return;
|
|
|
|
e.stopPropagation();
|
|
|
|
if (e.object.name.includes("Poster") && e.object.name !== focus) {
|
|
const objectPos = new Vector3();
|
|
const objectDir = new Vector3();
|
|
e.object.getWorldPosition(objectPos);
|
|
e.object.getWorldDirection(objectDir);
|
|
|
|
cameraPosition.current = objectPos
|
|
.clone()
|
|
.add(objectDir.multiplyScalar(-0.65));
|
|
cameraTarget.current = objectPos;
|
|
|
|
controls.current.enableZoom = false;
|
|
controls.current.enableRotate = false;
|
|
|
|
setFocus(e.object.name);
|
|
} else if (focus) {
|
|
cameraPosition.current = new Vector3(3, 3, -3);
|
|
cameraTarget.current = new Vector3(0, 1, 0);
|
|
|
|
setFocus(null);
|
|
}
|
|
|
|
controls.current.update();
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<ambientLight intensity={Math.PI / 2} color={"#ffffff"} />
|
|
<directionalLight intensity={5} position={[5, 10, 5]} castShadow />
|
|
<OrbitControls
|
|
ref={controls}
|
|
target={[0, 1, 0]}
|
|
maxPolarAngle={Math.PI / 2}
|
|
enablePan={false}
|
|
/>
|
|
|
|
<mesh onClick={handleClick}>
|
|
<primitive object={room} />
|
|
</mesh>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default function App() {
|
|
return (
|
|
<Canvas camera={{ position: [3, 3, -3] }}>
|
|
<Room />
|
|
</Canvas>
|
|
);
|
|
}
|