feat: enhance camera controls and switch view functionality with 3D toggle integration
This commit is contained in:
@@ -1,11 +1,14 @@
|
|||||||
|
import { Vector3 } from "three";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
import * as THREE from "three";
|
|
||||||
import type { CameraControls } from "@react-three/drei";
|
import type { CameraControls } from "@react-three/drei";
|
||||||
|
import { useThreeDStore } from "../../../../store/ui/useModuleStore";
|
||||||
|
import * as CONSTANTS from "../../../../types/world/worldConstants";
|
||||||
|
|
||||||
const CameraShortcutsControls = () => {
|
const CameraShortcutsControls = () => {
|
||||||
const { camera, controls } = useThree();
|
const { camera, controls } = useThree();
|
||||||
|
const { toggleThreeD, setToggleThreeD } = useThreeDStore();
|
||||||
|
|
||||||
const isTextInput = (element: Element | null): boolean =>
|
const isTextInput = (element: Element | null): boolean =>
|
||||||
element instanceof HTMLInputElement ||
|
element instanceof HTMLInputElement ||
|
||||||
element instanceof HTMLTextAreaElement ||
|
element instanceof HTMLTextAreaElement ||
|
||||||
@@ -18,54 +21,77 @@ const CameraShortcutsControls = () => {
|
|||||||
const cc = controls as CameraControls;
|
const cc = controls as CameraControls;
|
||||||
|
|
||||||
// get current target
|
// get current target
|
||||||
const target = new THREE.Vector3();
|
const target = new Vector3();
|
||||||
cc.getTarget(target);
|
cc.getTarget(target);
|
||||||
|
|
||||||
const distance = camera.position.distanceTo(target);
|
const distance = camera.position.distanceTo(target);
|
||||||
let pos: THREE.Vector3 | null = null;
|
let pos: Vector3 | null = null;
|
||||||
|
|
||||||
const dir = new THREE.Vector3().subVectors(camera.position, target).normalize();
|
const dir = new Vector3().subVectors(camera.position, target).normalize();
|
||||||
|
|
||||||
if (isTextInput(document.activeElement)) return;
|
if (isTextInput(document.activeElement)) return;
|
||||||
|
|
||||||
switch (e.key) {
|
switch (e.code) {
|
||||||
case "1": // Front
|
case "Numpad1": // Front
|
||||||
pos = new THREE.Vector3(0, 0, distance).add(target);
|
pos = new Vector3(0, 0, distance).add(target);
|
||||||
|
LookAt(pos);
|
||||||
break;
|
break;
|
||||||
case "3": // Right
|
case "Numpad3": // Right
|
||||||
pos = new THREE.Vector3(distance, 0, 0).add(target);
|
pos = new Vector3(distance, 0, 0).add(target);
|
||||||
|
LookAt(pos);
|
||||||
break;
|
break;
|
||||||
case "7": // Top
|
case "Numpad7": // Top
|
||||||
pos = new THREE.Vector3(0, distance, 0).add(target);
|
pos = new Vector3(0, distance, 0).add(target);
|
||||||
|
LookAt(pos);
|
||||||
break;
|
break;
|
||||||
case "9": {
|
case "Numpad9": {
|
||||||
// Opposite view logic
|
// Opposite view logic
|
||||||
if (Math.abs(dir.z) > Math.abs(dir.x) && Math.abs(dir.z) > Math.abs(dir.y)) {
|
if (Math.abs(dir.z) > Math.abs(dir.x) && Math.abs(dir.z) > Math.abs(dir.y)) {
|
||||||
// Currently looking Front/Back → flip Z
|
pos = new Vector3(0, 0, -Math.sign(dir.z) * distance).add(target);
|
||||||
pos = new THREE.Vector3(0, 0, -Math.sign(dir.z) * distance).add(target);
|
LookAt(pos);
|
||||||
} else if (Math.abs(dir.x) > Math.abs(dir.z) && Math.abs(dir.x) > Math.abs(dir.y)) {
|
} else if (
|
||||||
// Currently looking Right/Left → flip X
|
Math.abs(dir.x) > Math.abs(dir.z) &&
|
||||||
pos = new THREE.Vector3(-Math.sign(dir.x) * distance, 0, 0).add(target);
|
Math.abs(dir.x) > Math.abs(dir.y)
|
||||||
|
) {
|
||||||
|
pos = new Vector3(-Math.sign(dir.x) * distance, 0, 0).add(target);
|
||||||
|
LookAt(pos);
|
||||||
} else {
|
} else {
|
||||||
// Currently looking Top/Bottom → stay Top
|
pos = new Vector3(0, distance, 0).add(target);
|
||||||
pos = new THREE.Vector3(0, distance, 0).add(target);
|
LookAt(pos);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Numpad5": {
|
||||||
|
// Only apply when on Top view
|
||||||
|
|
||||||
|
if (!toggleThreeD) {
|
||||||
|
console.log("cc: ", cc.camera);
|
||||||
|
setToggleThreeD(true);
|
||||||
|
(cc as any).mouseButtons.left = CONSTANTS.threeDimension.leftMouse;
|
||||||
|
(cc as any).mouseButtons.right = CONSTANTS.threeDimension.rightMouse;
|
||||||
|
} else {
|
||||||
|
console.log("cc: ", cc.camera);
|
||||||
|
cc.setLookAt(0, distance, 0, target.x, target.y, target.z, false).then(
|
||||||
|
() => {
|
||||||
|
setToggleThreeD(false);
|
||||||
|
(cc as any).mouseButtons.left = CONSTANTS.twoDimension.leftMouse;
|
||||||
|
(cc as any).mouseButtons.right = CONSTANTS.twoDimension.rightMouse;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pos) {
|
function LookAt(pos: Vector3) {
|
||||||
cc.setLookAt(
|
console.log('hi lookat');
|
||||||
pos.x, pos.y, pos.z, // camera position
|
cc.setLookAt(pos.x, pos.y, pos.z, target.x, target.y, target.z, true);
|
||||||
target.x, target.y, target.z, // keep same target
|
|
||||||
true // smooth transition
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("keydown", handleKeyDown);
|
window.addEventListener("keydown", handleKeyDown);
|
||||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||||
}, [controls, camera]);
|
}, [controls, camera, toggleThreeD, setToggleThreeD]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ import { useParams } from "react-router-dom";
|
|||||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||||
import { getCameraApi } from "../../../services/factoryBuilder/camera/getCameraApi";
|
import { getCameraApi } from "../../../services/factoryBuilder/camera/getCameraApi";
|
||||||
import { useToggleView } from "../../../store/builder/store";
|
import { useToggleView } from "../../../store/builder/store";
|
||||||
|
import { useThreeDStore } from "../../../store/ui/useModuleStore";
|
||||||
|
|
||||||
export default function SwitchView() {
|
export default function SwitchView() {
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
const { controls } = useThree();
|
const { controls } = useThree();
|
||||||
const { projectId } = useParams();
|
const { projectId } = useParams();
|
||||||
|
const { toggleThreeD } = useThreeDStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (toggleView && controls) {
|
if (toggleView && controls) {
|
||||||
@@ -39,11 +41,11 @@ export default function SwitchView() {
|
|||||||
(controls as any).mouseButtons.right = CONSTANTS.threeDimension.rightMouse;
|
(controls as any).mouseButtons.right = CONSTANTS.threeDimension.rightMouse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [toggleView, controls]);
|
}, [toggleView, controls, projectId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{toggleView ? (
|
{(toggleView || !toggleThreeD) ? (
|
||||||
<OrthographicCamera
|
<OrthographicCamera
|
||||||
makeDefault
|
makeDefault
|
||||||
position={CONSTANTS.twoDimension.defaultPosition}
|
position={CONSTANTS.twoDimension.defaultPosition}
|
||||||
|
|||||||
Reference in New Issue
Block a user