Fix: Deterministic camera transitions
- Refactored setCameraView to use precise lookAt and position calculation instead of accumulating quaternion errors. - Forces camera UP vector (0,1,0) to prevent roll drift during isometric rotation.
This commit is contained in:
51
src/main.js
51
src/main.js
@@ -362,18 +362,16 @@ function setCameraView(direction, animate = true) {
|
||||
}
|
||||
}
|
||||
|
||||
// Calcular offset de la vista (diferencia entre posición y target)
|
||||
// Calcular offset de la vista (diferencia entre posición y target definidos en CAMERA_VIEWS)
|
||||
const viewOffset = view.position.clone().sub(view.target);
|
||||
|
||||
// Nueva posición de cámara centrada en el jugador
|
||||
const newPosition = playerPosition.clone().add(viewOffset);
|
||||
const newTarget = playerPosition.clone();
|
||||
const targetPosition = playerPosition.clone().add(viewOffset);
|
||||
const targetLookAt = playerPosition.clone();
|
||||
|
||||
if (animate && SESSION.currentView !== direction) {
|
||||
// Animación suave de transición
|
||||
const startPos = camera.position.clone();
|
||||
const startQuat = camera.quaternion.clone();
|
||||
const startTarget = controls.target.clone();
|
||||
const startPosition = camera.position.clone();
|
||||
const startLookAt = controls.target.clone();
|
||||
|
||||
const duration = 600; // ms
|
||||
const startTime = Date.now();
|
||||
@@ -382,48 +380,47 @@ function setCameraView(direction, animate = true) {
|
||||
const elapsed = Date.now() - startTime;
|
||||
const progress = Math.min(elapsed / duration, 1);
|
||||
|
||||
// Easing suave (ease-in-out)
|
||||
// Easing suave
|
||||
const eased = progress < 0.5
|
||||
? 2 * progress * progress
|
||||
: 1 - Math.pow(-2 * progress + 2, 2) / 2;
|
||||
|
||||
// Interpolar posición
|
||||
camera.position.lerpVectors(startPos, newPosition, eased);
|
||||
// Interpolación LINEAL de posición y target
|
||||
const currentPos = new THREE.Vector3().lerpVectors(startPosition, targetPosition, eased);
|
||||
const currentLookAt = new THREE.Vector3().lerpVectors(startLookAt, targetLookAt, eased);
|
||||
|
||||
// Interpolar quaternion (rotación suave)
|
||||
camera.quaternion.slerpQuaternions(startQuat, view.quaternion, eased);
|
||||
camera.position.copy(currentPos);
|
||||
camera.up.set(0, 1, 0); // FORZAR UP VECTOR SIEMPRE
|
||||
camera.lookAt(currentLookAt);
|
||||
|
||||
// Interpolar target
|
||||
controls.target.lerpVectors(startTarget, newTarget, eased);
|
||||
|
||||
camera.up.copy(view.up);
|
||||
controls.target.copy(currentLookAt);
|
||||
controls.update();
|
||||
|
||||
if (progress < 1) {
|
||||
requestAnimationFrame(animateTransition);
|
||||
} else {
|
||||
// Asegurar valores finales exactos
|
||||
camera.position.copy(newPosition);
|
||||
camera.quaternion.copy(view.quaternion);
|
||||
camera.up.copy(view.up);
|
||||
controls.target.copy(newTarget);
|
||||
// Asegurar estado final perfecto
|
||||
camera.position.copy(targetPosition);
|
||||
camera.up.set(0, 1, 0);
|
||||
camera.lookAt(targetLookAt);
|
||||
controls.target.copy(targetLookAt);
|
||||
controls.update();
|
||||
}
|
||||
};
|
||||
|
||||
animateTransition();
|
||||
} else {
|
||||
// Sin animación (cambio instantáneo)
|
||||
camera.position.copy(newPosition);
|
||||
camera.quaternion.copy(view.quaternion);
|
||||
camera.up.copy(view.up);
|
||||
controls.target.copy(newTarget);
|
||||
// Cambio inmediato
|
||||
camera.position.copy(targetPosition);
|
||||
camera.up.set(0, 1, 0); // FORZAR UP VECTOR
|
||||
camera.lookAt(targetLookAt);
|
||||
controls.target.copy(targetLookAt);
|
||||
controls.update();
|
||||
}
|
||||
|
||||
SESSION.currentView = direction;
|
||||
updateCompassUI();
|
||||
updateWallOpacities(); // Actualizar opacidades de paredes según nueva vista
|
||||
updateWallOpacities();
|
||||
}
|
||||
|
||||
// Establecer vista inicial
|
||||
|
||||
Reference in New Issue
Block a user