import * as THREE from 'three'; import { state } from '../state.js'; import { SceneFeature } from './SceneFeature.js'; import sceneFeatureManager from './SceneFeatureManager.js'; const minSwitchInterval = 2; const maxSwitchInterval = 10; export class CameraManager extends SceneFeature { constructor() { super(); this.cameras = []; this.activeCameraIndex = 0; this.switchInterval = 10; // seconds this.lastSwitchTime = 0; sceneFeatureManager.register(this); } init() { // The main camera from init.js is our first camera const mainCamera = state.camera; this.cameras.push({ camera: mainCamera, type: 'dynamic', name: 'MainDynamicCamera', update: this.updateDynamicCamera, // Assign its update function }); // --- Static Camera 1: Left Aisle View --- const staticCam1 = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100); staticCam1.position.set(-5, 3, -13); staticCam1.lookAt(0, 2, -18); // Look at the stage this.cameras.push({ camera: staticCam1, type: 'static', name: 'LeftAisleCam' }); // --- Static Camera 2: Right Aisle View --- const staticCam2 = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100); staticCam2.position.set(5, 4, -12); staticCam2.lookAt(0, 1.5, -18); // Look at the stage this.cameras.push({ camera: staticCam2, type: 'static', name: 'RightAisleCam' }); // --- Static Camera 3: Far-Back view --- const staticCam3 = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100); staticCam3.position.set(0, 3, 12); staticCam3.lookAt(0, 1.5, -20); // Look at the stage this.cameras.push({ camera: staticCam3, type: 'static', name: 'BackCam' }); // --- Static Camera 3: Back view --- const staticCam4 = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100); staticCam4.position.set(0, 4, 0); staticCam4.lookAt(0, 1.5, -20); // Look at the stage this.cameras.push({ camera: staticCam4, type: 'static', name: 'BackCam' }); // --- Add Debug Helpers --- if (state.debugCamera) { this.cameras.forEach(camData => { const helper = new THREE.CameraHelper(camData.camera); state.scene.add(helper); }); } this.lastSwitchTime = state.clock.getElapsedTime(); this.switchCamera(4); } // This is the logic moved from animate.js updateDynamicCamera() { const globalTime = Date.now() * 0.0001; const lookAtTime = Date.now() * 0.0002; const baseX = 0, baseY = 3.6, baseZ = -5.0; const camAmplitude = new THREE.Vector3(1.0, 1.0, 6.0); const baseTargetX = 0, baseTargetY = 1.6, baseTargetZ = -30.0; const lookAmplitude = 8.0; const camOffsetX = Math.sin(globalTime * 3.1) * camAmplitude.x; const camOffsetY = Math.cos(globalTime * 2.5) * camAmplitude.y; const camOffsetZ = Math.cos(globalTime * 3.2) * camAmplitude.z; state.camera.position.x = baseX + camOffsetX; state.camera.position.y = baseY + camOffsetY; state.camera.position.z = baseZ + camOffsetZ; const lookOffsetX = Math.sin(lookAtTime * 1.5) * lookAmplitude; const lookOffsetZ = Math.cos(lookAtTime * 2.5) * lookAmplitude; const lookOffsetY = Math.cos(lookAtTime * 1.2) * lookAmplitude * 0.5; state.camera.lookAt(baseTargetX + lookOffsetX, baseTargetY + lookOffsetY, baseTargetZ + lookOffsetZ); } switchCamera(index) { if (index >= this.cameras.length || index < 0) return; this.activeCameraIndex = index; const newCam = this.cameras[this.activeCameraIndex].camera; // Copy properties from the new camera to the main state camera state.camera.position.copy(newCam.position); state.camera.rotation.copy(newCam.rotation); state.camera.fov = newCam.fov; state.camera.aspect = newCam.aspect; state.camera.near = newCam.near; state.camera.far = newCam.far; state.camera.updateProjectionMatrix(); } update(deltaTime) { const time = state.clock.getElapsedTime(); // Handle camera switching if (time > this.lastSwitchTime + this.switchInterval) { const newIndex = Math.floor(Math.random() * this.cameras.length); this.switchCamera(newIndex); this.lastSwitchTime = time; this.switchInterval = minSwitchInterval + Math.random() * (maxSwitchInterval - minSwitchInterval); } // Update the currently active camera if it has an update function const activeCamData = this.cameras[this.activeCameraIndex]; if (activeCamData.update) { activeCamData.update(); } } } new CameraManager();