California gets roads, traffic, and a car to follow
This commit is contained in:
+61
-4
@@ -28,7 +28,7 @@
|
||||
*/
|
||||
|
||||
import * as THREE from "three";
|
||||
import { createBlocks, createLandmarks } from "./blocks.ts";
|
||||
import { createBlocks, createLandmarks, type BuildingReservation } from "./blocks.ts";
|
||||
import { createNightLights, type NightLights } from "./nightlights.ts";
|
||||
import { createFlightLayer, type FlightLayer } from "./flights.ts";
|
||||
import { createCloudLayer, type CloudLayer } from "./clouds.ts";
|
||||
@@ -41,7 +41,17 @@ import {
|
||||
type SatelliteLayer,
|
||||
} from "./satellites.ts";
|
||||
import { createSceneKit, type Pose } from "./scenekit.ts";
|
||||
import {
|
||||
createRoadTrafficLayer,
|
||||
type RoadTrafficLayer,
|
||||
type RoadTrafficOptions,
|
||||
type VehicleCameraMode,
|
||||
} from "./roadTraffic.ts";
|
||||
import type { Stage, StageScene } from "./stage.ts";
|
||||
import type {
|
||||
VehicleActionSnapshot,
|
||||
VehicleControllerState,
|
||||
} from "../transport/vehicleController.ts";
|
||||
import { createBridges, createRoads } from "./structures.ts";
|
||||
import { createShorePlates, createTerrain, createWater, paletteFor } from "./terrain.ts";
|
||||
import type {
|
||||
@@ -58,6 +68,17 @@ import { World, type FieldProgress } from "./world.ts";
|
||||
export interface SceneOptions {
|
||||
city: City;
|
||||
markerPalette?: MarkerPalette;
|
||||
/**
|
||||
* Markers available at construction time.
|
||||
*
|
||||
* Building glyphs in this first set reserve their footprints in the
|
||||
* anonymous block scatter. Later `setMarkers()` calls remain cheap and do
|
||||
* not rebuild a city, so callers should put stable destinations here and use
|
||||
* updates for genuinely live marker feeds.
|
||||
*/
|
||||
markers?: Marker[];
|
||||
/** Optional deterministic road traffic for a state/corridor-scale board. */
|
||||
roadTraffic?: RoadTrafficOptions;
|
||||
flights?: FlightSource;
|
||||
/**
|
||||
* Element sets to propagate, if this deployment has any.
|
||||
@@ -148,6 +169,12 @@ export interface SceneHandle {
|
||||
flyTo(chapterId: string): void;
|
||||
current(): string;
|
||||
onChapterChange(fn: (id: string) => void): void;
|
||||
/** Device-neutral input for the corridor hero; a no-op on boards without one. */
|
||||
setVehicleActions(actions: Partial<VehicleActionSnapshot>): void;
|
||||
/** Current playable corridor state, or null on a city-scale board. */
|
||||
vehicleState(): Readonly<VehicleControllerState> | null;
|
||||
setVehicleCamera(mode: VehicleCameraMode): void;
|
||||
vehicleCamera(): VehicleCameraMode | null;
|
||||
setMarkers(markers: Marker[]): void;
|
||||
/** Take this city off the stage and release everything it built. */
|
||||
dispose(): void;
|
||||
@@ -283,9 +310,19 @@ export async function createScene(
|
||||
scene.add(createShorePlates(world));
|
||||
scene.add(createTerrain(world));
|
||||
scene.add(createRoads(world));
|
||||
const blocks = createBlocks(world);
|
||||
const buildingReservations: BuildingReservation[] = [];
|
||||
for (const marker of options.markers ?? []) {
|
||||
const glyph = marker.glyph;
|
||||
if (marker.located === false || glyph?.kind !== "building") continue;
|
||||
const [x, z] = world.project(marker.lat, marker.lng);
|
||||
// Five metres of breathing room keeps an anonymous wall from sitting
|
||||
// exactly on the authored façade after both reservation circles touch.
|
||||
const radius = (Math.hypot(glyph.width, glyph.depth) / 2 + 5) / world.metresPerUnit;
|
||||
buildingReservations.push({ x, z, radius });
|
||||
}
|
||||
const blocks = createBlocks(world, buildingReservations);
|
||||
scene.add(blocks);
|
||||
scene.add(createLandmarks(world));
|
||||
scene.add(createLandmarks(world, buildingReservations));
|
||||
scene.add(createBridges(world));
|
||||
|
||||
/**
|
||||
@@ -300,8 +337,14 @@ export async function createScene(
|
||||
scene.add(clouds.group);
|
||||
|
||||
const markerLayer: MarkerLayer = createMarkerLayer(world, options.markerPalette ?? {});
|
||||
markerLayer.setMarkers(options.markers ?? []);
|
||||
scene.add(markerLayer.group);
|
||||
|
||||
const roadTraffic: RoadTrafficLayer | null = options.roadTraffic
|
||||
? createRoadTrafficLayer(world, kit.camera, kit.controls, options.roadTraffic)
|
||||
: null;
|
||||
if (roadTraffic) scene.add(roadTraffic.group);
|
||||
|
||||
let flightLayer: FlightLayer | null = null;
|
||||
let flightTimer = 0;
|
||||
if (options.flights) {
|
||||
@@ -365,7 +408,15 @@ export async function createScene(
|
||||
function flyTo(chapterId: string) {
|
||||
const ch = chapterById[chapterId];
|
||||
if (!ch) return;
|
||||
kit.flyTo(chapterPose(ch));
|
||||
const route = options.roadTraffic?.pack.routes.find((candidate) => candidate.id === chapterId);
|
||||
if (route && roadTraffic) {
|
||||
roadTraffic.setRoute(route.id);
|
||||
roadTraffic.setFollowing(true);
|
||||
} else {
|
||||
roadTraffic?.setFollowing(false);
|
||||
roadTraffic?.setVehicleActions({});
|
||||
kit.flyTo(chapterPose(ch));
|
||||
}
|
||||
if (currentChapter !== chapterId) {
|
||||
currentChapter = chapterId;
|
||||
for (const fn of chapterListeners) fn(chapterId);
|
||||
@@ -396,6 +447,7 @@ export async function createScene(
|
||||
onExit: () => kit.resetPick(),
|
||||
tick(dt) {
|
||||
kit.tick(dt);
|
||||
roadTraffic?.tick(dt);
|
||||
clouds.tick(dt);
|
||||
if (options.flights && flightLayer) {
|
||||
flightTimer -= dt;
|
||||
@@ -444,6 +496,7 @@ export async function createScene(
|
||||
clouds.dispose();
|
||||
nightLights.dispose();
|
||||
markerLayer.dispose();
|
||||
roadTraffic?.dispose();
|
||||
kit.dispose();
|
||||
scene.traverse((obj) => {
|
||||
const mesh = obj as THREE.Mesh;
|
||||
@@ -481,6 +534,10 @@ export async function createScene(
|
||||
onChapterChange(fn) {
|
||||
chapterListeners.push(fn);
|
||||
},
|
||||
setVehicleActions: (actions) => roadTraffic?.setVehicleActions(actions),
|
||||
vehicleState: () => roadTraffic?.hero() ?? null,
|
||||
setVehicleCamera: (mode) => roadTraffic?.setCameraMode(mode),
|
||||
vehicleCamera: () => roadTraffic?.cameraMode() ?? null,
|
||||
setMarkers(markers) {
|
||||
markerLayer.setMarkers(markers);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user