auto-git:
[change] src/app/App.tsx [change] src/assets/starter-environment-assets.ts [change] src/document/migrate-scene-document.ts [change] src/document/scene-document-validation.ts [change] src/document/scene-document.ts [change] src/document/world-settings.ts [change] src/rendering/world-background-renderer.ts [change] src/rendering/world-shader-sky.ts [change] src/runtime-three/runtime-host.ts [change] src/runtime-three/runtime-project-time.ts [change] src/shared-ui/world-background-style.ts [change] src/viewport-three/ViewportCanvas.tsx [change] src/viewport-three/viewport-host.ts [change] tests/domain/runtime-project-time.test.ts [change] tests/domain/scene-document-validation.test.ts [change] tests/domain/world-settings.test.ts [change] tests/serialization/project-document-json.test.ts [change] tests/serialization/scene-document-json.test.ts [change] tests/unit/world-shader-sky.test.ts
This commit is contained in:
@@ -183,7 +183,9 @@ describe("runtime project time", () => {
|
||||
midnight.background.topColorHex
|
||||
);
|
||||
expect(dawn.background.topColorHex).not.toBe(noon.background.topColorHex);
|
||||
expect(midnight.background.topColorHex).not.toBe(noon.background.topColorHex);
|
||||
expect(midnight.background.topColorHex).not.toBe(
|
||||
noon.background.topColorHex
|
||||
);
|
||||
expect(midnight.background.bottomColorHex).not.toBe(
|
||||
noon.background.bottomColorHex
|
||||
);
|
||||
|
||||
@@ -28,7 +28,11 @@ import {
|
||||
createTeleportTargetEntity,
|
||||
createTriggerVolumeEntity
|
||||
} from "../../src/entities/entity-instances";
|
||||
import { createProjectAssetStorageKey, type AudioAssetRecord, type ModelAssetRecord } from "../../src/assets/project-assets";
|
||||
import {
|
||||
createProjectAssetStorageKey,
|
||||
type AudioAssetRecord,
|
||||
type ModelAssetRecord
|
||||
} from "../../src/assets/project-assets";
|
||||
import {
|
||||
createControlInteractionLink,
|
||||
createRunSequenceInteractionLink
|
||||
@@ -199,11 +203,11 @@ describe("validateSceneDocument", () => {
|
||||
}
|
||||
};
|
||||
const soundVolumeAction = document.interactionLinks["link-sound-volume"]
|
||||
.action as typeof document.interactionLinks["link-sound-volume"]["action"] & {
|
||||
.action as (typeof document.interactionLinks)["link-sound-volume"]["action"] & {
|
||||
effect: { volume: number };
|
||||
};
|
||||
const ambientColorAction = document.interactionLinks["link-ambient-color"]
|
||||
.action as typeof document.interactionLinks["link-ambient-color"]["action"] & {
|
||||
.action as (typeof document.interactionLinks)["link-ambient-color"]["action"] & {
|
||||
effect: { colorHex: string };
|
||||
};
|
||||
soundVolumeAction.effect.volume = Number.NaN;
|
||||
@@ -289,24 +293,27 @@ describe("validateSceneDocument", () => {
|
||||
});
|
||||
const document = createEmptySceneDocument();
|
||||
document.entities[npc.id] = npc;
|
||||
document.sequences.sequences["sequence-guide-talk"] = createProjectSequence({
|
||||
id: "sequence-guide-talk",
|
||||
title: "Guide Talk",
|
||||
effects: [
|
||||
{
|
||||
stepClass: "impulse",
|
||||
type: "makeNpcTalk",
|
||||
npcEntityId: npc.id,
|
||||
dialogueId: "dialogue-guide"
|
||||
}
|
||||
]
|
||||
});
|
||||
document.interactionLinks["link-guide-talk"] = createRunSequenceInteractionLink({
|
||||
id: "link-guide-talk",
|
||||
sourceEntityId: npc.id,
|
||||
trigger: "click",
|
||||
sequenceId: "sequence-guide-talk"
|
||||
});
|
||||
document.sequences.sequences["sequence-guide-talk"] = createProjectSequence(
|
||||
{
|
||||
id: "sequence-guide-talk",
|
||||
title: "Guide Talk",
|
||||
effects: [
|
||||
{
|
||||
stepClass: "impulse",
|
||||
type: "makeNpcTalk",
|
||||
npcEntityId: npc.id,
|
||||
dialogueId: "dialogue-guide"
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
document.interactionLinks["link-guide-talk"] =
|
||||
createRunSequenceInteractionLink({
|
||||
id: "link-guide-talk",
|
||||
sourceEntityId: npc.id,
|
||||
trigger: "click",
|
||||
sequenceId: "sequence-guide-talk"
|
||||
});
|
||||
|
||||
const validation = validateSceneDocument(document);
|
||||
|
||||
@@ -324,32 +331,34 @@ describe("validateSceneDocument", () => {
|
||||
const document = createEmptySceneDocument();
|
||||
document.entities[npc.id] = npc;
|
||||
document.paths[path.id] = path;
|
||||
document.sequences.sequences["sequence-guard-patrol"] = createProjectSequence({
|
||||
id: "sequence-guard-patrol",
|
||||
title: "Guard Patrol",
|
||||
effects: [
|
||||
{
|
||||
stepClass: "held",
|
||||
type: "controlEffect",
|
||||
effect: createFollowActorPathControlEffect({
|
||||
target: createActorControlTargetRef("actor-guard"),
|
||||
pathId: path.id,
|
||||
speed: 1,
|
||||
loop: true,
|
||||
progressMode: "deriveFromTime"
|
||||
})
|
||||
}
|
||||
]
|
||||
});
|
||||
document.scheduler.routines["routine-guard-patrol"] = createProjectScheduleRoutine({
|
||||
id: "routine-guard-patrol",
|
||||
title: "Guard Patrol",
|
||||
target: createActorControlTargetRef("actor-guard"),
|
||||
startHour: 8,
|
||||
endHour: 18,
|
||||
sequenceId: "sequence-guard-patrol",
|
||||
effects: []
|
||||
});
|
||||
document.sequences.sequences["sequence-guard-patrol"] =
|
||||
createProjectSequence({
|
||||
id: "sequence-guard-patrol",
|
||||
title: "Guard Patrol",
|
||||
effects: [
|
||||
{
|
||||
stepClass: "held",
|
||||
type: "controlEffect",
|
||||
effect: createFollowActorPathControlEffect({
|
||||
target: createActorControlTargetRef("actor-guard"),
|
||||
pathId: path.id,
|
||||
speed: 1,
|
||||
loop: true,
|
||||
progressMode: "deriveFromTime"
|
||||
})
|
||||
}
|
||||
]
|
||||
});
|
||||
document.scheduler.routines["routine-guard-patrol"] =
|
||||
createProjectScheduleRoutine({
|
||||
id: "routine-guard-patrol",
|
||||
title: "Guard Patrol",
|
||||
target: createActorControlTargetRef("actor-guard"),
|
||||
startHour: 8,
|
||||
endHour: 18,
|
||||
sequenceId: "sequence-guard-patrol",
|
||||
effects: []
|
||||
});
|
||||
|
||||
const validation = validateSceneDocument(document);
|
||||
|
||||
@@ -444,31 +453,32 @@ describe("validateSceneDocument", () => {
|
||||
document.assets[npcModelAsset.id] = npcModelAsset;
|
||||
document.entities[npc.id] = npc;
|
||||
document.paths[path.id] = path;
|
||||
document.scheduler.routines["routine-patrol"] = createProjectScheduleRoutine({
|
||||
id: "routine-patrol",
|
||||
title: "Patrolling",
|
||||
target: actorTarget,
|
||||
startHour: 9,
|
||||
endHour: 13,
|
||||
effects: [
|
||||
createSetActorPresenceControlEffect({
|
||||
target: actorTarget,
|
||||
active: true
|
||||
}),
|
||||
createPlayActorAnimationControlEffect({
|
||||
target: actorTarget,
|
||||
clipName: "Walk",
|
||||
loop: true
|
||||
}),
|
||||
createFollowActorPathControlEffect({
|
||||
target: actorTarget,
|
||||
pathId: path.id,
|
||||
speed: 2,
|
||||
loop: false,
|
||||
progressMode: "deriveFromTime"
|
||||
})
|
||||
]
|
||||
});
|
||||
document.scheduler.routines["routine-patrol"] =
|
||||
createProjectScheduleRoutine({
|
||||
id: "routine-patrol",
|
||||
title: "Patrolling",
|
||||
target: actorTarget,
|
||||
startHour: 9,
|
||||
endHour: 13,
|
||||
effects: [
|
||||
createSetActorPresenceControlEffect({
|
||||
target: actorTarget,
|
||||
active: true
|
||||
}),
|
||||
createPlayActorAnimationControlEffect({
|
||||
target: actorTarget,
|
||||
clipName: "Walk",
|
||||
loop: true
|
||||
}),
|
||||
createFollowActorPathControlEffect({
|
||||
target: actorTarget,
|
||||
pathId: path.id,
|
||||
speed: 2,
|
||||
loop: false,
|
||||
progressMode: "deriveFromTime"
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
const validation = validateSceneDocument(document);
|
||||
|
||||
@@ -548,7 +558,9 @@ describe("validateSceneDocument", () => {
|
||||
crouch: {
|
||||
speedMultiplier: 0
|
||||
}
|
||||
} as unknown as ReturnType<typeof createPlayerStartEntity>["movementTemplate"],
|
||||
} as unknown as ReturnType<
|
||||
typeof createPlayerStartEntity
|
||||
>["movementTemplate"],
|
||||
inputBindings: {
|
||||
keyboard: {
|
||||
...createPlayerStartInputBindings().keyboard,
|
||||
@@ -564,7 +576,9 @@ describe("validateSceneDocument", () => {
|
||||
crouch: "invalidButton",
|
||||
pauseTime: "invalidButton"
|
||||
}
|
||||
} as unknown as ReturnType<typeof createPlayerStartEntity>["inputBindings"],
|
||||
} as unknown as ReturnType<
|
||||
typeof createPlayerStartEntity
|
||||
>["inputBindings"],
|
||||
collider: {
|
||||
mode: "capsule",
|
||||
eyeHeight: 3,
|
||||
@@ -980,7 +994,9 @@ describe("validateSceneDocument", () => {
|
||||
width: 512,
|
||||
height: 256,
|
||||
hasAlpha: false,
|
||||
warnings: ["Background images work best as a 2:1 equirectangular panorama."]
|
||||
warnings: [
|
||||
"Background images work best as a 2:1 equirectangular panorama."
|
||||
]
|
||||
}
|
||||
};
|
||||
const pointLight = createPointLightEntity({
|
||||
@@ -1159,7 +1175,7 @@ describe("validateSceneDocument", () => {
|
||||
document.world.shaderSky.clouds.coverage = 2;
|
||||
document.world.timeOfDay.dawn.background = {
|
||||
mode: "shader"
|
||||
} as (typeof document.world.timeOfDay.dawn.background);
|
||||
} as typeof document.world.timeOfDay.dawn.background;
|
||||
|
||||
const validation = validateSceneDocument(document);
|
||||
|
||||
|
||||
@@ -21,7 +21,9 @@ describe("world settings helpers", () => {
|
||||
expect(clone.shaderSky.clouds).not.toBe(source.shaderSky.clouds);
|
||||
expect(clone.sunLight.direction).not.toBe(source.sunLight.direction);
|
||||
expect(clone.advancedRendering).not.toBe(source.advancedRendering);
|
||||
expect(clone.advancedRendering.shadows).not.toBe(source.advancedRendering.shadows);
|
||||
expect(clone.advancedRendering.shadows).not.toBe(
|
||||
source.advancedRendering.shadows
|
||||
);
|
||||
expect(clone.advancedRendering.whiteboxBevel).not.toBe(
|
||||
source.advancedRendering.whiteboxBevel
|
||||
);
|
||||
@@ -59,7 +61,11 @@ describe("world settings helpers", () => {
|
||||
environmentIntensity: 0.5
|
||||
});
|
||||
|
||||
const nextImageBackground = changeWorldBackgroundMode(imageBackground, "image", "asset-background-panorama-2");
|
||||
const nextImageBackground = changeWorldBackgroundMode(
|
||||
imageBackground,
|
||||
"image",
|
||||
"asset-background-panorama-2"
|
||||
);
|
||||
|
||||
expect(nextImageBackground).toEqual({
|
||||
mode: "image",
|
||||
@@ -121,7 +127,8 @@ describe("world settings helpers", () => {
|
||||
expect(areWorldSettingsEqual(left, right)).toBe(false);
|
||||
|
||||
right.sunLight.direction.x = left.sunLight.direction.x;
|
||||
right.advancedRendering.bloom.intensity = right.advancedRendering.bloom.intensity + 0.1;
|
||||
right.advancedRendering.bloom.intensity =
|
||||
right.advancedRendering.bloom.intensity + 0.1;
|
||||
|
||||
expect(areWorldSettingsEqual(left, right)).toBe(false);
|
||||
});
|
||||
|
||||
@@ -71,9 +71,9 @@ describe("project document JSON", () => {
|
||||
activeScene.world.shaderSky.clouds.coverage = 0.63;
|
||||
activeScene.world.shaderSky.clouds.tintHex = "#ece7df";
|
||||
|
||||
expect(parseProjectDocumentJson(serializeProjectDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
expect(
|
||||
parseProjectDocumentJson(serializeProjectDocument(document))
|
||||
).toEqual(document);
|
||||
});
|
||||
|
||||
it("round-trips scene transition sequence effects", () => {
|
||||
@@ -94,22 +94,23 @@ describe("project document JSON", () => {
|
||||
});
|
||||
targetScene.entities[houseEntry.id] = houseEntry;
|
||||
document.scenes[targetScene.id] = targetScene;
|
||||
document.sequences.sequences["sequence-enter-house"] = createProjectSequence({
|
||||
id: "sequence-enter-house",
|
||||
title: "Enter House",
|
||||
effects: [
|
||||
{
|
||||
stepClass: "impulse",
|
||||
type: "startSceneTransition",
|
||||
targetSceneId: targetScene.id,
|
||||
targetEntryEntityId: houseEntry.id
|
||||
}
|
||||
]
|
||||
});
|
||||
document.sequences.sequences["sequence-enter-house"] =
|
||||
createProjectSequence({
|
||||
id: "sequence-enter-house",
|
||||
title: "Enter House",
|
||||
effects: [
|
||||
{
|
||||
stepClass: "impulse",
|
||||
type: "startSceneTransition",
|
||||
targetSceneId: targetScene.id,
|
||||
targetEntryEntityId: houseEntry.id
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
expect(parseProjectDocumentJson(serializeProjectDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
expect(
|
||||
parseProjectDocumentJson(serializeProjectDocument(document))
|
||||
).toEqual(document);
|
||||
});
|
||||
|
||||
it("round-trips NPC dialogue references in project scenes", () => {
|
||||
@@ -135,9 +136,9 @@ describe("project document JSON", () => {
|
||||
});
|
||||
document.scenes[document.activeSceneId]!.entities[npc.id] = npc;
|
||||
|
||||
expect(parseProjectDocumentJson(serializeProjectDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
expect(
|
||||
parseProjectDocumentJson(serializeProjectDocument(document))
|
||||
).toEqual(document);
|
||||
});
|
||||
|
||||
it("migrates legacy NPC dialogue speaker fields by deriving names from actor ids instead", () => {
|
||||
@@ -169,9 +170,9 @@ describe("project document JSON", () => {
|
||||
legacyDocument.version = NPC_ONLY_DIALOGUES_SCENE_DOCUMENT_VERSION;
|
||||
(
|
||||
(
|
||||
(
|
||||
legacyDocument.scenes as Record<string, unknown>
|
||||
)[document.activeSceneId] as {
|
||||
(legacyDocument.scenes as Record<string, unknown>)[
|
||||
document.activeSceneId
|
||||
] as {
|
||||
entities: Record<string, unknown>;
|
||||
}
|
||||
).entities[npc.id] as {
|
||||
@@ -184,8 +185,8 @@ describe("project document JSON", () => {
|
||||
const migratedDocument = parseProjectDocumentJson(
|
||||
JSON.stringify(legacyDocument)
|
||||
);
|
||||
const migratedNpc = migratedDocument.scenes[migratedDocument.activeSceneId]!
|
||||
.entities[npc.id];
|
||||
const migratedNpc =
|
||||
migratedDocument.scenes[migratedDocument.activeSceneId]!.entities[npc.id];
|
||||
|
||||
expect(migratedNpc).toEqual(
|
||||
expect.objectContaining({
|
||||
@@ -206,8 +207,7 @@ describe("project document JSON", () => {
|
||||
})
|
||||
);
|
||||
expect(
|
||||
"speakerName" in
|
||||
(migratedNpc as typeof npc).dialogues[0]!.lines[0]!
|
||||
"speakerName" in (migratedNpc as typeof npc).dialogues[0]!.lines[0]!
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
@@ -220,9 +220,10 @@ describe("project document JSON", () => {
|
||||
actorId: "actor-guard"
|
||||
});
|
||||
document.scenes[document.activeSceneId]!.entities[npc.id] = npc;
|
||||
document.scenes[document.activeSceneId]!.paths["path-guard"] = createScenePath({
|
||||
id: "path-guard"
|
||||
});
|
||||
document.scenes[document.activeSceneId]!.paths["path-guard"] =
|
||||
createScenePath({
|
||||
id: "path-guard"
|
||||
});
|
||||
document.sequences.sequences["sequence-patrol"] = createProjectSequence({
|
||||
id: "sequence-patrol",
|
||||
title: "Patrol",
|
||||
@@ -243,16 +244,21 @@ describe("project document JSON", () => {
|
||||
const legacyDocument = JSON.parse(
|
||||
serializeProjectDocument(document)
|
||||
) as Record<string, unknown>;
|
||||
legacyDocument.version = NPC_DIALOGUE_LINE_SPEAKER_REMOVED_SCENE_DOCUMENT_VERSION;
|
||||
legacyDocument.version =
|
||||
NPC_DIALOGUE_LINE_SPEAKER_REMOVED_SCENE_DOCUMENT_VERSION;
|
||||
delete (
|
||||
(
|
||||
(
|
||||
legacyDocument.sequences as {
|
||||
sequences: Record<string, { effects: Array<{ effect?: Record<string, unknown> }> }>;
|
||||
}
|
||||
).sequences["sequence-patrol"]!.effects[0]!.effect as Record<string, unknown>
|
||||
).smoothPath
|
||||
);
|
||||
legacyDocument.sequences as {
|
||||
sequences: Record<
|
||||
string,
|
||||
{ effects: Array<{ effect?: Record<string, unknown> }> }
|
||||
>;
|
||||
}
|
||||
).sequences["sequence-patrol"]!.effects[0]!.effect as Record<
|
||||
string,
|
||||
unknown
|
||||
>
|
||||
).smoothPath;
|
||||
|
||||
const migratedDocument = parseProjectDocumentJson(
|
||||
JSON.stringify(legacyDocument)
|
||||
@@ -303,8 +309,8 @@ describe("project document JSON", () => {
|
||||
const migratedDocument = parseProjectDocumentJson(
|
||||
JSON.stringify(legacyDocument)
|
||||
);
|
||||
const migratedNpc = migratedDocument.scenes[migratedDocument.activeSceneId]!
|
||||
.entities[npc.id];
|
||||
const migratedNpc =
|
||||
migratedDocument.scenes[migratedDocument.activeSceneId]!.entities[npc.id];
|
||||
|
||||
expect(migratedNpc).toEqual(
|
||||
expect.objectContaining({
|
||||
@@ -324,62 +330,68 @@ describe("project document JSON", () => {
|
||||
actorId: "actor-vendor"
|
||||
});
|
||||
|
||||
document.scenes[document.activeSceneId]!.entities[npc.id] = createNpcEntity({
|
||||
...npc,
|
||||
dialogues: [
|
||||
{
|
||||
id: "dialogue-market",
|
||||
title: "Market Greeting",
|
||||
lines: [
|
||||
{
|
||||
id: "dialogue-line-market-1",
|
||||
text: "Fresh fruit."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
defaultDialogueId: "dialogue-market"
|
||||
});
|
||||
document.sequences.sequences["sequence-market-dialogue"] = createProjectSequence({
|
||||
id: "sequence-market-dialogue",
|
||||
title: "Market Greeting Sequence",
|
||||
steps: [
|
||||
{
|
||||
stepClass: "impulse",
|
||||
type: "makeNpcTalk",
|
||||
npcEntityId: npc.id,
|
||||
dialogueId: "dialogue-market"
|
||||
}
|
||||
]
|
||||
});
|
||||
document.sequences.sequences["sequence-vendor-open"] = createProjectSequence({
|
||||
id: "sequence-vendor-open",
|
||||
title: "Vendor Open",
|
||||
steps: [
|
||||
{
|
||||
stepClass: "held",
|
||||
type: "controlEffect",
|
||||
effect: createSetActorPresenceControlEffect({
|
||||
target: createActorControlTargetRef("actor-vendor"),
|
||||
active: true
|
||||
})
|
||||
}
|
||||
]
|
||||
});
|
||||
document.scheduler.routines["routine-vendor-open"] = createProjectScheduleRoutine({
|
||||
id: "routine-vendor-open",
|
||||
title: "Vendor Open",
|
||||
target: createActorControlTargetRef("actor-vendor"),
|
||||
sequenceId: "sequence-vendor-open",
|
||||
effect: createSetActorPresenceControlEffect({
|
||||
target: createActorControlTargetRef("actor-vendor"),
|
||||
active: false
|
||||
})
|
||||
});
|
||||
document.scenes[document.activeSceneId]!.entities["entity-trigger-sequence"] =
|
||||
createTriggerVolumeEntity({
|
||||
id: "entity-trigger-sequence"
|
||||
document.scenes[document.activeSceneId]!.entities[npc.id] = createNpcEntity(
|
||||
{
|
||||
...npc,
|
||||
dialogues: [
|
||||
{
|
||||
id: "dialogue-market",
|
||||
title: "Market Greeting",
|
||||
lines: [
|
||||
{
|
||||
id: "dialogue-line-market-1",
|
||||
text: "Fresh fruit."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
defaultDialogueId: "dialogue-market"
|
||||
}
|
||||
);
|
||||
document.sequences.sequences["sequence-market-dialogue"] =
|
||||
createProjectSequence({
|
||||
id: "sequence-market-dialogue",
|
||||
title: "Market Greeting Sequence",
|
||||
steps: [
|
||||
{
|
||||
stepClass: "impulse",
|
||||
type: "makeNpcTalk",
|
||||
npcEntityId: npc.id,
|
||||
dialogueId: "dialogue-market"
|
||||
}
|
||||
]
|
||||
});
|
||||
document.sequences.sequences["sequence-vendor-open"] =
|
||||
createProjectSequence({
|
||||
id: "sequence-vendor-open",
|
||||
title: "Vendor Open",
|
||||
steps: [
|
||||
{
|
||||
stepClass: "held",
|
||||
type: "controlEffect",
|
||||
effect: createSetActorPresenceControlEffect({
|
||||
target: createActorControlTargetRef("actor-vendor"),
|
||||
active: true
|
||||
})
|
||||
}
|
||||
]
|
||||
});
|
||||
document.scheduler.routines["routine-vendor-open"] =
|
||||
createProjectScheduleRoutine({
|
||||
id: "routine-vendor-open",
|
||||
title: "Vendor Open",
|
||||
target: createActorControlTargetRef("actor-vendor"),
|
||||
sequenceId: "sequence-vendor-open",
|
||||
effect: createSetActorPresenceControlEffect({
|
||||
target: createActorControlTargetRef("actor-vendor"),
|
||||
active: false
|
||||
})
|
||||
});
|
||||
document.scenes[document.activeSceneId]!.entities[
|
||||
"entity-trigger-sequence"
|
||||
] = createTriggerVolumeEntity({
|
||||
id: "entity-trigger-sequence"
|
||||
});
|
||||
document.scenes[document.activeSceneId]!.interactionLinks["link-sequence"] =
|
||||
createRunSequenceInteractionLink({
|
||||
id: "link-sequence",
|
||||
@@ -387,9 +399,9 @@ describe("project document JSON", () => {
|
||||
sequenceId: "sequence-market-dialogue"
|
||||
});
|
||||
|
||||
expect(parseProjectDocumentJson(serializeProjectDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
expect(
|
||||
parseProjectDocumentJson(serializeProjectDocument(document))
|
||||
).toEqual(document);
|
||||
});
|
||||
|
||||
it("migrates v52 project documents without sequences to an empty project sequence library", () => {
|
||||
@@ -621,8 +633,8 @@ describe("project document JSON", () => {
|
||||
lightIntensityFactor: 0.19
|
||||
}
|
||||
};
|
||||
document.scenes["scene-cellar"].paths["path-cellar-patrol"] = createScenePath(
|
||||
{
|
||||
document.scenes["scene-cellar"].paths["path-cellar-patrol"] =
|
||||
createScenePath({
|
||||
id: "path-cellar-patrol",
|
||||
name: "Cellar Patrol",
|
||||
loop: true,
|
||||
@@ -652,8 +664,7 @@ describe("project document JSON", () => {
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const serializedDocument = serializeProjectDocument(document);
|
||||
|
||||
@@ -725,31 +736,32 @@ describe("project document JSON", () => {
|
||||
document.assets[npcModelAsset.id] = npcModelAsset;
|
||||
document.scenes[document.activeSceneId].entities[npc.id] = npc;
|
||||
document.scenes[document.activeSceneId].paths[path.id] = path;
|
||||
document.scheduler.routines["routine-patrol"] = createProjectScheduleRoutine({
|
||||
id: "routine-patrol",
|
||||
title: "Patrolling",
|
||||
target: createActorControlTargetRef(npc.actorId),
|
||||
startHour: 9,
|
||||
endHour: 13,
|
||||
effects: [
|
||||
createSetActorPresenceControlEffect({
|
||||
target: createActorControlTargetRef(npc.actorId),
|
||||
active: true
|
||||
}),
|
||||
createPlayActorAnimationControlEffect({
|
||||
target: createActorControlTargetRef(npc.actorId),
|
||||
clipName: "Walk",
|
||||
loop: true
|
||||
}),
|
||||
createFollowActorPathControlEffect({
|
||||
target: createActorControlTargetRef(npc.actorId),
|
||||
pathId: path.id,
|
||||
speed: 2,
|
||||
loop: false,
|
||||
progressMode: "deriveFromTime"
|
||||
})
|
||||
]
|
||||
});
|
||||
document.scheduler.routines["routine-patrol"] =
|
||||
createProjectScheduleRoutine({
|
||||
id: "routine-patrol",
|
||||
title: "Patrolling",
|
||||
target: createActorControlTargetRef(npc.actorId),
|
||||
startHour: 9,
|
||||
endHour: 13,
|
||||
effects: [
|
||||
createSetActorPresenceControlEffect({
|
||||
target: createActorControlTargetRef(npc.actorId),
|
||||
active: true
|
||||
}),
|
||||
createPlayActorAnimationControlEffect({
|
||||
target: createActorControlTargetRef(npc.actorId),
|
||||
clipName: "Walk",
|
||||
loop: true
|
||||
}),
|
||||
createFollowActorPathControlEffect({
|
||||
target: createActorControlTargetRef(npc.actorId),
|
||||
pathId: path.id,
|
||||
speed: 2,
|
||||
loop: false,
|
||||
progressMode: "deriveFromTime"
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
expect(
|
||||
parseProjectDocumentJson(serializeProjectDocument(document))
|
||||
@@ -765,7 +777,8 @@ describe("project document JSON", () => {
|
||||
intensity: 1.25
|
||||
});
|
||||
|
||||
document.scenes[document.activeSceneId].entities[pointLight.id] = pointLight;
|
||||
document.scenes[document.activeSceneId].entities[pointLight.id] =
|
||||
pointLight;
|
||||
document.scheduler.routines["routine-night-light"] =
|
||||
createProjectScheduleRoutine({
|
||||
id: "routine-night-light",
|
||||
@@ -910,7 +923,9 @@ describe("project document JSON", () => {
|
||||
const legacyScene = legacyProject.scenes[legacyProject.activeSceneId];
|
||||
|
||||
if (legacyScene === undefined) {
|
||||
throw new Error("Expected the legacy project to contain an active scene.");
|
||||
throw new Error(
|
||||
"Expected the legacy project to contain an active scene."
|
||||
);
|
||||
}
|
||||
|
||||
const migratedDocument = parseProjectDocumentJson(
|
||||
@@ -975,9 +990,7 @@ describe("project document JSON", () => {
|
||||
expect(
|
||||
migratedDocument.scenes[migratedDocument.activeSceneId]?.world.timeOfDay
|
||||
.night.background
|
||||
).toEqual(
|
||||
createDefaultWorldSettings().timeOfDay.night.background
|
||||
);
|
||||
).toEqual(createDefaultWorldSettings().timeOfDay.night.background);
|
||||
expect(
|
||||
migratedDocument.scenes[migratedDocument.activeSceneId]?.world
|
||||
.projectTimeLightingEnabled
|
||||
@@ -1044,16 +1057,16 @@ describe("project document JSON", () => {
|
||||
expect(migratedDocument.version).toBe(SCENE_DOCUMENT_VERSION);
|
||||
expect(migratedDocument.name).toBe(DEFAULT_PROJECT_NAME);
|
||||
expect(migratedDocument.scenes["scene-main"]?.name).toBe("Legacy Entry");
|
||||
expect(migratedDocument.scenes["scene-main"]?.editorPreferences).toMatchObject(
|
||||
{
|
||||
whiteboxSelectionMode: "object",
|
||||
whiteboxSnapEnabled: true,
|
||||
whiteboxSnapStep: DEFAULT_SCENE_EDITOR_SNAP_STEP,
|
||||
viewportGridVisible: true,
|
||||
viewportLayoutMode: "single",
|
||||
activeViewportPanelId: "topLeft"
|
||||
}
|
||||
);
|
||||
expect(
|
||||
migratedDocument.scenes["scene-main"]?.editorPreferences
|
||||
).toMatchObject({
|
||||
whiteboxSelectionMode: "object",
|
||||
whiteboxSnapEnabled: true,
|
||||
whiteboxSnapStep: DEFAULT_SCENE_EDITOR_SNAP_STEP,
|
||||
viewportGridVisible: true,
|
||||
viewportLayoutMode: "single",
|
||||
activeViewportPanelId: "topLeft"
|
||||
});
|
||||
});
|
||||
|
||||
it("migrates v23 project documents without Scene Entry entities", () => {
|
||||
@@ -1114,7 +1127,9 @@ describe("project document JSON", () => {
|
||||
})
|
||||
);
|
||||
|
||||
expect(migratedDocument.scenes["scene-main"]?.entities[legacyNpc.id]).toEqual(
|
||||
expect(
|
||||
migratedDocument.scenes["scene-main"]?.entities[legacyNpc.id]
|
||||
).toEqual(
|
||||
createNpcEntity({
|
||||
...legacyNpc,
|
||||
presence: {
|
||||
@@ -1154,21 +1169,22 @@ describe("project document JSON", () => {
|
||||
name: "House"
|
||||
});
|
||||
document.scenes[targetScene.id] = targetScene;
|
||||
document.sequences.sequences["sequence-enter-house"] = createProjectSequence({
|
||||
id: "sequence-enter-house",
|
||||
title: "Enter House",
|
||||
effects: [
|
||||
{
|
||||
stepClass: "impulse",
|
||||
type: "startSceneTransition",
|
||||
targetSceneId: targetScene.id,
|
||||
targetEntryEntityId: "missing-entry"
|
||||
}
|
||||
]
|
||||
});
|
||||
document.sequences.sequences["sequence-enter-house"] =
|
||||
createProjectSequence({
|
||||
id: "sequence-enter-house",
|
||||
title: "Enter House",
|
||||
effects: [
|
||||
{
|
||||
stepClass: "impulse",
|
||||
type: "startSceneTransition",
|
||||
targetSceneId: targetScene.id,
|
||||
targetEntryEntityId: "missing-entry"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
expect(() =>
|
||||
parseProjectDocumentJson(JSON.stringify(document))
|
||||
).toThrow("target entry");
|
||||
expect(() => parseProjectDocumentJson(JSON.stringify(document))).toThrow(
|
||||
"target entry"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,7 +6,10 @@ import {
|
||||
createPlayActorAnimationControlEffect,
|
||||
createSetActorPresenceControlEffect
|
||||
} from "../../src/controls/control-surface";
|
||||
import { createBoxBrush, deriveBoxBrushSizeFromGeometry } from "../../src/document/brushes";
|
||||
import {
|
||||
createBoxBrush,
|
||||
deriveBoxBrushSizeFromGeometry
|
||||
} from "../../src/document/brushes";
|
||||
import { createScenePath } from "../../src/document/paths";
|
||||
import { createDefaultProjectTimeSettings } from "../../src/document/project-time-settings";
|
||||
import { createTerrain } from "../../src/document/terrains";
|
||||
@@ -69,8 +72,16 @@ import {
|
||||
} from "../../src/interactions/interaction-links";
|
||||
import { STARTER_MATERIAL_LIBRARY } from "../../src/materials/starter-material-library";
|
||||
import { createModelInstance } from "../../src/assets/model-instances";
|
||||
import { createProjectAssetStorageKey, type AudioAssetRecord, type ImageAssetRecord, type ModelAssetRecord } from "../../src/assets/project-assets";
|
||||
import { parseSceneDocumentJson, serializeSceneDocument } from "../../src/serialization/scene-document-json";
|
||||
import {
|
||||
createProjectAssetStorageKey,
|
||||
type AudioAssetRecord,
|
||||
type ImageAssetRecord,
|
||||
type ModelAssetRecord
|
||||
} from "../../src/assets/project-assets";
|
||||
import {
|
||||
parseSceneDocumentJson,
|
||||
serializeSceneDocument
|
||||
} from "../../src/serialization/scene-document-json";
|
||||
|
||||
describe("scene document JSON", () => {
|
||||
it("round-trips the current empty schema", () => {
|
||||
@@ -125,7 +136,9 @@ describe("scene document JSON", () => {
|
||||
);
|
||||
|
||||
expect(migratedDocument.version).toBe(SCENE_DOCUMENT_VERSION);
|
||||
expect(migratedDocument.world.background).toEqual(document.world.background);
|
||||
expect(migratedDocument.world.background).toEqual(
|
||||
document.world.background
|
||||
);
|
||||
expect(migratedDocument.world.shaderSky.dayTopColorHex).toBe("#335577");
|
||||
expect(migratedDocument.world.shaderSky.dayBottomColorHex).toBe("#aaccee");
|
||||
});
|
||||
@@ -238,9 +251,9 @@ describe("scene document JSON", () => {
|
||||
})
|
||||
});
|
||||
|
||||
expect(
|
||||
parseSceneDocumentJson(serializeSceneDocument(document))
|
||||
).toEqual(document);
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
});
|
||||
|
||||
it("round-trips actor scheduler animation and follow-path routines in the scene document schema", () => {
|
||||
@@ -277,35 +290,36 @@ describe("scene document JSON", () => {
|
||||
document.assets[npcModelAsset.id] = npcModelAsset;
|
||||
document.entities[npc.id] = npc;
|
||||
document.paths[path.id] = path;
|
||||
document.scheduler.routines["routine-patrol"] = createProjectScheduleRoutine({
|
||||
id: "routine-patrol",
|
||||
title: "Patrolling",
|
||||
target: actorTarget,
|
||||
startHour: 9,
|
||||
endHour: 13,
|
||||
effects: [
|
||||
createSetActorPresenceControlEffect({
|
||||
target: actorTarget,
|
||||
active: true
|
||||
}),
|
||||
createPlayActorAnimationControlEffect({
|
||||
target: actorTarget,
|
||||
clipName: "Walk",
|
||||
loop: true
|
||||
}),
|
||||
createFollowActorPathControlEffect({
|
||||
target: actorTarget,
|
||||
pathId: path.id,
|
||||
speed: 2,
|
||||
loop: false,
|
||||
progressMode: "deriveFromTime"
|
||||
})
|
||||
]
|
||||
});
|
||||
document.scheduler.routines["routine-patrol"] =
|
||||
createProjectScheduleRoutine({
|
||||
id: "routine-patrol",
|
||||
title: "Patrolling",
|
||||
target: actorTarget,
|
||||
startHour: 9,
|
||||
endHour: 13,
|
||||
effects: [
|
||||
createSetActorPresenceControlEffect({
|
||||
target: actorTarget,
|
||||
active: true
|
||||
}),
|
||||
createPlayActorAnimationControlEffect({
|
||||
target: actorTarget,
|
||||
clipName: "Walk",
|
||||
loop: true
|
||||
}),
|
||||
createFollowActorPathControlEffect({
|
||||
target: actorTarget,
|
||||
pathId: path.id,
|
||||
speed: 2,
|
||||
loop: false,
|
||||
progressMode: "deriveFromTime"
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
expect(
|
||||
parseSceneDocumentJson(serializeSceneDocument(document))
|
||||
).toEqual(document);
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
});
|
||||
|
||||
it("round-trips a document containing a canonical box brush", () => {
|
||||
@@ -331,7 +345,9 @@ describe("scene document JSON", () => {
|
||||
}
|
||||
};
|
||||
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(document);
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
});
|
||||
|
||||
it("round-trips floating-point whitebox box transforms without accidental snapping", () => {
|
||||
@@ -361,7 +377,9 @@ describe("scene document JSON", () => {
|
||||
}
|
||||
};
|
||||
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(document);
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
});
|
||||
|
||||
it("round-trips per-face material and UV state", () => {
|
||||
@@ -401,7 +419,9 @@ describe("scene document JSON", () => {
|
||||
}
|
||||
};
|
||||
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(document);
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
});
|
||||
|
||||
it("round-trips whitebox box volume settings", () => {
|
||||
@@ -450,7 +470,9 @@ describe("scene document JSON", () => {
|
||||
}
|
||||
};
|
||||
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(document);
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
});
|
||||
|
||||
it("migrates pre-light-volume documents to the current schema version", () => {
|
||||
@@ -512,11 +534,15 @@ describe("scene document JSON", () => {
|
||||
}
|
||||
};
|
||||
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(document);
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
});
|
||||
|
||||
it("round-trips authored world environment settings", () => {
|
||||
const document = createEmptySceneDocument({ name: "World Environment Scene" });
|
||||
const document = createEmptySceneDocument({
|
||||
name: "World Environment Scene"
|
||||
});
|
||||
document.world.background = {
|
||||
mode: "verticalGradient",
|
||||
topColorHex: "#6a87ab",
|
||||
@@ -536,11 +562,15 @@ describe("scene document JSON", () => {
|
||||
}
|
||||
};
|
||||
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(document);
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
});
|
||||
|
||||
it("round-trips authored advanced rendering settings", () => {
|
||||
const document = createEmptySceneDocument({ name: "Advanced Rendering Scene" });
|
||||
const document = createEmptySceneDocument({
|
||||
name: "Advanced Rendering Scene"
|
||||
});
|
||||
document.world.advancedRendering = {
|
||||
enabled: true,
|
||||
shadows: {
|
||||
@@ -581,7 +611,9 @@ describe("scene document JSON", () => {
|
||||
}
|
||||
};
|
||||
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(document);
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
});
|
||||
|
||||
it("migrates v32 scene documents without whitebox bevel settings to defaults", () => {
|
||||
@@ -627,7 +659,9 @@ describe("scene document JSON", () => {
|
||||
waterPath: "quality"
|
||||
}
|
||||
},
|
||||
materials: STARTER_MATERIAL_LIBRARY.reduce<Record<string, (typeof STARTER_MATERIAL_LIBRARY)[number]>>((registry, material) => {
|
||||
materials: STARTER_MATERIAL_LIBRARY.reduce<
|
||||
Record<string, (typeof STARTER_MATERIAL_LIBRARY)[number]>
|
||||
>((registry, material) => {
|
||||
registry[material.id] = material;
|
||||
return registry;
|
||||
}, {}),
|
||||
@@ -652,7 +686,9 @@ describe("scene document JSON", () => {
|
||||
interactionLinks: {}
|
||||
} as unknown);
|
||||
|
||||
expect(migratedDocument.world.advancedRendering.waterReflectionMode).toBe("none");
|
||||
expect(migratedDocument.world.advancedRendering.waterReflectionMode).toBe(
|
||||
"none"
|
||||
);
|
||||
expect(migratedDocument.brushes["brush-water-legacy"]?.volume).toEqual({
|
||||
mode: "water",
|
||||
water: expect.objectContaining({
|
||||
@@ -663,8 +699,11 @@ describe("scene document JSON", () => {
|
||||
});
|
||||
|
||||
it("migrates legacy documents without advanced rendering settings to defaults", () => {
|
||||
const emptyScene = createEmptySceneDocument({ name: "Legacy Advanced Rendering Scene" });
|
||||
const { advancedRendering: _advancedRendering, ...legacyWorld } = emptyScene.world;
|
||||
const emptyScene = createEmptySceneDocument({
|
||||
name: "Legacy Advanced Rendering Scene"
|
||||
});
|
||||
const { advancedRendering: _advancedRendering, ...legacyWorld } =
|
||||
emptyScene.world;
|
||||
|
||||
const migratedDocument = migrateSceneDocument({
|
||||
version: SPATIAL_AUDIO_SCENE_DOCUMENT_VERSION,
|
||||
@@ -680,7 +719,9 @@ describe("scene document JSON", () => {
|
||||
});
|
||||
|
||||
expect(migratedDocument.version).toBe(SCENE_DOCUMENT_VERSION);
|
||||
expect(migratedDocument.world.advancedRendering).toEqual(emptyScene.world.advancedRendering);
|
||||
expect(migratedDocument.world.advancedRendering).toEqual(
|
||||
emptyScene.world.advancedRendering
|
||||
);
|
||||
});
|
||||
|
||||
it("round-trips authored local lights and an image background asset", () => {
|
||||
@@ -743,7 +784,9 @@ describe("scene document JSON", () => {
|
||||
environmentIntensity: 0.75
|
||||
};
|
||||
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(document);
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
});
|
||||
|
||||
it("round-trips a document containing an authored PlayerStart entity", () => {
|
||||
@@ -823,7 +866,9 @@ describe("scene document JSON", () => {
|
||||
}
|
||||
};
|
||||
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(document);
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
});
|
||||
|
||||
it("migrates version 14 documents without entity names", () => {
|
||||
@@ -920,7 +965,10 @@ describe("scene document JSON", () => {
|
||||
});
|
||||
|
||||
expect(migratedDocument.version).toBe(SCENE_DOCUMENT_VERSION);
|
||||
expect(migratedDocument.modelInstances["model-instance-legacy-collider"].collision).toEqual({
|
||||
expect(
|
||||
migratedDocument.modelInstances["model-instance-legacy-collider"]
|
||||
.collision
|
||||
).toEqual({
|
||||
mode: "none",
|
||||
visible: false
|
||||
});
|
||||
@@ -1128,7 +1176,9 @@ describe("scene document JSON", () => {
|
||||
id: "entity-player-start-legacy-pause-binding"
|
||||
});
|
||||
const legacyDocument = {
|
||||
...createEmptySceneDocument({ name: "Legacy Player Pause Binding Scene" }),
|
||||
...createEmptySceneDocument({
|
||||
name: "Legacy Player Pause Binding Scene"
|
||||
}),
|
||||
version: NPC_DIALOGUE_REFERENCE_SCENE_DOCUMENT_VERSION,
|
||||
entities: {
|
||||
[playerStart.id]: {
|
||||
@@ -1331,10 +1381,8 @@ describe("scene document JSON", () => {
|
||||
kind: "responsive"
|
||||
}
|
||||
});
|
||||
const {
|
||||
directionOnly: _directionOnly,
|
||||
...legacyJump
|
||||
} = playerStart.movementTemplate.jump;
|
||||
const { directionOnly: _directionOnly, ...legacyJump } =
|
||||
playerStart.movementTemplate.jump;
|
||||
const legacyDocument = {
|
||||
...createEmptySceneDocument({
|
||||
name: "Legacy Player Air Direction Scene"
|
||||
@@ -1366,7 +1414,9 @@ describe("scene document JSON", () => {
|
||||
kind: "model",
|
||||
sourceName: "legacy-authored-state.glb",
|
||||
mimeType: "model/gltf-binary",
|
||||
storageKey: createProjectAssetStorageKey("asset-model-authored-state-legacy"),
|
||||
storageKey: createProjectAssetStorageKey(
|
||||
"asset-model-authored-state-legacy"
|
||||
),
|
||||
byteLength: 2048,
|
||||
metadata: {
|
||||
kind: "model",
|
||||
@@ -1539,7 +1589,9 @@ describe("scene document JSON", () => {
|
||||
}
|
||||
};
|
||||
|
||||
const roundTripDocument = parseSceneDocumentJson(serializeSceneDocument(document));
|
||||
const roundTripDocument = parseSceneDocumentJson(
|
||||
serializeSceneDocument(document)
|
||||
);
|
||||
|
||||
expect(roundTripDocument).toEqual(document);
|
||||
expect(roundTripDocument.modelInstances).toEqual({});
|
||||
@@ -1655,7 +1707,9 @@ describe("scene document JSON", () => {
|
||||
}
|
||||
};
|
||||
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(document);
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
});
|
||||
|
||||
it("round-trips imported model assets and placed model instances", () => {
|
||||
@@ -1725,7 +1779,9 @@ describe("scene document JSON", () => {
|
||||
}
|
||||
};
|
||||
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(document);
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
});
|
||||
|
||||
it("round-trips authored model-instance collision settings", () => {
|
||||
@@ -1783,7 +1839,9 @@ describe("scene document JSON", () => {
|
||||
}
|
||||
};
|
||||
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(document);
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
});
|
||||
|
||||
it("round-trips authored static-simple model-instance collision settings", () => {
|
||||
@@ -1792,7 +1850,9 @@ describe("scene document JSON", () => {
|
||||
kind: "model",
|
||||
sourceName: "collision-static-simple.glb",
|
||||
mimeType: "model/gltf-binary",
|
||||
storageKey: createProjectAssetStorageKey("asset-model-static-simple-collider"),
|
||||
storageKey: createProjectAssetStorageKey(
|
||||
"asset-model-static-simple-collider"
|
||||
),
|
||||
byteLength: 64,
|
||||
metadata: {
|
||||
kind: "model",
|
||||
@@ -1832,7 +1892,9 @@ describe("scene document JSON", () => {
|
||||
}
|
||||
});
|
||||
const document = {
|
||||
...createEmptySceneDocument({ name: "Static Simple Model Collision Scene" }),
|
||||
...createEmptySceneDocument({
|
||||
name: "Static Simple Model Collision Scene"
|
||||
}),
|
||||
assets: {
|
||||
[asset.id]: asset
|
||||
},
|
||||
@@ -1841,7 +1903,9 @@ describe("scene document JSON", () => {
|
||||
}
|
||||
};
|
||||
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(document);
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
});
|
||||
|
||||
it("migrates version 29 scene documents to preserve existing model collision settings", () => {
|
||||
@@ -1904,7 +1968,9 @@ describe("scene document JSON", () => {
|
||||
const migratedDocument = migrateSceneDocument(legacyDocument);
|
||||
|
||||
expect(migratedDocument.version).toBe(SCENE_DOCUMENT_VERSION);
|
||||
expect(migratedDocument.modelInstances["model-instance-v29-collider"].collision).toEqual({
|
||||
expect(
|
||||
migratedDocument.modelInstances["model-instance-v29-collider"].collision
|
||||
).toEqual({
|
||||
mode: "dynamic",
|
||||
visible: true
|
||||
});
|
||||
@@ -1957,7 +2023,9 @@ describe("scene document JSON", () => {
|
||||
}
|
||||
};
|
||||
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(document);
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
});
|
||||
|
||||
it("migrates the foundation schema to the current schema version", () => {
|
||||
@@ -1977,7 +2045,9 @@ describe("scene document JSON", () => {
|
||||
expect(migratedDocument.version).toBe(SCENE_DOCUMENT_VERSION);
|
||||
expect(migratedDocument.brushes).toEqual({});
|
||||
expect(migratedDocument.name).toBe("Foundation Scene");
|
||||
expect(Object.keys(migratedDocument.materials)).toEqual(STARTER_MATERIAL_LIBRARY.map((material) => material.id));
|
||||
expect(Object.keys(migratedDocument.materials)).toEqual(
|
||||
STARTER_MATERIAL_LIBRARY.map((material) => material.id)
|
||||
);
|
||||
});
|
||||
|
||||
it("migrates NPC foundation entities to include default collider settings", () => {
|
||||
@@ -2150,7 +2220,9 @@ describe("scene document JSON", () => {
|
||||
});
|
||||
|
||||
expect(migratedDocument.version).toBe(SCENE_DOCUMENT_VERSION);
|
||||
expect(migratedDocument.brushes["brush-legacy"].faces.posZ.materialId).toBe("starter-amber-grid");
|
||||
expect(migratedDocument.brushes["brush-legacy"].faces.posZ.materialId).toBe(
|
||||
"starter-amber-grid"
|
||||
);
|
||||
expect(migratedDocument.brushes["brush-legacy"].faces.posZ.uv).toEqual({
|
||||
offset: {
|
||||
x: 0,
|
||||
@@ -2284,10 +2356,12 @@ describe("scene document JSON", () => {
|
||||
|
||||
expect(migratedDocument.version).toBe(SCENE_DOCUMENT_VERSION);
|
||||
expect(migratedDocument.brushes["brush-room-shell"].name).toBeUndefined();
|
||||
expect(migratedDocument.entities["entity-player-start-main"]).toMatchObject({
|
||||
kind: "playerStart",
|
||||
yawDegrees: 45
|
||||
});
|
||||
expect(migratedDocument.entities["entity-player-start-main"]).toMatchObject(
|
||||
{
|
||||
kind: "playerStart",
|
||||
yawDegrees: 45
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it("migrates slice 1.4 documents to the world-environment schema without changing authored solid backgrounds", () => {
|
||||
@@ -2395,10 +2469,12 @@ describe("scene document JSON", () => {
|
||||
});
|
||||
|
||||
expect(migratedDocument.version).toBe(SCENE_DOCUMENT_VERSION);
|
||||
expect(migratedDocument.entities["entity-player-start-main"]).toMatchObject({
|
||||
kind: "playerStart",
|
||||
yawDegrees: 90
|
||||
});
|
||||
expect(migratedDocument.entities["entity-player-start-main"]).toMatchObject(
|
||||
{
|
||||
kind: "playerStart",
|
||||
yawDegrees: 90
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it("migrates slice 2.1 documents to the interaction-link schema with empty interaction links", () => {
|
||||
@@ -2549,8 +2625,12 @@ describe("scene document JSON", () => {
|
||||
});
|
||||
|
||||
expect(migratedDocument.version).toBe(SCENE_DOCUMENT_VERSION);
|
||||
expect(migratedDocument.modelInstances["mi-1"].animationClipName).toBeUndefined();
|
||||
expect(migratedDocument.modelInstances["mi-1"].animationAutoplay).toBeUndefined();
|
||||
expect(
|
||||
migratedDocument.modelInstances["mi-1"].animationClipName
|
||||
).toBeUndefined();
|
||||
expect(
|
||||
migratedDocument.modelInstances["mi-1"].animationAutoplay
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("migrates v12 sound emitters to the current schema version", () => {
|
||||
@@ -2604,7 +2684,8 @@ describe("scene document JSON", () => {
|
||||
|
||||
it("migrates v13 documents without the advanced rendering block to the current schema version", () => {
|
||||
const emptyScene = createEmptySceneDocument();
|
||||
const { advancedRendering: _advancedRendering, ...legacyWorld } = emptyScene.world;
|
||||
const { advancedRendering: _advancedRendering, ...legacyWorld } =
|
||||
emptyScene.world;
|
||||
|
||||
const migratedDocument = migrateSceneDocument({
|
||||
version: SPATIAL_AUDIO_SCENE_DOCUMENT_VERSION,
|
||||
@@ -2620,7 +2701,9 @@ describe("scene document JSON", () => {
|
||||
});
|
||||
|
||||
expect(migratedDocument.version).toBe(SCENE_DOCUMENT_VERSION);
|
||||
expect(migratedDocument.world.advancedRendering).toEqual(emptyScene.world.advancedRendering);
|
||||
expect(migratedDocument.world.advancedRendering).toEqual(
|
||||
emptyScene.world.advancedRendering
|
||||
);
|
||||
});
|
||||
|
||||
it("migrates v19 whitebox boxes without volume settings to the current schema version", () => {
|
||||
@@ -2658,9 +2741,15 @@ describe("scene document JSON", () => {
|
||||
} as any);
|
||||
|
||||
expect(migratedDocument.version).toBe(SCENE_DOCUMENT_VERSION);
|
||||
expect(migratedDocument.brushes["brush-legacy"].volume).toEqual({ mode: "none" });
|
||||
expect(migratedDocument.world.advancedRendering.fogPath).toBe("performance");
|
||||
expect(migratedDocument.world.advancedRendering.waterPath).toBe("performance");
|
||||
expect(migratedDocument.brushes["brush-legacy"].volume).toEqual({
|
||||
mode: "none"
|
||||
});
|
||||
expect(migratedDocument.world.advancedRendering.fogPath).toBe(
|
||||
"performance"
|
||||
);
|
||||
expect(migratedDocument.world.advancedRendering.waterPath).toBe(
|
||||
"performance"
|
||||
);
|
||||
});
|
||||
|
||||
it("round-trips authored playAnimation and stopAnimation interaction links", () => {
|
||||
@@ -2717,7 +2806,9 @@ describe("scene document JSON", () => {
|
||||
}
|
||||
};
|
||||
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(document);
|
||||
expect(parseSceneDocumentJson(serializeSceneDocument(document))).toEqual(
|
||||
document
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects a v12 document where a playAnimation action has an empty clipName", () => {
|
||||
|
||||
@@ -107,9 +107,7 @@ describe("resolveWorldShaderSkyRenderState", () => {
|
||||
expect(noonSky?.sky.topColorHex).toBe("#88ccff");
|
||||
expect(noonSky?.sky.bottomColorHex).toBe("#dff3ff");
|
||||
expect(dawnSky?.sky.topColorHex).not.toBe(noonSky?.sky.topColorHex);
|
||||
expect(dawnSky?.sky.topColorHex).not.toBe(
|
||||
midnightSky?.sky.topColorHex
|
||||
);
|
||||
expect(dawnSky?.sky.topColorHex).not.toBe(midnightSky?.sky.topColorHex);
|
||||
expect(midnightSky?.stars.visibility ?? 0).toBeGreaterThan(
|
||||
dawnSky?.stars.visibility ?? 0
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user