feat: shade interaction

This commit is contained in:
2025-05-29 17:25:55 +02:00
parent 55c1ecf496
commit c1d778368c
5 changed files with 57 additions and 11 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/room.glb Normal file

Binary file not shown.

View File

@@ -1,3 +1,5 @@
// TODO: handle moving the camera vs actual click
import { useRef, useState, type ComponentRef } from "react";
import {
Canvas,
@@ -7,20 +9,23 @@ import {
useLoader,
} from "@react-three/fiber";
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 "./App.css";
const Room = () => {
const controls = useRef<ComponentRef<typeof OrbitControls>>(null);
const room = useLoader(FBXLoader, "/room.fbx");
const { mixer } = useAnimations(room.animations, room);
const room = useLoader(GLTFLoader, "/room.glb");
const { mixer } = useAnimations(room.animations, room.scene);
const [focus, setFocus] = useState<string | null>(null);
const cameraPosition = useRef<Vector3 | null>(null);
const cameraTarget = useRef<Vector3>(new Vector3(0, 1, 0));
const [windowState, setWindowState] = useState(false);
const [shadeState, setShadeState] = useState(false);
const pointerDownCameraPos = useRef(new Vector3());
const { camera } = useThree();
@@ -42,22 +47,20 @@ const Room = () => {
controls.current.target.lerp(cameraTarget.current, 0.1);
});
const handleClick = (e: ThreeEvent<MouseEvent>) => {
const handlePointerUp = (e: ThreeEvent<MouseEvent>) => {
if (!controls.current) return;
if (pointerDownCameraPos.current.distanceTo(camera.position) >= 0.3) return;
e.stopPropagation();
if (e.object.name.includes("Window")) {
const clip = room.animations.find(
(x) => x.name === "LowerWindow|LowerWindowAction",
);
const clip = room.animations.find((x) => x.name === "LowerWindowAction");
if (!clip) throw "no animation";
const action = mixer.clipAction(clip);
if (action.isRunning()) return;
action.clampWhenFinished = true;
action.setLoop(LoopOnce, 1);
action.timeScale = windowState ? -1 : 1;
action.timeScale = (windowState ? -1 : 1) * 1.5;
if (windowState) {
action.paused = false;
action.time = action.getClip().duration;
@@ -69,6 +72,46 @@ const Room = () => {
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) {
const objectPos = new Vector3();
const objectDir = new Vector3();
@@ -106,8 +149,11 @@ const Room = () => {
enablePan={false}
/>
<mesh onClick={handleClick}>
<primitive object={room} />
<mesh
onPointerDown={() => pointerDownCameraPos.current.copy(camera.position)}
onPointerUp={handlePointerUp}
>
<primitive object={room.scene} />
</mesh>
</>
);