diff --git a/src/runtime-three/runtime-host.ts b/src/runtime-three/runtime-host.ts index 8ea79cac..e5a75b70 100644 --- a/src/runtime-three/runtime-host.ts +++ b/src/runtime-three/runtime-host.ts @@ -30,7 +30,8 @@ import { applyAdvancedRenderingLightShadowFlags, applyAdvancedRenderingRenderableShadowFlags, configureAdvancedRenderingRenderer, - createAdvancedRenderingComposer + createAdvancedRenderingComposer, + resolveBoxVolumeRenderPaths } from "../rendering/advanced-rendering"; import { areAdvancedRenderingSettingsEqual, @@ -508,17 +509,18 @@ export class RuntimeHost { private rebuildBrushMeshes(brushes: RuntimeBoxBrushInstance[]) { this.clearBrushMeshes(); + const volumeRenderPaths = this.currentWorld === null ? { fog: "performance", water: "performance" } : resolveBoxVolumeRenderPaths(this.currentWorld.advancedRendering); for (const brush of brushes) { const geometry = buildBoxBrushDerivedMeshData(brush).geometry; const materials = [ - this.createFaceMaterial(brush.faces.posX.material), - this.createFaceMaterial(brush.faces.negX.material), - this.createFaceMaterial(brush.faces.posY.material), - this.createFaceMaterial(brush.faces.negY.material), - this.createFaceMaterial(brush.faces.posZ.material), - this.createFaceMaterial(brush.faces.negZ.material) + this.createFaceMaterial(brush, "posX", brush.faces.posX.material, volumeRenderPaths), + this.createFaceMaterial(brush, "negX", brush.faces.negX.material, volumeRenderPaths), + this.createFaceMaterial(brush, "posY", brush.faces.posY.material, volumeRenderPaths), + this.createFaceMaterial(brush, "negY", brush.faces.negY.material, volumeRenderPaths), + this.createFaceMaterial(brush, "posZ", brush.faces.posZ.material, volumeRenderPaths), + this.createFaceMaterial(brush, "negZ", brush.faces.negZ.material, volumeRenderPaths) ]; const mesh = new Mesh(geometry, materials); @@ -579,7 +581,47 @@ export class RuntimeHost { this.applyShadowState(); } - private createFaceMaterial(material: RuntimeBoxBrushInstance["faces"]["posX"]["material"]): MeshStandardMaterial { + private createFaceMaterial( + brush: RuntimeBoxBrushInstance, + faceId: "posX" | "negX" | "posY" | "negY" | "posZ" | "negZ", + material: RuntimeBoxBrushInstance["faces"]["posX"]["material"], + volumeRenderPaths: { fog: "performance" | "quality"; water: "performance" | "quality" } + ): MeshStandardMaterial { + if (brush.volume.mode === "water") { + const quality = volumeRenderPaths.water === "quality"; + const baseOpacity = Math.max(0.05, Math.min(1, brush.volume.water.surfaceOpacity)); + const topBoost = faceId === "posY" ? 0.18 : 0; + + return new MeshStandardMaterial({ + color: brush.volume.water.colorHex, + emissive: brush.volume.water.colorHex, + emissiveIntensity: quality ? 0.08 + brush.volume.water.waveStrength * 0.1 : 0.03, + roughness: quality ? 0.08 : 0.2, + metalness: quality ? 0.04 : 0.01, + transparent: true, + opacity: Math.min(1, baseOpacity + topBoost), + transmission: quality ? 0.25 : 0, + thickness: quality ? 0.6 : 0, + envMapIntensity: quality ? 1.15 : 1 + }); + } + + if (brush.volume.mode === "fog") { + const quality = volumeRenderPaths.fog === "quality"; + const densityOpacity = Math.max(0.08, Math.min(0.82, brush.volume.fog.density * (quality ? 0.65 : 0.9) + 0.1)); + + return new MeshStandardMaterial({ + color: brush.volume.fog.colorHex, + emissive: brush.volume.fog.colorHex, + emissiveIntensity: quality ? 0.08 : 0.04, + roughness: 1, + metalness: 0, + transparent: true, + opacity: densityOpacity, + depthWrite: false + }); + } + if (material === null) { return new MeshStandardMaterial({ color: FALLBACK_FACE_COLOR,