import * as THREE from "three"; const LOOK_SPEED = 0.003; const _yawQ = new THREE.Quaternion(); const _pitQ = new THREE.Quaternion(); const raycaster = new THREE.Raycaster(); const _ndc = new THREE.Vector2(); function raycastAt(mesh, camera, clientX, clientY) { _ndc.set((clientX / innerWidth) * 2 - 1, -(clientY / innerHeight) * 2 + 1); raycaster.setFromCamera(_ndc, camera); const hits = []; mesh.raycast(raycaster, hits); hits.sort((a, b) => a.distance - b.distance); return hits[0] ?? null; } export function setupControls(domElement, camera, mesh) { let dragging = false, dragButton = 0, lastX = 0, lastY = 0; domElement.addEventListener("pointerdown", (e) => { dragging = true; dragButton = e.button; lastX = e.clientX; lastY = e.clientY; domElement.setPointerCapture(e.pointerId); }); domElement.addEventListener("pointermove", (e) => { if (!dragging) return; const dx = e.clientX - lastX, dy = e.clientY - lastY; lastX = e.clientX; lastY = e.clientY; if (dragButton === 0) { // Left-drag: look around const camUp = new THREE.Vector3(0, 1, 0).applyQuaternion( camera.quaternion, ); camera.quaternion.premultiply( _yawQ.setFromAxisAngle(camUp, -dx * LOOK_SPEED), ); const camRight = new THREE.Vector3(1, 0, 0).applyQuaternion( camera.quaternion, ); camera.quaternion.premultiply( _pitQ.setFromAxisAngle(camRight, -dy * LOOK_SPEED), ); camera.quaternion.normalize(); } else if (dragButton === 2) { // Right-drag: pan const hit = raycastAt(mesh, camera, lastX, lastY); const panSpeed = (hit ? hit.distance : 5) * 0.001; const right = new THREE.Vector3(1, 0, 0).applyQuaternion( camera.quaternion, ); const up = new THREE.Vector3(0, 1, 0).applyQuaternion(camera.quaternion); camera.position.addScaledVector(right, -dx * panSpeed); camera.position.addScaledVector(up, dy * panSpeed); } }); domElement.addEventListener("pointerup", () => { dragging = false; }); domElement.addEventListener("contextmenu", (e) => e.preventDefault()); domElement.addEventListener( "wheel", (e) => { const hit = raycastAt(mesh, camera, e.clientX, e.clientY); const dist = hit ? hit.distance : 10; camera.position.addScaledVector( raycaster.ray.direction, -(dist * e.deltaY * 0.001), ); }, { passive: true }, ); }