From b66a64223435b216bd9c8b30129cc5e3b6d7103d Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Mon, 6 Apr 2026 08:19:45 +0200 Subject: [PATCH] Add validation for box volume mode and settings in scene document --- src/document/scene-document-validation.js | 49 ++++++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/document/scene-document-validation.js b/src/document/scene-document-validation.js index 56c8e052..cb011841 100644 --- a/src/document/scene-document-validation.js +++ b/src/document/scene-document-validation.js @@ -2,8 +2,8 @@ import {} from "../assets/project-assets"; import { isModelInstanceCollisionMode } from "../assets/model-instances"; import { isPlayerStartColliderMode, getPlayerStartColliderHeight } from "../entities/entity-instances"; import {} from "../interactions/interaction-links"; -import { BOX_FACE_IDS, BOX_VERTEX_IDS, hasPositiveBoxSize } from "./brushes"; -import { isAdvancedRenderingShadowMapSize, isAdvancedRenderingShadowType, isAdvancedRenderingToneMappingMode, isHexColorString } from "./world-settings"; +import { BOX_FACE_IDS, BOX_VERTEX_IDS, hasPositiveBoxSize, isBoxBrushVolumeMode } from "./brushes"; +import { isAdvancedRenderingShadowMapSize, isAdvancedRenderingShadowType, isBoxVolumeRenderPath, isAdvancedRenderingToneMappingMode, isHexColorString } from "./world-settings"; export function createDiagnostic(severity, code, message, path, scope = "document") { return { code, @@ -141,6 +141,12 @@ function validateWorldSettings(world, document, diagnostics) { if (!isPositiveFiniteNumber(advancedRendering.depthOfField.bokehScale)) { diagnostics.push(createDiagnostic("error", "invalid-advanced-rendering-dof-bokeh-scale", "Advanced rendering depth of field bokeh scale must be a positive finite number.", "world.advancedRendering.depthOfField.bokehScale")); } + if (!isBoxVolumeRenderPath(advancedRendering.fogPath)) { + diagnostics.push(createDiagnostic("error", "invalid-advanced-rendering-fog-path", "Advanced rendering fog path must be performance or quality.", "world.advancedRendering.fogPath")); + } + if (!isBoxVolumeRenderPath(advancedRendering.waterPath)) { + diagnostics.push(createDiagnostic("error", "invalid-advanced-rendering-water-path", "Advanced rendering water path must be performance or quality.", "world.advancedRendering.waterPath")); + } } function validatePointLightEntity(entity, path, diagnostics) { if (!isFiniteVec3(entity.position)) { @@ -568,6 +574,45 @@ export function validateSceneDocument(document) { diagnostics.push(createDiagnostic("error", "missing-material-ref", `Face material reference ${materialId} does not exist in the document material registry.`, `${path}.faces.${faceId}.materialId`)); } } + const volume = brush.volume; + if (!isBoxBrushVolumeMode(volume?.mode)) { + diagnostics.push(createDiagnostic("error", "invalid-box-volume-mode", "Box volume mode must be none, water, or fog.", `${path}.volume.mode`)); + continue; + } + if (volume.mode === "water") { + const water = volume.water; + if (water === undefined) { + diagnostics.push(createDiagnostic("error", "invalid-box-water-settings", "Water volumes must define water settings.", `${path}.volume.water`)); + } + else { + if (typeof water.colorHex !== "string" || !isHexColorString(water.colorHex)) { + diagnostics.push(createDiagnostic("error", "invalid-box-water-color", "Water volume color must use #RRGGBB format.", `${path}.volume.water.colorHex`)); + } + if (!isNonNegativeFiniteNumber(water.surfaceOpacity)) { + diagnostics.push(createDiagnostic("error", "invalid-box-water-surface-opacity", "Water surface opacity must be a non-negative finite number.", `${path}.volume.water.surfaceOpacity`)); + } + if (!isNonNegativeFiniteNumber(water.waveStrength)) { + diagnostics.push(createDiagnostic("error", "invalid-box-water-wave-strength", "Water wave strength must be a non-negative finite number.", `${path}.volume.water.waveStrength`)); + } + } + } + if (volume.mode === "fog") { + const fog = volume.fog; + if (fog === undefined) { + diagnostics.push(createDiagnostic("error", "invalid-box-fog-settings", "Fog volumes must define fog settings.", `${path}.volume.fog`)); + } + else { + if (typeof fog.colorHex !== "string" || !isHexColorString(fog.colorHex)) { + diagnostics.push(createDiagnostic("error", "invalid-box-fog-color", "Fog volume color must use #RRGGBB format.", `${path}.volume.fog.colorHex`)); + } + if (!isNonNegativeFiniteNumber(fog.density)) { + diagnostics.push(createDiagnostic("error", "invalid-box-fog-density", "Fog volume density must be a non-negative finite number.", `${path}.volume.fog.density`)); + } + if (!isNonNegativeFiniteNumber(fog.padding)) { + diagnostics.push(createDiagnostic("error", "invalid-box-fog-padding", "Fog volume padding must be a non-negative finite number.", `${path}.volume.fog.padding`)); + } + } + } } for (const [modelInstanceKey, modelInstance] of Object.entries(document.modelInstances)) { const path = `modelInstances.${modelInstanceKey}`;