diff --git a/src/app/App.tsx b/src/app/App.tsx index 8166fd64..19033091 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -344,7 +344,7 @@ import { } from "../entities/entity-instances"; import { createProjectDialogue, - createProjectDialogueLine, + createProjectDialogueLine } from "../dialogues/project-dialogues"; import type { ProjectDialogue, @@ -1289,7 +1289,9 @@ function getTerrainLabel(terrain: Terrain, index: number): string { } function getTerrainLabelById(terrainId: string, terrains: Terrain[]): string { - const terrainIndex = terrains.findIndex((terrain) => terrain.id === terrainId); + const terrainIndex = terrains.findIndex( + (terrain) => terrain.id === terrainId + ); return terrainIndex === -1 ? getTerrainKindLabel() : getTerrainLabel(terrains[terrainIndex], terrainIndex); @@ -1352,7 +1354,9 @@ function describeSelection( case "brushes": return `${selection.ids.length} solid${selection.ids.length === 1 ? "" : "s"} selected (${getSelectedBrushLabel(selection, brushes, activeSelectionId)})`; case "brushFace": { - const brush = brushes.find((candidate) => candidate.id === selection.brushId); + const brush = brushes.find( + (candidate) => candidate.id === selection.brushId + ); const faceLabel = brush === undefined ? selection.faceId @@ -1360,7 +1364,9 @@ function describeSelection( return `1 face selected (${faceLabel} on ${getBrushLabelById(selection.brushId, brushes)})`; } case "brushEdge": { - const brush = brushes.find((candidate) => candidate.id === selection.brushId); + const brush = brushes.find( + (candidate) => candidate.id === selection.brushId + ); const edgeLabel = brush === undefined ? selection.edgeId @@ -1368,7 +1374,9 @@ function describeSelection( return `1 edge selected (${edgeLabel} on ${getBrushLabelById(selection.brushId, brushes)})`; } case "brushVertex": { - const brush = brushes.find((candidate) => candidate.id === selection.brushId); + const brush = brushes.find( + (candidate) => candidate.id === selection.brushId + ); const vertexLabel = brush === undefined ? selection.vertexId @@ -1407,15 +1415,13 @@ function getMultiSelectionSummary( modelInstances: Record, assets: Record, entities: Record -): - | { - kindLabel: string; - count: number; - activeId: string; - activeLabel: string; - selectedItems: Array<{ id: string; label: string }>; - } - | null { +): { + kindLabel: string; + count: number; + activeId: string; + activeLabel: string; + selectedItems: Array<{ id: string; label: string }>; +} | null { const resolvedActiveSelectionId = resolveSelectionActiveId( selection, activeSelectionId @@ -1585,9 +1591,7 @@ function readVisibilityModeSelectValue( } } -function readSequenceVisibilityTargetKey( - value: string -): +function readSequenceVisibilityTargetKey(value: string): | { kind: "brush"; brushId: string; @@ -1622,7 +1626,9 @@ function readSequenceVisibilityTargetKey( }; } - throw new Error("Sequence visibility targets must reference a brush or model instance."); + throw new Error( + "Sequence visibility targets must reference a brush or model instance." + ); } function readSceneTransitionTargetKey(value: string): { @@ -2139,7 +2145,10 @@ export function App({ store, initialStatusMessage }: AppProps) { ); const materialList = sortDocumentMaterials(editorState.document.materials); const selectedBrush = getSelectedBoxBrush(editorState.selection, brushList); - const selectedTerrain = getSelectedTerrain(editorState.selection, terrainList); + const selectedTerrain = getSelectedTerrain( + editorState.selection, + terrainList + ); const selectedPath = getSelectedPath(editorState.selection, pathList); const selectedPathPointState = getSelectedPathPointState( editorState.selection, @@ -2186,13 +2195,15 @@ export function App({ store, initialStatusMessage }: AppProps) { selectedBrush === null ? [] : getBrushFaceIds(selectedBrush); const selectedBrushHasMixedFaceUvs = selectedBrush !== null - ? selectedBrushFaceIds.slice(1).some( - (faceId) => - !areFaceUvStatesEqual( - selectedBrush.faces[selectedBrushFaceIds[0]].uv, - selectedBrush.faces[faceId].uv - ) - ) + ? selectedBrushFaceIds + .slice(1) + .some( + (faceId) => + !areFaceUvStatesEqual( + selectedBrush.faces[selectedBrushFaceIds[0]].uv, + selectedBrush.faces[faceId].uv + ) + ) : false; const materialInspectorScope = whiteboxSelectionMode === "object" && selectedBrush !== null @@ -2207,7 +2218,7 @@ export function App({ store, initialStatusMessage }: AppProps) { materialInspectorScope === "brush" ? selectedBrushSharedMaterialId : materialInspectorScope === "face" - ? selectedFace?.materialId ?? null + ? (selectedFace?.materialId ?? null) : null; const materialInspectorMaterial = materialInspectorScope === "brush" @@ -2228,15 +2239,15 @@ export function App({ store, initialStatusMessage }: AppProps) { const materialInspectorMaterialSummary = materialInspectorScope === "brush" && selectedBrushHasMixedFaceMaterials ? "Mixed across faces" - : materialInspectorMaterial?.name ?? + : (materialInspectorMaterial?.name ?? (materialInspectorMaterialId === null ? "Fallback face color" - : materialInspectorMaterialId ?? "Fallback face color"); + : (materialInspectorMaterialId ?? "Fallback face color"))); const materialInspectorUvState = materialInspectorScope === "brush" && selectedBrush !== null ? selectedBrush.faces[selectedBrushFaceIds[0]].uv : materialInspectorScope === "face" - ? selectedFace?.uv ?? null + ? (selectedFace?.uv ?? null) : null; const selectedModelAsset = selectedModelInstance !== null @@ -2523,7 +2534,9 @@ export function App({ store, initialStatusMessage }: AppProps) { String(DEFAULT_BOX_VOLUME_LIGHT_SETTINGS.padding) ); const [boxVolumeLightFalloffDraft, setBoxVolumeLightFalloffDraft] = - useState(DEFAULT_BOX_VOLUME_LIGHT_SETTINGS.falloff); + useState( + DEFAULT_BOX_VOLUME_LIGHT_SETTINGS.falloff + ); const [whiteboxSnapStepDraft, setWhiteboxSnapStepDraft] = useState( String(editorState.whiteboxSnapStep) ); @@ -2661,12 +2674,8 @@ export function App({ store, initialStatusMessage }: AppProps) { const [terrainBrushSettings, setTerrainBrushSettings] = useState( createDefaultTerrainBrushSettings() ); - const [terrainSampleCountXDraft, setTerrainSampleCountXDraft] = useState( - "9" - ); - const [terrainSampleCountZDraft, setTerrainSampleCountZDraft] = useState( - "9" - ); + const [terrainSampleCountXDraft, setTerrainSampleCountXDraft] = useState("9"); + const [terrainSampleCountZDraft, setTerrainSampleCountZDraft] = useState("9"); const [terrainCellSizeDraft, setTerrainCellSizeDraft] = useState("1"); const activeTerrainBrushState: ArmedTerrainBrushState | null = selectedTerrain === null || armedTerrainBrushTool === null @@ -2675,7 +2684,9 @@ export function App({ store, initialStatusMessage }: AppProps) { ? { terrainId: selectedTerrain.id, tool: "paint", - layerIndex: clampTerrainPaintLayerIndex(activeTerrainPaintLayerIndex), + layerIndex: clampTerrainPaintLayerIndex( + activeTerrainPaintLayerIndex + ), radius: terrainBrushSettings.radius, strength: terrainBrushSettings.strength, falloff: terrainBrushSettings.falloff @@ -2737,9 +2748,9 @@ export function App({ store, initialStatusMessage }: AppProps) { const [selectedScheduleRoutineId, setSelectedScheduleRoutineId] = useState< string | null >(null); - const [selectedNpcDialogueId, setSelectedNpcDialogueId] = useState( - null - ); + const [selectedNpcDialogueId, setSelectedNpcDialogueId] = useState< + string | null + >(null); const [selectedSequenceId, setSelectedSequenceId] = useState( null ); @@ -2938,8 +2949,7 @@ export function App({ store, initialStatusMessage }: AppProps) { const [runtimeMessage, setRuntimeMessage] = useState(null); const [editorSimulationClockOverride, setEditorSimulationClockOverride] = useState(null); - const [editorSimulationPlaying, setEditorSimulationPlaying] = - useState(false); + const [editorSimulationPlaying, setEditorSimulationPlaying] = useState(false); const [editorSimulationScene, setEditorSimulationScene] = useState(null); const [editorSimulationMessage, setEditorSimulationMessage] = useState< @@ -3102,13 +3112,13 @@ export function App({ store, initialStatusMessage }: AppProps) { }; const commitSchedulePaneHeight = ( - nextHeight: - | number - | ((previousHeight: number) => number) + nextHeight: number | ((previousHeight: number) => number) ) => { setSchedulePaneHeight((previousHeight) => { const resolvedHeight = - typeof nextHeight === "function" ? nextHeight(previousHeight) : nextHeight; + typeof nextHeight === "function" + ? nextHeight(previousHeight) + : nextHeight; const clampedHeight = clampSchedulePaneHeight(resolvedHeight); schedulePaneHeightRef.current = clampedHeight; return clampedHeight; @@ -3360,7 +3370,9 @@ export function App({ store, initialStatusMessage }: AppProps) { if (selectedBrush.volume.mode === "light") { setBoxVolumeLightColorDraft(selectedBrush.volume.light.colorHex); - setBoxVolumeLightIntensityDraft(String(selectedBrush.volume.light.intensity)); + setBoxVolumeLightIntensityDraft( + String(selectedBrush.volume.light.intensity) + ); setBoxVolumeLightPaddingDraft(String(selectedBrush.volume.light.padding)); setBoxVolumeLightFalloffDraft(selectedBrush.volume.light.falloff); } @@ -3626,10 +3638,14 @@ export function App({ store, initialStatusMessage }: AppProps) { const tick = (timestamp: number) => { if (previousFrameTime !== null) { - const dtSeconds = Math.min(0.25, (timestamp - previousFrameTime) / 1000); + const dtSeconds = Math.min( + 0.25, + (timestamp - previousFrameTime) / 1000 + ); setEditorSimulationClockOverride((currentClock) => advanceRuntimeClockState( - currentClock ?? createRuntimeClockState(editorState.projectDocument.time), + currentClock ?? + createRuntimeClockState(editorState.projectDocument.time), dtSeconds ) ); @@ -3685,8 +3701,9 @@ export function App({ store, initialStatusMessage }: AppProps) { } if ( - editorState.projectDocument.scheduler.routines[selectedScheduleRoutineId] !== - undefined + editorState.projectDocument.scheduler.routines[ + selectedScheduleRoutineId + ] !== undefined ) { return; } @@ -3978,7 +3995,10 @@ export function App({ store, initialStatusMessage }: AppProps) { try { return await loadImageAssetFromStorage(storage, asset); } catch (error) { - if (storage !== null && (await restoreDeletedStoredAsset(storage, asset))) { + if ( + storage !== null && + (await restoreDeletedStoredAsset(storage, asset)) + ) { return loadImageAssetFromStorage(storage, asset); } @@ -4888,7 +4908,10 @@ export function App({ store, initialStatusMessage }: AppProps) { }; const resolveProjectScheduleTargetOption = (targetKey: string) => - getProjectScheduleTargetOptionByKey(projectScheduleTargetOptions, targetKey); + getProjectScheduleTargetOptionByKey( + projectScheduleTargetOptions, + targetKey + ); const createDefaultSequenceEffectsForTarget = ( targetOption: ProjectScheduleTargetOption @@ -4900,7 +4923,8 @@ export function App({ store, initialStatusMessage }: AppProps) { return []; } - const effectOption = listProjectScheduleEffectOptions(targetOption)[0] ?? null; + const effectOption = + listProjectScheduleEffectOptions(targetOption)[0] ?? null; if (effectOption === null) { throw new Error( @@ -4928,11 +4952,13 @@ export function App({ store, initialStatusMessage }: AppProps) { const authoredEffects = options.routine === undefined ? [] - : options.routine.effects.map((effect) => ({ - stepClass: "held" as const, - type: "controlEffect" as const, - effect: cloneControlEffect(effect) - })).filter((step) => step.effect.type !== "setActorPresence"); + : options.routine.effects + .map((effect) => ({ + stepClass: "held" as const, + type: "controlEffect" as const, + effect: cloneControlEffect(effect) + })) + .filter((step) => step.effect.type !== "setActorPresence"); return createProjectSequence({ title: options.title, @@ -4953,7 +4979,8 @@ export function App({ store, initialStatusMessage }: AppProps) { effects: options.sequence.effects.map((effect) => { if ( effect.type !== "controlEffect" || - getControlTargetRefKey(effect.effect.target) !== options.previousTargetKey + getControlTargetRefKey(effect.effect.target) !== + options.previousTargetKey ) { return cloneSequenceEffect(effect); } @@ -5031,7 +5058,8 @@ export function App({ store, initialStatusMessage }: AppProps) { }; const handleCreateAttachedSequenceForRoutine = (routineId: string) => { - const routine = editorState.projectDocument.scheduler.routines[routineId] ?? null; + const routine = + editorState.projectDocument.scheduler.routines[routineId] ?? null; if (routine === null) { setStatusMessage("Selected sequence placement no longer exists."); @@ -5077,11 +5105,17 @@ export function App({ store, initialStatusMessage }: AppProps) { }; const resolveSequenceControlTargetOption = (targetKey: string) => - getProjectScheduleTargetOptionByKey(projectScheduleTargetOptions, targetKey); + getProjectScheduleTargetOptionByKey( + projectScheduleTargetOptions, + targetKey + ); const createDefaultProjectSequenceControlStep = ( targetKey: string, - previousStep?: Extract | null + previousStep?: Extract< + ProjectSequence["effects"][number], + { type: "controlEffect" } + > | null ): Extract => { const targetOption = resolveSequenceControlTargetOption(targetKey); @@ -5089,7 +5123,8 @@ export function App({ store, initialStatusMessage }: AppProps) { throw new Error("The selected sequence control target no longer exists."); } - const effectOption = listProjectScheduleEffectOptions(targetOption)[0] ?? null; + const effectOption = + listProjectScheduleEffectOptions(targetOption)[0] ?? null; if (effectOption === null) { throw new Error( @@ -5113,7 +5148,10 @@ export function App({ store, initialStatusMessage }: AppProps) { const createProjectSequenceControlStepFromOption = ( targetKey: string, effectOptionId: ProjectScheduleEffectOptionId, - previousStep?: Extract | null + previousStep?: Extract< + ProjectSequence["effects"][number], + { type: "controlEffect" } + > | null ): Extract => { const targetOption = resolveSequenceControlTargetOption(targetKey); @@ -5122,9 +5160,8 @@ export function App({ store, initialStatusMessage }: AppProps) { } return { - stepClass: getProjectSequenceControlStepClassForEffectOptionId( - effectOptionId - ), + stepClass: + getProjectSequenceControlStepClassForEffectOptionId(effectOptionId), type: "controlEffect", effect: createProjectScheduleEffectFromOption({ targetOption, @@ -5213,8 +5250,7 @@ export function App({ store, initialStatusMessage }: AppProps) { "Added NPC dialogue.", (dialogues, defaultDialogueId) => ({ dialogues: [...dialogues, nextDialogue], - defaultDialogueId: - defaultDialogueId ?? nextDialogue.id + defaultDialogueId: defaultDialogueId ?? nextDialogue.id }) ); setSelectedNpcDialogueId(nextDialogue.id); @@ -5232,7 +5268,7 @@ export function App({ store, initialStatusMessage }: AppProps) { dialogues: nextDialogues, defaultDialogueId: defaultDialogueId === dialogueId - ? nextDialogues[0]?.id ?? null + ? (nextDialogues[0]?.id ?? null) : defaultDialogueId }; } @@ -5249,25 +5285,29 @@ export function App({ store, initialStatusMessage }: AppProps) { successMessage: string, mutate: (dialogue: ProjectDialogue) => void ) => { - updateSelectedNpcDialogues(label, successMessage, (dialogues, defaultDialogueId) => { - const nextDialogues = dialogues.map((dialogue) => { - if (dialogue.id !== dialogueId) { - return dialogue; - } + updateSelectedNpcDialogues( + label, + successMessage, + (dialogues, defaultDialogueId) => { + const nextDialogues = dialogues.map((dialogue) => { + if (dialogue.id !== dialogueId) { + return dialogue; + } - const nextDialogue: ProjectDialogue = { - ...dialogue, - lines: dialogue.lines.map((line) => ({ ...line })) + const nextDialogue: ProjectDialogue = { + ...dialogue, + lines: dialogue.lines.map((line) => ({ ...line })) + }; + mutate(nextDialogue); + return nextDialogue; + }); + + return { + dialogues: nextDialogues, + defaultDialogueId }; - mutate(nextDialogue); - return nextDialogue; - }); - - return { - dialogues: nextDialogues, - defaultDialogueId - }; - }); + } + ); }; const updateNpcDialogueLine = ( @@ -5444,7 +5484,9 @@ export function App({ store, initialStatusMessage }: AppProps) { ); if (targetOption === null) { - throw new Error("The current sequence control target no longer exists."); + throw new Error( + "The current sequence control target no longer exists." + ); } step.effect = createProjectScheduleEffectFromOption({ @@ -5799,7 +5841,9 @@ export function App({ store, initialStatusMessage }: AppProps) { "Updated visibility target.", (step) => { if (step.type !== "setVisibility") { - throw new Error("Only visibility effects expose a whitebox solid target."); + throw new Error( + "Only visibility effects expose a whitebox solid target." + ); } step.target = target; @@ -5827,7 +5871,6 @@ export function App({ store, initialStatusMessage }: AppProps) { ); }; - const updateWorldTimeOfDaySettings = ( label: string, successMessage: string, @@ -5891,7 +5934,8 @@ export function App({ store, initialStatusMessage }: AppProps) { mode: WorldBackgroundMode, imageAssetId?: string ) => { - const currentBackground = editorState.document.world.timeOfDay[phase].background; + const currentBackground = + editorState.document.world.timeOfDay[phase].background; if (mode === "image") { const currentBackgroundAssetId = @@ -5959,7 +6003,9 @@ export function App({ store, initialStatusMessage }: AppProps) { phase: WorldTimePhaseKey, colorHex: string ) => { - if (editorState.document.world.timeOfDay[phase].background.mode !== "solid") { + if ( + editorState.document.world.timeOfDay[phase].background.mode !== "solid" + ) { return; } @@ -6024,7 +6070,9 @@ export function App({ store, initialStatusMessage }: AppProps) { phase: WorldTimePhaseKey, draftValue: string ) => { - if (editorState.document.world.timeOfDay[phase].background.mode !== "image") { + if ( + editorState.document.world.timeOfDay[phase].background.mode !== "image" + ) { return; } @@ -7265,7 +7313,10 @@ export function App({ store, initialStatusMessage }: AppProps) { colorHex: overrides.colorHex ?? boxVolumeLightColorDraft, intensity: overrides.intensity ?? - readNonNegativeNumberDraft(boxVolumeLightIntensityDraft, "Light intensity"), + readNonNegativeNumberDraft( + boxVolumeLightIntensityDraft, + "Light intensity" + ), padding: overrides.padding ?? readNonNegativeNumberDraft(boxVolumeLightPaddingDraft, "Light padding"), @@ -7421,7 +7472,8 @@ export function App({ store, initialStatusMessage }: AppProps) { const handlePlayEditorSimulation = () => { setEditorSimulationClockOverride( (currentClock) => - currentClock ?? createRuntimeClockState(editorState.projectDocument.time) + currentClock ?? + createRuntimeClockState(editorState.projectDocument.time) ); setEditorSimulationPlaying(true); }; @@ -7439,7 +7491,8 @@ export function App({ store, initialStatusMessage }: AppProps) { setEditorSimulationPlaying(false); setEditorSimulationClockOverride((currentClock) => offsetRuntimeClockState( - currentClock ?? createRuntimeClockState(editorState.projectDocument.time), + currentClock ?? + createRuntimeClockState(editorState.projectDocument.time), deltaHours ) ); @@ -7941,7 +7994,8 @@ export function App({ store, initialStatusMessage }: AppProps) { } const nextMaterialId = materialId === "" ? null : materialId; - const currentMaterialId = selectedTerrain.layers[layerIndex]?.materialId ?? null; + const currentMaterialId = + selectedTerrain.layers[layerIndex]?.materialId ?? null; if (currentMaterialId === nextMaterialId) { return; @@ -7967,8 +8021,8 @@ export function App({ store, initialStatusMessage }: AppProps) { `${getTerrainLayerLabel(layerIndex)} now uses ${ nextMaterialId === null ? "no assigned material" - : editorState.document.materials[nextMaterialId]?.name ?? - nextMaterialId + : (editorState.document.materials[nextMaterialId]?.name ?? + nextMaterialId) }.` ); } catch (error) { @@ -8226,7 +8280,10 @@ export function App({ store, initialStatusMessage }: AppProps) { }; const handleTerrainCollisionEnabledChange = (enabled: boolean) => { - if (selectedTerrain === null || selectedTerrain.collisionEnabled === enabled) { + if ( + selectedTerrain === null || + selectedTerrain.collisionEnabled === enabled + ) { return; } @@ -8599,7 +8656,10 @@ export function App({ store, initialStatusMessage }: AppProps) { const updateSelectedNpcDialogues = ( _label: string, successMessage: string, - mutate: (dialogues: ProjectDialogue[], defaultDialogueId: string | null) => { + mutate: ( + dialogues: ProjectDialogue[], + defaultDialogueId: string | null + ) => { dialogues: ProjectDialogue[]; defaultDialogueId: string | null; } @@ -9553,7 +9613,10 @@ export function App({ store, initialStatusMessage }: AppProps) { createRunSequenceInteractionLink({ id: link.id, sourceEntityId: sourceEntity.id, - trigger: getCanonicalInteractionLinkTrigger(sourceEntity, link.trigger), + trigger: getCanonicalInteractionLinkTrigger( + sourceEntity, + link.trigger + ), sequenceId: defaultSequence.id }), "Switched link action to run sequence." @@ -10214,7 +10277,9 @@ export function App({ store, initialStatusMessage }: AppProps) { @@ -10525,10 +10590,7 @@ export function App({ store, initialStatusMessage }: AppProps) { ); }; - const applyShaderSkyDayColor = ( - edge: "top" | "bottom", - colorHex: string - ) => { + const applyShaderSkyDayColor = (edge: "top" | "bottom", colorHex: string) => { applyShaderSkySettings( edge === "top" ? "Set shader sky day top color" @@ -11462,7 +11524,9 @@ export function App({ store, initialStatusMessage }: AppProps) { materialInspectorScope === "brush" ) { if (selectedBrushSharedMaterialId === materialId) { - setStatusMessage("All faces on the selected whitebox already use that material."); + setStatusMessage( + "All faces on the selected whitebox already use that material." + ); return; } @@ -11520,7 +11584,10 @@ export function App({ store, initialStatusMessage }: AppProps) { whiteboxSelectionMode === "object" && materialInspectorScope === "brush" ) { - if (selectedBrushSharedMaterialId === null && !selectedBrushHasMixedFaceMaterials) { + if ( + selectedBrushSharedMaterialId === null && + !selectedBrushHasMixedFaceMaterials + ) { setStatusMessage( "All faces on the selected whitebox already use the fallback face material." ); @@ -11700,7 +11767,9 @@ export function App({ store, initialStatusMessage }: AppProps) { }) }) ); - setStatusMessage("Rotated all face UVs 90 degrees on the selected whitebox."); + setStatusMessage( + "Rotated all face UVs 90 degrees on the selected whitebox." + ); return; } @@ -11777,7 +11846,7 @@ export function App({ store, initialStatusMessage }: AppProps) { const currentMaterial = currentMaterialId === null ? null - : editorState.document.materials[currentMaterialId] ?? null; + : (editorState.document.materials[currentMaterialId] ?? null); return currentMaterial === null ? createFitToFaceBoxBrushFaceUvState(selectedBrush, faceId) @@ -12512,8 +12581,8 @@ export function App({ store, initialStatusMessage }: AppProps) {
Whitebox Solids
{brushList.length === 0 ? (
- Use Add > Whitebox Primitives > Box and click in the viewport to create - the first solid. + Use Add > Whitebox Primitives > Box and click in the + viewport to create the first solid.
) : (
setSchedulePaneOpen(false)} - onCreateRoutineSequence={handleCreateAttachedSequenceForRoutine} + onCreateRoutineSequence={ + handleCreateAttachedSequenceForRoutine + } onSetRoutineTarget={(routineId, targetKey) => updateProjectSequencerState( "Set project sequencer target", @@ -13251,20 +13320,24 @@ export function App({ store, initialStatusMessage }: AppProps) { ); } - const previousTargetKey = getControlTargetRefKey(routine.target); + const previousTargetKey = getControlTargetRefKey( + routine.target + ); if (targetOption.target.kind === "actor") { const attachedSequence = routine.sequenceId === null ? null - : sequences.sequences[routine.sequenceId] ?? null; + : (sequences.sequences[routine.sequenceId] ?? + null); routine.target = targetOption.target; routine.effects = []; if ( attachedSequence !== null && - getProjectSequenceHeldSteps(attachedSequence).length > 0 + getProjectSequenceHeldSteps(attachedSequence) + .length > 0 ) { const retargetedSequence = cloneSequenceForRetargetedPlacement({ @@ -13277,11 +13350,12 @@ export function App({ store, initialStatusMessage }: AppProps) { routine.sequenceId = retargetedSequence.id; setSelectedSequenceId(retargetedSequence.id); } else { - const nextSequence = createAttachedSequenceForRoutine({ - title: routine.title, - targetOption, - routine - }); + const nextSequence = + createAttachedSequenceForRoutine({ + title: routine.title, + targetOption, + routine + }); sequences.sequences[nextSequence.id] = nextSequence; routine.sequenceId = nextSequence.id; setSelectedSequenceId(nextSequence.id); @@ -13293,7 +13367,8 @@ export function App({ store, initialStatusMessage }: AppProps) { const attachedSequence = routine.sequenceId === null ? null - : sequences.sequences[routine.sequenceId] ?? null; + : (sequences.sequences[routine.sequenceId] ?? + null); routine.target = targetOption.target; routine.effects = []; @@ -13304,7 +13379,9 @@ export function App({ store, initialStatusMessage }: AppProps) { attachedSequence === null ? [] : attachedSequence.effects - .filter((effect) => effect.stepClass === "impulse") + .filter( + (effect) => effect.stepClass === "impulse" + ) .map(cloneSequenceEffect) }); sequences.sequences[nextSequence.id] = nextSequence; @@ -13319,13 +13396,13 @@ export function App({ store, initialStatusMessage }: AppProps) { const nextEffectOptionId = currentPrimaryEffect === null ? effectOptions[0]?.id - : effectOptions.find( + : (effectOptions.find( (effectOption) => effectOption.id === getProjectScheduleEffectOptionId( currentPrimaryEffect ) - )?.id ?? effectOptions[0]?.id; + )?.id ?? effectOptions[0]?.id); if (nextEffectOptionId === undefined) { throw new Error( @@ -13336,20 +13413,22 @@ export function App({ store, initialStatusMessage }: AppProps) { const attachedSequence = routine.sequenceId === null ? null - : sequences.sequences[routine.sequenceId] ?? null; + : (sequences.sequences[routine.sequenceId] ?? null); routine.target = targetOption.target; routine.effects = []; if ( attachedSequence !== null && - getProjectSequenceHeldSteps(attachedSequence).length > 0 + getProjectSequenceHeldSteps(attachedSequence).length > + 0 ) { - const retargetedSequence = cloneSequenceForRetargetedPlacement({ - sequence: attachedSequence, - targetOption, - previousTargetKey - }); + const retargetedSequence = + cloneSequenceForRetargetedPlacement({ + sequence: attachedSequence, + targetOption, + previousTargetKey + }); sequences.sequences[retargetedSequence.id] = retargetedSequence; routine.sequenceId = retargetedSequence.id; @@ -13473,7 +13552,9 @@ export function App({ store, initialStatusMessage }: AppProps) { } onAddVisibilityStep={handleAddProjectSequenceVisibilityStep} onDeleteStep={handleDeleteProjectSequenceStep} - onSetControlStepTarget={updateProjectSequenceControlStepTarget} + onSetControlStepTarget={ + updateProjectSequenceControlStepTarget + } onSetControlStepEffectOption={ updateProjectSequenceControlStepEffectOption } @@ -13489,7 +13570,9 @@ export function App({ store, initialStatusMessage }: AppProps) { onSetControlStepAnimationLoop={ updateProjectSequenceControlStepAnimationLoop } - onSetControlStepPathId={updateProjectSequenceControlStepPathId} + onSetControlStepPathId={ + updateProjectSequenceControlStepPathId + } onSetControlStepPathSpeed={ updateProjectSequenceControlStepPathSpeed } @@ -13602,8 +13685,11 @@ export function App({ store, initialStatusMessage }: AppProps) {
Clock
Day {editorSimulationClock.dayCount + 1} ·{" "} - {formatTimeOfDayHours(editorSimulationClock.timeOfDayHours)} ·{" "} - {formatRuntimeDayPhaseLabel(editorSimulationTimeState.dayPhase)} + {formatTimeOfDayHours(editorSimulationClock.timeOfDayHours)}{" "} + ·{" "} + {formatRuntimeDayPhaseLabel( + editorSimulationTimeState.dayPhase + )}
{editorSimulationClockOverride === null @@ -13656,7 +13742,9 @@ export function App({ store, initialStatusMessage }: AppProps) {
{editorSimulationMessage === null ? null : ( -
{editorSimulationMessage}
+
+ {editorSimulationMessage} +
)}
@@ -14009,8 +14097,8 @@ export function App({ store, initialStatusMessage }: AppProps) {
Draws sun and moon visuals in both the editor viewport and - the runner. In shader mode, the sky shader owns those - discs directly. + the runner. In shader mode, the sky shader owns those discs + directly.
@@ -14890,16 +14978,19 @@ export function App({ store, initialStatusMessage }: AppProps) {
{describeWorldBackground( - editorState.document.world.timeOfDay.dawn.background, + editorState.document.world.timeOfDay.dawn + .background, editorState.document.assets, { emptyImageLabel: @@ -15176,16 +15267,19 @@ export function App({ store, initialStatusMessage }: AppProps) {
{describeWorldBackground( - editorState.document.world.timeOfDay.dusk.background, + editorState.document.world.timeOfDay.dusk + .background, editorState.document.assets, { emptyImageLabel: @@ -16498,7 +16592,9 @@ export function App({ store, initialStatusMessage }: AppProps) { <>
Selection Kind
-
{multiSelectionSummary.kindLabel}
+
+ {multiSelectionSummary.kindLabel} +
{multiSelectionSummary.count} selected
@@ -16506,7 +16602,9 @@ export function App({ store, initialStatusMessage }: AppProps) {
Active Item
-
{multiSelectionSummary.activeLabel}
+
+ {multiSelectionSummary.activeLabel} +
{multiSelectionSummary.activeId}
@@ -16535,7 +16633,9 @@ export function App({ store, initialStatusMessage }: AppProps) { : "Selected"}
- {item.id} + + {item.id} +
))} @@ -16550,7 +16650,8 @@ export function App({ store, initialStatusMessage }: AppProps) { {getTerrainLabelById(selectedTerrain.id, terrainList)}
- {selectedTerrain.sampleCountX} x {selectedTerrain.sampleCountZ} samples + {selectedTerrain.sampleCountX} x{" "} + {selectedTerrain.sampleCountZ} samples {" · "} {selectedTerrain.cellSize}m cells
@@ -16572,7 +16673,9 @@ export function App({ store, initialStatusMessage }: AppProps) {
Collision
- Hidden terrain keeps collision. Disabled terrain is removed - from editor picking, rendering, and runtime collision. + Hidden terrain keeps collision. Disabled terrain is + removed from editor picking, rendering, and runtime + collision.
@@ -16769,7 +16873,9 @@ export function App({ store, initialStatusMessage }: AppProps) { data-testid="terrain-paint-active-layer" value={resolvedTerrainPaintLayerIndex} onChange={(event) => - handleTerrainPaintLayerChange(event.currentTarget.value) + handleTerrainPaintLayerChange( + event.currentTarget.value + ) } > {Array.from( @@ -16783,7 +16889,8 @@ export function App({ store, initialStatusMessage }: AppProps) {
- {getTerrainLayerLabel(resolvedTerrainPaintLayerIndex)} uses{" "} + {getTerrainLayerLabel(resolvedTerrainPaintLayerIndex)}{" "} + uses{" "} {selectedTerrainActivePaintMaterial?.name ?? selectedTerrainActivePaintLayer?.materialId ?? "no assigned material"} @@ -16791,10 +16898,7 @@ export function App({ store, initialStatusMessage }: AppProps) {
{selectedTerrain.layers.map((layer, layerIndex) => ( -
@@ -19605,7 +19710,8 @@ export function App({ store, initialStatusMessage }: AppProps) { "Rename NPC dialogue", "Updated NPC dialogue title.", (dialogue) => { - dialogue.title = title.trim() || "Untitled Dialogue"; + dialogue.title = + title.trim() || "Untitled Dialogue"; } ) } @@ -19615,7 +19721,9 @@ export function App({ store, initialStatusMessage }: AppProps) { "Add NPC dialogue line", "Added NPC dialogue line.", (dialogue) => { - dialogue.lines.push(createProjectDialogueLine()); + dialogue.lines.push( + createProjectDialogueLine() + ); } ) } @@ -20432,7 +20540,6 @@ export function App({ store, initialStatusMessage }: AppProps) { )} ) : null} - ) : selectedBrush === null ? (
diff --git a/src/assets/starter-environment-assets.ts b/src/assets/starter-environment-assets.ts index 3a9b1090..76da8878 100644 --- a/src/assets/starter-environment-assets.ts +++ b/src/assets/starter-environment-assets.ts @@ -159,7 +159,9 @@ function isDefaultDayBackground(background: WorldBackgroundSettings): boolean { ); } -function isDefaultNightBackground(background: WorldBackgroundSettings): boolean { +function isDefaultNightBackground( + background: WorldBackgroundSettings +): boolean { return areWorldBackgroundSettingsEqual( background, createDefaultWorldTimeOfDaySettings().night.background @@ -189,8 +191,9 @@ export function isStarterEnvironmentImageAsset( function getStarterEnvironmentDefinitionById(assetId: string) { return ( - STARTER_ENVIRONMENT_LIBRARY.find((definition) => definition.asset.id === assetId) ?? - null + STARTER_ENVIRONMENT_LIBRARY.find( + (definition) => definition.asset.id === assetId + ) ?? null ); } @@ -228,7 +231,8 @@ export function mergeStarterEnvironmentAssets( const mergedAssets: Record = {}; for (const definition of STARTER_ENVIRONMENT_LIBRARY) { - mergedAssets[definition.asset.id] = cloneStarterEnvironmentAsset(definition); + mergedAssets[definition.asset.id] = + cloneStarterEnvironmentAsset(definition); } for (const [assetId, asset] of Object.entries(assets)) { @@ -257,10 +261,11 @@ export function applyStarterEnvironmentAssetsToProjectDocument( } if (isDefaultNightBackground(nextWorld.timeOfDay.night.background)) { - nextWorld.timeOfDay.night.background = createStarterEnvironmentBackground( - STARTER_NIGHT_ENVIRONMENT_ASSET_ID, - DEFAULT_NIGHT_IMAGE_ENVIRONMENT_INTENSITY - ); + nextWorld.timeOfDay.night.background = + createStarterEnvironmentBackground( + STARTER_NIGHT_ENVIRONMENT_ASSET_ID, + DEFAULT_NIGHT_IMAGE_ENVIRONMENT_INTENSITY + ); } else { nextWorld.timeOfDay.night.background = cloneWorldBackground( nextWorld.timeOfDay.night.background diff --git a/src/document/migrate-scene-document.ts b/src/document/migrate-scene-document.ts index c2ffdc5a..096fe186 100644 --- a/src/document/migrate-scene-document.ts +++ b/src/document/migrate-scene-document.ts @@ -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( diff --git a/src/document/scene-document-validation.ts b/src/document/scene-document-validation.ts index 96b97ce5..f86a98c9 100644 --- a/src/document/scene-document-validation.ts +++ b/src/document/scene-document-validation.ts @@ -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 { diff --git a/src/document/scene-document.ts b/src/document/scene-document.ts index 71e284c2..da8addba 100644 --- a/src/document/scene-document.ts +++ b/src/document/scene-document.ts @@ -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; diff --git a/src/document/world-settings.ts b/src/document/world-settings.ts index 96ccc5fe..76524431 100644 --- a/src/document/world-settings.ts +++ b/src/document/world-settings.ts @@ -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 }; } diff --git a/src/rendering/world-background-renderer.ts b/src/rendering/world-background-renderer.ts index f36bd288..74eaa525 100644 --- a/src/rendering/world-background-renderer.ts +++ b/src/rendering/world-background-renderer.ts @@ -636,18 +636,22 @@ export class WorldBackgroundRenderer { opacity: 0 }); private readonly celestialGeometry = new PlaneGeometry(1, 1); - private readonly sunMaterial = createCelestialBodyMaterial( - SUN_FRAGMENT_SHADER - ); - private readonly moonMaterial = createCelestialBodyMaterial( - MOON_FRAGMENT_SHADER - ); + private readonly sunMaterial = + createCelestialBodyMaterial(SUN_FRAGMENT_SHADER); + private readonly moonMaterial = + createCelestialBodyMaterial(MOON_FRAGMENT_SHADER); private readonly shaderMesh = new Mesh(this.geometry, this.shaderSkyMaterial); - private readonly gradientMesh = new Mesh(this.geometry, this.gradientMaterial); + private readonly gradientMesh = new Mesh( + this.geometry, + this.gradientMaterial + ); private readonly imageMesh = new Mesh(this.geometry, this.imageMaterial); private readonly overlayMesh = new Mesh(this.geometry, this.overlayMaterial); private readonly sunMesh = new Mesh(this.celestialGeometry, this.sunMaterial); - private readonly moonMesh = new Mesh(this.celestialGeometry, this.moonMaterial); + private readonly moonMesh = new Mesh( + this.celestialGeometry, + this.moonMaterial + ); private readonly celestialBodyPosition = new Vector3(); private sunState: WorldCelestialBodyState | null = null; private moonState: WorldCelestialBodyState | null = null; @@ -730,9 +734,12 @@ export class WorldBackgroundRenderer { this.overlayMaterial.opacity = overlayOpacity; this.overlayMesh.visible = !showShaderBackground && overlayOpacity > NIGHT_BACKGROUND_EPSILON; - this.sunState = showShaderBackground ? null : celestialBodies?.sun ?? null; - this.moonState = - showShaderBackground ? null : celestialBodies?.moon ?? null; + this.sunState = showShaderBackground + ? null + : (celestialBodies?.sun ?? null); + this.moonState = showShaderBackground + ? null + : (celestialBodies?.moon ?? null); this.syncCelestialBodyVisualState( this.sunMesh, this.sunMaterial, @@ -805,7 +812,8 @@ export class WorldBackgroundRenderer { state.celestial.sunIntensity; this.shaderSkyMaterial.uniforms.uSunDiscSizeDegrees.value = state.celestial.sunDiscSizeDegrees; - this.shaderSkyMaterial.uniforms.uSunVisible.value = state.celestial.sunVisible + this.shaderSkyMaterial.uniforms.uSunVisible.value = state.celestial + .sunVisible ? 1 : 0; this.shaderSkyMaterial.uniforms.uMoonDirection.value.set( @@ -820,7 +828,8 @@ export class WorldBackgroundRenderer { state.celestial.moonIntensity; this.shaderSkyMaterial.uniforms.uMoonDiscSizeDegrees.value = state.celestial.moonDiscSizeDegrees; - this.shaderSkyMaterial.uniforms.uMoonVisible.value = state.celestial.moonVisible + this.shaderSkyMaterial.uniforms.uMoonVisible.value = state.celestial + .moonVisible ? 1 : 0; this.shaderSkyMaterial.uniforms.uDaylightFactor.value = diff --git a/src/rendering/world-shader-sky.ts b/src/rendering/world-shader-sky.ts index 8ce24f11..888480c7 100644 --- a/src/rendering/world-shader-sky.ts +++ b/src/rendering/world-shader-sky.ts @@ -72,7 +72,9 @@ function parseHexColor(colorHex: string): { r: number; g: number; b: number } { function formatHexColor(color: { r: number; g: number; b: number }): string { const toHex = (value: number) => - Math.round(clamp(value, 0, 255)).toString(16).padStart(2, "0"); + Math.round(clamp(value, 0, 255)) + .toString(16) + .padStart(2, "0"); return `#${toHex(color.r)}${toHex(color.g)}${toHex(color.b)}`; } @@ -86,8 +88,7 @@ function blendHexColorsByWeights( }, weights: RuntimeDayNightPhaseWeights ): string { - const totalWeight = - weights.day + weights.dawn + weights.dusk + weights.night; + const totalWeight = weights.day + weights.dawn + weights.dusk + weights.night; if (totalWeight <= 1e-6) { return colors.day; diff --git a/src/runtime-three/runtime-host.ts b/src/runtime-three/runtime-host.ts index 62e76145..b4354eb0 100644 --- a/src/runtime-three/runtime-host.ts +++ b/src/runtime-three/runtime-host.ts @@ -352,7 +352,10 @@ export class RuntimeHost { string, Mesh >(); - private readonly terrainMeshes = new Map>(); + private readonly terrainMeshes = new Map< + string, + Mesh + >(); private volumeTime = 0; private readonly volumeAnimatedUniforms: Array<{ value: number }> = []; private readonly runtimeWaterContactUniforms: RuntimeWaterContactUniformBinding[] = @@ -361,7 +364,10 @@ export class RuntimeHost { string, LocalLightRenderObjects >(); - private readonly lightVolumeObjects = new Map(); + private readonly lightVolumeObjects = new Map< + string, + LightVolumeRenderObjects + >(); private readonly modelRenderObjects = new Map(); private readonly materialTextureCache = new Map< string, @@ -1348,8 +1354,16 @@ export class RuntimeHost { } if (!shadowsEnabled || this.currentCelestialShadowCaster === null) { - configureAdvancedRenderingShadowLight(this.sunLight, advancedRendering, false); - configureAdvancedRenderingShadowLight(this.moonLight, advancedRendering, false); + configureAdvancedRenderingShadowLight( + this.sunLight, + advancedRendering, + false + ); + configureAdvancedRenderingShadowLight( + this.moonLight, + advancedRendering, + false + ); return; } @@ -1374,8 +1388,16 @@ export class RuntimeHost { }); if (fit === null) { - configureAdvancedRenderingShadowLight(this.sunLight, advancedRendering, false); - configureAdvancedRenderingShadowLight(this.moonLight, advancedRendering, false); + configureAdvancedRenderingShadowLight( + this.sunLight, + advancedRendering, + false + ); + configureAdvancedRenderingShadowLight( + this.moonLight, + advancedRendering, + false + ); return; } @@ -1661,7 +1683,9 @@ export class RuntimeHost { target.entityKind === "pointLight" ? this.runtimeScene.localLights.pointLights : this.runtimeScene.localLights.spotLights; - const light = lights.find((candidate) => candidate.entityId === target.entityId); + const light = lights.find( + (candidate) => candidate.entityId === target.entityId + ); if (light !== undefined) { mutate(light); @@ -1702,7 +1726,10 @@ export class RuntimeHost { renderObjects.light.intensity = intensity; } - private applyLightColorControl(target: LightControlTargetRef, colorHex: string) { + private applyLightColorControl( + target: LightControlTargetRef, + colorHex: string + ) { this.mutateRuntimeLightState(target, (light) => { light.colorHex = colorHex; }); @@ -2167,7 +2194,10 @@ export class RuntimeHost { kind: "terrain", enabled: true }).geometry; - const mesh = new Mesh(geometry, this.createRuntimeTerrainMaterial(terrain)); + const mesh = new Mesh( + geometry, + this.createRuntimeTerrainMaterial(terrain) + ); mesh.position.set( terrain.position.x, @@ -2186,8 +2216,9 @@ export class RuntimeHost { private createRuntimeTerrainMaterial(terrain: RuntimeTerrain): Material { const layerTextures = terrain.layers.map((layer) => - getTerrainLayerTexture(layer.material, (material) => - this.getOrCreateTextureSet(material).baseColor + getTerrainLayerTexture( + layer.material, + (material) => this.getOrCreateTextureSet(material).baseColor ) ) as [Texture, Texture, Texture, Texture]; @@ -2504,10 +2535,9 @@ export class RuntimeHost { waveStrength: brush.volume.water.waveStrength, surfaceDisplacementEnabled: brush.volume.water.surfaceDisplacementEnabled, - opacity: - isTopFace - ? Math.min(1, baseOpacity + 0.18) - : baseOpacity * 0.5, + opacity: isTopFace + ? Math.min(1, baseOpacity + 0.18) + : baseOpacity * 0.5, quality: volumeRenderPaths.water === "quality", wireframe: false, isTopFace, @@ -3433,7 +3463,11 @@ export class RuntimeHost { if (renderGroup !== undefined) { renderGroup.visible = npc.visible && npc.active; - renderGroup.position.set(npc.position.x, npc.position.y, npc.position.z); + renderGroup.position.set( + npc.position.x, + npc.position.y, + npc.position.z + ); renderGroup.rotation.set(0, (npc.yawDegrees * Math.PI) / 180, 0); } @@ -3552,8 +3586,9 @@ export class RuntimeHost { if (this.runtimeScene !== null) { const brush = - this.runtimeScene.brushes.find((candidate) => candidate.id === brushId) ?? - null; + this.runtimeScene.brushes.find( + (candidate) => candidate.id === brushId + ) ?? null; if (brush !== null) { brush.visible = visible ?? !brush.visible; @@ -3567,8 +3602,7 @@ export class RuntimeHost { target: SequenceVisibilityTarget, mode: SequenceVisibilityMode ) { - const explicitVisible = - mode === "toggle" ? undefined : mode === "show"; + const explicitVisible = mode === "toggle" ? undefined : mode === "show"; if (target.kind === "brush") { this.applyToggleBrushVisibilityAction(target.brushId, explicitVisible); @@ -3703,7 +3737,9 @@ export class RuntimeHost { return null; } - const dialogue = npc.dialogues.find((candidate) => candidate.id === dialogueId); + const dialogue = npc.dialogues.find( + (candidate) => candidate.id === dialogueId + ); if (dialogue === undefined) { return null; @@ -3776,10 +3812,7 @@ export class RuntimeHost { } const resolvedDialogueId = - dialogueId ?? - npc.defaultDialogueId ?? - npc.dialogues[0]?.id ?? - null; + dialogueId ?? npc.defaultDialogueId ?? npc.dialogues[0]?.id ?? null; if (resolvedDialogueId === null) { console.warn(`dialogue: npc ${npcEntityId} has no dialogue to speak`); diff --git a/src/runtime-three/runtime-project-time.ts b/src/runtime-three/runtime-project-time.ts index 072687f9..a983ec03 100644 --- a/src/runtime-three/runtime-project-time.ts +++ b/src/runtime-three/runtime-project-time.ts @@ -139,10 +139,7 @@ function rotateAroundAxis(vector: Vec3, axis: Vec3, radians: number): Vec3 { return normalizeVec3( addVec3( - addVec3( - scaleVec3(vector, cosine), - scaleVec3(cross(axis, vector), sine) - ), + addVec3(scaleVec3(vector, cosine), scaleVec3(cross(axis, vector), sine)), scaleVec3(axis, dot(axis, vector) * (1 - cosine)) ) ); @@ -158,7 +155,9 @@ function parseHexColor(colorHex: string): { r: number; g: number; b: number } { function formatHexColor(color: { r: number; g: number; b: number }): string { const toHex = (value: number) => - Math.round(clamp(value, 0, 255)).toString(16).padStart(2, "0"); + Math.round(clamp(value, 0, 255)) + .toString(16) + .padStart(2, "0"); return `#${toHex(color.r)}${toHex(color.g)}${toHex(color.b)}`; } @@ -170,8 +169,7 @@ function blendHexColorsByWeights( nightHex: string, weights: RuntimeDayNightPhaseWeights ): string { - const totalWeight = - weights.day + weights.dawn + weights.dusk + weights.night; + const totalWeight = weights.day + weights.dawn + weights.dusk + weights.night; if (totalWeight <= 1e-6) { return dayHex; @@ -211,19 +209,19 @@ function blendScalarByWeights( nightValue: number, weights: RuntimeDayNightPhaseWeights ): number { - const totalWeight = - weights.day + weights.dawn + weights.dusk + weights.night; + const totalWeight = weights.day + weights.dawn + weights.dusk + weights.night; if (totalWeight <= 1e-6) { return dayValue; } return ( - dayValue * weights.day + - dawnValue * weights.dawn + - duskValue * weights.dusk + - nightValue * weights.night - ) / totalWeight; + (dayValue * weights.day + + dawnValue * weights.dawn + + duskValue * weights.dusk + + nightValue * weights.night) / + totalWeight + ); } function resolveNoonSunDirection(direction: Vec3): Vec3 { @@ -279,7 +277,9 @@ function hasTimeBoundaryBeenCrossed( currentTimeOfDayHours: number, boundaryTimeOfDayHours: number ): boolean { - const normalizedPreviousTime = normalizeTimeOfDayHours(previousTimeOfDayHours); + const normalizedPreviousTime = normalizeTimeOfDayHours( + previousTimeOfDayHours + ); if (areTimesEquivalent(normalizedPreviousTime, boundaryTimeOfDayHours)) { return false; @@ -563,7 +563,9 @@ function hasConfiguredImageBackground( return background.mode === "image" && background.assetId.trim().length > 0; } -function resolveBackgroundTopColor(background: WorldBackgroundSettings): string { +function resolveBackgroundTopColor( + background: WorldBackgroundSettings +): string { if (background.mode === "solid") { return background.colorHex; } @@ -575,7 +577,9 @@ function resolveBackgroundTopColor(background: WorldBackgroundSettings): string return "#000000"; } -function resolveBackgroundBottomColor(background: WorldBackgroundSettings): string { +function resolveBackgroundBottomColor( + background: WorldBackgroundSettings +): string { if (background.mode === "solid") { return background.colorHex; } @@ -900,10 +904,15 @@ export function resolveRuntimeDayNightWorldState( world: WorldSettings, settings: ProjectTimeSettings, clock: RuntimeClockState | null, - resolvedTime: RuntimeResolvedTimeState | null = - clock === null ? null : resolveRuntimeTimeState(settings, clock) + resolvedTime: RuntimeResolvedTimeState | null = clock === null + ? null + : resolveRuntimeTimeState(settings, clock) ): RuntimeDayNightWorldState { - if (clock === null || resolvedTime === null || !world.projectTimeLightingEnabled) { + if ( + clock === null || + resolvedTime === null || + !world.projectTimeLightingEnabled + ) { return { ambientLight: { colorHex: world.ambientLight.colorHex, diff --git a/src/shared-ui/world-background-style.ts b/src/shared-ui/world-background-style.ts index aba86ea0..ef6c1b45 100644 --- a/src/shared-ui/world-background-style.ts +++ b/src/shared-ui/world-background-style.ts @@ -5,12 +5,10 @@ import type { WorldBackgroundSettings } from "../document/world-settings"; export function createWorldBackgroundStyle( background: WorldBackgroundSettings, imageUrl: string | null = null, - shaderPreview: - | { - topColorHex: string; - bottomColorHex: string; - } - | null = null + shaderPreview: { + topColorHex: string; + bottomColorHex: string; + } | null = null ): CSSProperties { if (background.mode === "solid") { return { @@ -24,9 +22,7 @@ export function createWorldBackgroundStyle( backgroundColor: shaderPreview?.bottomColorHex ?? "#8cbef6", backgroundImage: `linear-gradient(180deg, ${ shaderPreview?.topColorHex ?? "#5f8fd3" - } 0%, ${ - shaderPreview?.bottomColorHex ?? "#d8eeff" - } 100%)` + } 0%, ${shaderPreview?.bottomColorHex ?? "#d8eeff"} 100%)` }; } diff --git a/src/viewport-three/ViewportCanvas.tsx b/src/viewport-three/ViewportCanvas.tsx index 5e441706..542f4e6d 100644 --- a/src/viewport-three/ViewportCanvas.tsx +++ b/src/viewport-three/ViewportCanvas.tsx @@ -162,7 +162,10 @@ export function ViewportCanvas({ }, [world]); useLayoutEffect(() => { - hostRef.current?.updateSimulation(editorSimulationScene, editorSimulationClock); + hostRef.current?.updateSimulation( + editorSimulationScene, + editorSimulationClock + ); }, [ editorSimulationScene, editorSimulationClock?.dayCount, @@ -293,9 +296,10 @@ export function ViewportCanvas({ toolPreview.kind === "create" && toolPreview.center !== null; const transformPreviewVisible = transformSession.kind === "active"; - const selectedWhiteboxLabel = toolMode === "select" - ? getWhiteboxSelectionFeedbackLabel(sceneDocument, selection) - : null; + const selectedWhiteboxLabel = + toolMode === "select" + ? getWhiteboxSelectionFeedbackLabel(sceneDocument, selection) + : null; const terrainBrushOverlayVisible = toolMode === "select" && terrainBrushState != null; const resolvedViewportBackground = @@ -329,8 +333,8 @@ export function ViewportCanvas({ : createWorldBackgroundStyle( resolvedViewportBackground, resolvedViewportBackground.mode === "image" - ? (loadedImageAssets[resolvedViewportBackground.assetId]?.previewUrl ?? - null) + ? (loadedImageAssets[resolvedViewportBackground.assetId] + ?.previewUrl ?? null) : null, resolvedViewportBackground.mode === "shader" ? { @@ -376,7 +380,7 @@ export function ViewportCanvas({ ] .filter((part) => part !== null) .join(" · ")} -
+
)} {!terrainBrushOverlayVisible ? null : (
(); - private readonly terrainRenderObjects = new Map(); + private readonly terrainRenderObjects = new Map< + string, + TerrainRenderObjects + >(); private readonly pathRenderObjects = new Map(); private readonly entityRenderObjects = new Map(); private readonly localLightRenderObjects = new Map< @@ -1085,7 +1081,7 @@ export class ViewportHost { ? this.currentTerrainBrushState.layerIndex !== terrainBrushState.layerIndex : this.currentTerrainBrushState?.tool === "paint" || - terrainBrushState?.tool === "paint"; + terrainBrushState?.tool === "paint"; this.currentTerrainBrushState = terrainBrushState; @@ -1524,14 +1520,16 @@ export class ViewportHost { const world = this.currentSimulationScene?.world ?? this.currentWorld; const resolvedTime = - this.currentSimulationScene !== null && this.currentSimulationClock !== null + this.currentSimulationScene !== null && + this.currentSimulationClock !== null ? resolveRuntimeTimeState( this.currentSimulationScene.time, this.currentSimulationClock ) : null; const resolvedWorld = - this.currentSimulationScene !== null && this.currentSimulationClock !== null + this.currentSimulationScene !== null && + this.currentSimulationClock !== null ? resolveRuntimeDayNightWorldState( world, this.currentSimulationScene.time, @@ -1542,9 +1540,9 @@ export class ViewportHost { const rendererSettings = this.displayMode !== "normal" ? { - ...cloneAdvancedRenderingSettings(world.advancedRendering), - enabled: false - } + ...cloneAdvancedRenderingSettings(world.advancedRendering), + enabled: false + } : world.advancedRendering; const displayedAmbientLight = resolvedWorld?.ambientLight ?? world.ambientLight; @@ -1630,22 +1628,21 @@ export class ViewportHost { displayedSunLight, displayedMoonLight ); - const shaderSkyResolvedWorld = - resolvedWorld ?? { - ambientLight: { - ...world.ambientLight - }, - sunLight: { - ...world.sunLight, - direction: { - ...world.sunLight.direction - } - }, - moonLight: null, - background: world.background, - nightBackgroundOverlay: null, - daylightFactor: 1 - }; + const shaderSkyResolvedWorld = resolvedWorld ?? { + ambientLight: { + ...world.ambientLight + }, + sunLight: { + ...world.sunLight, + direction: { + ...world.sunLight.direction + } + }, + moonLight: null, + background: world.background, + nightBackgroundOverlay: null, + daylightFactor: 1 + }; const shaderSkyState = world.background.mode === "shader" ? resolveWorldShaderSkyRenderState( @@ -1744,10 +1741,7 @@ export class ViewportHost { } for (const renderObjects of this.entityRenderObjects.values()) { - applyAdvancedRenderingRenderableShadowFlags( - renderObjects.group, - false - ); + applyAdvancedRenderingRenderableShadowFlags(renderObjects.group, false); } for (const renderGroup of this.modelRenderObjects.values()) { @@ -1773,12 +1767,12 @@ export class ViewportHost { } const halfWidth = - Math.abs(this.orthographicCamera.right - this.orthographicCamera.left) / - Math.max(this.orthographicCamera.zoom, 0.0001) * + (Math.abs(this.orthographicCamera.right - this.orthographicCamera.left) / + Math.max(this.orthographicCamera.zoom, 0.0001)) * 0.5; const halfHeight = - Math.abs(this.orthographicCamera.top - this.orthographicCamera.bottom) / - Math.max(this.orthographicCamera.zoom, 0.0001) * + (Math.abs(this.orthographicCamera.top - this.orthographicCamera.bottom) / + Math.max(this.orthographicCamera.zoom, 0.0001)) * 0.5; return { @@ -1818,8 +1812,16 @@ export class ViewportHost { } if (!shadowsEnabled || this.currentCelestialShadowCaster === null) { - configureAdvancedRenderingShadowLight(this.sunLight, advancedRendering, false); - configureAdvancedRenderingShadowLight(this.moonLight, advancedRendering, false); + configureAdvancedRenderingShadowLight( + this.sunLight, + advancedRendering, + false + ); + configureAdvancedRenderingShadowLight( + this.moonLight, + advancedRendering, + false + ); return; } @@ -1845,8 +1847,16 @@ export class ViewportHost { }); if (fit === null) { - configureAdvancedRenderingShadowLight(this.sunLight, advancedRendering, false); - configureAdvancedRenderingShadowLight(this.moonLight, advancedRendering, false); + configureAdvancedRenderingShadowLight( + this.sunLight, + advancedRendering, + false + ); + configureAdvancedRenderingShadowLight( + this.moonLight, + advancedRendering, + false + ); return; } @@ -2471,7 +2481,9 @@ export class ViewportHost { return path?.enabled === true && path.visible === true; } - private isTransformTargetDisplayedInViewport(session: ActiveTransformSession): boolean { + private isTransformTargetDisplayedInViewport( + session: ActiveTransformSession + ): boolean { switch (session.target.kind) { case "brush": case "brushFace": @@ -2503,7 +2515,9 @@ export class ViewportHost { private getDisplayedTransformSession(): ActiveTransformSession | null { if (this.currentTransformSession.kind === "active") { - return this.isTransformTargetDisplayedInViewport(this.currentTransformSession) + return this.isTransformTargetDisplayedInViewport( + this.currentTransformSession + ) ? this.currentTransformSession : null; } @@ -2858,7 +2872,7 @@ export class ViewportHost { ? session.target.initialPosition : session.target.kind === "pathPoint" ? session.target.initialPosition - : session.target.initialPosition; + : session.target.initialPosition; let nextPosition = { ...initialPosition }; @@ -3604,7 +3618,10 @@ export class ViewportHost { return preview; } - const supportPoints = this.collectSurfaceSnapSupportPoints(session, preview); + const supportPoints = this.collectSurfaceSnapSupportPoints( + session, + preview + ); const axisVector = axisConstraint === null ? null @@ -3718,19 +3735,13 @@ export class ViewportHost { while (current !== null) { const brushId = current.userData.brushId; - if ( - typeof brushId === "string" && - excludedIds.brushIds.has(brushId) - ) { + if (typeof brushId === "string" && excludedIds.brushIds.has(brushId)) { return true; } const entityId = current.userData.entityId; - if ( - typeof entityId === "string" && - excludedIds.entityIds.has(entityId) - ) { + if (typeof entityId === "string" && excludedIds.entityIds.has(entityId)) { return true; } @@ -4822,7 +4833,8 @@ export class ViewportHost { break; case "brushes": if (this.currentTransformSession.preview.kind === "brushes") { - for (const previewItem of this.currentTransformSession.preview.items) { + for (const previewItem of this.currentTransformSession.preview + .items) { const brush = this.currentDocument?.brushes[previewItem.brushId]; if (brush === undefined) { @@ -4861,7 +4873,8 @@ export class ViewportHost { break; case "modelInstances": if (this.currentTransformSession.preview.kind === "modelInstances") { - for (const previewItem of this.currentTransformSession.preview.items) { + for (const previewItem of this.currentTransformSession.preview + .items) { const modelInstance = this.currentDocument?.modelInstances[previewItem.modelInstanceId]; @@ -4902,19 +4915,17 @@ export class ViewportHost { const previewPointId = activeTransformSession.target.pointId; const previewPosition = activeTransformSession.preview.position; - this.updatePathRenderObjectState( - { - ...currentPath, - points: currentPath.points.map((point) => - point.id === previewPointId - ? { - ...point, - position: previewPosition - } - : point - ) - } - ); + this.updatePathRenderObjectState({ + ...currentPath, + points: currentPath.points.map((point) => + point.id === previewPointId + ? { + ...point, + position: previewPosition + } + : point + ) + }); break; } case "entity": { @@ -5001,7 +5012,8 @@ export class ViewportHost { } for (const previewItem of this.currentTransformSession.preview.items) { - const currentEntity = this.currentDocument.entities[previewItem.entityId]; + const currentEntity = + this.currentDocument.entities[previewItem.entityId]; if (currentEntity === undefined) { continue; @@ -5062,7 +5074,8 @@ export class ViewportHost { this.clearLocalLights(); if (this.currentSimulationScene !== null) { - for (const pointLight of this.currentSimulationScene.localLights.pointLights) { + for (const pointLight of this.currentSimulationScene.localLights + .pointLights) { const renderObjects = this.createPointLightRuntimeObjects(pointLight); renderObjects.group.visible = pointLight.enabled && this.displayMode !== "wireframe"; @@ -5070,7 +5083,8 @@ export class ViewportHost { this.localLightRenderObjects.set(pointLight.entityId, renderObjects); } - for (const spotLight of this.currentSimulationScene.localLights.spotLights) { + for (const spotLight of this.currentSimulationScene.localLights + .spotLights) { const renderObjects = this.createSpotLightRuntimeObjects(spotLight); renderObjects.group.visible = spotLight.enabled && this.displayMode !== "wireframe"; @@ -5234,7 +5248,9 @@ export class ViewportHost { this.applyShadowState(); } - private resolveTerrainLayerMaterial(materialId: string | null): MaterialDef | null { + private resolveTerrainLayerMaterial( + materialId: string | null + ): MaterialDef | null { if (materialId === null || this.currentDocument === null) { return null; } @@ -5590,7 +5606,8 @@ export class ViewportHost { ) { this.clearModelInstances(); - const runtimeModelInstances = this.currentSimulationScene?.modelInstances ?? null; + const runtimeModelInstances = + this.currentSimulationScene?.modelInstances ?? null; const authoredModelInstancesById = new Map( getModelInstances(document.modelInstances).map((modelInstance) => [ modelInstance.id, @@ -5599,8 +5616,9 @@ export class ViewportHost { ); const displayedModelInstances = runtimeModelInstances?.map((runtimeModelInstance) => { - const authoredModelInstance = - authoredModelInstancesById.get(runtimeModelInstance.instanceId); + const authoredModelInstance = authoredModelInstancesById.get( + runtimeModelInstance.instanceId + ); return createModelInstance({ id: runtimeModelInstance.instanceId, @@ -5611,11 +5629,10 @@ export class ViewportHost { position: runtimeModelInstance.position, rotationDegrees: runtimeModelInstance.rotationDegrees, scale: runtimeModelInstance.scale, - collision: - authoredModelInstance?.collision ?? { - mode: "none", - visible: false - }, + collision: authoredModelInstance?.collision ?? { + mode: "none", + visible: false + }, animationClipName: runtimeModelInstance.animationClipName, animationAutoplay: runtimeModelInstance.animationAutoplay }); @@ -5884,7 +5901,12 @@ export class ViewportHost { private createSpotLightRuntimeObjects( entity: Pick< SpotLightEntity, - "position" | "direction" | "colorHex" | "intensity" | "distance" | "angleDegrees" + | "position" + | "direction" + | "colorHex" + | "intensity" + | "distance" + | "angleDegrees" > ): LocalLightRenderObjects { const group = new Group(); @@ -6207,10 +6229,7 @@ export class ViewportHost { const body = new Mesh(new BoxGeometry(0.08, 0.08, 0.34), facingMaterial); body.position.set(0, colliderTop + 0.12, 0.06); - const arrowHead = new Mesh( - new ConeGeometry(0.1, 0.22, 14), - facingMaterial - ); + const arrowHead = new Mesh(new ConeGeometry(0.1, 0.22, 14), facingMaterial); arrowHead.rotation.x = Math.PI * 0.5; arrowHead.position.set(0, colliderTop + 0.12, 0.28); @@ -6233,7 +6252,7 @@ export class ViewportHost { const asset = entity.modelAssetId === null ? null - : this.projectAssets[entity.modelAssetId] ?? null; + : (this.projectAssets[entity.modelAssetId] ?? null); if (entity.modelAssetId !== null && asset?.kind === "model") { const loadedAsset = this.loadedModelAssets[entity.modelAssetId]; @@ -6655,8 +6674,7 @@ export class ViewportHost { } if (brush.volume.mode === "light") { - const baseOpacity = - this.displayMode === "authoring" ? 0.03 : 0; + const baseOpacity = this.displayMode === "authoring" ? 0.03 : 0; const opacity = baseOpacity + (selectedFace ? 0.14 : hoveredFace ? 0.08 : 0); const lightMaterial = new MeshBasicMaterial({ @@ -7332,9 +7350,11 @@ export class ViewportHost { continue; } - const displayedTerrain = this.getDisplayedTerrainState(terrain.id) ?? terrain; + const displayedTerrain = + this.getDisplayedTerrainState(terrain.id) ?? terrain; const previousMaterial = renderObjects.mesh.material; - renderObjects.mesh.material = this.createTerrainMaterial(displayedTerrain); + renderObjects.mesh.material = + this.createTerrainMaterial(displayedTerrain); previousMaterial.dispose(); } } @@ -7373,7 +7393,9 @@ export class ViewportHost { ); } - private getTerrainBrushPreviewColor(brushState: ArmedTerrainBrushState): number { + private getTerrainBrushPreviewColor( + brushState: ArmedTerrainBrushState + ): number { switch (brushState.tool) { case "raise": return TERRAIN_BRUSH_PREVIEW_RAISE_COLOR; @@ -7413,13 +7435,16 @@ export class ViewportHost { !this.isTerrainBrushActive() || this.currentTerrainBrushState === null || this.terrainBrushHover === null || - this.terrainBrushHover.terrainId !== this.currentTerrainBrushState.terrainId + this.terrainBrushHover.terrainId !== + this.currentTerrainBrushState.terrainId ) { this.terrainBrushPreviewGroup.visible = false; return; } - const terrain = this.getDisplayedTerrainState(this.terrainBrushHover.terrainId); + const terrain = this.getDisplayedTerrainState( + this.terrainBrushHover.terrainId + ); if (terrain === null) { this.terrainBrushPreviewGroup.visible = false; @@ -7800,8 +7825,9 @@ export class ViewportHost { } const baseTerrain = - this.currentDocument?.terrains[this.activeTerrainBrushStroke.toolState.terrainId] ?? - null; + this.currentDocument?.terrains[ + this.activeTerrainBrushStroke.toolState.terrainId + ] ?? null; const commit = !cancelled && baseTerrain !== null && @@ -7845,7 +7871,11 @@ export class ViewportHost { const hovered = isPathSelected(this.hoveredSelection, path.id); renderObjects.line.material.color.setHex( - selected ? PATH_SELECTED_COLOR : hovered ? PATH_HOVERED_COLOR : PATH_COLOR + selected + ? PATH_SELECTED_COLOR + : hovered + ? PATH_HOVERED_COLOR + : PATH_COLOR ); for (const pointMesh of renderObjects.pointMeshes) { @@ -8075,7 +8105,9 @@ export class ViewportHost { case "brushVertex": return `brushVertex:${selection.brushId}:${selection.vertexId}`; case "terrains": - return selection.ids.length === 1 ? `terrain:${selection.ids[0]}` : null; + return selection.ids.length === 1 + ? `terrain:${selection.ids[0]}` + : null; case "paths": return selection.ids.length === 1 ? `path:${selection.ids[0]}` : null; case "pathPoint": @@ -8211,8 +8243,9 @@ export class ViewportHost { renderObjects.line, ...renderObjects.pointMeshes.map((pointMesh) => pointMesh.mesh) ]).flat(), - ...Array.from(this.terrainRenderObjects.values(), (renderObjects) => - renderObjects.mesh + ...Array.from( + this.terrainRenderObjects.values(), + (renderObjects) => renderObjects.mesh ), ...Array.from(this.modelRenderObjects.values()), ...this.getBrushPickableObjects() @@ -8283,7 +8316,9 @@ export class ViewportHost { if (transformPointerIntent.commitActiveTransform) { if (this.currentTransformSession.kind !== "active") { - throw new Error("Active transform intent resolved without an active session."); + throw new Error( + "Active transform intent resolved without an active session." + ); } event.preventDefault(); @@ -8735,7 +8770,10 @@ export class ViewportHost { case "cone-brush": return this.getBoxCreationPreviewCenter(event, DEFAULT_BOX_BRUSH_SIZE); case "torus-brush": - return this.getBoxCreationPreviewCenter(event, DEFAULT_TORUS_BRUSH_SIZE); + return this.getBoxCreationPreviewCenter( + event, + DEFAULT_TORUS_BRUSH_SIZE + ); case "entity": switch (target.entityKind) { case "triggerVolume": @@ -9159,12 +9197,14 @@ export class ViewportHost { : toolPreview.target.kind === "torus-brush" ? { kind: "torus-brush", - majorSegmentCount: toolPreview.target.majorSegmentCount, - tubeSegmentCount: toolPreview.target.tubeSegmentCount + majorSegmentCount: + toolPreview.target.majorSegmentCount, + tubeSegmentCount: + toolPreview.target.tubeSegmentCount } - : { - kind: "box-brush" - }, + : { + kind: "box-brush" + }, center: toolPreview.center === null ? null : { ...toolPreview.center } }; diff --git a/tests/domain/runtime-project-time.test.ts b/tests/domain/runtime-project-time.test.ts index 47175ec6..38a02776 100644 --- a/tests/domain/runtime-project-time.test.ts +++ b/tests/domain/runtime-project-time.test.ts @@ -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 ); diff --git a/tests/domain/scene-document-validation.test.ts b/tests/domain/scene-document-validation.test.ts index 28162043..5fcd479f 100644 --- a/tests/domain/scene-document-validation.test.ts +++ b/tests/domain/scene-document-validation.test.ts @@ -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["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["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); diff --git a/tests/domain/world-settings.test.ts b/tests/domain/world-settings.test.ts index 1f4d58ab..8f7dc533 100644 --- a/tests/domain/world-settings.test.ts +++ b/tests/domain/world-settings.test.ts @@ -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); }); diff --git a/tests/serialization/project-document-json.test.ts b/tests/serialization/project-document-json.test.ts index bc137e08..4a39c4e3 100644 --- a/tests/serialization/project-document-json.test.ts +++ b/tests/serialization/project-document-json.test.ts @@ -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 - )[document.activeSceneId] as { + (legacyDocument.scenes as Record)[ + document.activeSceneId + ] as { entities: Record; } ).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; - 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 }> }>; - } - ).sequences["sequence-patrol"]!.effects[0]!.effect as Record - ).smoothPath - ); + legacyDocument.sequences as { + sequences: Record< + string, + { effects: Array<{ effect?: Record }> } + >; + } + ).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" + ); }); }); diff --git a/tests/serialization/scene-document-json.test.ts b/tests/serialization/scene-document-json.test.ts index 37ddf157..26009597 100644 --- a/tests/serialization/scene-document-json.test.ts +++ b/tests/serialization/scene-document-json.test.ts @@ -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>((registry, material) => { + materials: STARTER_MATERIAL_LIBRARY.reduce< + Record + >((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", () => { diff --git a/tests/unit/world-shader-sky.test.ts b/tests/unit/world-shader-sky.test.ts index 6c98829c..46abef58 100644 --- a/tests/unit/world-shader-sky.test.ts +++ b/tests/unit/world-shader-sky.test.ts @@ -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 );