ai-chatbot/apps/editor/components/editor/custom-camera-controls.tsx
2026-01-22 08:14:42 +09:00

115 lines
3.1 KiB
TypeScript

"use client";
import {
type CameraControlEvent,
emitter,
sceneRegistry,
useScene,
} from "@pascal-app/core";
import { useViewer } from "@pascal-app/viewer";
import { CameraControls, CameraControlsImpl } from "@react-three/drei";
import { useEffect, useMemo, useRef } from "react";
import { Vector3 } from "three";
const currentTarget = new Vector3();
export const CustomCameraControls = () => {
const controls = useRef<CameraControlsImpl>(null!);
const currentLevelId = useViewer((state) => state.selection.levelId);
const firstLoad = useRef(true);
useEffect(() => {
let targetY = 0;
if (currentLevelId) {
const levelMesh = sceneRegistry.nodes.get(currentLevelId);
if (levelMesh) {
targetY = levelMesh.position.y;
}
}
if (firstLoad.current) {
firstLoad.current = false;
(controls.current as CameraControlsImpl).setLookAt(
20,
20,
20,
0,
0,
0,
true,
);
}
(controls.current as CameraControlsImpl).getTarget(currentTarget);
(controls.current as CameraControlsImpl).moveTo(
currentTarget.x,
targetY,
currentTarget.z,
true,
);
}, [currentLevelId]);
// Configure mouse buttons based on control mode and camera mode
const mouseButtons = useMemo(() => {
// Use ZOOM for orthographic camera, DOLLY for perspective camera
// const wheelAction =
// cameraMode === 'orthographic'
// ? CameraControlsImpl.ACTION.ZOOM
// : CameraControlsImpl.ACTION.DOLLY
const wheelAction = CameraControlsImpl.ACTION.DOLLY;
return {
left: CameraControlsImpl.ACTION.NONE,
middle: CameraControlsImpl.ACTION.SCREEN_PAN,
right: CameraControlsImpl.ACTION.ROTATE,
wheel: wheelAction,
};
}, []);
useEffect(() => {
const handleNodeCapture = ({ nodeId }: CameraControlEvent) => {
if (!controls.current) return;
const position = new Vector3();
const target = new Vector3();
controls.current.getPosition(position);
controls.current.getTarget(target);
const state = useScene.getState();
state.updateNode(nodeId, {
camera: {
position: [position.x, position.y, position.z],
target: [target.x, target.y, target.z],
mode: useViewer.getState().cameraMode,
},
});
};
const handleNodeView = ({ nodeId }: CameraControlEvent) => {
if (!controls.current) return;
const node = useScene.getState().nodes[nodeId];
if (!node || !node.camera) return;
const { position, target } = node.camera;
controls.current.setLookAt(
position[0],
position[1],
position[2],
target[0],
target[1],
target[2],
true,
);
};
emitter.on("camera-controls:capture", handleNodeCapture);
emitter.on("camera-controls:view", handleNodeView);
return () => {
emitter.off("camera-controls:capture", handleNodeCapture);
emitter.off("camera-controls:view", handleNodeView);
};
}, []);
return <CameraControls ref={controls} mouseButtons={mouseButtons} />;
};