feat: improved visualisation and x4 data import

This commit is contained in:
2026-03-18 20:58:17 -04:00
parent 358122a74a
commit f98c47a8a7
45 changed files with 32840 additions and 1482 deletions

View File

@@ -2,6 +2,14 @@ import * as THREE from "three";
import { ACTIVE_SYSTEM_DETAIL_SCALE, PROJECTED_GALAXY_RADIUS } from "./viewerConstants";
import { computeMoonLocalPosition, computePlanetLocalPosition, currentWorldTimeSeconds, scaleLocalVector } from "./viewerMath";
import type { PlanetVisual, ShipVisual, SystemVisual, WorldState } from "./viewerTypes";
import { rawObject } from "./viewerScenePrimitives";
const MIN_ICON_PIXELS = 25;
const MAX_ICON_PIXELS = 50;
export function iconWorldScale(distToCamera: number, camera: THREE.PerspectiveCamera, pixels: number): number {
return pixels * distToCamera * 2 * Math.tan((camera.fov * Math.PI / 180) / 2) / window.innerHeight;
}
export function getAnimatedShipLocalPosition(visual: ShipVisual, now = performance.now()) {
const elapsedMs = now - visual.receivedAtMs;
@@ -26,6 +34,7 @@ export function updatePlanetPresentation(
world: WorldState | undefined,
worldTimeSyncMs: number,
planetVisuals: PlanetVisual[],
systemCamera: THREE.PerspectiveCamera,
) {
const nowSeconds = currentWorldTimeSeconds(world, worldTimeSyncMs);
// In systemScene all positions use scaleLocalVector * ACTIVE_SYSTEM_DETAIL_SCALE.
@@ -34,23 +43,44 @@ export function updatePlanetPresentation(
const position = scaleLocalVector(computePlanetLocalPosition(visual.planet, nowSeconds))
.multiplyScalar(ACTIVE_SYSTEM_DETAIL_SCALE);
visual.orbit.setScaleScalar(ACTIVE_SYSTEM_DETAIL_SCALE);
visual.orbit.setPosition(new THREE.Vector3(0, 0, 0));
visual.mesh.setPosition(position);
visual.icon.setPosition(position);
const iconWorldPos = visual.icon.getWorldPosition(new THREE.Vector3());
const distToIcon = systemCamera.position.distanceTo(iconWorldPos);
const t = THREE.MathUtils.clamp(distToIcon / 300, 0, 1);
const rawScale = visual.iconBaseScale * t * Math.sqrt(t);
const planetIconScale = THREE.MathUtils.clamp(rawScale, iconWorldScale(distToIcon, systemCamera, MIN_ICON_PIXELS), iconWorldScale(distToIcon, systemCamera, MAX_ICON_PIXELS));
visual.icon.setScaleScalar(planetIconScale);
if (visual.ring) {
visual.ring.setPosition(position);
}
const distToPlanet = systemCamera.position.distanceTo(position);
const moonOrbitOpacity = THREE.MathUtils.clamp(1 - distToPlanet / 500, 0, 1) * 0.18;
const clusterVisible = distToPlanet < 300;
for (const [moonIndex, moon] of visual.moons.entries()) {
moon.orbit.setPosition(position);
moon.orbit.setScaleScalar(ACTIVE_SYSTEM_DETAIL_SCALE);
moon.mesh.setPosition(
position.clone().add(
scaleLocalVector(computeMoonLocalPosition(visual.planet, moonIndex, nowSeconds, world?.seed ?? 1))
.multiplyScalar(ACTIVE_SYSTEM_DETAIL_SCALE),
),
const moonPos = position.clone().add(
scaleLocalVector(computeMoonLocalPosition(visual.planet.moons[moonIndex], nowSeconds))
.multiplyScalar(ACTIVE_SYSTEM_DETAIL_SCALE),
);
moon.mesh.setPosition(moonPos);
moon.mesh.setVisible(clusterVisible);
moon.icon.setPosition(moonPos);
moon.icon.setVisible(clusterVisible);
if (clusterVisible) {
const iconWorldPos = moon.icon.getWorldPosition(new THREE.Vector3());
const moonDist = systemCamera.position.distanceTo(iconWorldPos);
const t = THREE.MathUtils.clamp(moonDist / 120, 0, 1);
const rawMoonScale = moon.iconBaseScale * t * Math.sqrt(t);
const moonIconScale = THREE.MathUtils.clamp(rawMoonScale, iconWorldScale(moonDist, systemCamera, MIN_ICON_PIXELS), iconWorldScale(moonDist, systemCamera, MAX_ICON_PIXELS));
moon.icon.setScaleScalar(moonIconScale);
}
moon.orbit.setPosition(position);
const orbitObj = rawObject(moon.orbit);
if (orbitObj instanceof THREE.LineLoop) {
(orbitObj.material as THREE.LineBasicMaterial).opacity = moonOrbitOpacity;
}
}
}
}