import * as THREE from "three"; import { createTank } from "./scene/Tank"; import { createWater } from "./scene/Water"; import { setupLighting } from "./scene/Lighting"; import { createFishSchool } from "./scene/Fish"; import { createParallaxCamera } from "./scene/Camera"; const canvas = document.querySelector("#aquarium"); if (!canvas) { throw new Error('Canvas element with id "aquarium" not found'); } // Renderer const renderer = new THREE.WebGLRenderer({ canvas, antialias: true }); renderer.setPixelRatio(window.devicePixelRatio); // Scene const scene = new THREE.Scene(); scene.background = new THREE.Color(0x0a0a0f); // Camera — frames the tank, positioned slightly above its center const camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 100 ); camera.position.set(0, 1.5, 6); camera.lookAt(0, 1.2, 0); // Scene content setupLighting(scene, renderer); createTank(scene); const water = createWater(scene); const fishSchool = createFishSchool(scene, 6); const parallax = createParallaxCamera(camera); // Mouse tracking (normalized to -1..1) let mouseX = 0, mouseY = 0; window.addEventListener("mousemove", (e) => { mouseX = (e.clientX / window.innerWidth) * 2 - 1; mouseY = (e.clientY / window.innerHeight) * 2 - 1; }); // Resize handling function resize(): void { const width = window.innerWidth; const height = window.innerHeight; camera.aspect = width / height; camera.updateProjectionMatrix(); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(width, height); } resize(); window.addEventListener("resize", resize); // Render loop const startTime = performance.now(); let lastFrameTime = performance.now(); function animate(): void { requestAnimationFrame(animate); const now = performance.now(); const time = (now - startTime) / 1000; const dt = Math.min((now - lastFrameTime) / 1000, 0.1); lastFrameTime = now; water.update(time); fishSchool.update(dt, time); parallax.update(mouseX, mouseY, time, dt); renderer.render(scene, camera); } animate();