import * as THREE from 'three'; import { state } from '../state.js'; import { updateVcrDisplay } from '../vcr-display.js'; function updateCamera() { const globalTime = Date.now() * 0.00005; const lookAtTime = Date.now() * 0.00003; const camAmplitude = 0.7; const lookAmplitude = 0.05; // Base Camera Position in front of the TV const baseX = -0.5; const baseY = 1.5; const baseZ = 2.5; // Base LookAt target (Center of the screen) const baseTargetX = -0.7; const baseTargetY = 1.7; const baseTargetZ = -0.3; // Camera Position Offsets (Drift) const camOffsetX = Math.sin(globalTime * 3.1) * camAmplitude; const camOffsetY = Math.cos(globalTime * 2.5) * camAmplitude * 0.4; const camOffsetZ = Math.cos(globalTime * 3.2) * camAmplitude * 1.4; state.camera.position.x = baseX + camOffsetX; state.camera.position.y = baseY + camOffsetY; state.camera.position.z = baseZ + camOffsetZ; // LookAt Target Offsets (Subtle Gaze Shift) const lookOffsetX = Math.sin(lookAtTime * 1.5) * lookAmplitude; const lookOffsetY = Math.cos(lookAtTime * 1.2) * lookAmplitude; // Apply lookAt to the subtly shifted target state.camera.lookAt(baseTargetX + lookOffsetX, baseTargetY + lookOffsetY, baseTargetZ); } function updateLampFlicker() { const flickerChance = 0.995; const restoreRate = 0.15; if (Math.random() > flickerChance) { // Flickers quickly to a dimmer random value (between 0.3 and 1.05) let lampLightIntensity = state.originalLampIntensity * (0.3 + Math.random() * 0.7); state.lampLightSpot.intensity = lampLightIntensity; state.lampLightPoint.intensity = lampLightIntensity; } else if (state.lampLightPoint.intensity < state.originalLampIntensity) { // Smoothly restore original intensity let lampLightIntensity = THREE.MathUtils.lerp(state.lampLightPoint.intensity, state.originalLampIntensity, restoreRate); state.lampLightSpot.intensity = lampLightIntensity; state.lampLightPoint.intensity = lampLightIntensity; } } function updateScreenLight() { if (state.isVideoLoaded && state.screenLight.intensity > 0) { const pulseTarget = state.originalScreenIntensity + (Math.random() - 0.5) * state.screenIntensityPulse; state.screenLight.intensity = THREE.MathUtils.lerp(state.screenLight.intensity, pulseTarget, 0.1); const lightTime = Date.now() * 0.0001; const radius = 0.01; const centerX = 0; const centerY = 1.5; state.screenLight.position.x = centerX + Math.cos(lightTime) * radius; state.screenLight.position.y = centerY + Math.sin(lightTime * 1.5) * radius * 0.5; // Slightly different freq for Y } } function updateVideo() { if (state.videoTexture) { state.videoTexture.needsUpdate = true; } } function updateVcr() { const currentTime = state.baseTime + state.videoElement.currentTime; if (Math.abs(currentTime - state.lastUpdateTime) > 0.1) { updateVcrDisplay(currentTime); state.lastUpdateTime = currentTime; } if (currentTime - state.lastBlinkToggleTime > 0.5) { // Blink every 0.5 seconds state.blinkState = !state.blinkState; state.lastBlinkToggleTime = currentTime; } } // --- Animation Loop --- export function animate() { requestAnimationFrame(animate); state.effectsManager.update(); updateCamera(); updateLampFlicker(); updateScreenLight(); updateVideo(); updateVcr(); // RENDER! state.renderer.render(state.scene, state.camera); } // --- Window Resize Handler --- export function onWindowResize() { state.camera.aspect = window.innerWidth / window.innerHeight; state.camera.updateProjectionMatrix(); state.renderer.setSize(window.innerWidth, window.innerHeight); }