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:
@@ -235,9 +235,7 @@ import {
|
||||
createProjectSequence,
|
||||
type ProjectSequenceLibrary
|
||||
} from "../sequencer/project-sequences";
|
||||
import {
|
||||
type SequenceClip
|
||||
} from "../sequencer/project-sequence-steps";
|
||||
import { type SequenceClip } from "../sequencer/project-sequence-steps";
|
||||
import {
|
||||
createScenePath,
|
||||
createScenePathPoint,
|
||||
@@ -375,10 +373,15 @@ function readNpcDialogues(
|
||||
|
||||
return createProjectDialogue({
|
||||
id: expectString(dialogueValue.id, `${label}.${dialogueIndex}.id`),
|
||||
title: expectString(dialogueValue.title, `${label}.${dialogueIndex}.title`),
|
||||
title: expectString(
|
||||
dialogueValue.title,
|
||||
`${label}.${dialogueIndex}.title`
|
||||
),
|
||||
lines: linesValue.map((lineValue, lineIndex) => {
|
||||
if (!isRecord(lineValue)) {
|
||||
throw new Error(`${label}.${dialogueIndex}.lines.${lineIndex} must be an object.`);
|
||||
throw new Error(
|
||||
`${label}.${dialogueIndex}.lines.${lineIndex} must be an object.`
|
||||
);
|
||||
}
|
||||
|
||||
return createProjectDialogueLine({
|
||||
@@ -1906,13 +1909,19 @@ function readTerrain(value: unknown, label: string): Terrain {
|
||||
value.layers === undefined
|
||||
? undefined
|
||||
: (() => {
|
||||
if (!Array.isArray(value.layers) || value.layers.some((layer) => !isRecord(layer))) {
|
||||
throw new Error(`${label}.layers must be an array of layer objects.`);
|
||||
if (
|
||||
!Array.isArray(value.layers) ||
|
||||
value.layers.some((layer) => !isRecord(layer))
|
||||
) {
|
||||
throw new Error(
|
||||
`${label}.layers must be an array of layer objects.`
|
||||
);
|
||||
}
|
||||
|
||||
return value.layers.map((layerValue, layerIndex) => ({
|
||||
materialId:
|
||||
layerValue.materialId === undefined || layerValue.materialId === null
|
||||
layerValue.materialId === undefined ||
|
||||
layerValue.materialId === null
|
||||
? null
|
||||
: expectString(
|
||||
layerValue.materialId,
|
||||
@@ -2053,7 +2062,9 @@ function readMaterialRegistry(
|
||||
);
|
||||
|
||||
if (legacyMaterialId !== materialId) {
|
||||
throw new Error(`${label}.${materialId}.id must match the registry key.`);
|
||||
throw new Error(
|
||||
`${label}.${materialId}.id must match the registry key.`
|
||||
);
|
||||
}
|
||||
|
||||
if (starterRegistry[materialId] === undefined) {
|
||||
@@ -2211,13 +2222,14 @@ function readBoxBrushFaces(
|
||||
materials: SceneDocument["materials"],
|
||||
allowMissingUvState: boolean
|
||||
): BoxBrushFaces {
|
||||
return readBrushFaces(
|
||||
value,
|
||||
label,
|
||||
materials,
|
||||
allowMissingUvState,
|
||||
["posX", "negX", "posY", "negY", "posZ", "negZ"]
|
||||
) as BoxBrushFaces;
|
||||
return readBrushFaces(value, label, materials, allowMissingUvState, [
|
||||
"posX",
|
||||
"negX",
|
||||
"posY",
|
||||
"negY",
|
||||
"posZ",
|
||||
"negZ"
|
||||
]) as BoxBrushFaces;
|
||||
}
|
||||
|
||||
function readBrushFaces(
|
||||
@@ -2232,7 +2244,9 @@ function readBrushFaces(
|
||||
}
|
||||
|
||||
const supportedFaceIds = new Set(faceIds);
|
||||
const extraFaceKeys = Object.keys(value).filter((faceId) => !supportedFaceIds.has(faceId));
|
||||
const extraFaceKeys = Object.keys(value).filter(
|
||||
(faceId) => !supportedFaceIds.has(faceId)
|
||||
);
|
||||
|
||||
if (extraFaceKeys.length > 0) {
|
||||
throw new Error(
|
||||
@@ -2733,11 +2747,15 @@ function readWorldTimePhaseProfile(
|
||||
};
|
||||
|
||||
return {
|
||||
background: readWorldBackgroundSettings(value.background, `${label}.background`, {
|
||||
allowMissing: true,
|
||||
allowShader: false,
|
||||
defaultValue: fallbackBackground
|
||||
}),
|
||||
background: readWorldBackgroundSettings(
|
||||
value.background,
|
||||
`${label}.background`,
|
||||
{
|
||||
allowMissing: true,
|
||||
allowShader: false,
|
||||
defaultValue: fallbackBackground
|
||||
}
|
||||
),
|
||||
skyTopColorHex,
|
||||
skyBottomColorHex,
|
||||
ambientColorHex: expectHexColor(
|
||||
@@ -3870,27 +3888,29 @@ function readProjectScheduler(
|
||||
routineValue.target,
|
||||
`${label}.routines.${routineId}.target`
|
||||
);
|
||||
const effects =
|
||||
Array.isArray(routineValue.effects)
|
||||
? routineValue.effects.map((effectValue, effectIndex) =>
|
||||
readControlEffect(
|
||||
effectValue,
|
||||
`${label}.routines.${routineId}.effects.${effectIndex}`
|
||||
)
|
||||
const effects = Array.isArray(routineValue.effects)
|
||||
? routineValue.effects.map((effectValue, effectIndex) =>
|
||||
readControlEffect(
|
||||
effectValue,
|
||||
`${label}.routines.${routineId}.effects.${effectIndex}`
|
||||
)
|
||||
: routineValue.effect === undefined
|
||||
? undefined
|
||||
: [
|
||||
readControlEffect(
|
||||
routineValue.effect,
|
||||
`${label}.routines.${routineId}.effect`
|
||||
)
|
||||
];
|
||||
)
|
||||
: routineValue.effect === undefined
|
||||
? undefined
|
||||
: [
|
||||
readControlEffect(
|
||||
routineValue.effect,
|
||||
`${label}.routines.${routineId}.effect`
|
||||
)
|
||||
];
|
||||
|
||||
return [
|
||||
routineId,
|
||||
createProjectScheduleRoutine({
|
||||
id: expectString(routineValue.id, `${label}.routines.${routineId}.id`),
|
||||
id: expectString(
|
||||
routineValue.id,
|
||||
`${label}.routines.${routineId}.id`
|
||||
),
|
||||
title: expectString(
|
||||
routineValue.title,
|
||||
`${label}.routines.${routineId}.title`
|
||||
@@ -4018,7 +4038,10 @@ function readProjectDialogueLibrary(
|
||||
}
|
||||
|
||||
dialogues[dialogueKey] = createProjectDialogue({
|
||||
id: expectString(dialogueValue.id, `${label}.dialogues.${dialogueKey}.id`),
|
||||
id: expectString(
|
||||
dialogueValue.id,
|
||||
`${label}.dialogues.${dialogueKey}.id`
|
||||
),
|
||||
title: expectString(
|
||||
dialogueValue.title,
|
||||
`${label}.dialogues.${dialogueKey}.title`
|
||||
@@ -4049,7 +4072,10 @@ function readProjectDialogueLibrary(
|
||||
};
|
||||
}
|
||||
|
||||
function readProjectSequenceEffect(value: unknown, label: string): SequenceClip {
|
||||
function readProjectSequenceEffect(
|
||||
value: unknown,
|
||||
label: string
|
||||
): SequenceClip {
|
||||
if (!isRecord(value)) {
|
||||
throw new Error(`${label} must be an object.`);
|
||||
}
|
||||
@@ -4070,7 +4096,9 @@ function readProjectSequenceEffect(value: unknown, label: string): SequenceClip
|
||||
};
|
||||
case "makeNpcTalk":
|
||||
if (stepClass !== "impulse") {
|
||||
throw new Error(`${label}.makeNpcTalk effects must use the impulse class.`);
|
||||
throw new Error(
|
||||
`${label}.makeNpcTalk effects must use the impulse class.`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -4084,7 +4112,9 @@ function readProjectSequenceEffect(value: unknown, label: string): SequenceClip
|
||||
};
|
||||
case "teleportPlayer":
|
||||
if (stepClass !== "impulse") {
|
||||
throw new Error(`${label}.teleportPlayer effects must use the impulse class.`);
|
||||
throw new Error(
|
||||
`${label}.teleportPlayer effects must use the impulse class.`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -4119,7 +4149,9 @@ function readProjectSequenceEffect(value: unknown, label: string): SequenceClip
|
||||
const isLegacyToggle = value.type === "toggleVisibility";
|
||||
|
||||
if (stepClass !== "impulse") {
|
||||
throw new Error(`${label}.${String(value.type)} effects must use the impulse class.`);
|
||||
throw new Error(
|
||||
`${label}.${String(value.type)} effects must use the impulse class.`
|
||||
);
|
||||
}
|
||||
|
||||
if (isLegacyToggle) {
|
||||
@@ -4135,8 +4167,7 @@ function readProjectSequenceEffect(value: unknown, label: string): SequenceClip
|
||||
kind: "brush",
|
||||
brushId: expectString(value.targetBrushId, `${label}.targetBrushId`)
|
||||
},
|
||||
mode:
|
||||
visible === undefined ? "toggle" : visible ? "show" : "hide"
|
||||
mode: visible === undefined ? "toggle" : visible ? "show" : "hide"
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4144,7 +4175,10 @@ function readProjectSequenceEffect(value: unknown, label: string): SequenceClip
|
||||
throw new Error(`${label}.target must be an object.`);
|
||||
}
|
||||
|
||||
const targetKind = expectString(value.target.kind, `${label}.target.kind`);
|
||||
const targetKind = expectString(
|
||||
value.target.kind,
|
||||
`${label}.target.kind`
|
||||
);
|
||||
|
||||
if (targetKind !== "brush" && targetKind !== "modelInstance") {
|
||||
throw new Error(`${label}.target.kind must be brush or modelInstance.`);
|
||||
@@ -4224,7 +4258,10 @@ function readProjectSequenceLibrary(
|
||||
}
|
||||
|
||||
sequences[sequenceKey] = createProjectSequence({
|
||||
id: expectString(sequenceValue.id, `${label}.sequences.${sequenceKey}.id`),
|
||||
id: expectString(
|
||||
sequenceValue.id,
|
||||
`${label}.sequences.${sequenceKey}.id`
|
||||
),
|
||||
title: expectString(
|
||||
sequenceValue.title,
|
||||
`${label}.sequences.${sequenceKey}.title`
|
||||
@@ -5095,8 +5132,10 @@ export function migrateSceneDocument(source: unknown): SceneDocument {
|
||||
source.version !== PROJECT_SEQUENCE_CLIPS_SCENE_DOCUMENT_VERSION &&
|
||||
source.version !== PROJECT_SEQUENCE_TIMING_SCENE_DOCUMENT_VERSION &&
|
||||
source.version !== PROJECT_SEQUENCE_EFFECTS_SCENE_DOCUMENT_VERSION &&
|
||||
source.version !== PROJECT_SEQUENCE_UNIFIED_VISIBILITY_SCENE_DOCUMENT_VERSION &&
|
||||
source.version !== SCENE_TRANSITION_SEQUENCE_EFFECTS_SCENE_DOCUMENT_VERSION &&
|
||||
source.version !==
|
||||
PROJECT_SEQUENCE_UNIFIED_VISIBILITY_SCENE_DOCUMENT_VERSION &&
|
||||
source.version !==
|
||||
SCENE_TRANSITION_SEQUENCE_EFFECTS_SCENE_DOCUMENT_VERSION &&
|
||||
source.version !== AUTHORED_TERRAIN_FOUNDATION_SCENE_DOCUMENT_VERSION &&
|
||||
source.version !== AUTHORED_TERRAIN_PAINT_SCENE_DOCUMENT_VERSION &&
|
||||
source.version !== AUTHORED_TERRAIN_COLLISION_SCENE_DOCUMENT_VERSION &&
|
||||
@@ -5115,9 +5154,13 @@ export function migrateSceneDocument(source: unknown): SceneDocument {
|
||||
source.version < STARTER_PBR_MATERIAL_LIBRARY_SCENE_DOCUMENT_VERSION
|
||||
});
|
||||
const assets = readAssets(source.assets);
|
||||
const legacyDialogues = readProjectDialogueLibrary(source.dialogues, "dialogues", {
|
||||
allowMissing: true
|
||||
});
|
||||
const legacyDialogues = readProjectDialogueLibrary(
|
||||
source.dialogues,
|
||||
"dialogues",
|
||||
{
|
||||
allowMissing: true
|
||||
}
|
||||
);
|
||||
|
||||
const migratedDocument: SceneDocument = {
|
||||
version: SCENE_DOCUMENT_VERSION,
|
||||
@@ -5130,7 +5173,8 @@ export function migrateSceneDocument(source: unknown): SceneDocument {
|
||||
source.version < PROJECT_SCHEDULER_FOUNDATION_SCENE_DOCUMENT_VERSION
|
||||
}),
|
||||
sequences: readProjectSequenceLibrary(source.sequences, "sequences", {
|
||||
allowMissing: source.version < PROJECT_SEQUENCE_LIBRARY_SCENE_DOCUMENT_VERSION
|
||||
allowMissing:
|
||||
source.version < PROJECT_SEQUENCE_LIBRARY_SCENE_DOCUMENT_VERSION
|
||||
}),
|
||||
world: readWorldSettings(source.world, {
|
||||
legacyProjectTimeValue:
|
||||
@@ -5248,9 +5292,13 @@ export function migrateProjectDocument(source: unknown): ProjectDocument {
|
||||
source.version < SCENE_EDITOR_PREFERENCES_SCENE_DOCUMENT_VERSION;
|
||||
const allowMissingTimeSettings =
|
||||
source.version < PROJECT_TIME_SYSTEM_SCENE_DOCUMENT_VERSION;
|
||||
const legacyDialogues = readProjectDialogueLibrary(source.dialogues, "dialogues", {
|
||||
allowMissing: true
|
||||
});
|
||||
const legacyDialogues = readProjectDialogueLibrary(
|
||||
source.dialogues,
|
||||
"dialogues",
|
||||
{
|
||||
allowMissing: true
|
||||
}
|
||||
);
|
||||
|
||||
for (const [sceneKey, sceneValue] of Object.entries(source.scenes)) {
|
||||
scenes[sceneKey] = readProjectScene(
|
||||
|
||||
@@ -56,7 +56,10 @@ import {
|
||||
normalizeTorusMajorSegmentCount,
|
||||
normalizeTorusTubeSegmentCount
|
||||
} from "./brushes";
|
||||
import { getBrushFaceIds, getBrushVertexIds } from "../geometry/whitebox-topology";
|
||||
import {
|
||||
getBrushFaceIds,
|
||||
getBrushVertexIds
|
||||
} from "../geometry/whitebox-topology";
|
||||
import {
|
||||
createSceneDocumentFromProject,
|
||||
type ProjectDocument,
|
||||
@@ -3315,7 +3318,12 @@ function validateNpcEntity(
|
||||
|
||||
for (const [dialogueIndex, dialogue] of entity.dialogues.entries()) {
|
||||
const dialoguePath = `${path}.dialogues.${dialogueIndex}`;
|
||||
registerAuthoredId(dialogue.id, dialoguePath, seenNpcDialogueIds, diagnostics);
|
||||
registerAuthoredId(
|
||||
dialogue.id,
|
||||
dialoguePath,
|
||||
seenNpcDialogueIds,
|
||||
diagnostics
|
||||
);
|
||||
validateProjectDialogue(
|
||||
dialogue,
|
||||
dialoguePath,
|
||||
@@ -3326,7 +3334,9 @@ function validateNpcEntity(
|
||||
|
||||
if (
|
||||
entity.defaultDialogueId !== null &&
|
||||
!entity.dialogues.some((dialogue) => dialogue.id === entity.defaultDialogueId)
|
||||
!entity.dialogues.some(
|
||||
(dialogue) => dialogue.id === entity.defaultDialogueId
|
||||
)
|
||||
) {
|
||||
diagnostics.push(
|
||||
createDiagnostic(
|
||||
@@ -3479,7 +3489,10 @@ function validateSoundEmitterControlTarget(
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetEntity.kind !== target.entityKind || targetEntity.kind !== "soundEmitter") {
|
||||
if (
|
||||
targetEntity.kind !== target.entityKind ||
|
||||
targetEntity.kind !== "soundEmitter"
|
||||
) {
|
||||
diagnostics.push(
|
||||
createDiagnostic(
|
||||
"error",
|
||||
@@ -4014,7 +4027,8 @@ function validateInteractionLink(
|
||||
break;
|
||||
}
|
||||
case "runSequence": {
|
||||
const sequence = document.sequences.sequences[link.action.sequenceId] ?? null;
|
||||
const sequence =
|
||||
document.sequences.sequences[link.action.sequenceId] ?? null;
|
||||
|
||||
if (sequence === null) {
|
||||
diagnostics.push(
|
||||
@@ -4214,7 +4228,10 @@ function validateProjectScheduler(
|
||||
);
|
||||
}
|
||||
|
||||
if (!isFiniteNumber(routine.priority) || !Number.isInteger(routine.priority)) {
|
||||
if (
|
||||
!isFiniteNumber(routine.priority) ||
|
||||
!Number.isInteger(routine.priority)
|
||||
) {
|
||||
diagnostics.push(
|
||||
createDiagnostic(
|
||||
"error",
|
||||
@@ -4229,7 +4246,11 @@ function validateProjectScheduler(
|
||||
routine.sequenceId === null
|
||||
? []
|
||||
: getProjectSequenceImpulseSteps(
|
||||
sequences.sequences[routine.sequenceId] ?? { id: "", title: "", effects: [] }
|
||||
sequences.sequences[routine.sequenceId] ?? {
|
||||
id: "",
|
||||
title: "",
|
||||
effects: []
|
||||
}
|
||||
);
|
||||
|
||||
if (resolvedEffects.length === 0) {
|
||||
@@ -4392,7 +4413,9 @@ function recordProjectSchedulerSceneTargets(
|
||||
context.actorIds.add(entity.actorId);
|
||||
const usages = context.actorUsagesById.get(entity.actorId) ?? [];
|
||||
const targetAsset =
|
||||
entity.modelAssetId === null ? undefined : scene.assets[entity.modelAssetId];
|
||||
entity.modelAssetId === null
|
||||
? undefined
|
||||
: scene.assets[entity.modelAssetId];
|
||||
usages.push({
|
||||
sceneId,
|
||||
entityId: entity.id,
|
||||
@@ -4408,7 +4431,10 @@ function recordProjectSchedulerSceneTargets(
|
||||
}
|
||||
|
||||
if (entity.kind === "soundEmitter") {
|
||||
context.soundEmitterHasAudioById.set(entity.id, entity.audioAssetId !== null);
|
||||
context.soundEmitterHasAudioById.set(
|
||||
entity.id,
|
||||
entity.audioAssetId !== null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4442,13 +4468,17 @@ function createProjectSchedulerValidationContextFromProjectDocument(
|
||||
const context = createEmptyProjectSchedulerValidationContext();
|
||||
|
||||
for (const scene of Object.values(document.scenes)) {
|
||||
recordProjectSchedulerSceneTargets(context, {
|
||||
brushes: scene.brushes,
|
||||
entities: scene.entities,
|
||||
modelInstances: scene.modelInstances,
|
||||
assets: document.assets,
|
||||
paths: scene.paths
|
||||
}, scene.id);
|
||||
recordProjectSchedulerSceneTargets(
|
||||
context,
|
||||
{
|
||||
brushes: scene.brushes,
|
||||
entities: scene.entities,
|
||||
modelInstances: scene.modelInstances,
|
||||
assets: document.assets,
|
||||
paths: scene.paths
|
||||
},
|
||||
scene.id
|
||||
);
|
||||
}
|
||||
|
||||
return context;
|
||||
@@ -4490,7 +4520,10 @@ function validateProjectSchedulerSingleActorUsage(
|
||||
): ProjectSchedulerActorValidationUsage | null {
|
||||
validateProjectSchedulerActorTarget(target, path, context, diagnostics);
|
||||
|
||||
const usages = getProjectSchedulerActorValidationUsages(context, target.actorId);
|
||||
const usages = getProjectSchedulerActorValidationUsages(
|
||||
context,
|
||||
target.actorId
|
||||
);
|
||||
|
||||
if (usages.length === 1) {
|
||||
return usages[0] ?? null;
|
||||
@@ -4616,7 +4649,8 @@ function validateProjectSchedulerModelTarget(
|
||||
context: ProjectSchedulerValidationContext,
|
||||
diagnostics: SceneDiagnostic[]
|
||||
) {
|
||||
const modelInstanceCount = context.modelInstanceCounts.get(target.modelInstanceId) ?? 0;
|
||||
const modelInstanceCount =
|
||||
context.modelInstanceCounts.get(target.modelInstanceId) ?? 0;
|
||||
|
||||
if (modelInstanceCount === 0) {
|
||||
diagnostics.push(
|
||||
@@ -4675,7 +4709,12 @@ function validateProjectSchedulerControlTarget(
|
||||
validateProjectSchedulerEntityTarget(target, path, context, diagnostics);
|
||||
return;
|
||||
case "interaction":
|
||||
validateProjectSchedulerInteractionTarget(target, path, context, diagnostics);
|
||||
validateProjectSchedulerInteractionTarget(
|
||||
target,
|
||||
path,
|
||||
context,
|
||||
diagnostics
|
||||
);
|
||||
return;
|
||||
case "scene":
|
||||
validateProjectSchedulerSceneTarget(target, path, diagnostics);
|
||||
@@ -4901,7 +4940,8 @@ function validateProjectSchedulerEffect(
|
||||
}
|
||||
|
||||
const animationNames =
|
||||
context.modelAnimationNamesById.get(effect.target.modelInstanceId) ?? [];
|
||||
context.modelAnimationNamesById.get(effect.target.modelInstanceId) ??
|
||||
[];
|
||||
|
||||
if (
|
||||
animationNames.length > 0 &&
|
||||
@@ -5046,7 +5086,11 @@ function validateProjectSchedulerEffect(
|
||||
}
|
||||
return;
|
||||
case "setAmbientLightIntensity":
|
||||
validateProjectSchedulerSceneTarget(effect.target, `${path}.target`, diagnostics);
|
||||
validateProjectSchedulerSceneTarget(
|
||||
effect.target,
|
||||
`${path}.target`,
|
||||
diagnostics
|
||||
);
|
||||
if (!isNonNegativeFiniteNumber(effect.intensity)) {
|
||||
diagnostics.push(
|
||||
createDiagnostic(
|
||||
@@ -5059,7 +5103,11 @@ function validateProjectSchedulerEffect(
|
||||
}
|
||||
return;
|
||||
case "setAmbientLightColor":
|
||||
validateProjectSchedulerSceneTarget(effect.target, `${path}.target`, diagnostics);
|
||||
validateProjectSchedulerSceneTarget(
|
||||
effect.target,
|
||||
`${path}.target`,
|
||||
diagnostics
|
||||
);
|
||||
if (!isHexColorString(effect.colorHex)) {
|
||||
diagnostics.push(
|
||||
createDiagnostic(
|
||||
@@ -5072,7 +5120,11 @@ function validateProjectSchedulerEffect(
|
||||
}
|
||||
return;
|
||||
case "setSunLightIntensity":
|
||||
validateProjectSchedulerSceneTarget(effect.target, `${path}.target`, diagnostics);
|
||||
validateProjectSchedulerSceneTarget(
|
||||
effect.target,
|
||||
`${path}.target`,
|
||||
diagnostics
|
||||
);
|
||||
if (!isNonNegativeFiniteNumber(effect.intensity)) {
|
||||
diagnostics.push(
|
||||
createDiagnostic(
|
||||
@@ -5085,7 +5137,11 @@ function validateProjectSchedulerEffect(
|
||||
}
|
||||
return;
|
||||
case "setSunLightColor":
|
||||
validateProjectSchedulerSceneTarget(effect.target, `${path}.target`, diagnostics);
|
||||
validateProjectSchedulerSceneTarget(
|
||||
effect.target,
|
||||
`${path}.target`,
|
||||
diagnostics
|
||||
);
|
||||
if (!isHexColorString(effect.colorHex)) {
|
||||
diagnostics.push(
|
||||
createDiagnostic(
|
||||
@@ -5185,7 +5241,6 @@ function validateProjectDialogue(
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5270,7 +5325,9 @@ function validateProjectSequence(
|
||||
|
||||
if (
|
||||
effect.dialogueId !== null &&
|
||||
!targetNpc.dialogues.some((dialogue) => dialogue.id === effect.dialogueId)
|
||||
!targetNpc.dialogues.some(
|
||||
(dialogue) => dialogue.id === effect.dialogueId
|
||||
)
|
||||
) {
|
||||
diagnostics.push(
|
||||
createDiagnostic(
|
||||
@@ -5343,7 +5400,10 @@ function validateProjectSequence(
|
||||
)
|
||||
);
|
||||
}
|
||||
} else if ((context.modelInstanceCounts.get(effect.target.modelInstanceId) ?? 0) === 0) {
|
||||
} else if (
|
||||
(context.modelInstanceCounts.get(effect.target.modelInstanceId) ??
|
||||
0) === 0
|
||||
) {
|
||||
diagnostics.push(
|
||||
createDiagnostic(
|
||||
"error",
|
||||
@@ -5403,7 +5463,9 @@ function validateProjectResources(
|
||||
validateProjectAsset(asset, path, diagnostics);
|
||||
}
|
||||
|
||||
for (const [sequenceKey, sequence] of Object.entries(document.sequences.sequences)) {
|
||||
for (const [sequenceKey, sequence] of Object.entries(
|
||||
document.sequences.sequences
|
||||
)) {
|
||||
const path = `sequences.sequences.${sequenceKey}`;
|
||||
|
||||
if (sequence.id !== sequenceKey) {
|
||||
@@ -5673,7 +5735,9 @@ export function validateSceneDocument(
|
||||
validateProjectAsset(asset, path, diagnostics);
|
||||
}
|
||||
|
||||
for (const [sequenceKey, sequence] of Object.entries(document.sequences.sequences)) {
|
||||
for (const [sequenceKey, sequence] of Object.entries(
|
||||
document.sequences.sequences
|
||||
)) {
|
||||
const path = `sequences.sequences.${sequenceKey}`;
|
||||
|
||||
if (sequence.id !== sequenceKey) {
|
||||
@@ -6367,7 +6431,6 @@ export function validateProjectDocument(
|
||||
path: prefixDiagnosticPath(`${scenePath}.`, diagnostic.path)
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -43,8 +43,7 @@ export const NPC_DIALOGUE_LINE_SPEAKER_REMOVED_SCENE_DOCUMENT_VERSION =
|
||||
export const NPC_ONLY_DIALOGUES_SCENE_DOCUMENT_VERSION = 62 as const;
|
||||
export const WHITEBOX_EXPANDED_PRIMITIVES_SCENE_DOCUMENT_VERSION = 61 as const;
|
||||
export const WHITEBOX_PRIMITIVES_SCENE_DOCUMENT_VERSION = 60 as const;
|
||||
export const STARTER_PBR_MATERIAL_LIBRARY_SCENE_DOCUMENT_VERSION =
|
||||
59 as const;
|
||||
export const STARTER_PBR_MATERIAL_LIBRARY_SCENE_DOCUMENT_VERSION = 59 as const;
|
||||
export const SCENE_TRANSITION_SEQUENCE_EFFECTS_SCENE_DOCUMENT_VERSION =
|
||||
58 as const;
|
||||
export const PROJECT_SEQUENCE_UNIFIED_VISIBILITY_SCENE_DOCUMENT_VERSION =
|
||||
@@ -56,7 +55,8 @@ export const PROJECT_SEQUENCE_LIBRARY_SCENE_DOCUMENT_VERSION = 53 as const;
|
||||
export const PLAYER_START_PAUSE_BINDINGS_SCENE_DOCUMENT_VERSION = 52 as const;
|
||||
export const NPC_DIALOGUE_REFERENCE_SCENE_DOCUMENT_VERSION = 51 as const;
|
||||
export const PROJECT_DIALOGUE_LIBRARY_SCENE_DOCUMENT_VERSION = 50 as const;
|
||||
export const SCHEDULER_ACTOR_ROUTINE_EFFECTS_SCENE_DOCUMENT_VERSION = 49 as const;
|
||||
export const SCHEDULER_ACTOR_ROUTINE_EFFECTS_SCENE_DOCUMENT_VERSION =
|
||||
49 as const;
|
||||
export const SCHEDULER_CONTROL_EFFECTS_SCENE_DOCUMENT_VERSION = 48 as const;
|
||||
export const EXPANDED_CONTROL_SURFACE_SCENE_DOCUMENT_VERSION = 47 as const;
|
||||
export const PROJECT_SCHEDULER_FOUNDATION_SCENE_DOCUMENT_VERSION = 46 as const;
|
||||
|
||||
@@ -8,19 +8,40 @@ export type WorldBackgroundMode =
|
||||
| "image"
|
||||
| "shader";
|
||||
export type WorldTimePhase = "dawn" | "dusk" | "night";
|
||||
export type WorldShaderSkyPresetId = (typeof WORLD_SHADER_SKY_PRESET_IDS)[number];
|
||||
export type WorldShaderSkyPresetId =
|
||||
(typeof WORLD_SHADER_SKY_PRESET_IDS)[number];
|
||||
|
||||
export const ADVANCED_RENDERING_SHADOW_MAP_SIZES = [512, 1024, 2048, 4096] as const;
|
||||
export const ADVANCED_RENDERING_SHADOW_TYPES = ["basic", "pcf", "pcfSoft"] as const;
|
||||
export const ADVANCED_RENDERING_TONE_MAPPING_MODES = ["none", "linear", "reinhard", "cineon", "acesFilmic"] as const;
|
||||
export const ADVANCED_RENDERING_SHADOW_MAP_SIZES = [
|
||||
512, 1024, 2048, 4096
|
||||
] as const;
|
||||
export const ADVANCED_RENDERING_SHADOW_TYPES = [
|
||||
"basic",
|
||||
"pcf",
|
||||
"pcfSoft"
|
||||
] as const;
|
||||
export const ADVANCED_RENDERING_TONE_MAPPING_MODES = [
|
||||
"none",
|
||||
"linear",
|
||||
"reinhard",
|
||||
"cineon",
|
||||
"acesFilmic"
|
||||
] as const;
|
||||
export const BOX_VOLUME_RENDER_PATHS = ["performance", "quality"] as const;
|
||||
export const ADVANCED_RENDERING_WATER_REFLECTION_MODES = ["none", "world", "all"] as const;
|
||||
export const ADVANCED_RENDERING_WATER_REFLECTION_MODES = [
|
||||
"none",
|
||||
"world",
|
||||
"all"
|
||||
] as const;
|
||||
|
||||
export type AdvancedRenderingShadowMapSize = (typeof ADVANCED_RENDERING_SHADOW_MAP_SIZES)[number];
|
||||
export type AdvancedRenderingShadowType = (typeof ADVANCED_RENDERING_SHADOW_TYPES)[number];
|
||||
export type AdvancedRenderingToneMappingMode = (typeof ADVANCED_RENDERING_TONE_MAPPING_MODES)[number];
|
||||
export type AdvancedRenderingShadowMapSize =
|
||||
(typeof ADVANCED_RENDERING_SHADOW_MAP_SIZES)[number];
|
||||
export type AdvancedRenderingShadowType =
|
||||
(typeof ADVANCED_RENDERING_SHADOW_TYPES)[number];
|
||||
export type AdvancedRenderingToneMappingMode =
|
||||
(typeof ADVANCED_RENDERING_TONE_MAPPING_MODES)[number];
|
||||
export type BoxVolumeRenderPath = (typeof BOX_VOLUME_RENDER_PATHS)[number];
|
||||
export type AdvancedRenderingWaterReflectionMode = (typeof ADVANCED_RENDERING_WATER_REFLECTION_MODES)[number];
|
||||
export type AdvancedRenderingWaterReflectionMode =
|
||||
(typeof ADVANCED_RENDERING_WATER_REFLECTION_MODES)[number];
|
||||
|
||||
export interface WorldSolidBackgroundSettings {
|
||||
mode: "solid";
|
||||
@@ -186,7 +207,8 @@ const DEFAULT_GRADIENT_BOTTOM_COLOR = "#141a22";
|
||||
export const DEFAULT_NIGHT_IMAGE_ENVIRONMENT_INTENSITY = 0.35 as const;
|
||||
export const DEFAULT_TIME_PHASE_IMAGE_ENVIRONMENT_INTENSITY = 0.5 as const;
|
||||
const DEFAULT_ADVANCED_RENDERING_SHADOW_MAP_SIZE: AdvancedRenderingShadowMapSize = 2048;
|
||||
const DEFAULT_ADVANCED_RENDERING_SHADOW_TYPE: AdvancedRenderingShadowType = "pcfSoft";
|
||||
const DEFAULT_ADVANCED_RENDERING_SHADOW_TYPE: AdvancedRenderingShadowType =
|
||||
"pcfSoft";
|
||||
const DEFAULT_ADVANCED_RENDERING_SHADOW_BIAS = -0.0005;
|
||||
const DEFAULT_ADVANCED_RENDERING_AMBIENT_OCCLUSION_INTENSITY = 1;
|
||||
const DEFAULT_ADVANCED_RENDERING_AMBIENT_OCCLUSION_RADIUS = 0.5;
|
||||
@@ -194,7 +216,8 @@ const DEFAULT_ADVANCED_RENDERING_AMBIENT_OCCLUSION_SAMPLES = 8;
|
||||
const DEFAULT_ADVANCED_RENDERING_BLOOM_INTENSITY = 0.75;
|
||||
const DEFAULT_ADVANCED_RENDERING_BLOOM_THRESHOLD = 0.85;
|
||||
const DEFAULT_ADVANCED_RENDERING_BLOOM_RADIUS = 0.35;
|
||||
const DEFAULT_ADVANCED_RENDERING_TONE_MAPPING_MODE: AdvancedRenderingToneMappingMode = "acesFilmic";
|
||||
const DEFAULT_ADVANCED_RENDERING_TONE_MAPPING_MODE: AdvancedRenderingToneMappingMode =
|
||||
"acesFilmic";
|
||||
const DEFAULT_ADVANCED_RENDERING_TONE_MAPPING_EXPOSURE = 1;
|
||||
const DEFAULT_ADVANCED_RENDERING_DEPTH_OF_FIELD_FOCUS_DISTANCE = 10;
|
||||
const DEFAULT_ADVANCED_RENDERING_DEPTH_OF_FIELD_FOCAL_LENGTH = 0.03;
|
||||
@@ -202,7 +225,8 @@ const DEFAULT_ADVANCED_RENDERING_DEPTH_OF_FIELD_BOKEH_SCALE = 1.5;
|
||||
const DEFAULT_ADVANCED_RENDERING_WHITEBOX_BEVEL_EDGE_WIDTH = 0.14;
|
||||
const DEFAULT_ADVANCED_RENDERING_WHITEBOX_BEVEL_NORMAL_STRENGTH = 0.75;
|
||||
const DEFAULT_BOX_VOLUME_RENDER_PATH: BoxVolumeRenderPath = "performance";
|
||||
const DEFAULT_ADVANCED_RENDERING_WATER_REFLECTION_MODE: AdvancedRenderingWaterReflectionMode = "none";
|
||||
const DEFAULT_ADVANCED_RENDERING_WATER_REFLECTION_MODE: AdvancedRenderingWaterReflectionMode =
|
||||
"none";
|
||||
const DEFAULT_SHADER_SKY_DAY_TOP_COLOR = "#5f8fd3";
|
||||
const DEFAULT_SHADER_SKY_DAY_BOTTOM_COLOR = "#d8eeff";
|
||||
const DEFAULT_SHADER_SKY_SUN_DISC_SIZE_DEGREES = 2.6;
|
||||
@@ -247,24 +271,42 @@ function resolveShaderSkyDayGradient(
|
||||
};
|
||||
}
|
||||
|
||||
export function isAdvancedRenderingShadowMapSize(value: unknown): value is AdvancedRenderingShadowMapSize {
|
||||
return ADVANCED_RENDERING_SHADOW_MAP_SIZES.includes(value as AdvancedRenderingShadowMapSize);
|
||||
export function isAdvancedRenderingShadowMapSize(
|
||||
value: unknown
|
||||
): value is AdvancedRenderingShadowMapSize {
|
||||
return ADVANCED_RENDERING_SHADOW_MAP_SIZES.includes(
|
||||
value as AdvancedRenderingShadowMapSize
|
||||
);
|
||||
}
|
||||
|
||||
export function isAdvancedRenderingShadowType(value: unknown): value is AdvancedRenderingShadowType {
|
||||
return ADVANCED_RENDERING_SHADOW_TYPES.includes(value as AdvancedRenderingShadowType);
|
||||
export function isAdvancedRenderingShadowType(
|
||||
value: unknown
|
||||
): value is AdvancedRenderingShadowType {
|
||||
return ADVANCED_RENDERING_SHADOW_TYPES.includes(
|
||||
value as AdvancedRenderingShadowType
|
||||
);
|
||||
}
|
||||
|
||||
export function isAdvancedRenderingToneMappingMode(value: unknown): value is AdvancedRenderingToneMappingMode {
|
||||
return ADVANCED_RENDERING_TONE_MAPPING_MODES.includes(value as AdvancedRenderingToneMappingMode);
|
||||
export function isAdvancedRenderingToneMappingMode(
|
||||
value: unknown
|
||||
): value is AdvancedRenderingToneMappingMode {
|
||||
return ADVANCED_RENDERING_TONE_MAPPING_MODES.includes(
|
||||
value as AdvancedRenderingToneMappingMode
|
||||
);
|
||||
}
|
||||
|
||||
export function isBoxVolumeRenderPath(value: unknown): value is BoxVolumeRenderPath {
|
||||
export function isBoxVolumeRenderPath(
|
||||
value: unknown
|
||||
): value is BoxVolumeRenderPath {
|
||||
return BOX_VOLUME_RENDER_PATHS.includes(value as BoxVolumeRenderPath);
|
||||
}
|
||||
|
||||
export function isAdvancedRenderingWaterReflectionMode(value: unknown): value is AdvancedRenderingWaterReflectionMode {
|
||||
return ADVANCED_RENDERING_WATER_REFLECTION_MODES.includes(value as AdvancedRenderingWaterReflectionMode);
|
||||
export function isAdvancedRenderingWaterReflectionMode(
|
||||
value: unknown
|
||||
): value is AdvancedRenderingWaterReflectionMode {
|
||||
return ADVANCED_RENDERING_WATER_REFLECTION_MODES.includes(
|
||||
value as AdvancedRenderingWaterReflectionMode
|
||||
);
|
||||
}
|
||||
|
||||
export function createDefaultAdvancedRenderingSettings(): AdvancedRenderingSettings {
|
||||
@@ -478,7 +520,9 @@ export function isHexColorString(value: string): boolean {
|
||||
return /^#[0-9a-f]{6}$/i.test(value);
|
||||
}
|
||||
|
||||
export function isWorldBackgroundMode(value: unknown): value is WorldBackgroundMode {
|
||||
export function isWorldBackgroundMode(
|
||||
value: unknown
|
||||
): value is WorldBackgroundMode {
|
||||
return (
|
||||
value === "solid" ||
|
||||
value === "verticalGradient" ||
|
||||
@@ -487,7 +531,9 @@ export function isWorldBackgroundMode(value: unknown): value is WorldBackgroundM
|
||||
);
|
||||
}
|
||||
|
||||
export function cloneWorldBackgroundSettings(background: WorldBackgroundSettings): WorldBackgroundSettings {
|
||||
export function cloneWorldBackgroundSettings(
|
||||
background: WorldBackgroundSettings
|
||||
): WorldBackgroundSettings {
|
||||
if (background.mode === "solid") {
|
||||
return {
|
||||
mode: "solid",
|
||||
@@ -572,7 +618,9 @@ export function cloneWorldSettings(world: WorldSettings): WorldSettings {
|
||||
};
|
||||
}
|
||||
|
||||
export function cloneAdvancedRenderingSettings(settings: AdvancedRenderingSettings): AdvancedRenderingSettings {
|
||||
export function cloneAdvancedRenderingSettings(
|
||||
settings: AdvancedRenderingSettings
|
||||
): AdvancedRenderingSettings {
|
||||
return {
|
||||
enabled: settings.enabled,
|
||||
shadows: {
|
||||
@@ -599,7 +647,10 @@ export function cloneAdvancedRenderingSettings(settings: AdvancedRenderingSettin
|
||||
};
|
||||
}
|
||||
|
||||
export function areWorldBackgroundSettingsEqual(left: WorldBackgroundSettings, right: WorldBackgroundSettings): boolean {
|
||||
export function areWorldBackgroundSettingsEqual(
|
||||
left: WorldBackgroundSettings,
|
||||
right: WorldBackgroundSettings
|
||||
): boolean {
|
||||
if (left.mode !== right.mode) {
|
||||
return false;
|
||||
}
|
||||
@@ -609,14 +660,22 @@ export function areWorldBackgroundSettingsEqual(left: WorldBackgroundSettings, r
|
||||
}
|
||||
|
||||
if (left.mode === "verticalGradient" && right.mode === "verticalGradient") {
|
||||
return left.topColorHex === right.topColorHex && left.bottomColorHex === right.bottomColorHex;
|
||||
return (
|
||||
left.topColorHex === right.topColorHex &&
|
||||
left.bottomColorHex === right.bottomColorHex
|
||||
);
|
||||
}
|
||||
|
||||
if (left.mode === "shader" && right.mode === "shader") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return left.mode === "image" && right.mode === "image" && left.assetId === right.assetId && left.environmentIntensity === right.environmentIntensity;
|
||||
return (
|
||||
left.mode === "image" &&
|
||||
right.mode === "image" &&
|
||||
left.assetId === right.assetId &&
|
||||
left.environmentIntensity === right.environmentIntensity
|
||||
);
|
||||
}
|
||||
|
||||
export function areWorldShaderSkySettingsEqual(
|
||||
@@ -628,7 +687,8 @@ export function areWorldShaderSkySettingsEqual(
|
||||
left.dayTopColorHex === right.dayTopColorHex &&
|
||||
left.dayBottomColorHex === right.dayBottomColorHex &&
|
||||
left.celestial.sunDiscSizeDegrees === right.celestial.sunDiscSizeDegrees &&
|
||||
left.celestial.moonDiscSizeDegrees === right.celestial.moonDiscSizeDegrees &&
|
||||
left.celestial.moonDiscSizeDegrees ===
|
||||
right.celestial.moonDiscSizeDegrees &&
|
||||
left.stars.density === right.stars.density &&
|
||||
left.stars.brightness === right.stars.brightness &&
|
||||
left.clouds.coverage === right.clouds.coverage &&
|
||||
@@ -684,7 +744,10 @@ export function areWorldTimeOfDaySettingsEqual(
|
||||
);
|
||||
}
|
||||
|
||||
export function areWorldSettingsEqual(left: WorldSettings, right: WorldSettings): boolean {
|
||||
export function areWorldSettingsEqual(
|
||||
left: WorldSettings,
|
||||
right: WorldSettings
|
||||
): boolean {
|
||||
return (
|
||||
left.projectTimeLightingEnabled === right.projectTimeLightingEnabled &&
|
||||
left.showCelestialBodies === right.showCelestialBodies &&
|
||||
@@ -698,11 +761,17 @@ export function areWorldSettingsEqual(left: WorldSettings, right: WorldSettings)
|
||||
left.sunLight.direction.y === right.sunLight.direction.y &&
|
||||
left.sunLight.direction.z === right.sunLight.direction.z &&
|
||||
areWorldTimeOfDaySettingsEqual(left.timeOfDay, right.timeOfDay) &&
|
||||
areAdvancedRenderingSettingsEqual(left.advancedRendering, right.advancedRendering)
|
||||
areAdvancedRenderingSettingsEqual(
|
||||
left.advancedRendering,
|
||||
right.advancedRendering
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function areAdvancedRenderingSettingsEqual(left: AdvancedRenderingSettings, right: AdvancedRenderingSettings): boolean {
|
||||
export function areAdvancedRenderingSettingsEqual(
|
||||
left: AdvancedRenderingSettings,
|
||||
right: AdvancedRenderingSettings
|
||||
): boolean {
|
||||
return (
|
||||
left.enabled === right.enabled &&
|
||||
left.shadows.enabled === right.shadows.enabled &&
|
||||
@@ -736,8 +805,7 @@ export function changeWorldBackgroundMode(
|
||||
background: WorldBackgroundSettings,
|
||||
mode: WorldBackgroundMode,
|
||||
imageAssetId?: string,
|
||||
imageEnvironmentIntensity: number =
|
||||
DEFAULT_TIME_PHASE_IMAGE_ENVIRONMENT_INTENSITY,
|
||||
imageEnvironmentIntensity: number = DEFAULT_TIME_PHASE_IMAGE_ENVIRONMENT_INTENSITY,
|
||||
shaderSkySettings?: WorldShaderSkySettings
|
||||
): WorldBackgroundSettings {
|
||||
if (mode === "image") {
|
||||
@@ -746,7 +814,9 @@ export function changeWorldBackgroundMode(
|
||||
return cloneWorldBackgroundSettings(background);
|
||||
}
|
||||
|
||||
throw new Error("An image asset must be selected to use an image background.");
|
||||
throw new Error(
|
||||
"An image asset must be selected to use an image background."
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -779,7 +849,7 @@ export function changeWorldBackgroundMode(
|
||||
? background.topColorHex
|
||||
: background.mode === "shader" && shaderSkySettings !== undefined
|
||||
? shaderSkySettings.dayTopColorHex
|
||||
: DEFAULT_SOLID_BACKGROUND_COLOR
|
||||
: DEFAULT_SOLID_BACKGROUND_COLOR
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user