forked from EXT/VR180-Web-Player
169 lines
5.2 KiB
TypeScript
169 lines
5.2 KiB
TypeScript
import type { ProjectionMode } from './config.js';
|
|
|
|
type CameraControlsCallbacks = {
|
|
hideControls: () => void;
|
|
isEnabled: () => boolean;
|
|
showControls: () => void;
|
|
};
|
|
|
|
const MOUSE_SENSITIVITY = 0.002;
|
|
const TOUCH_SENSITIVITY = 0.003;
|
|
const MOMENTUM_DAMPING = 0.8;
|
|
const MAX_PITCH = Math.PI * (45 / 180);
|
|
const MAX_YAW = Math.PI * (45 / 180);
|
|
|
|
export class FallbackCameraControls {
|
|
private camera: any;
|
|
private readonly callbacks: CameraControlsCallbacks;
|
|
private cameraRotation = { yaw: 0, pitch: 0 };
|
|
private cameraVelocity = { yaw: 0, pitch: 0 };
|
|
private dragging = false;
|
|
private lastMouseX = 0;
|
|
private lastMouseY = 0;
|
|
private lastTouchX = 0;
|
|
private lastTouchY = 0;
|
|
|
|
constructor(camera: any, callbacks: CameraControlsCallbacks) {
|
|
this.camera = camera;
|
|
this.callbacks = callbacks;
|
|
}
|
|
|
|
get isDragging(): boolean {
|
|
return this.dragging;
|
|
}
|
|
|
|
reset(): void {
|
|
this.cameraRotation = { yaw: 0, pitch: 0 };
|
|
this.cameraVelocity = { yaw: 0, pitch: 0 };
|
|
this.dragging = false;
|
|
if (this.camera) {
|
|
this.camera.rotation.set(0, 0, 0);
|
|
}
|
|
}
|
|
|
|
updateCameraRotation(): void {
|
|
if (!this.camera) return;
|
|
|
|
this.cameraRotation.yaw += this.cameraVelocity.yaw;
|
|
this.cameraRotation.pitch += this.cameraVelocity.pitch;
|
|
|
|
this.cameraRotation.pitch = Math.max(-MAX_PITCH, Math.min(MAX_PITCH, this.cameraRotation.pitch));
|
|
this.cameraRotation.yaw = Math.max(-MAX_YAW, Math.min(MAX_YAW, this.cameraRotation.yaw));
|
|
|
|
this.cameraVelocity.yaw *= MOMENTUM_DAMPING;
|
|
this.cameraVelocity.pitch *= MOMENTUM_DAMPING;
|
|
|
|
if (Math.abs(this.cameraVelocity.yaw) < 0.001) this.cameraVelocity.yaw = 0;
|
|
if (Math.abs(this.cameraVelocity.pitch) < 0.001) this.cameraVelocity.pitch = 0;
|
|
|
|
this.camera.rotation.set(this.cameraRotation.pitch, this.cameraRotation.yaw, 0);
|
|
}
|
|
|
|
addEventListeners(canvas: HTMLElement, projectionMode: ProjectionMode): void {
|
|
canvas.addEventListener('mousemove', this.onCanvasMouseMove);
|
|
|
|
if (projectionMode === 'vr180') {
|
|
canvas.addEventListener('mousedown', this.onMouseDown);
|
|
canvas.addEventListener('mousemove', this.onMouseMove);
|
|
canvas.addEventListener('mouseup', this.onMouseUp);
|
|
canvas.addEventListener('touchstart', this.onTouchStart, { passive: false });
|
|
canvas.addEventListener('touchmove', this.onTouchMove, { passive: false });
|
|
canvas.addEventListener('touchend', this.onTouchEnd, { passive: false });
|
|
} else {
|
|
canvas.addEventListener('touchstart', this.onCanvasTouchStart, { passive: true });
|
|
}
|
|
}
|
|
|
|
removeEventListeners(canvas: HTMLElement): void {
|
|
canvas.removeEventListener('mousemove', this.onCanvasMouseMove);
|
|
canvas.removeEventListener('mousedown', this.onMouseDown);
|
|
canvas.removeEventListener('mousemove', this.onMouseMove);
|
|
canvas.removeEventListener('mouseup', this.onMouseUp);
|
|
canvas.removeEventListener('touchstart', this.onTouchStart);
|
|
canvas.removeEventListener('touchmove', this.onTouchMove);
|
|
canvas.removeEventListener('touchend', this.onTouchEnd);
|
|
canvas.removeEventListener('touchstart', this.onCanvasTouchStart);
|
|
}
|
|
|
|
private readonly onCanvasMouseMove = (): void => {
|
|
if (this.callbacks.isEnabled() && !this.dragging) {
|
|
this.callbacks.showControls();
|
|
}
|
|
};
|
|
|
|
private readonly onCanvasTouchStart = (): void => {
|
|
if (this.callbacks.isEnabled()) {
|
|
this.callbacks.showControls();
|
|
}
|
|
};
|
|
|
|
private readonly onMouseDown = (event: MouseEvent): void => {
|
|
if (!this.callbacks.isEnabled()) return;
|
|
|
|
this.dragging = true;
|
|
this.lastMouseX = event.clientX;
|
|
this.lastMouseY = event.clientY;
|
|
this.cameraVelocity.yaw = 0;
|
|
this.cameraVelocity.pitch = 0;
|
|
this.callbacks.hideControls();
|
|
};
|
|
|
|
private readonly onMouseMove = (event: MouseEvent): void => {
|
|
if (!this.callbacks.isEnabled() || !this.dragging) return;
|
|
|
|
const deltaX = event.clientX - this.lastMouseX;
|
|
const deltaY = event.clientY - this.lastMouseY;
|
|
|
|
this.cameraVelocity.yaw = deltaX * MOUSE_SENSITIVITY;
|
|
this.cameraVelocity.pitch = deltaY * MOUSE_SENSITIVITY;
|
|
|
|
this.lastMouseX = event.clientX;
|
|
this.lastMouseY = event.clientY;
|
|
};
|
|
|
|
private readonly onMouseUp = (): void => {
|
|
if (!this.callbacks.isEnabled()) return;
|
|
|
|
this.dragging = false;
|
|
this.callbacks.showControls();
|
|
};
|
|
|
|
private readonly onTouchStart = (event: TouchEvent): void => {
|
|
if (!this.callbacks.isEnabled()) return;
|
|
|
|
event.preventDefault();
|
|
if (event.touches.length === 1) {
|
|
this.dragging = true;
|
|
this.lastTouchX = event.touches[0].clientX;
|
|
this.lastTouchY = event.touches[0].clientY;
|
|
this.cameraVelocity.yaw = 0;
|
|
this.cameraVelocity.pitch = 0;
|
|
this.callbacks.hideControls();
|
|
}
|
|
};
|
|
|
|
private readonly onTouchMove = (event: TouchEvent): void => {
|
|
if (!this.callbacks.isEnabled() || !this.dragging) return;
|
|
|
|
event.preventDefault();
|
|
if (event.touches.length === 1) {
|
|
const deltaX = event.touches[0].clientX - this.lastTouchX;
|
|
const deltaY = event.touches[0].clientY - this.lastTouchY;
|
|
|
|
this.cameraVelocity.yaw = deltaX * TOUCH_SENSITIVITY;
|
|
this.cameraVelocity.pitch = deltaY * TOUCH_SENSITIVITY;
|
|
|
|
this.lastTouchX = event.touches[0].clientX;
|
|
this.lastTouchY = event.touches[0].clientY;
|
|
}
|
|
};
|
|
|
|
private readonly onTouchEnd = (event: TouchEvent): void => {
|
|
if (!this.callbacks.isEnabled()) return;
|
|
|
|
event.preventDefault();
|
|
this.dragging = false;
|
|
this.callbacks.showControls();
|
|
};
|
|
}
|