Compare commits
1 Commits
main
...
claude/iss
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87486d2c7f |
17
js/agents.js
17
js/agents.js
@@ -90,19 +90,26 @@ class Agent {
|
||||
this.group.add(this.sprite);
|
||||
}
|
||||
|
||||
update(time) {
|
||||
update(time, nightFactor = 0) {
|
||||
const pulse = Math.sin(time * 0.002 + this.pulsePhase);
|
||||
const active = this.state === 'active';
|
||||
|
||||
const intensity = active ? 0.6 + pulse * 0.4 : 0.2 + pulse * 0.1;
|
||||
// Agents glow brighter at night
|
||||
const nightBoost = nightFactor * 0.45;
|
||||
const intensity = active ? 0.6 + pulse * 0.4 + nightBoost : 0.2 + pulse * 0.1 + nightBoost;
|
||||
this.core.material.emissiveIntensity = intensity;
|
||||
this.light.intensity = active ? 2 + pulse : 0.8 + pulse * 0.3;
|
||||
|
||||
const lightBase = active ? 2 + pulse : 0.8 + pulse * 0.3;
|
||||
this.light.intensity = lightBase * (1 + nightFactor * 0.6);
|
||||
|
||||
const scale = active ? 1 + pulse * 0.08 : 1 + pulse * 0.03;
|
||||
this.core.scale.setScalar(scale);
|
||||
this.ring.rotation.y += active ? 0.03 : 0.008;
|
||||
this.ring.material.opacity = 0.3 + pulse * 0.2;
|
||||
|
||||
// Glow shell more visible at night
|
||||
this.glow.material.opacity = 0.04 + nightFactor * 0.12 + (active ? pulse * 0.04 : 0);
|
||||
|
||||
this.group.position.y = this.position.y + Math.sin(time * 0.001 + this.pulsePhase) * 0.15;
|
||||
}
|
||||
|
||||
@@ -161,8 +168,8 @@ function buildConnectionLines() {
|
||||
}
|
||||
}
|
||||
|
||||
export function updateAgents(time) {
|
||||
agents.forEach(agent => agent.update(time));
|
||||
export function updateAgents(time, nightFactor = 0) {
|
||||
agents.forEach(agent => agent.update(time, nightFactor));
|
||||
}
|
||||
|
||||
export function getAgentCount() {
|
||||
|
||||
127
js/daynight.js
Normal file
127
js/daynight.js
Normal file
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Day/night cycle based on real UTC time.
|
||||
*
|
||||
* Sunrise ~06:00 UTC, Sunset ~18:00 UTC.
|
||||
* Drives: sun directional light, ambient light, fog color, star visibility.
|
||||
* Returns a nightFactor (0=day, 1=night) for consumers (e.g. agent glow).
|
||||
*/
|
||||
import * as THREE from 'three';
|
||||
|
||||
let sunLight = null;
|
||||
let ambientRef = null;
|
||||
let sceneRef = null;
|
||||
let starMatRef = null;
|
||||
|
||||
/** Call once during world init. */
|
||||
export function initDayNight(scene, ambientLight) {
|
||||
sceneRef = scene;
|
||||
ambientRef = ambientLight;
|
||||
|
||||
sunLight = new THREE.DirectionalLight(0xffffff, 0);
|
||||
sunLight.position.set(0, 80, 0);
|
||||
scene.add(sunLight);
|
||||
}
|
||||
|
||||
/** Called by effects.js after it creates the star PointsMaterial. */
|
||||
export function setStarMaterial(mat) {
|
||||
starMatRef = mat;
|
||||
}
|
||||
|
||||
/** Returns seconds elapsed since UTC midnight. */
|
||||
function utcSeconds() {
|
||||
const n = new Date();
|
||||
return n.getUTCHours() * 3600 + n.getUTCMinutes() * 60 + n.getUTCSeconds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sun elevation angle: 1 at UTC noon, -1 at UTC midnight.
|
||||
* Zero-crossings at 06:00 (dawn) and 18:00 (dusk).
|
||||
*/
|
||||
function sunElevation() {
|
||||
const frac = utcSeconds() / 86400; // 0–1
|
||||
return Math.sin(2 * Math.PI * (frac - 0.25));
|
||||
}
|
||||
|
||||
/**
|
||||
* Smooth step from a to b.
|
||||
* @param {number} edge0
|
||||
* @param {number} edge1
|
||||
* @param {number} x
|
||||
*/
|
||||
function smoothstep(edge0, edge1, x) {
|
||||
const t = Math.max(0, Math.min(1, (x - edge0) / (edge1 - edge0)));
|
||||
return t * t * (3 - 2 * t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all day/night-driven scene properties.
|
||||
* Called every animation frame.
|
||||
* @returns {number} nightFactor — 0 (full day) to 1 (full night)
|
||||
*/
|
||||
export function updateDayNight() {
|
||||
const elev = sunElevation();
|
||||
|
||||
// dayFactor: 1 at full day (elev > 0.15), 0 at full night (elev < -0.15)
|
||||
const dayFactor = smoothstep(-0.15, 0.15, elev);
|
||||
const nightFactor = 1 - dayFactor;
|
||||
|
||||
// ── Sun directional light ─────────────────────────────────────────────────
|
||||
if (sunLight) {
|
||||
// Arc: east (positive X) at dawn, overhead at noon, west at dusk
|
||||
const frac = utcSeconds() / 86400;
|
||||
const angle = 2 * Math.PI * (frac - 0.25);
|
||||
const sx = -Math.cos(angle) * 60;
|
||||
const sy = Math.sin(angle) * 80;
|
||||
const sz = -30;
|
||||
sunLight.position.set(sx, Math.max(sy, 0.1), sz);
|
||||
|
||||
// Color: warm orange near horizon, muted matrix-green at zenith
|
||||
const horizonBlend = 1 - Math.min(1, Math.abs(elev) / 0.4);
|
||||
const r = (0.3 + horizonBlend * 0.7) * dayFactor;
|
||||
const g = (0.6 - horizonBlend * 0.2) * dayFactor;
|
||||
const b = (0.3 - horizonBlend * 0.25) * dayFactor;
|
||||
sunLight.color.setRGB(r, g, b);
|
||||
sunLight.intensity = dayFactor * 1.2;
|
||||
}
|
||||
|
||||
// ── Ambient light ─────────────────────────────────────────────────────────
|
||||
if (ambientRef) {
|
||||
// Night: very dark greenish. Day: slightly brighter, cyan tint.
|
||||
ambientRef.intensity = 0.35 + dayFactor * 0.35;
|
||||
const ar = dayFactor * 0.04;
|
||||
const ag = 0.07 + dayFactor * 0.05;
|
||||
const ab = dayFactor * 0.06;
|
||||
ambientRef.color.setRGB(ar, ag, ab);
|
||||
}
|
||||
|
||||
// ── Fog color ─────────────────────────────────────────────────────────────
|
||||
if (sceneRef && sceneRef.fog) {
|
||||
const fr = dayFactor * 0.008;
|
||||
const fg = dayFactor * 0.016;
|
||||
const fb = dayFactor * 0.010;
|
||||
sceneRef.fog.color.setRGB(fr, fg, fb);
|
||||
}
|
||||
|
||||
// ── Stars ─────────────────────────────────────────────────────────────────
|
||||
if (starMatRef) {
|
||||
starMatRef.opacity = 0.05 + nightFactor * 0.80;
|
||||
// Night: bright green. Day: nearly invisible dark green.
|
||||
const sg = 0.05 + nightFactor * 0.55;
|
||||
starMatRef.color.setRGB(0, sg, 0);
|
||||
starMatRef.needsUpdate = true;
|
||||
}
|
||||
|
||||
return nightFactor;
|
||||
}
|
||||
|
||||
/** Remove sun light from scene and clear module refs. */
|
||||
export function disposeDayNight() {
|
||||
if (sunLight && sceneRef) {
|
||||
sceneRef.remove(sunLight);
|
||||
sunLight.dispose();
|
||||
}
|
||||
sunLight = null;
|
||||
ambientRef = null;
|
||||
sceneRef = null;
|
||||
starMatRef = null;
|
||||
}
|
||||
8
js/effects.js
vendored
8
js/effects.js
vendored
@@ -1,5 +1,6 @@
|
||||
import * as THREE from 'three';
|
||||
import { getQualityTier } from './quality.js';
|
||||
import { setStarMaterial } from './daynight.js';
|
||||
|
||||
let rainParticles;
|
||||
let rainPositions;
|
||||
@@ -70,11 +71,14 @@ function initStarfield(scene, tier) {
|
||||
color: 0x003300,
|
||||
size: 0.08,
|
||||
transparent: true,
|
||||
opacity: 0.5,
|
||||
opacity: 0.05, // daynight.js will drive this each frame
|
||||
});
|
||||
|
||||
const stars = new THREE.Points(geo, mat);
|
||||
scene.add(stars);
|
||||
|
||||
// Register with day/night system so it can control star visibility
|
||||
setStarMaterial(mat);
|
||||
}
|
||||
|
||||
export function updateEffects(_time) {
|
||||
@@ -114,4 +118,6 @@ export function disposeEffects() {
|
||||
rainVelocities = null;
|
||||
rainCount = 0;
|
||||
frameCounter = 0;
|
||||
// Clear star material ref in daynight (daynight.js handles its own cleanup)
|
||||
setStarMaterial(null);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
disposeAgents, getAgentStates, applyAgentStates,
|
||||
} from './agents.js';
|
||||
import { initEffects, updateEffects, disposeEffects } from './effects.js';
|
||||
import { updateDayNight } from './daynight.js';
|
||||
import { initUI, updateUI } from './ui.js';
|
||||
import { initInteraction, updateControls, disposeInteraction } from './interaction.js';
|
||||
import { initWebSocket, getConnectionState, getJobCount } from './websocket.js';
|
||||
@@ -73,7 +74,8 @@ function buildWorld(firstInit, stateSnapshot) {
|
||||
|
||||
updateControls();
|
||||
updateEffects(now);
|
||||
updateAgents(now);
|
||||
const nightFactor = updateDayNight();
|
||||
updateAgents(now, nightFactor);
|
||||
updateUI({
|
||||
fps: currentFps,
|
||||
agentCount: getAgentCount(),
|
||||
|
||||
10
js/world.js
10
js/world.js
@@ -1,5 +1,6 @@
|
||||
import * as THREE from 'three';
|
||||
import { getMaxPixelRatio, getQualityTier } from './quality.js';
|
||||
import { initDayNight, disposeDayNight } from './daynight.js';
|
||||
|
||||
let scene, camera, renderer;
|
||||
const _worldObjects = [];
|
||||
@@ -30,14 +31,16 @@ export function initWorld(existingCanvas) {
|
||||
document.body.prepend(renderer.domElement);
|
||||
}
|
||||
|
||||
addLights(scene);
|
||||
const { ambient } = addLights(scene);
|
||||
addGrid(scene, tier);
|
||||
|
||||
initDayNight(scene, ambient);
|
||||
|
||||
return { scene, camera, renderer };
|
||||
}
|
||||
|
||||
function addLights(scene) {
|
||||
const ambient = new THREE.AmbientLight(0x001a00, 0.6);
|
||||
const ambient = new THREE.AmbientLight(0x001a00, 0.35);
|
||||
scene.add(ambient);
|
||||
|
||||
const point = new THREE.PointLight(0x00ff41, 2, 80);
|
||||
@@ -47,6 +50,8 @@ function addLights(scene) {
|
||||
const fill = new THREE.DirectionalLight(0x003300, 0.4);
|
||||
fill.position.set(-10, 10, 10);
|
||||
scene.add(fill);
|
||||
|
||||
return { ambient };
|
||||
}
|
||||
|
||||
function addGrid(scene, tier) {
|
||||
@@ -85,6 +90,7 @@ export function disposeWorld(disposeRenderer, _scene) {
|
||||
}
|
||||
}
|
||||
_worldObjects.length = 0;
|
||||
disposeDayNight();
|
||||
disposeRenderer.dispose();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user