Phase 1 of app.js modularization (Refs #409, Fixes #420). Adds four modules under modules/core/: - theme.js: exports NEXUS with full NEXUS.theme (colors, fonts, lineWeights, glowParams, opacity). NEXUS.colors preserved as backwards-compat alias. - state.js: exports zoneIntensity (shared mutable object), state (agentCount, blockHeight, starPulseIntensity, weather), and totalActivity(). - scene.js: creates and exports scene, camera, renderer, orbitControls, raycaster, lighting. Registers camera+renderer resize handler. Intentionally does not append renderer.domElement — app.js controls DOM insertion order. - ticker.js: single requestAnimationFrame loop. Exports subscribe(), unsubscribe(), start(), stop(). app.js registers animate() via tickerSubscribe and calls tickerStart() instead of RAF directly. app.js changes: - Imports NEXUS from theme.js (removes inline NEXUS definition) - Imports scene/camera/renderer/orbitControls/raycaster from scene.js (removes duplicate creation blocks) - Imports zoneIntensity/state/totalActivity from state.js (removes local defs) - animate() is now a ticker subscriber function, not a recursive RAF caller - _activeAgentCount → state.agentCount - _starPulseIntensity → state.starPulseIntensity - lastKnownBlockHeight → state.blockHeight - EffectComposer resize listener preserved separately in app.js node --check app.js passes. No visual regressions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
73 lines
2.7 KiB
JavaScript
73 lines
2.7 KiB
JavaScript
/**
|
|
* modules/core/scene.js — Core 3D scene infrastructure.
|
|
*
|
|
* Creates and exports: THREE.Scene, camera, renderer, lighting, OrbitControls, raycaster.
|
|
* Registers the window resize handler for camera + renderer.
|
|
*
|
|
* IMPORTANT: renderer.domElement is NOT appended to document.body here.
|
|
* app.js controls DOM insertion order — the 2D matrix canvas must be inserted
|
|
* before the Three.js canvas so the Z-order (matrix behind Three.js) is correct.
|
|
*/
|
|
|
|
import * as THREE from 'three';
|
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
|
|
// === SCENE ===
|
|
export const scene = new THREE.Scene();
|
|
// Background is null — the matrix rain canvas shows through the transparent renderer.
|
|
|
|
// === CAMERA ===
|
|
export const camera = new THREE.PerspectiveCamera(
|
|
75,
|
|
window.innerWidth / window.innerHeight,
|
|
0.1,
|
|
2000
|
|
);
|
|
camera.position.set(0, 6, 11);
|
|
|
|
// === RAYCASTER ===
|
|
export const raycaster = new THREE.Raycaster();
|
|
|
|
// === LIGHTING ===
|
|
// AmbientLight provides a dark-blue fill so unlit geometry stays visible.
|
|
const ambientLight = new THREE.AmbientLight(0x0a1428, 1.4);
|
|
scene.add(ambientLight);
|
|
|
|
// SpotLight replaces PointLight: shadows need only one depth map instead of six.
|
|
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);
|
|
|
|
// === RENDERER ===
|
|
// NOTE: renderer.domElement is intentionally NOT appended here — see module docblock.
|
|
export const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
|
|
renderer.setClearColor(0x000000, 0);
|
|
renderer.setPixelRatio(window.devicePixelRatio);
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
// PCFSoftShadowMap gives smooth penumbra edges matching the holographic aesthetic.
|
|
renderer.shadowMap.enabled = true;
|
|
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
|
|
|
|
// === ORBIT CONTROLS ===
|
|
// Free-camera controls used in photo mode only (enabled = false by default).
|
|
export const orbitControls = new OrbitControls(camera, renderer.domElement);
|
|
orbitControls.enableDamping = true;
|
|
orbitControls.dampingFactor = 0.05;
|
|
orbitControls.enabled = false;
|
|
|
|
// === RESIZE HANDLER ===
|
|
// Keeps camera aspect ratio and renderer size in sync with the browser window.
|
|
// The EffectComposer (owned by app.js) must register its own resize listener.
|
|
window.addEventListener('resize', () => {
|
|
camera.aspect = window.innerWidth / window.innerHeight;
|
|
camera.updateProjectionMatrix();
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
});
|