feat: shade interaction
This commit is contained in:
Binary file not shown.
Binary file not shown.
BIN
public/room.fbx
BIN
public/room.fbx
Binary file not shown.
BIN
public/room.glb
Normal file
BIN
public/room.glb
Normal file
Binary file not shown.
68
src/App.tsx
68
src/App.tsx
@@ -1,3 +1,5 @@
|
|||||||
|
// TODO: handle moving the camera vs actual click
|
||||||
|
|
||||||
import { useRef, useState, type ComponentRef } from "react";
|
import { useRef, useState, type ComponentRef } from "react";
|
||||||
import {
|
import {
|
||||||
Canvas,
|
Canvas,
|
||||||
@@ -7,20 +9,23 @@ import {
|
|||||||
useLoader,
|
useLoader,
|
||||||
} from "@react-three/fiber";
|
} from "@react-three/fiber";
|
||||||
import { OrbitControls, useAnimations } from "@react-three/drei";
|
import { OrbitControls, useAnimations } from "@react-three/drei";
|
||||||
import { FBXLoader } from "three/examples/jsm/Addons.js";
|
import { GLTFLoader } from "three/examples/jsm/Addons.js";
|
||||||
import { LoopOnce, Vector3 } from "three";
|
import { LoopOnce, Vector3 } from "three";
|
||||||
|
|
||||||
import "./App.css";
|
import "./App.css";
|
||||||
|
|
||||||
const Room = () => {
|
const Room = () => {
|
||||||
const controls = useRef<ComponentRef<typeof OrbitControls>>(null);
|
const controls = useRef<ComponentRef<typeof OrbitControls>>(null);
|
||||||
const room = useLoader(FBXLoader, "/room.fbx");
|
const room = useLoader(GLTFLoader, "/room.glb");
|
||||||
const { mixer } = useAnimations(room.animations, room);
|
const { mixer } = useAnimations(room.animations, room.scene);
|
||||||
const [focus, setFocus] = useState<string | null>(null);
|
const [focus, setFocus] = useState<string | null>(null);
|
||||||
|
|
||||||
const cameraPosition = useRef<Vector3 | null>(null);
|
const cameraPosition = useRef<Vector3 | null>(null);
|
||||||
const cameraTarget = useRef<Vector3>(new Vector3(0, 1, 0));
|
const cameraTarget = useRef<Vector3>(new Vector3(0, 1, 0));
|
||||||
const [windowState, setWindowState] = useState(false);
|
const [windowState, setWindowState] = useState(false);
|
||||||
|
const [shadeState, setShadeState] = useState(false);
|
||||||
|
|
||||||
|
const pointerDownCameraPos = useRef(new Vector3());
|
||||||
|
|
||||||
const { camera } = useThree();
|
const { camera } = useThree();
|
||||||
|
|
||||||
@@ -42,22 +47,20 @@ const Room = () => {
|
|||||||
controls.current.target.lerp(cameraTarget.current, 0.1);
|
controls.current.target.lerp(cameraTarget.current, 0.1);
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleClick = (e: ThreeEvent<MouseEvent>) => {
|
const handlePointerUp = (e: ThreeEvent<MouseEvent>) => {
|
||||||
if (!controls.current) return;
|
if (!controls.current) return;
|
||||||
|
if (pointerDownCameraPos.current.distanceTo(camera.position) >= 0.3) return;
|
||||||
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
if (e.object.name.includes("Window")) {
|
if (e.object.name.includes("Window")) {
|
||||||
const clip = room.animations.find(
|
const clip = room.animations.find((x) => x.name === "LowerWindowAction");
|
||||||
(x) => x.name === "LowerWindow|LowerWindowAction",
|
|
||||||
);
|
|
||||||
if (!clip) throw "no animation";
|
if (!clip) throw "no animation";
|
||||||
|
|
||||||
const action = mixer.clipAction(clip);
|
const action = mixer.clipAction(clip);
|
||||||
if (action.isRunning()) return;
|
if (action.isRunning()) return;
|
||||||
action.clampWhenFinished = true;
|
action.clampWhenFinished = true;
|
||||||
action.setLoop(LoopOnce, 1);
|
action.setLoop(LoopOnce, 1);
|
||||||
action.timeScale = windowState ? -1 : 1;
|
action.timeScale = (windowState ? -1 : 1) * 1.5;
|
||||||
if (windowState) {
|
if (windowState) {
|
||||||
action.paused = false;
|
action.paused = false;
|
||||||
action.time = action.getClip().duration;
|
action.time = action.getClip().duration;
|
||||||
@@ -69,6 +72,46 @@ const Room = () => {
|
|||||||
setWindowState(!windowState);
|
setWindowState(!windowState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (e.object.name.includes("Shade")) {
|
||||||
|
{
|
||||||
|
const clip = room.animations.find((x) => x.name === "Cube.010Action");
|
||||||
|
if (!clip) throw "no animation";
|
||||||
|
|
||||||
|
const action = mixer.clipAction(clip);
|
||||||
|
if (action.isRunning()) return;
|
||||||
|
action.clampWhenFinished = true;
|
||||||
|
action.setLoop(LoopOnce, 1);
|
||||||
|
action.timeScale = (shadeState ? -1 : 1) * 1.5;
|
||||||
|
if (shadeState) {
|
||||||
|
action.paused = false;
|
||||||
|
action.time = action.getClip().duration;
|
||||||
|
} else {
|
||||||
|
action.reset();
|
||||||
|
}
|
||||||
|
action.play();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const clip = room.animations.find(
|
||||||
|
(x) => x.name === "ShadeHandleAction",
|
||||||
|
);
|
||||||
|
if (!clip) throw "no animation";
|
||||||
|
|
||||||
|
const action = mixer.clipAction(clip);
|
||||||
|
if (action.isRunning()) return;
|
||||||
|
action.clampWhenFinished = true;
|
||||||
|
action.setLoop(LoopOnce, 1);
|
||||||
|
action.timeScale = (shadeState ? -1 : 1) * 1.5;
|
||||||
|
if (shadeState) {
|
||||||
|
action.paused = false;
|
||||||
|
action.time = action.getClip().duration;
|
||||||
|
} else {
|
||||||
|
action.reset();
|
||||||
|
}
|
||||||
|
action.play();
|
||||||
|
}
|
||||||
|
setShadeState(!shadeState);
|
||||||
|
}
|
||||||
|
|
||||||
if (e.object.name.includes("Poster") && e.object.name !== focus) {
|
if (e.object.name.includes("Poster") && e.object.name !== focus) {
|
||||||
const objectPos = new Vector3();
|
const objectPos = new Vector3();
|
||||||
const objectDir = new Vector3();
|
const objectDir = new Vector3();
|
||||||
@@ -106,8 +149,11 @@ const Room = () => {
|
|||||||
enablePan={false}
|
enablePan={false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<mesh onClick={handleClick}>
|
<mesh
|
||||||
<primitive object={room} />
|
onPointerDown={() => pointerDownCameraPos.current.copy(camera.position)}
|
||||||
|
onPointerUp={handlePointerUp}
|
||||||
|
>
|
||||||
|
<primitive object={room.scene} />
|
||||||
</mesh>
|
</mesh>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user