2026-04-11 03:46:30 +02:00
|
|
|
import { createOpaqueId } from "../core/ids";
|
2026-04-11 14:25:01 +02:00
|
|
|
import type { WhiteboxSelectionMode } from "../core/whitebox-selection-mode";
|
2026-03-31 02:02:50 +02:00
|
|
|
import type { Brush } from "./brushes";
|
2026-03-31 17:31:48 +02:00
|
|
|
import type { ModelInstance } from "../assets/model-instances";
|
|
|
|
|
import type { ProjectAssetRecord } from "../assets/project-assets";
|
2026-03-31 03:00:38 +02:00
|
|
|
import type { EntityInstance } from "../entities/entity-instances";
|
2026-03-31 06:15:41 +02:00
|
|
|
import type { InteractionLink } from "../interactions/interaction-links";
|
2026-04-11 04:19:50 +02:00
|
|
|
import {
|
|
|
|
|
cloneMaterialRegistry,
|
|
|
|
|
createStarterMaterialRegistry,
|
|
|
|
|
type MaterialDef
|
|
|
|
|
} from "../materials/starter-material-library";
|
|
|
|
|
import {
|
|
|
|
|
createDefaultWorldSettings,
|
|
|
|
|
type WorldSettings
|
|
|
|
|
} from "./world-settings";
|
2026-04-12 04:29:52 +02:00
|
|
|
import {
|
|
|
|
|
createDefaultProjectTimeSettings,
|
|
|
|
|
type ProjectTimeSettings
|
|
|
|
|
} from "./project-time-settings";
|
2026-04-13 21:19:30 +02:00
|
|
|
import { type ScenePath } from "./paths";
|
2026-04-14 01:53:02 +02:00
|
|
|
import {
|
|
|
|
|
createEmptyProjectScheduler,
|
|
|
|
|
type ProjectScheduler
|
|
|
|
|
} from "../scheduler/project-scheduler";
|
2026-04-14 19:53:31 +02:00
|
|
|
import {
|
|
|
|
|
createEmptyProjectDialogueLibrary,
|
|
|
|
|
type ProjectDialogueLibrary
|
|
|
|
|
} from "../dialogues/project-dialogues";
|
2026-04-14 23:32:27 +02:00
|
|
|
import {
|
|
|
|
|
createEmptyProjectSequenceLibrary,
|
|
|
|
|
type ProjectSequenceLibrary
|
|
|
|
|
} from "../sequencer/project-sequences";
|
2026-03-31 01:29:35 +02:00
|
|
|
|
2026-04-15 01:08:53 +02:00
|
|
|
export const SCENE_DOCUMENT_VERSION = 56 as const;
|
|
|
|
|
export const PROJECT_SEQUENCE_EFFECTS_SCENE_DOCUMENT_VERSION = 56 as const;
|
2026-04-15 00:46:45 +02:00
|
|
|
export const PROJECT_SEQUENCE_TIMING_SCENE_DOCUMENT_VERSION = 55 as const;
|
2026-04-15 00:18:24 +02:00
|
|
|
export const PROJECT_SEQUENCE_CLIPS_SCENE_DOCUMENT_VERSION = 54 as const;
|
2026-04-14 23:32:27 +02:00
|
|
|
export const PROJECT_SEQUENCE_LIBRARY_SCENE_DOCUMENT_VERSION = 53 as const;
|
2026-04-14 22:39:04 +02:00
|
|
|
export const PLAYER_START_PAUSE_BINDINGS_SCENE_DOCUMENT_VERSION = 52 as const;
|
2026-04-14 20:32:05 +02:00
|
|
|
export const NPC_DIALOGUE_REFERENCE_SCENE_DOCUMENT_VERSION = 51 as const;
|
2026-04-14 19:53:31 +02:00
|
|
|
export const PROJECT_DIALOGUE_LIBRARY_SCENE_DOCUMENT_VERSION = 50 as const;
|
2026-04-14 13:41:31 +02:00
|
|
|
export const SCHEDULER_ACTOR_ROUTINE_EFFECTS_SCENE_DOCUMENT_VERSION = 49 as const;
|
2026-04-14 03:05:39 +02:00
|
|
|
export const SCHEDULER_CONTROL_EFFECTS_SCENE_DOCUMENT_VERSION = 48 as const;
|
2026-04-14 02:40:09 +02:00
|
|
|
export const EXPANDED_CONTROL_SURFACE_SCENE_DOCUMENT_VERSION = 47 as const;
|
2026-04-14 01:53:02 +02:00
|
|
|
export const PROJECT_SCHEDULER_FOUNDATION_SCENE_DOCUMENT_VERSION = 46 as const;
|
2026-04-14 01:33:18 +02:00
|
|
|
export const CONTROL_SURFACE_FOUNDATION_SCENE_DOCUMENT_VERSION = 45 as const;
|
2026-04-13 23:48:16 +02:00
|
|
|
export const NPC_PRESENCE_SCENE_DOCUMENT_VERSION = 44 as const;
|
2026-04-13 21:19:30 +02:00
|
|
|
export const PATH_FOUNDATION_SCENE_DOCUMENT_VERSION = 43 as const;
|
2026-04-13 17:10:39 +02:00
|
|
|
export const NPC_COLLIDER_SCENE_DOCUMENT_VERSION = 42 as const;
|
2026-04-13 16:17:31 +02:00
|
|
|
export const NPC_ENTITY_FOUNDATION_SCENE_DOCUMENT_VERSION = 41 as const;
|
2026-04-13 14:43:48 +02:00
|
|
|
export const WORLD_TIME_ENVIRONMENT_SCENE_DOCUMENT_VERSION = 40 as const;
|
2026-04-12 14:31:58 +02:00
|
|
|
export const PROJECT_TIME_NIGHT_BACKGROUND_SCENE_DOCUMENT_VERSION = 39 as const;
|
2026-04-14 01:40:15 +02:00
|
|
|
export const PROJECT_TIME_DAY_NIGHT_PROFILE_SCENE_DOCUMENT_VERSION =
|
|
|
|
|
38 as const;
|
2026-04-12 04:29:52 +02:00
|
|
|
export const PROJECT_TIME_SYSTEM_SCENE_DOCUMENT_VERSION = 37 as const;
|
2026-04-12 03:33:13 +02:00
|
|
|
export const AUTHORED_OBJECT_STATE_SCENE_DOCUMENT_VERSION = 36 as const;
|
2026-04-12 02:18:53 +02:00
|
|
|
export const PLAYER_START_AIR_DIRECTION_CONTROL_SCENE_DOCUMENT_VERSION =
|
|
|
|
|
35 as const;
|
2026-04-12 02:06:00 +02:00
|
|
|
export const PLAYER_START_AIR_CONTROL_SCENE_DOCUMENT_VERSION = 34 as const;
|
2026-04-12 01:03:50 +02:00
|
|
|
export const WHITEBOX_BEVEL_SCENE_DOCUMENT_VERSION = 32 as const;
|
2026-04-11 18:23:00 +02:00
|
|
|
export const PLAYER_START_LOCOMOTION_CORE_SCENE_DOCUMENT_VERSION = 32 as const;
|
2026-04-11 17:58:47 +02:00
|
|
|
export const PLAYER_START_MOVEMENT_TEMPLATE_SCENE_DOCUMENT_VERSION =
|
|
|
|
|
31 as const;
|
2026-04-11 16:30:55 +02:00
|
|
|
export const STATIC_SIMPLE_MODEL_COLLIDERS_SCENE_DOCUMENT_VERSION = 30 as const;
|
2026-04-11 14:25:01 +02:00
|
|
|
export const SCENE_EDITOR_PREFERENCES_SCENE_DOCUMENT_VERSION = 29 as const;
|
2026-04-11 13:24:58 +02:00
|
|
|
export const PROJECT_NAME_SCENE_DOCUMENT_VERSION = 28 as const;
|
2026-04-14 01:40:15 +02:00
|
|
|
export const PLAYER_START_GAMEPAD_CAMERA_LOOK_SCENE_DOCUMENT_VERSION =
|
|
|
|
|
27 as const;
|
2026-04-11 12:12:48 +02:00
|
|
|
export const PLAYER_START_INPUT_BINDINGS_SCENE_DOCUMENT_VERSION = 26 as const;
|
2026-04-11 11:14:03 +02:00
|
|
|
export const PLAYER_START_NAVIGATION_MODE_SCENE_DOCUMENT_VERSION = 25 as const;
|
2026-04-11 04:29:16 +02:00
|
|
|
export const SCENE_TRANSITION_ENTITIES_SCENE_DOCUMENT_VERSION = 24 as const;
|
2026-04-11 03:46:30 +02:00
|
|
|
export const MULTI_SCENE_FOUNDATION_SCENE_DOCUMENT_VERSION = 22 as const;
|
2026-04-07 07:10:29 +02:00
|
|
|
export const WATER_SURFACE_DISPLACEMENT_SCENE_DOCUMENT_VERSION = 21 as const;
|
2026-04-06 08:18:03 +02:00
|
|
|
export const WHITEBOX_BOX_VOLUME_SCENE_DOCUMENT_VERSION = 20 as const;
|
2026-04-05 02:27:13 +02:00
|
|
|
export const WHITEBOX_GEOMETRY_SCENE_DOCUMENT_VERSION = 19 as const;
|
2026-04-04 19:25:43 +02:00
|
|
|
export const WHITEBOX_FLOAT_TRANSFORM_SCENE_DOCUMENT_VERSION = 18 as const;
|
2026-04-11 04:19:50 +02:00
|
|
|
export const PLAYER_START_COLLIDER_SETTINGS_SCENE_DOCUMENT_VERSION =
|
|
|
|
|
17 as const;
|
2026-04-04 07:51:38 +02:00
|
|
|
export const IMPORTED_MODEL_COLLIDERS_SCENE_DOCUMENT_VERSION = 16 as const;
|
2026-04-03 01:02:53 +02:00
|
|
|
export const ENTITY_NAMES_SCENE_DOCUMENT_VERSION = 15 as const;
|
2026-04-02 19:36:28 +02:00
|
|
|
export const SPATIAL_AUDIO_SCENE_DOCUMENT_VERSION = 13 as const;
|
2026-04-01 00:09:49 +02:00
|
|
|
export const ANIMATION_PLAYBACK_SCENE_DOCUMENT_VERSION = 12 as const;
|
2026-03-31 19:57:31 +02:00
|
|
|
export const LOCAL_LIGHTS_AND_SKYBOX_SCENE_DOCUMENT_VERSION = 10 as const;
|
2026-03-31 17:31:48 +02:00
|
|
|
export const MODEL_ASSET_PIPELINE_SCENE_DOCUMENT_VERSION = 9 as const;
|
2026-03-31 02:02:50 +02:00
|
|
|
export const FOUNDATION_SCENE_DOCUMENT_VERSION = 1 as const;
|
2026-03-31 02:33:18 +02:00
|
|
|
export const BOX_BRUSH_SCENE_DOCUMENT_VERSION = 2 as const;
|
2026-03-31 03:00:38 +02:00
|
|
|
export const FACE_MATERIALS_SCENE_DOCUMENT_VERSION = 3 as const;
|
2026-03-31 04:22:46 +02:00
|
|
|
export const RUNNER_V1_SCENE_DOCUMENT_VERSION = 4 as const;
|
2026-03-31 05:09:25 +02:00
|
|
|
export const FIRST_ROOM_POLISH_SCENE_DOCUMENT_VERSION = 5 as const;
|
2026-03-31 05:50:35 +02:00
|
|
|
export const WORLD_ENVIRONMENT_SCENE_DOCUMENT_VERSION = 6 as const;
|
2026-03-31 06:15:41 +02:00
|
|
|
export const ENTITY_SYSTEM_FOUNDATION_SCENE_DOCUMENT_VERSION = 7 as const;
|
2026-04-11 04:19:50 +02:00
|
|
|
export const TRIGGER_ACTION_TARGET_FOUNDATION_SCENE_DOCUMENT_VERSION =
|
|
|
|
|
8 as const;
|
2026-04-11 04:07:43 +02:00
|
|
|
export const RUNNER_LOADING_SCREEN_SCENE_DOCUMENT_VERSION = 23 as const;
|
2026-03-31 01:29:35 +02:00
|
|
|
|
2026-04-11 13:24:58 +02:00
|
|
|
export const DEFAULT_PROJECT_NAME = "Untitled Project" as const;
|
2026-04-11 03:46:30 +02:00
|
|
|
export const DEFAULT_PROJECT_SCENE_ID = "scene-main" as const;
|
2026-04-11 14:25:01 +02:00
|
|
|
export const DEFAULT_SCENE_EDITOR_SNAP_STEP = 1 as const;
|
|
|
|
|
|
|
|
|
|
export type SceneEditorViewportLayoutMode = "single" | "quad";
|
|
|
|
|
export type SceneEditorViewportPanelId =
|
|
|
|
|
| "topLeft"
|
|
|
|
|
| "topRight"
|
|
|
|
|
| "bottomLeft"
|
|
|
|
|
| "bottomRight";
|
|
|
|
|
export type SceneEditorViewportViewMode =
|
|
|
|
|
| "perspective"
|
|
|
|
|
| "top"
|
|
|
|
|
| "front"
|
|
|
|
|
| "side";
|
|
|
|
|
export type SceneEditorViewportDisplayMode =
|
|
|
|
|
| "normal"
|
|
|
|
|
| "authoring"
|
|
|
|
|
| "wireframe";
|
|
|
|
|
|
|
|
|
|
export interface SceneEditorPanelPreferences {
|
|
|
|
|
viewMode: SceneEditorViewportViewMode;
|
|
|
|
|
displayMode: SceneEditorViewportDisplayMode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface SceneEditorPreferences {
|
|
|
|
|
whiteboxSelectionMode: WhiteboxSelectionMode;
|
|
|
|
|
whiteboxSnapEnabled: boolean;
|
|
|
|
|
whiteboxSnapStep: number;
|
|
|
|
|
viewportGridVisible: boolean;
|
|
|
|
|
viewportLayoutMode: SceneEditorViewportLayoutMode;
|
|
|
|
|
activeViewportPanelId: SceneEditorViewportPanelId;
|
|
|
|
|
viewportQuadSplit: {
|
|
|
|
|
x: number;
|
|
|
|
|
y: number;
|
|
|
|
|
};
|
2026-04-14 01:40:15 +02:00
|
|
|
viewportPanels: Record<
|
|
|
|
|
SceneEditorViewportPanelId,
|
|
|
|
|
SceneEditorPanelPreferences
|
|
|
|
|
>;
|
2026-04-11 14:25:01 +02:00
|
|
|
}
|
2026-04-11 03:46:30 +02:00
|
|
|
|
2026-04-11 04:07:43 +02:00
|
|
|
export interface SceneLoadingScreenSettings {
|
|
|
|
|
colorHex: string;
|
|
|
|
|
headline: string | null;
|
|
|
|
|
description: string | null;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 03:46:30 +02:00
|
|
|
export interface ProjectScene {
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
2026-04-11 04:07:43 +02:00
|
|
|
loadingScreen: SceneLoadingScreenSettings;
|
2026-04-11 14:25:01 +02:00
|
|
|
editorPreferences: SceneEditorPreferences;
|
2026-04-11 03:46:30 +02:00
|
|
|
world: WorldSettings;
|
|
|
|
|
brushes: Record<string, Brush>;
|
2026-04-13 21:19:30 +02:00
|
|
|
paths: Record<string, ScenePath>;
|
2026-04-11 03:46:30 +02:00
|
|
|
modelInstances: Record<string, ModelInstance>;
|
|
|
|
|
entities: Record<string, EntityInstance>;
|
|
|
|
|
interactionLinks: Record<string, InteractionLink>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ProjectDocument {
|
|
|
|
|
version: typeof SCENE_DOCUMENT_VERSION;
|
2026-04-11 13:24:58 +02:00
|
|
|
name: string;
|
2026-04-12 04:29:52 +02:00
|
|
|
time: ProjectTimeSettings;
|
2026-04-14 01:53:02 +02:00
|
|
|
scheduler: ProjectScheduler;
|
2026-04-14 19:53:31 +02:00
|
|
|
dialogues: ProjectDialogueLibrary;
|
2026-04-14 23:32:27 +02:00
|
|
|
sequences: ProjectSequenceLibrary;
|
2026-04-11 03:46:30 +02:00
|
|
|
activeSceneId: string;
|
|
|
|
|
scenes: Record<string, ProjectScene>;
|
|
|
|
|
materials: Record<string, MaterialDef>;
|
|
|
|
|
textures: Record<string, never>;
|
|
|
|
|
assets: Record<string, ProjectAssetRecord>;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-31 01:29:35 +02:00
|
|
|
export interface SceneDocument {
|
|
|
|
|
version: typeof SCENE_DOCUMENT_VERSION;
|
|
|
|
|
name: string;
|
2026-04-12 04:29:52 +02:00
|
|
|
time: ProjectTimeSettings;
|
2026-04-14 01:53:02 +02:00
|
|
|
scheduler: ProjectScheduler;
|
2026-04-14 19:53:31 +02:00
|
|
|
dialogues: ProjectDialogueLibrary;
|
2026-04-14 23:32:27 +02:00
|
|
|
sequences: ProjectSequenceLibrary;
|
2026-03-31 01:29:35 +02:00
|
|
|
world: WorldSettings;
|
2026-03-31 02:33:18 +02:00
|
|
|
materials: Record<string, MaterialDef>;
|
2026-03-31 01:29:35 +02:00
|
|
|
textures: Record<string, never>;
|
2026-03-31 17:31:48 +02:00
|
|
|
assets: Record<string, ProjectAssetRecord>;
|
2026-03-31 02:02:50 +02:00
|
|
|
brushes: Record<string, Brush>;
|
2026-04-13 21:19:30 +02:00
|
|
|
paths: Record<string, ScenePath>;
|
2026-03-31 17:31:48 +02:00
|
|
|
modelInstances: Record<string, ModelInstance>;
|
2026-03-31 03:00:38 +02:00
|
|
|
entities: Record<string, EntityInstance>;
|
2026-03-31 06:15:41 +02:00
|
|
|
interactionLinks: Record<string, InteractionLink>;
|
2026-03-31 01:29:35 +02:00
|
|
|
}
|
|
|
|
|
|
2026-04-11 04:19:50 +02:00
|
|
|
export function createEmptySceneDocument(
|
2026-04-12 04:29:52 +02:00
|
|
|
overrides: Partial<
|
|
|
|
|
Pick<SceneDocument, "name" | "time" | "world" | "materials">
|
|
|
|
|
> = {}
|
2026-04-11 04:19:50 +02:00
|
|
|
): SceneDocument {
|
2026-03-31 01:29:35 +02:00
|
|
|
return {
|
|
|
|
|
version: SCENE_DOCUMENT_VERSION,
|
|
|
|
|
name: overrides.name ?? "Untitled Scene",
|
2026-04-12 04:29:52 +02:00
|
|
|
time: overrides.time ?? createDefaultProjectTimeSettings(),
|
2026-04-14 01:53:02 +02:00
|
|
|
scheduler: createEmptyProjectScheduler(),
|
2026-04-14 19:53:31 +02:00
|
|
|
dialogues: createEmptyProjectDialogueLibrary(),
|
2026-04-14 23:32:27 +02:00
|
|
|
sequences: createEmptyProjectSequenceLibrary(),
|
2026-03-31 01:29:35 +02:00
|
|
|
world: overrides.world ?? createDefaultWorldSettings(),
|
2026-04-11 04:19:50 +02:00
|
|
|
materials: cloneMaterialRegistry(
|
|
|
|
|
overrides.materials ?? createStarterMaterialRegistry()
|
|
|
|
|
),
|
2026-03-31 01:29:35 +02:00
|
|
|
textures: {},
|
|
|
|
|
assets: {},
|
|
|
|
|
brushes: {},
|
2026-04-13 21:19:30 +02:00
|
|
|
paths: {},
|
2026-03-31 01:29:35 +02:00
|
|
|
modelInstances: {},
|
|
|
|
|
entities: {},
|
|
|
|
|
interactionLinks: {}
|
|
|
|
|
};
|
|
|
|
|
}
|
2026-04-11 03:46:30 +02:00
|
|
|
|
|
|
|
|
export function createEmptyProjectScene(
|
2026-04-11 04:19:50 +02:00
|
|
|
overrides: Partial<
|
2026-04-11 14:25:01 +02:00
|
|
|
Pick<
|
|
|
|
|
ProjectScene,
|
|
|
|
|
"id" | "name" | "loadingScreen" | "editorPreferences" | "world"
|
|
|
|
|
>
|
2026-04-11 04:19:50 +02:00
|
|
|
> = {}
|
2026-04-11 03:46:30 +02:00
|
|
|
): ProjectScene {
|
|
|
|
|
return {
|
|
|
|
|
id: overrides.id ?? createOpaqueId("scene"),
|
|
|
|
|
name: overrides.name ?? "Untitled Scene",
|
2026-04-11 04:13:45 +02:00
|
|
|
loadingScreen: cloneSceneLoadingScreenSettings(
|
|
|
|
|
overrides.loadingScreen ?? createDefaultSceneLoadingScreenSettings()
|
|
|
|
|
),
|
2026-04-11 14:25:01 +02:00
|
|
|
editorPreferences: cloneSceneEditorPreferences(
|
|
|
|
|
overrides.editorPreferences ?? createDefaultSceneEditorPreferences()
|
|
|
|
|
),
|
2026-04-11 03:46:30 +02:00
|
|
|
world: overrides.world ?? createDefaultWorldSettings(),
|
|
|
|
|
brushes: {},
|
2026-04-13 21:19:30 +02:00
|
|
|
paths: {},
|
2026-04-11 03:46:30 +02:00
|
|
|
modelInstances: {},
|
|
|
|
|
entities: {},
|
|
|
|
|
interactionLinks: {}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function createEmptyProjectDocument(
|
|
|
|
|
overrides: Partial<
|
2026-04-11 13:24:58 +02:00
|
|
|
Pick<
|
|
|
|
|
ProjectDocument,
|
2026-04-14 01:53:02 +02:00
|
|
|
| "name"
|
|
|
|
|
| "time"
|
|
|
|
|
| "scheduler"
|
2026-04-14 19:53:31 +02:00
|
|
|
| "dialogues"
|
2026-04-14 23:32:27 +02:00
|
|
|
| "sequences"
|
2026-04-14 01:53:02 +02:00
|
|
|
| "activeSceneId"
|
|
|
|
|
| "materials"
|
|
|
|
|
| "textures"
|
|
|
|
|
| "assets"
|
2026-04-11 13:24:58 +02:00
|
|
|
>
|
2026-04-11 03:46:30 +02:00
|
|
|
> & {
|
|
|
|
|
sceneId?: string;
|
|
|
|
|
sceneName?: string;
|
|
|
|
|
world?: WorldSettings;
|
|
|
|
|
} = {}
|
|
|
|
|
): ProjectDocument {
|
|
|
|
|
const initialScene = createEmptyProjectScene({
|
2026-04-11 04:19:50 +02:00
|
|
|
id:
|
|
|
|
|
overrides.sceneId ?? overrides.activeSceneId ?? DEFAULT_PROJECT_SCENE_ID,
|
2026-04-11 03:46:30 +02:00
|
|
|
name: overrides.sceneName,
|
|
|
|
|
world: overrides.world
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
version: SCENE_DOCUMENT_VERSION,
|
2026-04-11 13:24:58 +02:00
|
|
|
name: overrides.name ?? DEFAULT_PROJECT_NAME,
|
2026-04-12 04:29:52 +02:00
|
|
|
time: overrides.time ?? createDefaultProjectTimeSettings(),
|
2026-04-14 01:53:02 +02:00
|
|
|
scheduler: overrides.scheduler ?? createEmptyProjectScheduler(),
|
2026-04-14 19:53:31 +02:00
|
|
|
dialogues: overrides.dialogues ?? createEmptyProjectDialogueLibrary(),
|
2026-04-14 23:32:27 +02:00
|
|
|
sequences: overrides.sequences ?? createEmptyProjectSequenceLibrary(),
|
2026-04-11 03:46:30 +02:00
|
|
|
activeSceneId: initialScene.id,
|
|
|
|
|
scenes: {
|
|
|
|
|
[initialScene.id]: initialScene
|
|
|
|
|
},
|
|
|
|
|
materials: cloneMaterialRegistry(
|
|
|
|
|
overrides.materials ?? createStarterMaterialRegistry()
|
|
|
|
|
),
|
|
|
|
|
textures: overrides.textures ?? {},
|
|
|
|
|
assets: overrides.assets ?? {}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getProjectScene(
|
|
|
|
|
projectDocument: ProjectDocument,
|
|
|
|
|
sceneId = projectDocument.activeSceneId
|
|
|
|
|
): ProjectScene {
|
|
|
|
|
const scene = projectDocument.scenes[sceneId];
|
|
|
|
|
|
|
|
|
|
if (scene === undefined) {
|
|
|
|
|
throw new Error(`Project scene ${sceneId} does not exist.`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return scene;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function createSceneDocumentFromProject(
|
|
|
|
|
projectDocument: ProjectDocument,
|
|
|
|
|
sceneId = projectDocument.activeSceneId
|
|
|
|
|
): SceneDocument {
|
|
|
|
|
const scene = getProjectScene(projectDocument, sceneId);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
version: projectDocument.version,
|
|
|
|
|
name: scene.name,
|
2026-04-12 04:29:52 +02:00
|
|
|
time: projectDocument.time,
|
2026-04-14 01:53:02 +02:00
|
|
|
scheduler: projectDocument.scheduler,
|
2026-04-14 19:53:31 +02:00
|
|
|
dialogues: projectDocument.dialogues,
|
2026-04-14 23:32:27 +02:00
|
|
|
sequences: projectDocument.sequences,
|
2026-04-11 03:46:30 +02:00
|
|
|
world: scene.world,
|
|
|
|
|
materials: projectDocument.materials,
|
|
|
|
|
textures: projectDocument.textures,
|
|
|
|
|
assets: projectDocument.assets,
|
|
|
|
|
brushes: scene.brushes,
|
2026-04-13 21:19:30 +02:00
|
|
|
paths: scene.paths,
|
2026-04-11 03:46:30 +02:00
|
|
|
modelInstances: scene.modelInstances,
|
|
|
|
|
entities: scene.entities,
|
|
|
|
|
interactionLinks: scene.interactionLinks
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function createProjectDocumentFromSceneDocument(
|
|
|
|
|
sceneDocument: SceneDocument,
|
2026-04-11 13:24:58 +02:00
|
|
|
sceneId = DEFAULT_PROJECT_SCENE_ID,
|
|
|
|
|
projectName = DEFAULT_PROJECT_NAME
|
2026-04-11 03:46:30 +02:00
|
|
|
): ProjectDocument {
|
|
|
|
|
return {
|
|
|
|
|
version: SCENE_DOCUMENT_VERSION,
|
2026-04-11 13:24:58 +02:00
|
|
|
name: projectName,
|
2026-04-12 04:29:52 +02:00
|
|
|
time: sceneDocument.time,
|
2026-04-14 01:53:02 +02:00
|
|
|
scheduler: sceneDocument.scheduler,
|
2026-04-14 19:53:31 +02:00
|
|
|
dialogues: sceneDocument.dialogues,
|
2026-04-14 23:32:27 +02:00
|
|
|
sequences: sceneDocument.sequences,
|
2026-04-11 03:46:30 +02:00
|
|
|
activeSceneId: sceneId,
|
|
|
|
|
scenes: {
|
|
|
|
|
[sceneId]: {
|
|
|
|
|
id: sceneId,
|
|
|
|
|
name: sceneDocument.name,
|
2026-04-11 04:07:43 +02:00
|
|
|
loadingScreen: createDefaultSceneLoadingScreenSettings(),
|
2026-04-11 14:25:01 +02:00
|
|
|
editorPreferences: createDefaultSceneEditorPreferences(),
|
2026-04-11 03:46:30 +02:00
|
|
|
world: sceneDocument.world,
|
|
|
|
|
brushes: sceneDocument.brushes,
|
2026-04-13 21:19:30 +02:00
|
|
|
paths: sceneDocument.paths,
|
2026-04-11 03:46:30 +02:00
|
|
|
modelInstances: sceneDocument.modelInstances,
|
|
|
|
|
entities: sceneDocument.entities,
|
|
|
|
|
interactionLinks: sceneDocument.interactionLinks
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
materials: sceneDocument.materials,
|
|
|
|
|
textures: sceneDocument.textures,
|
|
|
|
|
assets: sceneDocument.assets
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function applySceneDocumentToProject(
|
|
|
|
|
projectDocument: ProjectDocument,
|
|
|
|
|
sceneId: string,
|
|
|
|
|
sceneDocument: SceneDocument
|
|
|
|
|
): ProjectDocument {
|
|
|
|
|
const previousScene = getProjectScene(projectDocument, sceneId);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...projectDocument,
|
|
|
|
|
version: SCENE_DOCUMENT_VERSION,
|
2026-04-12 04:29:52 +02:00
|
|
|
time: sceneDocument.time,
|
2026-04-14 01:53:02 +02:00
|
|
|
scheduler: sceneDocument.scheduler,
|
2026-04-14 19:53:31 +02:00
|
|
|
dialogues: sceneDocument.dialogues,
|
2026-04-14 23:32:27 +02:00
|
|
|
sequences: sceneDocument.sequences,
|
2026-04-11 03:46:30 +02:00
|
|
|
materials: sceneDocument.materials,
|
|
|
|
|
textures: sceneDocument.textures,
|
|
|
|
|
assets: sceneDocument.assets,
|
|
|
|
|
scenes: {
|
|
|
|
|
...projectDocument.scenes,
|
|
|
|
|
[sceneId]: {
|
|
|
|
|
...previousScene,
|
|
|
|
|
name: sceneDocument.name,
|
|
|
|
|
world: sceneDocument.world,
|
|
|
|
|
brushes: sceneDocument.brushes,
|
2026-04-13 21:19:30 +02:00
|
|
|
paths: sceneDocument.paths,
|
2026-04-11 03:46:30 +02:00
|
|
|
modelInstances: sceneDocument.modelInstances,
|
|
|
|
|
entities: sceneDocument.entities,
|
|
|
|
|
interactionLinks: sceneDocument.interactionLinks
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
2026-04-11 04:07:43 +02:00
|
|
|
|
|
|
|
|
export function createDefaultSceneLoadingScreenSettings(): SceneLoadingScreenSettings {
|
|
|
|
|
return {
|
|
|
|
|
colorHex: "#0d1117",
|
|
|
|
|
headline: null,
|
|
|
|
|
description: null
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 14:25:01 +02:00
|
|
|
export function createDefaultSceneEditorPreferences(): SceneEditorPreferences {
|
|
|
|
|
return {
|
|
|
|
|
whiteboxSelectionMode: "object",
|
|
|
|
|
whiteboxSnapEnabled: true,
|
|
|
|
|
whiteboxSnapStep: DEFAULT_SCENE_EDITOR_SNAP_STEP,
|
|
|
|
|
viewportGridVisible: true,
|
|
|
|
|
viewportLayoutMode: "single",
|
|
|
|
|
activeViewportPanelId: "topLeft",
|
|
|
|
|
viewportQuadSplit: {
|
|
|
|
|
x: 0.5,
|
|
|
|
|
y: 0.5
|
|
|
|
|
},
|
|
|
|
|
viewportPanels: {
|
|
|
|
|
topLeft: {
|
|
|
|
|
viewMode: "perspective",
|
|
|
|
|
displayMode: "normal"
|
|
|
|
|
},
|
|
|
|
|
topRight: {
|
|
|
|
|
viewMode: "top",
|
|
|
|
|
displayMode: "authoring"
|
|
|
|
|
},
|
|
|
|
|
bottomLeft: {
|
|
|
|
|
viewMode: "front",
|
|
|
|
|
displayMode: "authoring"
|
|
|
|
|
},
|
|
|
|
|
bottomRight: {
|
|
|
|
|
viewMode: "side",
|
|
|
|
|
displayMode: "authoring"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function cloneSceneEditorPreferences(
|
|
|
|
|
preferences: SceneEditorPreferences
|
|
|
|
|
): SceneEditorPreferences {
|
|
|
|
|
return {
|
|
|
|
|
whiteboxSelectionMode: preferences.whiteboxSelectionMode,
|
|
|
|
|
whiteboxSnapEnabled: preferences.whiteboxSnapEnabled,
|
|
|
|
|
whiteboxSnapStep: preferences.whiteboxSnapStep,
|
|
|
|
|
viewportGridVisible: preferences.viewportGridVisible,
|
|
|
|
|
viewportLayoutMode: preferences.viewportLayoutMode,
|
|
|
|
|
activeViewportPanelId: preferences.activeViewportPanelId,
|
|
|
|
|
viewportQuadSplit: {
|
|
|
|
|
...preferences.viewportQuadSplit
|
|
|
|
|
},
|
|
|
|
|
viewportPanels: {
|
|
|
|
|
topLeft: {
|
|
|
|
|
...preferences.viewportPanels.topLeft
|
|
|
|
|
},
|
|
|
|
|
topRight: {
|
|
|
|
|
...preferences.viewportPanels.topRight
|
|
|
|
|
},
|
|
|
|
|
bottomLeft: {
|
|
|
|
|
...preferences.viewportPanels.bottomLeft
|
|
|
|
|
},
|
|
|
|
|
bottomRight: {
|
|
|
|
|
...preferences.viewportPanels.bottomRight
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 04:07:43 +02:00
|
|
|
export function cloneSceneLoadingScreenSettings(
|
|
|
|
|
settings: SceneLoadingScreenSettings
|
|
|
|
|
): SceneLoadingScreenSettings {
|
|
|
|
|
return {
|
|
|
|
|
colorHex: settings.colorHex,
|
|
|
|
|
headline: settings.headline,
|
|
|
|
|
description: settings.description
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function areSceneLoadingScreenSettingsEqual(
|
|
|
|
|
left: SceneLoadingScreenSettings,
|
|
|
|
|
right: SceneLoadingScreenSettings
|
|
|
|
|
): boolean {
|
|
|
|
|
return (
|
|
|
|
|
left.colorHex === right.colorHex &&
|
|
|
|
|
left.headline === right.headline &&
|
|
|
|
|
left.description === right.description
|
|
|
|
|
);
|
|
|
|
|
}
|