diff --git a/app.js b/app.js index 051fe82..bd531d2 100644 --- a/app.js +++ b/app.js @@ -1,12 +1,161 @@ -// ... existing code ... +import * as THREE from 'three'; + +// === COLOR PALETTE === +const NEXUS = { + colors: { + bg: 0x000008, + starCore: 0xffffff, + starDim: 0x8899cc, + constellationLine: 0x334488, + constellationFade: 0x112244, + accent: 0x4488ff, + } +}; + +// === SCENE SETUP === +const scene = new THREE.Scene(); +scene.background = new THREE.Color(NEXUS.colors.bg); + +const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000); +camera.position.set(0, 0, 5); + +const renderer = new THREE.WebGLRenderer({ antialias: true }); +renderer.setPixelRatio(window.devicePixelRatio); +renderer.setSize(window.innerWidth, window.innerHeight); +document.body.appendChild(renderer.domElement); + +// === STAR FIELD === +const STAR_COUNT = 800; +const STAR_SPREAD = 400; +const CONSTELLATION_DISTANCE = 30; // max distance to draw a line between stars + +const starPositions = []; +const starGeo = new THREE.BufferGeometry(); +const posArray = new Float32Array(STAR_COUNT * 3); +const sizeArray = new Float32Array(STAR_COUNT); + +for (let i = 0; i < STAR_COUNT; i++) { + const x = (Math.random() - 0.5) * STAR_SPREAD; + const y = (Math.random() - 0.5) * STAR_SPREAD; + const z = (Math.random() - 0.5) * STAR_SPREAD; + posArray[i * 3] = x; + posArray[i * 3 + 1] = y; + posArray[i * 3 + 2] = z; + sizeArray[i] = Math.random() * 2.5 + 0.5; + starPositions.push(new THREE.Vector3(x, y, z)); +} + +starGeo.setAttribute('position', new THREE.BufferAttribute(posArray, 3)); +starGeo.setAttribute('size', new THREE.BufferAttribute(sizeArray, 1)); + +const starMaterial = new THREE.PointsMaterial({ + color: NEXUS.colors.starCore, + size: 0.6, + sizeAttenuation: true, + transparent: true, + opacity: 0.9, +}); + +const stars = new THREE.Points(starGeo, starMaterial); +scene.add(stars); + +// === CONSTELLATION LINES === +// Connect nearby stars with faint lines, limited to avoid clutter +function buildConstellationLines() { + const linePositions = []; + const MAX_CONNECTIONS_PER_STAR = 3; + const connectionCount = new Array(STAR_COUNT).fill(0); + + for (let i = 0; i < STAR_COUNT; i++) { + if (connectionCount[i] >= MAX_CONNECTIONS_PER_STAR) continue; + + // Find nearest neighbors + const neighbors = []; + for (let j = i + 1; j < STAR_COUNT; j++) { + if (connectionCount[j] >= MAX_CONNECTIONS_PER_STAR) continue; + const dist = starPositions[i].distanceTo(starPositions[j]); + if (dist < CONSTELLATION_DISTANCE) { + neighbors.push({ j, dist }); + } + } + + // Sort by distance and connect closest ones + neighbors.sort((a, b) => a.dist - b.dist); + const toConnect = neighbors.slice(0, MAX_CONNECTIONS_PER_STAR - connectionCount[i]); + + for (const { j } of toConnect) { + linePositions.push( + starPositions[i].x, starPositions[i].y, starPositions[i].z, + starPositions[j].x, starPositions[j].y, starPositions[j].z + ); + connectionCount[i]++; + connectionCount[j]++; + } + } + + const lineGeo = new THREE.BufferGeometry(); + lineGeo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(linePositions), 3)); + + const lineMat = new THREE.LineBasicMaterial({ + color: NEXUS.colors.constellationLine, + transparent: true, + opacity: 0.18, + }); + + return new THREE.LineSegments(lineGeo, lineMat); +} + +const constellationLines = buildConstellationLines(); +scene.add(constellationLines); + +// === MOUSE-DRIVEN ROTATION === +let mouseX = 0; +let mouseY = 0; +let targetRotX = 0; +let targetRotY = 0; + +document.addEventListener('mousemove', (e) => { + mouseX = (e.clientX / window.innerWidth - 0.5) * 2; + mouseY = (e.clientY / window.innerHeight - 0.5) * 2; +}); + +// === RESIZE HANDLER === +window.addEventListener('resize', () => { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); +}); + +// === ANIMATION LOOP === +const clock = new THREE.Clock(); + +function animate() { + requestAnimationFrame(animate); + const elapsed = clock.getElapsedTime(); + + // Slow auto-rotation + targetRotX += (mouseY * 0.3 - targetRotX) * 0.02; + targetRotY += (mouseX * 0.3 - targetRotY) * 0.02; + + stars.rotation.x = targetRotX + elapsed * 0.01; + stars.rotation.y = targetRotY + elapsed * 0.015; + + constellationLines.rotation.x = stars.rotation.x; + constellationLines.rotation.y = stars.rotation.y; + + // Subtle pulse on constellation opacity + constellationLines.material.opacity = 0.12 + Math.sin(elapsed * 0.5) * 0.06; + + renderer.render(scene, camera); +} + +animate(); // === WEBSOCKET CLIENT === import { wsClient } from './ws-client.js'; -// Initialize WebSocket client wsClient.connect(); -// Handle WebSocket events window.addEventListener('player-joined', (event) => { console.log('Player joined:', event.detail); }); @@ -19,9 +168,6 @@ window.addEventListener('chat-message', (event) => { console.log('Chat message:', event.detail); }); -// Clean up on page unload window.addEventListener('beforeunload', () => { wsClient.disconnect(); }); - -// ... existing code ... diff --git a/index.html b/index.html index 34af931..ed8f8bd 100644 --- a/index.html +++ b/index.html @@ -14,10 +14,17 @@ + +
- -