Spaces:
Sleeping
Sleeping
| import * as THREE from 'three'; | |
| import { STATE, CONFIG } from '../core/State.js'; | |
| import { updateHUD } from '../ui/HUD.js'; | |
| export const raycaster = new THREE.Raycaster(); | |
| raycaster.params.Points.threshold = 0.5; | |
| export function setupInputListeners(camera, controls) { | |
| let isDragging = false; | |
| let dragged = false; | |
| let startX = 0, startY = 0; | |
| let startRotationY = 0, startRotationX = 0; | |
| // HUD Transparency | |
| controls.addEventListener('lock', () => { | |
| const hud = document.getElementById('hud-sidebar'); | |
| if (hud) hud.style.opacity = '0.2'; | |
| const guideline = document.getElementById('pointerlock-guideline'); | |
| if (guideline) guideline.style.display = 'block'; | |
| if (STATE.velocity) STATE.velocity.set(0, 0, 0); | |
| if (STATE.moveState) { | |
| STATE.moveState.forward = false; | |
| STATE.moveState.backward = false; | |
| STATE.moveState.left = false; | |
| STATE.moveState.right = false; | |
| STATE.moveState.up = false; | |
| STATE.moveState.down = false; | |
| STATE.moveState.shift = false; | |
| } | |
| }); | |
| controls.addEventListener('unlock', () => { | |
| const hud = document.getElementById('hud-sidebar'); | |
| if (hud) hud.style.opacity = '1.0'; | |
| const guideline = document.getElementById('pointerlock-guideline'); | |
| if (guideline) guideline.style.display = 'none'; | |
| if (STATE.velocity) STATE.velocity.set(0, 0, 0); | |
| if (STATE.moveState) { | |
| STATE.moveState.forward = false; | |
| STATE.moveState.backward = false; | |
| STATE.moveState.left = false; | |
| STATE.moveState.right = false; | |
| STATE.moveState.up = false; | |
| STATE.moveState.down = false; | |
| STATE.moveState.shift = false; | |
| } | |
| if (camera && camera.rotation) { | |
| camera.rotation.z = 0; | |
| } | |
| }); | |
| const onKeyDown = (e) => { | |
| if (!STATE.isReady) return; | |
| const active = document.activeElement; | |
| const isTyping = active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || active.tagName === 'SELECT' || active.isContentEditable); | |
| if (isTyping) return; | |
| switch (e.code) { | |
| case 'KeyW': STATE.moveState.forward = true; break; | |
| case 'KeyS': STATE.moveState.backward = true; break; | |
| case 'KeyA': STATE.moveState.left = true; break; | |
| case 'KeyD': STATE.moveState.right = true; break; | |
| case 'KeyQ': STATE.moveState.up = true; break; | |
| case 'KeyE': STATE.moveState.down = true; break; | |
| case 'ShiftLeft': STATE.moveState.shift = true; break; | |
| case 'ArrowLeft': | |
| if (STATE.hunterActive && STATE.hunterResults.length > 0) { | |
| STATE.hunterIndex = (STATE.hunterIndex - 1 + STATE.hunterResults.length) % STATE.hunterResults.length; | |
| STATE.currentSelectedIndex = STATE.hunterResults[STATE.hunterIndex]; | |
| const x = STATE.currentSelectedIndex * CONFIG.pointSpacing; | |
| camera.position.x = x; | |
| } else { | |
| STATE.currentSelectedIndex = Math.max(0, STATE.currentSelectedIndex - 1); | |
| } | |
| STATE.currentAnalysis = null; | |
| updateHUD(); | |
| break; | |
| case 'ArrowRight': | |
| if (STATE.hunterActive && STATE.hunterResults.length > 0) { | |
| STATE.hunterIndex = (STATE.hunterIndex + 1) % STATE.hunterResults.length; | |
| STATE.currentSelectedIndex = STATE.hunterResults[STATE.hunterIndex]; | |
| const x = STATE.currentSelectedIndex * CONFIG.pointSpacing; | |
| camera.position.x = x; | |
| } else { | |
| STATE.currentSelectedIndex = Math.min(STATE.vertexCount - 1, STATE.currentSelectedIndex + 1); | |
| } | |
| STATE.currentAnalysis = null; | |
| updateHUD(); | |
| break; | |
| case 'KeyF': | |
| const idx = STATE.currentSelectedIndex; | |
| const mode = STATE.currentZMode; | |
| const spacing = CONFIG.pointSpacing || 0.1; | |
| let targetX = 0, targetY = 0, targetZ = 0; | |
| let camX = 0, camY = 0, camZ = 0; | |
| if (mode === 'radial') { | |
| // Radial Mode | |
| targetX = 0; targetY = 0; targetZ = 0; | |
| camX = 5; camY = 5; camZ = (idx * spacing) + 15; | |
| } else { | |
| // Linear/Log Mode | |
| targetX = idx * spacing; | |
| targetY = 0; targetZ = 0; | |
| camX = targetX; camY = 5; camZ = 10; | |
| } | |
| if (camera) { | |
| camera.position.set(camX, camY, camZ); | |
| camera.lookAt(targetX, targetY, targetZ); | |
| } | |
| break; | |
| } | |
| }; | |
| const onKeyUp = (e) => { | |
| switch (e.code) { | |
| case 'KeyW': STATE.moveState.forward = false; break; | |
| case 'KeyS': STATE.moveState.backward = false; break; | |
| case 'KeyA': STATE.moveState.left = false; break; | |
| case 'KeyD': STATE.moveState.right = false; break; | |
| case 'KeyQ': STATE.moveState.up = false; break; | |
| case 'KeyE': STATE.moveState.down = false; break; | |
| case 'ShiftLeft': STATE.moveState.shift = false; break; | |
| } | |
| }; | |
| const isInteractiveUIElement = (target) => { | |
| if (!target) return false; | |
| return target.closest( | |
| 'input, button, select, option, label, a, textarea, table, tr, td, th, svg, path, ' + | |
| '#sidebar, #hud-sidebar, #relief-hud, #dimension-inspector, .app-panel, .modal-overlay, ' + | |
| '[class*="panel"], [id*="panel"], [class*="modal"], [id*="modal"], [class*="hud"], [id*="hud"]' | |
| ); | |
| }; | |
| const safeLock = () => { | |
| if (!controls || controls.isLocked) return; | |
| try { | |
| const p = controls.lock(); | |
| if (p && typeof p.catch === 'function') { | |
| p.catch(() => {}); | |
| } | |
| } catch (e) {} | |
| }; | |
| const safeUnlock = () => { | |
| if (!controls || !controls.isLocked) return; | |
| try { | |
| controls.unlock(); | |
| } catch (e) {} | |
| }; | |
| document.addEventListener('keydown', onKeyDown); | |
| document.addEventListener('keyup', onKeyUp); | |
| document.addEventListener('mousedown', (e) => { | |
| if (!STATE.isReady || e.button !== 0) return; | |
| if (isInteractiveUIElement(e.target)) return; | |
| dragged = false; | |
| if (controls.isLocked) { | |
| STATE.isTargetLocked = !STATE.isTargetLocked; | |
| // Fijar/liberar la lectura del HUD de Relieve sobre el punto apuntado al hacer clic. | |
| if (STATE.isTargetLocked) { | |
| STATE.reliefPinned = STATE.reliefHover ? { ...STATE.reliefHover } : null; | |
| safeUnlock(); | |
| } else { | |
| STATE.reliefPinned = null; | |
| } | |
| } else if (STATE.isTargetLocked) { | |
| STATE.isTargetLocked = false; | |
| STATE.reliefPinned = null; | |
| safeLock(); | |
| } else { | |
| // Drag-to-look fallback initialization | |
| isDragging = true; | |
| startX = e.clientX; | |
| startY = e.clientY; | |
| startRotationY = camera.rotation.y; | |
| startRotationX = camera.rotation.x; | |
| } | |
| }); | |
| document.addEventListener('mousemove', (e) => { | |
| if (!isDragging || controls.isLocked) return; | |
| const deltaX = e.clientX - startX; | |
| const deltaY = e.clientY - startY; | |
| if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) { | |
| dragged = true; | |
| } | |
| const sensitivity = 0.002; | |
| camera.rotation.y = startRotationY - deltaX * sensitivity; | |
| camera.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, startRotationX - deltaY * sensitivity)); | |
| }); | |
| document.addEventListener('mouseup', () => { | |
| isDragging = false; | |
| }); | |
| // Touch events for drag-to-look fallback | |
| document.addEventListener('touchstart', (e) => { | |
| if (!STATE.isReady || controls.isLocked || e.touches.length === 0) return; | |
| if (isInteractiveUIElement(e.target)) return; | |
| isDragging = true; | |
| dragged = false; | |
| startX = e.touches[0].clientX; | |
| startY = e.touches[0].clientY; | |
| startRotationY = camera.rotation.y; | |
| startRotationX = camera.rotation.x; | |
| }, { passive: true }); | |
| document.addEventListener('touchmove', (e) => { | |
| if (!isDragging || controls.isLocked || e.touches.length === 0) return; | |
| const deltaX = e.touches[0].clientX - startX; | |
| const deltaY = e.touches[0].clientY - startY; | |
| if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) { | |
| dragged = true; | |
| } | |
| const sensitivity = 0.003; | |
| camera.rotation.y = startRotationY - deltaX * sensitivity; | |
| camera.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, startRotationX - deltaY * sensitivity)); | |
| }, { passive: true }); | |
| document.addEventListener('touchend', () => { | |
| isDragging = false; | |
| }, { passive: true }); | |
| // PointerLockError fallback registration | |
| document.addEventListener('pointerlockerror', (e) => { | |
| console.warn("PointerLock error encountered, using drag-to-look fallback:", e); | |
| }); | |
| document.addEventListener('click', (e) => { | |
| if (!STATE.isReady || controls.isLocked || STATE.isTargetLocked) return; | |
| if (dragged) { | |
| dragged = false; | |
| return; | |
| } | |
| if (!isInteractiveUIElement(e.target)) { | |
| safeLock(); | |
| } | |
| }); | |
| } | |