123 lines
3.9 KiB
JavaScript
123 lines
3.9 KiB
JavaScript
|
|
// === SCENE SETUP + LIGHTING + SHADOWS + STAR FIELD + CONSTELLATION LINES ===
|
||
|
|
import * as THREE from 'three';
|
||
|
|
import { NEXUS } from './constants.js';
|
||
|
|
|
||
|
|
// === SCENE SETUP ===
|
||
|
|
export const scene = new THREE.Scene();
|
||
|
|
|
||
|
|
export const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000);
|
||
|
|
camera.position.set(0, 6, 11);
|
||
|
|
|
||
|
|
export const raycaster = new THREE.Raycaster();
|
||
|
|
export const forwardVector = new THREE.Vector3();
|
||
|
|
|
||
|
|
// === LIGHTING ===
|
||
|
|
export const ambientLight = new THREE.AmbientLight(0x0a1428, 1.4);
|
||
|
|
scene.add(ambientLight);
|
||
|
|
|
||
|
|
export const overheadLight = new THREE.SpotLight(0x8899bb, 0.6, 80, Math.PI / 3.5, 0.5, 1.0);
|
||
|
|
overheadLight.position.set(0, 25, 0);
|
||
|
|
overheadLight.target.position.set(0, 0, 0);
|
||
|
|
overheadLight.castShadow = true;
|
||
|
|
overheadLight.shadow.mapSize.set(2048, 2048);
|
||
|
|
overheadLight.shadow.camera.near = 5;
|
||
|
|
overheadLight.shadow.camera.far = 60;
|
||
|
|
overheadLight.shadow.bias = -0.001;
|
||
|
|
scene.add(overheadLight);
|
||
|
|
scene.add(overheadLight.target);
|
||
|
|
|
||
|
|
export const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
||
|
|
renderer.setClearColor(0x000000, 0);
|
||
|
|
renderer.setPixelRatio(window.devicePixelRatio);
|
||
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
||
|
|
// === SHADOW SYSTEM ===
|
||
|
|
renderer.shadowMap.enabled = true;
|
||
|
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
||
|
|
document.body.appendChild(renderer.domElement);
|
||
|
|
|
||
|
|
// === STAR FIELD ===
|
||
|
|
const STAR_COUNT = 800;
|
||
|
|
const STAR_SPREAD = 400;
|
||
|
|
const CONSTELLATION_DISTANCE = 30;
|
||
|
|
|
||
|
|
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));
|
||
|
|
|
||
|
|
export const starMaterial = new THREE.PointsMaterial({
|
||
|
|
color: NEXUS.colors.starCore,
|
||
|
|
size: 0.6,
|
||
|
|
sizeAttenuation: true,
|
||
|
|
transparent: true,
|
||
|
|
opacity: 0.9,
|
||
|
|
});
|
||
|
|
|
||
|
|
export const stars = new THREE.Points(starGeo, starMaterial);
|
||
|
|
scene.add(stars);
|
||
|
|
|
||
|
|
// Star pulse state
|
||
|
|
export const STAR_BASE_OPACITY = 0.3;
|
||
|
|
export const STAR_PEAK_OPACITY = 1.0;
|
||
|
|
export const STAR_PULSE_DECAY = 0.012;
|
||
|
|
|
||
|
|
// === CONSTELLATION LINES ===
|
||
|
|
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;
|
||
|
|
|
||
|
|
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 });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
|
||
|
|
export const constellationLines = buildConstellationLines();
|
||
|
|
scene.add(constellationLines);
|