1
0

feat: complete actor handoff and piloted aircraft presence

This commit is contained in:
2026-08-11 22:17:06 -07:00
parent 655c383061
commit 3326d2e6d0
20 changed files with 907 additions and 41 deletions
+4
View File
@@ -40,3 +40,7 @@ export {
type SceneAircraftOptions,
type SceneAircraftView,
} from "./sceneAircraft.ts";
export {
createAircraftPoseSnapshot,
type AircraftRealtimeIdentity,
} from "./realtime.ts";
+60
View File
@@ -0,0 +1,60 @@
/** Renderer-independent bridge from deterministic flight state to realtime protocol state. */
import type { AircraftPoseSnapshot } from "../realtime/types.ts";
import type { AircraftControllerSnapshot } from "./controller.ts";
export interface AircraftRealtimeIdentity {
aircraftId: string;
pilotActorId: string;
}
function identity(value: string, name: string): string {
if (typeof value !== "string" || value.length === 0 || value.length > 256) {
throw new RangeError(`${name} must be a non-empty string of at most 256 characters`);
}
return value;
}
/**
* Converts one controller snapshot into the exact geographic wire contract.
* Geographic velocity axes are east/up/north; heading zero therefore travels
* north even though the Three.js presentation model faces local -Z.
*/
export function createAircraftPoseSnapshot(
identityValue: AircraftRealtimeIdentity,
state: AircraftControllerSnapshot,
sequence: number,
timestampMs: number,
): AircraftPoseSnapshot {
const heading = state.headingDeg * Math.PI / 180;
const pitch = state.pitchDeg * Math.PI / 180;
const horizontalSpeedMps = state.speedMps * Math.cos(pitch);
return {
entity: "aircraft",
aircraftId: identity(identityValue.aircraftId, "aircraftId"),
kind: "electric-vtail",
pilotActorId: identity(identityValue.pilotActorId, "pilotActorId"),
sequence,
timestampMs,
pose: {
space: "geographic",
lat: state.lat,
lng: state.lng,
altitudeM: state.altitudeM,
headingDeg: state.headingDeg,
pitchDeg: state.pitchDeg,
},
velocity: {
xMps: Math.sin(heading) * horizontalSpeedMps,
yMps: state.verticalSpeedMps,
zMps: Math.cos(heading) * horizontalSpeedMps,
yawDegPerSec: 0,
},
rollDeg: state.rollDeg,
throttle: state.throttle,
rollInput: state.rollInput,
pitchInput: state.pitchInput,
yawInput: state.yawInput,
fanRadians: state.fanRadians,
};
}