working on panning, currently kinda glitchy
This commit is contained in:
parent
f5db701d3c
commit
e7ab6a2d76
1 changed files with 100 additions and 19 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
import { useThree } from "@react-three/fiber";
|
import { useThree } from "@react-three/fiber";
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
|
import { Plane, Raycaster, Vector2, Vector3 } from "three";
|
||||||
|
|
||||||
interface CameraTarget {
|
interface CameraTarget {
|
||||||
x: number;
|
x: number;
|
||||||
|
|
@ -25,8 +26,18 @@ interface Props {
|
||||||
export default function OrbitCameraControls(props: Props) {
|
export default function OrbitCameraControls(props: Props) {
|
||||||
const { target, clampVerticalRotation, minPhi = 0.01, maxPhi = Math.PI - 0.01 } = props;
|
const { target, clampVerticalRotation, minPhi = 0.01, maxPhi = Math.PI - 0.01 } = props;
|
||||||
const { camera, gl } = useThree();
|
const { camera, gl } = useThree();
|
||||||
|
const panPlane = useRef(new Plane());
|
||||||
|
const panStartPoint = useRef(new Vector3());
|
||||||
|
const raycaster = useRef(new Raycaster());
|
||||||
|
const mouse = useRef(new Vector2());
|
||||||
|
const targetRef = useRef({
|
||||||
|
x: target?.x ?? 0,
|
||||||
|
y: target?.y ?? 0,
|
||||||
|
z: target?.z ?? 0,
|
||||||
|
});
|
||||||
|
|
||||||
const isDragging = useRef(false);
|
const isDragging = useRef(false);
|
||||||
|
const isPanning = useRef(false);
|
||||||
const lastPos = useRef({ x: 0, y: 0 });
|
const lastPos = useRef({ x: 0, y: 0 });
|
||||||
|
|
||||||
// Spherical coords
|
// Spherical coords
|
||||||
|
|
@ -38,7 +49,7 @@ export default function OrbitCameraControls(props: Props) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const canvas = gl.domElement;
|
const canvas = gl.domElement;
|
||||||
|
canvas.addEventListener("contextmenu", e => e.preventDefault());
|
||||||
const updateCamera = () => {
|
const updateCamera = () => {
|
||||||
const { radius, theta, phi } = spherical.current;
|
const { radius, theta, phi } = spherical.current;
|
||||||
|
|
||||||
|
|
@ -47,41 +58,111 @@ export default function OrbitCameraControls(props: Props) {
|
||||||
const z = radius * Math.sin(phi) * Math.cos(theta);
|
const z = radius * Math.sin(phi) * Math.cos(theta);
|
||||||
|
|
||||||
camera.position.set(x, y, z);
|
camera.position.set(x, y, z);
|
||||||
camera.lookAt(target?.x ?? 0, target?.y ?? 0, target?.z ?? 0);
|
camera.lookAt(
|
||||||
|
targetRef.current.x,
|
||||||
|
targetRef.current.y,
|
||||||
|
targetRef.current.z
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
updateCamera();
|
updateCamera();
|
||||||
|
|
||||||
const handleMouseDown = (e: MouseEvent) => {
|
const handleMouseDown = (e: MouseEvent) => {
|
||||||
if (e.target !== canvas) return;
|
if (e.target !== canvas) return;
|
||||||
isDragging.current = true;
|
|
||||||
lastPos.current = { x: e.clientX, y: e.clientY };
|
lastPos.current = { x: e.clientX, y: e.clientY };
|
||||||
|
|
||||||
|
// Left click = rotate
|
||||||
|
if (e.button === 0) {
|
||||||
|
isDragging.current = true;
|
||||||
|
isPanning.current = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right click = pan
|
||||||
|
if (e.button === 2) {
|
||||||
|
isPanning.current = true;
|
||||||
|
isDragging.current = false;
|
||||||
|
|
||||||
|
// plane facing camera through target
|
||||||
|
const camDir = new Vector3();
|
||||||
|
camera.getWorldDirection(camDir);
|
||||||
|
|
||||||
|
panPlane.current.setFromNormalAndCoplanarPoint(
|
||||||
|
camDir,
|
||||||
|
new Vector3(
|
||||||
|
targetRef.current.x,
|
||||||
|
targetRef.current.y,
|
||||||
|
targetRef.current.z
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// starting intersection point
|
||||||
|
raycaster.current.setFromCamera(
|
||||||
|
new Vector2(
|
||||||
|
(e.clientX / window.innerWidth) * 2 - 1,
|
||||||
|
-(e.clientY / window.innerHeight) * 2 + 1
|
||||||
|
),
|
||||||
|
camera
|
||||||
|
);
|
||||||
|
|
||||||
|
raycaster.current.ray.intersectPlane(
|
||||||
|
panPlane.current,
|
||||||
|
panStartPoint.current
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseMove = (e: MouseEvent) => {
|
|
||||||
if (!isDragging.current) return;
|
|
||||||
|
|
||||||
|
|
||||||
|
const handleMouseMove = (e: MouseEvent) => {
|
||||||
const deltaX = e.clientX - lastPos.current.x;
|
const deltaX = e.clientX - lastPos.current.x;
|
||||||
const deltaY = e.clientY - lastPos.current.y;
|
const deltaY = e.clientY - lastPos.current.y;
|
||||||
|
|
||||||
|
// ROTATE
|
||||||
|
if (isDragging.current) {
|
||||||
spherical.current.theta -= deltaX * 0.005;
|
spherical.current.theta -= deltaX * 0.005;
|
||||||
spherical.current.phi -= deltaY * 0.005;
|
spherical.current.phi -= deltaY * 0.005;
|
||||||
|
|
||||||
// Clamp vertical rotation (prevents flipping)
|
|
||||||
if (clampVerticalRotation) {
|
if (clampVerticalRotation) {
|
||||||
spherical.current.phi = Math.max(
|
spherical.current.phi = Math.max(
|
||||||
minPhi,
|
minPhi,
|
||||||
Math.min(maxPhi, spherical.current.phi)
|
Math.min(maxPhi, spherical.current.phi)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PAN
|
||||||
|
if (isPanning.current) {
|
||||||
|
const raycaster = new Raycaster();
|
||||||
|
|
||||||
|
mouse.current.x = (e.clientX / window.innerWidth) * 2 - 1;
|
||||||
|
mouse.current.y = -(e.clientY / window.innerHeight) * 2 + 1;
|
||||||
|
|
||||||
|
raycaster.setFromCamera(mouse.current, camera);
|
||||||
|
|
||||||
|
const currentPoint = new Vector3();
|
||||||
|
|
||||||
|
raycaster.ray.intersectPlane(panPlane.current, currentPoint);
|
||||||
|
|
||||||
|
const delta = new Vector3().subVectors(
|
||||||
|
panStartPoint.current,
|
||||||
|
currentPoint
|
||||||
|
);
|
||||||
|
|
||||||
|
targetRef.current.x += delta.x;
|
||||||
|
targetRef.current.y += delta.y;
|
||||||
|
targetRef.current.z += delta.z;
|
||||||
|
|
||||||
|
panStartPoint.current.copy(currentPoint);
|
||||||
|
}
|
||||||
|
|
||||||
lastPos.current = { x: e.clientX, y: e.clientY };
|
lastPos.current = { x: e.clientX, y: e.clientY };
|
||||||
|
|
||||||
updateCamera();
|
updateCamera();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseUp = () => {
|
const handleMouseUp = () => {
|
||||||
isDragging.current = false;
|
isDragging.current = false;
|
||||||
|
isPanning.current = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleWheel = (e: WheelEvent) => {
|
const handleWheel = (e: WheelEvent) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue