diff --git a/app/src/modules/scene/camera/camMode.tsx b/app/src/modules/scene/camera/camMode.tsx index 77e2f31..60d0798 100644 --- a/app/src/modules/scene/camera/camMode.tsx +++ b/app/src/modules/scene/camera/camMode.tsx @@ -1,12 +1,13 @@ import { useFrame, useThree } from "@react-three/fiber"; import React, { useEffect, useState } from "react"; +import { useKeyboardControls } from "@react-three/drei"; import * as CONSTANTS from "../../../types/world/worldConstants"; import { useCamMode, useToggleView } from "../../../store/builder/store"; -import { useKeyboardControls } from "@react-three/drei"; -import switchToThirdPerson from "./switchToThirdPerson"; -import switchToFirstPerson from "./switchToFirstPerson"; + +import switchToThirdPerson from "./functions/switchToThirdPerson"; +import switchToFirstPerson from "./functions/switchToFirstPerson"; import { detectModifierKeys } from "../../../utils/shortcutkeys/detectModifierKeys"; -import { firstPersonCamera } from "./firstPersonCamera"; +import { firstPersonCamera } from "./functions/firstPersonCamera"; const CamMode: React.FC = () => { const { camMode, setCamMode } = useCamMode(); diff --git a/app/src/modules/scene/camera/firstPersonCamera.ts b/app/src/modules/scene/camera/functions/firstPersonCamera.ts similarity index 92% rename from app/src/modules/scene/camera/firstPersonCamera.ts rename to app/src/modules/scene/camera/functions/firstPersonCamera.ts index 85aacb3..27620f3 100644 --- a/app/src/modules/scene/camera/firstPersonCamera.ts +++ b/app/src/modules/scene/camera/functions/firstPersonCamera.ts @@ -1,39 +1,39 @@ -import * as CONSTANTS from "../../../types/world/worldConstants"; - -interface FirstPersonCameraProps { - setIsTransitioning?: (value: boolean) => void; - state: any; -} - -interface FirstPersonCameraParams extends FirstPersonCameraProps { - camMode: string; - setCamMode: (mode: string) => void; - switchToFirstPerson: (controls: any, camera: any) => Promise; - switchToThirdPerson: (controls: any, camera: any) => Promise; -} - -export async function firstPersonCamera({ - setIsTransitioning, - state, - camMode, - setCamMode, - switchToFirstPerson, - switchToThirdPerson -}: FirstPersonCameraParams): Promise { - setIsTransitioning && setIsTransitioning(true); - - state.controls.mouseButtons.left = CONSTANTS.controlsTransition.leftMouse; - state.controls.mouseButtons.right = CONSTANTS.controlsTransition.rightMouse; - state.controls.mouseButtons.wheel = CONSTANTS.controlsTransition.wheelMouse; - state.controls.mouseButtons.middle = CONSTANTS.controlsTransition.middleMouse; - - if (camMode === "ThirdPerson") { - setCamMode("FirstPerson"); - await switchToFirstPerson(state.controls, state.camera); - } else if (camMode === "FirstPerson") { - setCamMode("ThirdPerson"); - await switchToThirdPerson(state.controls, state.camera); - } - - setIsTransitioning && setIsTransitioning(false); -} +import * as CONSTANTS from "../../../../types/world/worldConstants"; + +interface FirstPersonCameraProps { + setIsTransitioning?: (value: boolean) => void; + state: any; +} + +interface FirstPersonCameraParams extends FirstPersonCameraProps { + camMode: string; + setCamMode: (mode: string) => void; + switchToFirstPerson: (controls: any, camera: any) => Promise; + switchToThirdPerson: (controls: any, camera: any) => Promise; +} + +export async function firstPersonCamera({ + setIsTransitioning, + state, + camMode, + setCamMode, + switchToFirstPerson, + switchToThirdPerson +}: FirstPersonCameraParams): Promise { + setIsTransitioning && setIsTransitioning(true); + + state.controls.mouseButtons.left = CONSTANTS.controlsTransition.leftMouse; + state.controls.mouseButtons.right = CONSTANTS.controlsTransition.rightMouse; + state.controls.mouseButtons.wheel = CONSTANTS.controlsTransition.wheelMouse; + state.controls.mouseButtons.middle = CONSTANTS.controlsTransition.middleMouse; + + if (camMode === "ThirdPerson") { + setCamMode("FirstPerson"); + await switchToFirstPerson(state.controls, state.camera); + } else if (camMode === "FirstPerson") { + setCamMode("ThirdPerson"); + await switchToThirdPerson(state.controls, state.camera); + } + + setIsTransitioning && setIsTransitioning(false); +} diff --git a/app/src/modules/scene/camera/switchToFirstPerson.ts b/app/src/modules/scene/camera/functions/switchToFirstPerson.ts similarity index 91% rename from app/src/modules/scene/camera/switchToFirstPerson.ts rename to app/src/modules/scene/camera/functions/switchToFirstPerson.ts index a5371c4..a8a35de 100644 --- a/app/src/modules/scene/camera/switchToFirstPerson.ts +++ b/app/src/modules/scene/camera/functions/switchToFirstPerson.ts @@ -1,25 +1,25 @@ -import * as THREE from 'three'; -import * as CONSTANTS from '../../../types/world/worldConstants'; - -export default async function switchToFirstPerson( - controls: any, - camera: any -) { - if (!controls) return; - - const cameraDirection = new THREE.Vector3(); - camera.getWorldDirection(cameraDirection); - cameraDirection.normalize(); - - await controls.setPosition(camera.position.x, 2, camera.position.z, true); - controls.setTarget(camera.position.x, 2, camera.position.z, true); - controls.mouseButtons.left = CONSTANTS.firstPersonControls.leftMouse; - controls.lockPointer(); - - controls.azimuthRotateSpeed = CONSTANTS.firstPersonControls.azimuthRotateSpeed; - controls.polarRotateSpeed = CONSTANTS.firstPersonControls.polarRotateSpeed; - controls.truckSpeed = CONSTANTS.firstPersonControls.truckSpeed; - controls.minDistance = CONSTANTS.firstPersonControls.minDistance; - controls.maxDistance = CONSTANTS.firstPersonControls.maxDistance; - controls.maxPolarAngle = CONSTANTS.firstPersonControls.maxPolarAngle; +import * as THREE from 'three'; +import * as CONSTANTS from '../../../../types/world/worldConstants'; + +export default async function switchToFirstPerson( + controls: any, + camera: any +) { + if (!controls) return; + + const cameraDirection = new THREE.Vector3(); + camera.getWorldDirection(cameraDirection); + cameraDirection.normalize(); + + await controls.setPosition(camera.position.x, 2, camera.position.z, true); + controls.setTarget(camera.position.x, 2, camera.position.z, true); + controls.mouseButtons.left = CONSTANTS.firstPersonControls.leftMouse; + controls.lockPointer(); + + controls.azimuthRotateSpeed = CONSTANTS.firstPersonControls.azimuthRotateSpeed; + controls.polarRotateSpeed = CONSTANTS.firstPersonControls.polarRotateSpeed; + controls.truckSpeed = CONSTANTS.firstPersonControls.truckSpeed; + controls.minDistance = CONSTANTS.firstPersonControls.minDistance; + controls.maxDistance = CONSTANTS.firstPersonControls.maxDistance; + controls.maxPolarAngle = CONSTANTS.firstPersonControls.maxPolarAngle; } \ No newline at end of file diff --git a/app/src/modules/scene/camera/switchToThirdPerson.ts b/app/src/modules/scene/camera/functions/switchToThirdPerson.ts similarity index 93% rename from app/src/modules/scene/camera/switchToThirdPerson.ts rename to app/src/modules/scene/camera/functions/switchToThirdPerson.ts index 1e59749..b66adc5 100644 --- a/app/src/modules/scene/camera/switchToThirdPerson.ts +++ b/app/src/modules/scene/camera/functions/switchToThirdPerson.ts @@ -1,29 +1,29 @@ -import * as THREE from 'three'; -import * as CONSTANTS from '../../../types/world/worldConstants'; - -export default async function switchToThirdPerson( - controls: any, - camera: any -) { - if (!controls) return; - controls.mouseButtons.left = CONSTANTS.thirdPersonControls.leftMouse; - controls.mouseButtons.right = CONSTANTS.thirdPersonControls.rightMouse; - controls.mouseButtons.middle = CONSTANTS.thirdPersonControls.middleMouse; - controls.mouseButtons.wheel = CONSTANTS.thirdPersonControls.wheelMouse; - controls.unlockPointer(); - - const cameraDirection = new THREE.Vector3(); - camera.getWorldDirection(cameraDirection); - const targetOffset = cameraDirection.multiplyScalar(CONSTANTS.thirdPersonControls.targetOffset); - const targetPosition = new THREE.Vector3(camera.position.x, camera.position.y, camera.position.z).add(targetOffset); - - controls.setPosition(camera.position.x, CONSTANTS.thirdPersonControls.cameraHeight, camera.position.z, true); - controls.setTarget(targetPosition.x, 0, targetPosition.z, true); - - controls.azimuthRotateSpeed = CONSTANTS.thirdPersonControls.azimuthRotateSpeed; - controls.polarRotateSpeed = CONSTANTS.thirdPersonControls.polarRotateSpeed; - controls.truckSpeed = CONSTANTS.thirdPersonControls.truckSpeed; - controls.minDistance = CONSTANTS.threeDimension.minDistance; - controls.maxDistance = CONSTANTS.thirdPersonControls.maxDistance; - controls.maxPolarAngle = CONSTANTS.thirdPersonControls.maxPolarAngle; +import * as THREE from 'three'; +import * as CONSTANTS from '../../../../types/world/worldConstants'; + +export default async function switchToThirdPerson( + controls: any, + camera: any +) { + if (!controls) return; + controls.mouseButtons.left = CONSTANTS.thirdPersonControls.leftMouse; + controls.mouseButtons.right = CONSTANTS.thirdPersonControls.rightMouse; + controls.mouseButtons.middle = CONSTANTS.thirdPersonControls.middleMouse; + controls.mouseButtons.wheel = CONSTANTS.thirdPersonControls.wheelMouse; + controls.unlockPointer(); + + const cameraDirection = new THREE.Vector3(); + camera.getWorldDirection(cameraDirection); + const targetOffset = cameraDirection.multiplyScalar(CONSTANTS.thirdPersonControls.targetOffset); + const targetPosition = new THREE.Vector3(camera.position.x, camera.position.y, camera.position.z).add(targetOffset); + + controls.setPosition(camera.position.x, CONSTANTS.thirdPersonControls.cameraHeight, camera.position.z, true); + controls.setTarget(targetPosition.x, 0, targetPosition.z, true); + + controls.azimuthRotateSpeed = CONSTANTS.thirdPersonControls.azimuthRotateSpeed; + controls.polarRotateSpeed = CONSTANTS.thirdPersonControls.polarRotateSpeed; + controls.truckSpeed = CONSTANTS.thirdPersonControls.truckSpeed; + controls.minDistance = CONSTANTS.threeDimension.minDistance; + controls.maxDistance = CONSTANTS.thirdPersonControls.maxDistance; + controls.maxPolarAngle = CONSTANTS.thirdPersonControls.maxPolarAngle; } \ No newline at end of file diff --git a/app/src/modules/scene/camera/updateCameraPosition.ts b/app/src/modules/scene/camera/functions/updateCameraPosition.ts similarity index 88% rename from app/src/modules/scene/camera/updateCameraPosition.ts rename to app/src/modules/scene/camera/functions/updateCameraPosition.ts index 26e22ed..fb8c956 100644 --- a/app/src/modules/scene/camera/updateCameraPosition.ts +++ b/app/src/modules/scene/camera/functions/updateCameraPosition.ts @@ -1,26 +1,26 @@ -import { Socket } from "socket.io-client"; -import * as THREE from "three"; -import { getUserData } from "../../../functions/getUserData"; - -export default function updateCamPosition( - controls: any, - socket: Socket, - position: THREE.Vector3, - rotation: THREE.Euler, - projectId?: string -) { - const { userId, organization } = getUserData(); - if (!controls.current) return; - const target = controls.current.getTarget(new THREE.Vector3()); - - const camData = { - organization, - userId: userId, - position: position, - target: new THREE.Vector3(target.x, 0, target.z), - rotation: new THREE.Vector3(rotation.x, rotation.y, rotation.z), - socketId: socket.id, - projectId, - }; - socket.emit("v1:Camera:set", camData); -} +import { Socket } from "socket.io-client"; +import * as THREE from "three"; +import { getUserData } from "../../../../functions/getUserData"; + +export default function updateCamPosition( + controls: any, + socket: Socket, + position: THREE.Vector3, + rotation: THREE.Euler, + projectId?: string +) { + const { userId, organization } = getUserData(); + if (!controls.current) return; + const target = controls.current.getTarget(new THREE.Vector3()); + + const camData = { + organization, + userId: userId, + position: position, + target: new THREE.Vector3(target.x, 0, target.z), + rotation: new THREE.Vector3(rotation.x, rotation.y, rotation.z), + socketId: socket.id, + projectId, + }; + socket.emit("v1:Camera:set", camData); +} diff --git a/app/src/hooks/useCameraShortcuts.ts b/app/src/modules/scene/camera/shortcutsControls/cameraShortcutsControls.tsx similarity index 50% rename from app/src/hooks/useCameraShortcuts.ts rename to app/src/modules/scene/camera/shortcutsControls/cameraShortcutsControls.tsx index e4b897a..a073e40 100644 --- a/app/src/hooks/useCameraShortcuts.ts +++ b/app/src/modules/scene/camera/shortcutsControls/cameraShortcutsControls.tsx @@ -3,20 +3,24 @@ import { useThree } from "@react-three/fiber"; import * as THREE from "three"; import type { CameraControls } from "@react-three/drei"; -export const useCameraShortcuts = (controlsRef: React.RefObject) => { - const { camera } = useThree(); +const CameraShortcutsControls = () => { + const { camera, controls } = useThree(); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { - if (!controlsRef.current) return; + if (!controls) return; - // get current distance from camera to target + const cc = controls as CameraControls; + + // get current target const target = new THREE.Vector3(); - controlsRef.current.getTarget(target); + cc.getTarget(target); const distance = camera.position.distanceTo(target); let pos: THREE.Vector3 | null = null; + const dir = new THREE.Vector3().subVectors(camera.position, target).normalize(); + switch (e.key) { case "1": // Front pos = new THREE.Vector3(0, 0, distance).add(target); @@ -27,13 +31,24 @@ export const useCameraShortcuts = (controlsRef: React.RefObject) case "7": // Top pos = new THREE.Vector3(0, distance, 0).add(target); break; - case "9": // Back - pos = new THREE.Vector3(0, 0, -distance).add(target); + case "9": { + // Opposite view logic + 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 THREE.Vector3(0, 0, -Math.sign(dir.z) * distance).add(target); + } else if (Math.abs(dir.x) > Math.abs(dir.z) && Math.abs(dir.x) > Math.abs(dir.y)) { + // Currently looking Right/Left → flip X + pos = new THREE.Vector3(-Math.sign(dir.x) * distance, 0, 0).add(target); + } else { + // Currently looking Top/Bottom → stay Top + pos = new THREE.Vector3(0, distance, 0).add(target); + } break; + } } if (pos) { - controlsRef.current.setLookAt( + cc.setLookAt( pos.x, pos.y, pos.z, // camera position target.x, target.y, target.z, // keep same target true // smooth transition @@ -43,5 +58,9 @@ export const useCameraShortcuts = (controlsRef: React.RefObject) window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); - }, [controlsRef, camera]); + }, [controls, camera]); + + return null; }; + +export default CameraShortcutsControls; diff --git a/app/src/modules/scene/controls/controls.tsx b/app/src/modules/scene/controls/controls.tsx index d63f0ac..a3c9638 100644 --- a/app/src/modules/scene/controls/controls.tsx +++ b/app/src/modules/scene/controls/controls.tsx @@ -3,22 +3,22 @@ import { useRef, useEffect } from "react"; import { useThree } from "@react-three/fiber"; import * as THREE from "three"; import * as CONSTANTS from '../../../types/world/worldConstants'; - import { useSocketStore, useToggleView, useResetCamera } from "../../../store/builder/store"; -import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi"; -import updateCamPosition from "../camera/updateCameraPosition"; + import CamMode from "../camera/camMode"; import SwitchView from "../camera/switchView"; -import SelectionControls3D from "./selectionControls/selection3D/selectionControls3D"; -import TransformControl from "./transformControls/transformControls"; -import { useParams } from "react-router-dom"; -import { getUserData } from "../../../functions/getUserData"; - import ContextControls from "./contextControls/contextControls"; +import TransformControl from "./transformControls/transformControls"; import SelectionControls2D from "./selectionControls/selection2D/selectionControls2D"; +import SelectionControls3D from "./selectionControls/selection3D/selectionControls3D"; import UndoRedo2DControls from "./undoRedoControls/undoRedo2D/undoRedo2DControls"; import UndoRedo3DControls from "./undoRedoControls/undoRedo3D/undoRedo3DControls"; -import { useCameraShortcuts } from "../../../hooks/useCameraShortcuts"; +import CameraShortcutsControls from "../camera/shortcutsControls/cameraShortcutsControls"; + +import { useParams } from "react-router-dom"; +import { getUserData } from "../../../functions/getUserData"; +import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi"; +import updateCamPosition from "../camera/functions/updateCameraPosition"; export default function Controls() { const controlsRef = useRef(null); @@ -117,7 +117,6 @@ export default function Controls() { stopInterval(); }; }, [toggleView, state, socket]); - useCameraShortcuts(controlsRef); return ( <> @@ -140,6 +139,8 @@ export default function Controls() { + +