Refactor simulation and viewer architecture
This commit is contained in:
126
apps/viewer/src/viewerPresentation.ts
Normal file
126
apps/viewer/src/viewerPresentation.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import * as THREE from "three";
|
||||
import { ACTIVE_SYSTEM_DETAIL_SCALE, PROJECTED_GALAXY_RADIUS } from "./viewerConstants";
|
||||
import { computeMoonLocalPosition, computePlanetLocalPosition, currentWorldTimeSeconds } from "./viewerMath";
|
||||
import type { PlanetVisual, ShipVisual, SystemSummaryVisual, SystemVisual, WorldState } from "./viewerTypes";
|
||||
|
||||
export function getAnimatedShipLocalPosition(visual: ShipVisual, now = performance.now()) {
|
||||
const elapsedMs = now - visual.receivedAtMs;
|
||||
const blendT = THREE.MathUtils.clamp(elapsedMs / visual.blendDurationMs, 0, 1);
|
||||
return new THREE.Vector3().lerpVectors(visual.startPosition, visual.authoritativePosition, blendT);
|
||||
}
|
||||
|
||||
export function resolveShipHeading(visual: ShipVisual, worldPosition: THREE.Vector3, orbitYaw: number) {
|
||||
const desiredHeading = visual.targetPosition.clone().sub(worldPosition);
|
||||
if (desiredHeading.lengthSq() > 0.01) {
|
||||
return desiredHeading;
|
||||
}
|
||||
|
||||
if (visual.velocity.lengthSq() > 0.01) {
|
||||
return visual.velocity.clone();
|
||||
}
|
||||
|
||||
return new THREE.Vector3(Math.cos(orbitYaw), 0, Math.sin(orbitYaw));
|
||||
}
|
||||
|
||||
export function updatePlanetPresentation(
|
||||
world: WorldState | undefined,
|
||||
worldTimeSyncMs: number,
|
||||
activeSystemId: string | undefined,
|
||||
systemFocusLocal: THREE.Vector3,
|
||||
planetVisuals: PlanetVisual[],
|
||||
) {
|
||||
const nowSeconds = currentWorldTimeSeconds(world, worldTimeSyncMs);
|
||||
for (const visual of planetVisuals) {
|
||||
const scale = visual.systemId === activeSystemId ? ACTIVE_SYSTEM_DETAIL_SCALE : 1;
|
||||
const localPosition = computePlanetLocalPosition(visual.planet, nowSeconds);
|
||||
const orbitOffset = visual.systemId === activeSystemId
|
||||
? systemFocusLocal.clone().multiplyScalar(-scale)
|
||||
: new THREE.Vector3();
|
||||
const position = visual.systemId === activeSystemId
|
||||
? localPosition.clone().sub(systemFocusLocal).multiplyScalar(scale)
|
||||
: localPosition.multiplyScalar(scale);
|
||||
|
||||
visual.orbit.scale.setScalar(scale);
|
||||
visual.orbit.position.copy(orbitOffset);
|
||||
visual.mesh.position.copy(position);
|
||||
visual.icon.position.copy(position);
|
||||
if (visual.ring) {
|
||||
visual.ring.position.copy(position);
|
||||
}
|
||||
|
||||
for (const [moonIndex, moon] of visual.moons.entries()) {
|
||||
moon.orbit.position.copy(position);
|
||||
moon.orbit.scale.setScalar(scale);
|
||||
moon.mesh.position.copy(position).add(
|
||||
computeMoonLocalPosition(visual.planet, moonIndex, nowSeconds, world?.seed ?? 1).multiplyScalar(scale),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function updateSystemSummaryPresentation(
|
||||
systemSummaryVisuals: Map<string, SystemSummaryVisual>,
|
||||
camera: THREE.PerspectiveCamera,
|
||||
activeSystemId?: string,
|
||||
) {
|
||||
const distanceScale = activeSystemId ? 0.05 : 0.085;
|
||||
for (const [systemId, visual] of systemSummaryVisuals.entries()) {
|
||||
const worldPosition = visual.sprite.getWorldPosition(new THREE.Vector3());
|
||||
const distance = camera.position.distanceTo(worldPosition);
|
||||
const minimumScale = activeSystemId && systemId !== activeSystemId ? 1200 : 1400;
|
||||
const scale = Math.max(minimumScale, distance * distanceScale);
|
||||
visual.sprite.scale.set(scale, scale * 0.3125, 1);
|
||||
}
|
||||
}
|
||||
|
||||
export function updateSystemStarPresentation(
|
||||
systemVisuals: Map<string, SystemVisual>,
|
||||
activeSystemId: string | undefined,
|
||||
systemFocusLocal: THREE.Vector3,
|
||||
camera: THREE.PerspectiveCamera,
|
||||
setShellReticleOpacity: (sprite: THREE.Sprite, opacity: number) => void,
|
||||
) {
|
||||
const activeSystem = activeSystemId ? systemVisuals.get(activeSystemId) : undefined;
|
||||
|
||||
for (const [systemId, visual] of systemVisuals.entries()) {
|
||||
visual.root.position.copy(visual.galaxyPosition);
|
||||
visual.shellReticle.scale.setScalar(visual.shellReticleBaseScale);
|
||||
|
||||
if (!activeSystem) {
|
||||
visual.starCluster.position.set(0, 0, 0);
|
||||
visual.icon.position.set(0, 0, 0);
|
||||
visual.icon.visible = true;
|
||||
visual.shellReticle.position.set(0, 0, 0);
|
||||
visual.shellReticle.visible = false;
|
||||
setShellReticleOpacity(visual.shellReticle, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (systemId !== activeSystemId) {
|
||||
visual.starCluster.position.set(0, 0, 0);
|
||||
visual.icon.position.set(0, 0, 0);
|
||||
visual.icon.visible = false;
|
||||
visual.shellReticle.position.set(0, 0, 0);
|
||||
visual.shellReticle.visible = true;
|
||||
setShellReticleOpacity(visual.shellReticle, 1);
|
||||
const direction = visual.galaxyPosition.clone().sub(activeSystem.galaxyPosition);
|
||||
if (direction.lengthSq() > 0.0001) {
|
||||
visual.root.position.copy(
|
||||
activeSystem.galaxyPosition.clone().add(direction.normalize().multiplyScalar(PROJECTED_GALAXY_RADIUS)),
|
||||
);
|
||||
}
|
||||
const reticleWorldPosition = visual.root.getWorldPosition(new THREE.Vector3());
|
||||
const reticleDistance = camera.position.distanceTo(reticleWorldPosition);
|
||||
const reticleScale = Math.max(900, reticleDistance * 0.032);
|
||||
visual.shellReticle.scale.setScalar(reticleScale);
|
||||
continue;
|
||||
}
|
||||
|
||||
const offset = systemFocusLocal.clone().multiplyScalar(-ACTIVE_SYSTEM_DETAIL_SCALE);
|
||||
visual.starCluster.position.copy(offset);
|
||||
visual.icon.position.copy(offset);
|
||||
visual.icon.visible = true;
|
||||
visual.shellReticle.visible = false;
|
||||
setShellReticleOpacity(visual.shellReticle, 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user