Integrate runtime schedule synchronization into editor simulation controller
This commit is contained in:
@@ -9,18 +9,17 @@ import {
|
|||||||
type RuntimeClockState
|
type RuntimeClockState
|
||||||
} from "./runtime-project-time";
|
} from "./runtime-project-time";
|
||||||
import {
|
import {
|
||||||
applyRuntimeProjectScheduleToControlState,
|
|
||||||
resolveRuntimeProjectScheduleState
|
|
||||||
} from "./runtime-project-scheduler";
|
|
||||||
import {
|
|
||||||
applyActorScheduleStateToNpcDefinition,
|
|
||||||
buildRuntimeNpcCollider,
|
|
||||||
buildRuntimeSceneFromDocument,
|
buildRuntimeSceneFromDocument,
|
||||||
createRuntimeNpcFromDefinition,
|
|
||||||
type BuildRuntimeSceneOptions,
|
type BuildRuntimeSceneOptions,
|
||||||
type RuntimeSceneDefinition
|
type RuntimeSceneDefinition
|
||||||
} from "./runtime-scene-build";
|
} from "./runtime-scene-build";
|
||||||
import { applyResolvedControlStateToRuntimeScene } from "./runtime-scene-editor-simulation";
|
import { applyResolvedControlStateToRuntimeScene } from "./runtime-scene-editor-simulation";
|
||||||
|
import {
|
||||||
|
commitRuntimeScheduleSyncResult,
|
||||||
|
createRuntimeScheduleSyncContext,
|
||||||
|
syncRuntimeSceneScheduleToClock,
|
||||||
|
type RuntimeScheduleSyncContext
|
||||||
|
} from "./runtime-schedule-sync";
|
||||||
|
|
||||||
const DEFAULT_EDITOR_SIMULATION_UI_SNAPSHOT_INTERVAL_SECONDS = 1 / 12;
|
const DEFAULT_EDITOR_SIMULATION_UI_SNAPSHOT_INTERVAL_SECONDS = 1 / 12;
|
||||||
const MAX_EDITOR_SIMULATION_FRAME_DT_SECONDS = 0.25;
|
const MAX_EDITOR_SIMULATION_FRAME_DT_SECONDS = 0.25;
|
||||||
@@ -69,59 +68,20 @@ function getErrorMessage(error: unknown): string {
|
|||||||
return error instanceof Error ? error.message : "Editor simulation failed.";
|
return error instanceof Error ? error.message : "Editor simulation failed.";
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRuntimePathLookup(
|
|
||||||
runtimeScene: RuntimeSceneDefinition
|
|
||||||
): ReadonlyMap<string, RuntimeSceneDefinition["paths"][number]> {
|
|
||||||
return new Map(runtimeScene.paths.map((path) => [path.id, path]));
|
|
||||||
}
|
|
||||||
|
|
||||||
function isNonNull<TValue>(value: TValue | null): value is TValue {
|
|
||||||
return value !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function syncRuntimeSceneToClock(
|
export function syncRuntimeSceneToClock(
|
||||||
runtimeScene: RuntimeSceneDefinition,
|
runtimeScene: RuntimeSceneDefinition,
|
||||||
clock: RuntimeClockState
|
clock: RuntimeClockState,
|
||||||
|
context: RuntimeScheduleSyncContext = createRuntimeScheduleSyncContext(
|
||||||
|
runtimeScene
|
||||||
|
)
|
||||||
): RuntimeSceneDefinition {
|
): RuntimeSceneDefinition {
|
||||||
const nextResolvedScheduler = resolveRuntimeProjectScheduleState({
|
const syncResult = syncRuntimeSceneScheduleToClock({
|
||||||
scheduler: runtimeScene.scheduler.document,
|
runtimeScene,
|
||||||
sequences: runtimeScene.sequences,
|
clock,
|
||||||
actorIds: runtimeScene.npcDefinitions.map((npc) => npc.actorId),
|
context
|
||||||
dayNumber: clock.dayCount + 1,
|
|
||||||
timeOfDayHours: clock.timeOfDayHours,
|
|
||||||
pathsById: createRuntimePathLookup(runtimeScene)
|
|
||||||
});
|
});
|
||||||
const actorStates = new Map(
|
|
||||||
nextResolvedScheduler.actors.map((actorState) => [
|
|
||||||
actorState.actorId,
|
|
||||||
actorState
|
|
||||||
])
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const npc of runtimeScene.npcDefinitions) {
|
|
||||||
applyActorScheduleStateToNpcDefinition(
|
|
||||||
npc,
|
|
||||||
actorStates.get(npc.actorId) ?? null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
runtimeScene.entities.npcs = runtimeScene.npcDefinitions
|
|
||||||
.filter((npc) => npc.active)
|
|
||||||
.map((npc) => createRuntimeNpcFromDefinition(npc));
|
|
||||||
runtimeScene.colliders = [
|
|
||||||
...runtimeScene.staticColliders,
|
|
||||||
...runtimeScene.entities.npcs
|
|
||||||
.map((npc) => buildRuntimeNpcCollider(npc))
|
|
||||||
.filter(isNonNull)
|
|
||||||
];
|
|
||||||
|
|
||||||
runtimeScene.scheduler.resolved = nextResolvedScheduler;
|
|
||||||
runtimeScene.control.resolved = applyRuntimeProjectScheduleToControlState(
|
|
||||||
runtimeScene.control.resolved,
|
|
||||||
nextResolvedScheduler,
|
|
||||||
runtimeScene.control.baselineResolved
|
|
||||||
);
|
|
||||||
|
|
||||||
|
commitRuntimeScheduleSyncResult(runtimeScene, syncResult);
|
||||||
return applyResolvedControlStateToRuntimeScene(runtimeScene);
|
return applyResolvedControlStateToRuntimeScene(runtimeScene);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,6 +107,7 @@ export class EditorSimulationController {
|
|||||||
private document: SceneDocument | null = null;
|
private document: SceneDocument | null = null;
|
||||||
private loadedModelAssets: Record<string, LoadedModelAsset> | null = null;
|
private loadedModelAssets: Record<string, LoadedModelAsset> | null = null;
|
||||||
private runtimeScene: RuntimeSceneDefinition | null = null;
|
private runtimeScene: RuntimeSceneDefinition | null = null;
|
||||||
|
private runtimeScheduleSyncContext: RuntimeScheduleSyncContext | null = null;
|
||||||
private currentClock: RuntimeClockState | null = null;
|
private currentClock: RuntimeClockState | null = null;
|
||||||
private clockOverride: RuntimeClockState | null = null;
|
private clockOverride: RuntimeClockState | null = null;
|
||||||
private playing = false;
|
private playing = false;
|
||||||
@@ -355,6 +316,7 @@ export class EditorSimulationController {
|
|||||||
this.currentClock === null
|
this.currentClock === null
|
||||||
) {
|
) {
|
||||||
this.runtimeScene = null;
|
this.runtimeScene = null;
|
||||||
|
this.runtimeScheduleSyncContext = null;
|
||||||
this.message = null;
|
this.message = null;
|
||||||
this.emitFrame();
|
this.emitFrame();
|
||||||
this.publishUiSnapshot(true);
|
this.publishUiSnapshot(true);
|
||||||
@@ -362,16 +324,22 @@ export class EditorSimulationController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.runtimeScene = syncRuntimeSceneToClock(
|
const runtimeScene = this.buildScene(this.document, {
|
||||||
this.buildScene(this.document, {
|
|
||||||
loadedModelAssets: this.loadedModelAssets,
|
loadedModelAssets: this.loadedModelAssets,
|
||||||
runtimeClock: this.currentClock
|
runtimeClock: this.currentClock
|
||||||
}),
|
});
|
||||||
this.currentClock
|
const syncContext = createRuntimeScheduleSyncContext(runtimeScene);
|
||||||
|
|
||||||
|
this.runtimeScene = syncRuntimeSceneToClock(
|
||||||
|
runtimeScene,
|
||||||
|
this.currentClock,
|
||||||
|
syncContext
|
||||||
);
|
);
|
||||||
|
this.runtimeScheduleSyncContext = syncContext;
|
||||||
this.message = null;
|
this.message = null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.runtimeScene = null;
|
this.runtimeScene = null;
|
||||||
|
this.runtimeScheduleSyncContext = null;
|
||||||
this.message = getErrorMessage(error);
|
this.message = getErrorMessage(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,10 +354,18 @@ export class EditorSimulationController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
syncRuntimeSceneToClock(this.runtimeScene, this.currentClock);
|
this.runtimeScheduleSyncContext ??= createRuntimeScheduleSyncContext(
|
||||||
|
this.runtimeScene
|
||||||
|
);
|
||||||
|
syncRuntimeSceneToClock(
|
||||||
|
this.runtimeScene,
|
||||||
|
this.currentClock,
|
||||||
|
this.runtimeScheduleSyncContext
|
||||||
|
);
|
||||||
this.message = null;
|
this.message = null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.runtimeScene = null;
|
this.runtimeScene = null;
|
||||||
|
this.runtimeScheduleSyncContext = null;
|
||||||
this.sceneVersion += 1;
|
this.sceneVersion += 1;
|
||||||
this.message = getErrorMessage(error);
|
this.message = getErrorMessage(error);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user