feat: Refactor camera controls and view switching; enhance event handling for improved user experience and state management
This commit is contained in:
@@ -9,124 +9,89 @@ import { detectModifierKeys } from "../../../utils/shortcutkeys/detectModifierKe
|
|||||||
import { firstPersonCamera } from "./firstPersonCamera";
|
import { firstPersonCamera } from "./firstPersonCamera";
|
||||||
|
|
||||||
const CamMode: React.FC = () => {
|
const CamMode: React.FC = () => {
|
||||||
const { camMode, setCamMode } = useCamMode();
|
const { camMode, setCamMode } = useCamMode();
|
||||||
const [, get] = useKeyboardControls();
|
const [_, get] = useKeyboardControls();
|
||||||
const [isTransitioning, setIsTransitioning] = useState(false);
|
const [isTransitioning, setIsTransitioning] = useState(false);
|
||||||
const state: any = useThree();
|
const state: any = useThree();
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
const [isShiftActive, setIsShiftActive] = useState(false);
|
const [isShiftActive, setIsShiftActive] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handlePointerLockChange = async () => {
|
const handlePointerLockChange = async () => {
|
||||||
if (document.pointerLockElement && !toggleView) {
|
if (document.pointerLockElement && !toggleView) {
|
||||||
// Pointer is locked
|
} else if (camMode === "FirstPerson" && !toggleView) {
|
||||||
} else if (camMode === "FirstPerson" && !toggleView) {
|
setCamMode("ThirdPerson");
|
||||||
// Pointer is unlocked
|
await switchToThirdPerson(state.controls, state.camera);
|
||||||
setCamMode("ThirdPerson");
|
}
|
||||||
await switchToThirdPerson(state.controls, state.camera);
|
};
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener("pointerlockchange", handlePointerLockChange);
|
document.addEventListener("pointerlockchange", handlePointerLockChange);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener(
|
document.removeEventListener("pointerlockchange", handlePointerLockChange);
|
||||||
"pointerlockchange",
|
};
|
||||||
handlePointerLockChange
|
}, [camMode, toggleView, setCamMode, state.controls, state.camera]);
|
||||||
);
|
|
||||||
};
|
|
||||||
}, [camMode, toggleView, setCamMode, state.controls, state.camera]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
const handleKeyPress = async (event: KeyboardEvent) => {
|
||||||
if (event.key === "Shift") {
|
if (!state.controls) return;
|
||||||
setIsShiftActive(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleKeyUp = (event: KeyboardEvent) => {
|
const keyCombination = detectModifierKeys(event);
|
||||||
if (event.key === "Shift") {
|
|
||||||
setIsShiftActive(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener("keydown", handleKeyDown);
|
if (keyCombination === "/" && !isTransitioning && !toggleView) {
|
||||||
window.addEventListener("keyup", handleKeyUp);
|
firstPersonCamera({
|
||||||
|
setIsTransitioning,
|
||||||
|
state,
|
||||||
|
camMode,
|
||||||
|
setCamMode,
|
||||||
|
switchToFirstPerson,
|
||||||
|
switchToThirdPerson,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
if (keyCombination === 'Shift') {
|
||||||
window.removeEventListener("keydown", handleKeyDown);
|
setIsShiftActive(true);
|
||||||
window.removeEventListener("keyup", handleKeyUp);
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
const handleKeyUp = (event: KeyboardEvent) => {
|
||||||
const handleKeyPress = async (event: KeyboardEvent) => {
|
if (event.key === "Shift") {
|
||||||
if (!state.controls) return;
|
setIsShiftActive(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const keyCombination = detectModifierKeys(event);
|
window.addEventListener("keydown", handleKeyPress);
|
||||||
|
window.addEventListener("keyup", handleKeyUp);
|
||||||
|
|
||||||
if (keyCombination === "/" && !isTransitioning && !toggleView) {
|
return () => {
|
||||||
firstPersonCamera({
|
window.removeEventListener("keydown", handleKeyPress);
|
||||||
setIsTransitioning,
|
window.removeEventListener("keyup", handleKeyUp);
|
||||||
state,
|
};
|
||||||
camMode,
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
setCamMode,
|
}, [camMode, isTransitioning, toggleView, state.controls, state.camera, setCamMode]);
|
||||||
switchToFirstPerson,
|
|
||||||
switchToThirdPerson,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener("keydown", handleKeyPress);
|
useFrame(() => {
|
||||||
return () => {
|
const { forward, backward, left, right } = get();
|
||||||
window.removeEventListener("keydown", handleKeyPress);
|
if (!state.controls) return;
|
||||||
};
|
if (camMode === "ThirdPerson" || !document.pointerLockElement) return;
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [
|
|
||||||
camMode,
|
|
||||||
isTransitioning,
|
|
||||||
toggleView,
|
|
||||||
state.controls,
|
|
||||||
state.camera,
|
|
||||||
setCamMode,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useFrame(() => {
|
const speedMultiplier = isShiftActive ? 4 : 1;
|
||||||
const { forward, backward, left, right } = get();
|
|
||||||
if (!state.controls) return;
|
|
||||||
if (camMode === "ThirdPerson" || !document.pointerLockElement) return;
|
|
||||||
|
|
||||||
const speedMultiplier = isShiftActive ? 4 : 1;
|
if (forward) {
|
||||||
|
state.controls.forward(CONSTANTS.firstPersonControls.forwardSpeed * speedMultiplier, true);
|
||||||
|
}
|
||||||
|
if (backward) {
|
||||||
|
state.controls.forward(CONSTANTS.firstPersonControls.backwardSpeed * speedMultiplier, true);
|
||||||
|
}
|
||||||
|
if (left) {
|
||||||
|
state.controls.truck(CONSTANTS.firstPersonControls.leftSpeed * speedMultiplier, 0, true);
|
||||||
|
}
|
||||||
|
if (right) {
|
||||||
|
state.controls.truck(CONSTANTS.firstPersonControls.rightSpeed * speedMultiplier, 0, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (forward) {
|
return null;
|
||||||
state.controls.forward(
|
|
||||||
CONSTANTS.firstPersonControls.forwardSpeed * speedMultiplier,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (backward) {
|
|
||||||
state.controls.forward(
|
|
||||||
CONSTANTS.firstPersonControls.backwardSpeed * speedMultiplier,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (left) {
|
|
||||||
state.controls.truck(
|
|
||||||
CONSTANTS.firstPersonControls.leftSpeed * speedMultiplier,
|
|
||||||
0,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (right) {
|
|
||||||
state.controls.truck(
|
|
||||||
CONSTANTS.firstPersonControls.rightSpeed * speedMultiplier,
|
|
||||||
0,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return null; // This component does not render any UI
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default CamMode;
|
export default CamMode;
|
||||||
|
|||||||
@@ -1,73 +1,68 @@
|
|||||||
import * as THREE from "three";
|
import { useEffect } from "react";
|
||||||
import { useEffect, useRef } from "react";
|
|
||||||
import { useToggleView } from "../../../store/builder/store";
|
|
||||||
import { useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi";
|
import * as THREE from 'three';
|
||||||
import * as CONSTANTS from '../../../types/world/worldConstants';
|
import { PerspectiveCamera, OrthographicCamera, CameraControls } from '@react-three/drei';
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
import * as CONSTANTS from '../../../types/world/worldConstants';
|
||||||
|
import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi";
|
||||||
import { getUserData } from "../../../functions/getUserData";
|
import { getUserData } from "../../../functions/getUserData";
|
||||||
import { CameraControls } from "@react-three/drei";
|
import { useToggleView } from "../../../store/builder/store";
|
||||||
|
|
||||||
export default function SwitchView() {
|
export default function SwitchView() {
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
const state: any = useThree();
|
const { controls } = useThree();
|
||||||
const { set } = useThree();
|
const { projectId } = useParams();
|
||||||
const perspectiveCamera = useRef<THREE.PerspectiveCamera | null>(null);
|
const { organization } = getUserData();
|
||||||
const orthoCamera = useRef<THREE.OrthographicCamera | null>(null);
|
|
||||||
orthoCamera.current = new THREE.OrthographicCamera(-window.innerWidth / 2, window.innerWidth / 2, window.innerHeight / 2, -window.innerHeight / 2, 0.01, 1000);
|
|
||||||
perspectiveCamera.current = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 1000);
|
|
||||||
const { projectId } = useParams();
|
|
||||||
const { organization } = getUserData();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!perspectiveCamera.current || !orthoCamera.current) return;
|
if (toggleView && controls) {
|
||||||
if (toggleView) {
|
(controls as any).mouseButtons.left = CONSTANTS.twoDimension.leftMouse;
|
||||||
orthoCamera.current.zoom = 10;
|
(controls as any).mouseButtons.right = CONSTANTS.twoDimension.rightMouse;
|
||||||
orthoCamera.current.position.set(...CONSTANTS.twoDimension.defaultPosition);
|
} else {
|
||||||
orthoCamera.current.lookAt(new THREE.Vector3(...CONSTANTS.twoDimension.defaultTarget));
|
try {
|
||||||
orthoCamera.current.updateProjectionMatrix();
|
getCamera(organization, localStorage.getItem('userId')!, projectId).then((data) => {
|
||||||
set({ camera: orthoCamera.current });
|
if (data && data.position && data.target) {
|
||||||
orthoCamera.current.updateProjectionMatrix();
|
(controls as CameraControls)?.setPosition(data.position.x, data.position.y, data.position.z);
|
||||||
} else if (!toggleView) {
|
(controls as CameraControls)?.setTarget(data.target.x, data.target.y, data.target.z);
|
||||||
perspectiveCamera.current.position.set(...CONSTANTS.threeDimension.defaultPosition);
|
} else {
|
||||||
perspectiveCamera.current.lookAt(new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget));
|
(controls as CameraControls)?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
|
||||||
set({ camera: perspectiveCamera.current });
|
(controls as CameraControls)?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
|
||||||
}
|
}
|
||||||
}, [toggleView, set]);
|
});
|
||||||
|
} catch (error) {
|
||||||
|
echo.error("Failed to retrieve camera position or target");
|
||||||
|
(controls as CameraControls)?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
|
||||||
|
(controls as CameraControls)?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
if (controls) {
|
||||||
if (toggleView && state.controls) {
|
(controls as any).mouseButtons.left = CONSTANTS.threeDimension.leftMouse;
|
||||||
state.controls.mouseButtons.left = CONSTANTS.twoDimension.leftMouse;
|
(controls as any).mouseButtons.right = CONSTANTS.threeDimension.rightMouse;
|
||||||
state.controls.mouseButtons.right = CONSTANTS.twoDimension.rightMouse;
|
}
|
||||||
} else {
|
}
|
||||||
try {
|
}, [toggleView, controls]);
|
||||||
getCamera(organization, localStorage.getItem('userId')!, projectId).then((data) => {
|
|
||||||
if (data && data.position && data.target) {
|
|
||||||
// state.controls?.setLookAt(data.position.x, data.position.y, data.position.z, data.target.x, data.target.y, data.target.z, true)
|
|
||||||
state.controls?.setPosition(data.position.x, data.position.y, data.position.z);
|
|
||||||
state.controls?.setTarget(data.target.x, data.target.y, data.target.z);
|
|
||||||
} else {
|
|
||||||
// state.controls?.setLookAt(...CONSTANTS.threeDimension.defaultPosition, ...CONSTANTS.threeDimension.defaultTarget, true);
|
|
||||||
state.controls?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
|
|
||||||
state.controls?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
echo.error("Failed to retrieve camera position or target");
|
|
||||||
console.error("Failed to retrieve camera position or target:", error);
|
|
||||||
// state.controls?.setLookAt(...CONSTANTS.threeDimension.defaultPosition, ...CONSTANTS.threeDimension.defaultTarget, true);
|
|
||||||
state.controls?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
|
|
||||||
state.controls?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.controls) {
|
return (
|
||||||
state.controls.mouseButtons.left = CONSTANTS.threeDimension.leftMouse;
|
<>
|
||||||
state.controls.mouseButtons.right = CONSTANTS.threeDimension.rightMouse;
|
{toggleView ? (
|
||||||
}
|
<OrthographicCamera
|
||||||
}
|
makeDefault
|
||||||
}, [toggleView, state.controls]);
|
position={CONSTANTS.twoDimension.defaultPosition}
|
||||||
|
zoom={10}
|
||||||
return (
|
near={0.01}
|
||||||
<></>
|
far={1000}
|
||||||
);
|
onUpdate={(self) => self.lookAt(new THREE.Vector3(...CONSTANTS.twoDimension.defaultTarget))}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<PerspectiveCamera
|
||||||
|
makeDefault
|
||||||
|
fov={75}
|
||||||
|
position={CONSTANTS.threeDimension.defaultPosition}
|
||||||
|
near={0.01}
|
||||||
|
far={1000}
|
||||||
|
onUpdate={(self) => self.lookAt(new THREE.Vector3(...CONSTANTS.threeDimension.defaultTarget))}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -13,16 +13,17 @@ import SelectionControls3D from "./selectionControls/selection3D/selectionContro
|
|||||||
import TransformControl from "./transformControls/transformControls";
|
import TransformControl from "./transformControls/transformControls";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { getUserData } from "../../../functions/getUserData";
|
import { getUserData } from "../../../functions/getUserData";
|
||||||
|
|
||||||
import SelectionControls2D from "./selectionControls/selection2D/selectionControls2D";
|
import SelectionControls2D from "./selectionControls/selection2D/selectionControls2D";
|
||||||
import UndoRedo2DControls from "./undoRedoControls/undoRedo2D/undoRedo2DControls";
|
import UndoRedo2DControls from "./undoRedoControls/undoRedo2D/undoRedo2DControls";
|
||||||
|
|
||||||
export default function Controls() {
|
export default function Controls() {
|
||||||
const controlsRef = useRef<CameraControls>(null);
|
const controlsRef = useRef<CameraControls>(null);
|
||||||
|
|
||||||
|
const state = useThree();
|
||||||
const { toggleView } = useToggleView();
|
const { toggleView } = useToggleView();
|
||||||
const { resetCamera, setResetCamera } = useResetCamera();
|
const { resetCamera, setResetCamera } = useResetCamera();
|
||||||
const { socket } = useSocketStore();
|
const { socket } = useSocketStore();
|
||||||
const state = useThree();
|
|
||||||
const { projectId } = useParams();
|
const { projectId } = useParams();
|
||||||
const { userId, organization } = getUserData();
|
const { userId, organization } = getUserData();
|
||||||
|
|
||||||
@@ -33,7 +34,6 @@ export default function Controls() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getCamera(organization, userId, projectId).then((data) => {
|
getCamera(organization, userId, projectId).then((data) => {
|
||||||
// console.log('data: ', data);
|
|
||||||
if (data && data.position && data.target) {
|
if (data && data.position && data.target) {
|
||||||
controlsRef.current?.setPosition(data.position.x, data.position.y, data.position.z);
|
controlsRef.current?.setPosition(data.position.x, data.position.y, data.position.z);
|
||||||
controlsRef.current?.setTarget(data.target.x, data.target.y, data.target.z);
|
controlsRef.current?.setTarget(data.target.x, data.target.y, data.target.z);
|
||||||
@@ -41,8 +41,7 @@ export default function Controls() {
|
|||||||
controlsRef.current?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
|
controlsRef.current?.setPosition(...CONSTANTS.threeDimension.defaultPosition);
|
||||||
controlsRef.current?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
|
controlsRef.current?.setTarget(...CONSTANTS.threeDimension.defaultTarget);
|
||||||
}
|
}
|
||||||
})
|
}).catch((error) => console.error("Failed to fetch camera data:", error));
|
||||||
.catch((error) => console.error("Failed to fetch camera data:", error));
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -60,7 +59,6 @@ export default function Controls() {
|
|||||||
socketId: socket.id,
|
socketId: socket.id,
|
||||||
projectId
|
projectId
|
||||||
};
|
};
|
||||||
// console.log('CameracamData: ', camData);
|
|
||||||
socket.emit('v1:Camera:set', camData)
|
socket.emit('v1:Camera:set', camData)
|
||||||
|
|
||||||
setResetCamera(false);
|
setResetCamera(false);
|
||||||
@@ -130,7 +128,7 @@ export default function Controls() {
|
|||||||
camera={state.camera}
|
camera={state.camera}
|
||||||
verticalDragToForward={true}
|
verticalDragToForward={true}
|
||||||
boundaryEnclosesCamera={true}
|
boundaryEnclosesCamera={true}
|
||||||
dollyToCursor={toggleView}
|
dollyDragInverted
|
||||||
>
|
>
|
||||||
|
|
||||||
<SwitchView />
|
<SwitchView />
|
||||||
|
|||||||
@@ -333,6 +333,7 @@ const SelectionControls3D: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
||||||
<MoveControls3D movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
<MoveControls3D movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
||||||
|
|
||||||
<RotateControls3D rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} />
|
<RotateControls3D rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} />
|
||||||
@@ -340,6 +341,7 @@ const SelectionControls3D: React.FC = () => {
|
|||||||
<DuplicationControls3D duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} setpastedObjects={setpastedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
<DuplicationControls3D duplicatedObjects={duplicatedObjects} setDuplicatedObjects={setDuplicatedObjects} setpastedObjects={setpastedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
||||||
|
|
||||||
<CopyPasteControls3D copiedObjects={copiedObjects} setCopiedObjects={setCopiedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} setDuplicatedObjects={setDuplicatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
<CopyPasteControls3D copiedObjects={copiedObjects} setCopiedObjects={setCopiedObjects} pastedObjects={pastedObjects} setpastedObjects={setpastedObjects} setDuplicatedObjects={setDuplicatedObjects} movedObjects={movedObjects} setMovedObjects={setMovedObjects} rotatedObjects={rotatedObjects} setRotatedObjects={setRotatedObjects} boundingBoxRef={boundingBoxRef} />
|
||||||
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ export const thirdPersonControls: ThirdPersonControls = {
|
|||||||
polarRotateSpeed: 1, // Speed of rotation around the polar axis
|
polarRotateSpeed: 1, // Speed of rotation around the polar axis
|
||||||
truckSpeed: 2, // Speed of truck movement
|
truckSpeed: 2, // Speed of truck movement
|
||||||
maxDistance: 100, // Maximum distance from the target
|
maxDistance: 100, // Maximum distance from the target
|
||||||
maxPolarAngle: Math.PI / 2 - 0.05, // Maximum polar angle
|
maxPolarAngle: Math.PI / 2, // Maximum polar angle
|
||||||
minZoom: 6, // Minimum zoom level
|
minZoom: 6, // Minimum zoom level
|
||||||
maxZoom: 100, // Maximum zoom level
|
maxZoom: 100, // Maximum zoom level
|
||||||
targetOffset: 20, // Offset of the target from the camera
|
targetOffset: 20, // Offset of the target from the camera
|
||||||
|
|||||||
Reference in New Issue
Block a user