Add support for water material in runtime host

This commit is contained in:
2026-04-06 17:27:03 +02:00
parent 4d59e00ac9
commit 0529975ef7

View File

@@ -38,6 +38,7 @@ import {
resolveBoxVolumeRenderPaths,
type ResolvedBoxVolumeRenderPaths
} from "../rendering/advanced-rendering";
import { collectWaterContactPatches, createWaterMaterial } from "../rendering/water-material";
import {
areAdvancedRenderingSettingsEqual,
cloneAdvancedRenderingSettings,
@@ -88,6 +89,7 @@ export class RuntimeHost {
private readonly brushMeshes = new Map<string, Mesh<BufferGeometry, Material[]>>();
private volumeTime = 0;
private readonly volumeAnimatedMaterials: ShaderMaterial[] = [];
private readonly volumeAnimatedUniforms: Array<{ value: number }> = [];
private readonly localLightObjects = new Map<string, Group>();
private readonly modelRenderObjects = new Map<string, Group>();
private readonly materialTextureCache = new Map<string, CachedMaterialTexture>();
@@ -518,17 +520,32 @@ export class RuntimeHost {
this.clearBrushMeshes();
const volumeRenderPaths: ResolvedBoxVolumeRenderPaths =
this.currentWorld === null ? { fog: "performance", water: "performance" } : resolveBoxVolumeRenderPaths(this.currentWorld.advancedRendering);
const colliderBounds = this.runtimeScene?.colliders.map((collider) => ({
min: collider.worldBounds.min,
max: collider.worldBounds.max
})) ?? [];
for (const brush of brushes) {
const geometry = buildBoxBrushDerivedMeshData(brush).geometry;
const contactPatches =
brush.volume.mode === "water"
? collectWaterContactPatches(
{
center: brush.center,
rotationDegrees: brush.rotationDegrees,
size: brush.size
},
colliderBounds
)
: [];
const materials = [
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)
this.createFaceMaterial(brush, "posX", brush.faces.posX.material, volumeRenderPaths, contactPatches),
this.createFaceMaterial(brush, "negX", brush.faces.negX.material, volumeRenderPaths, contactPatches),
this.createFaceMaterial(brush, "posY", brush.faces.posY.material, volumeRenderPaths, contactPatches),
this.createFaceMaterial(brush, "negY", brush.faces.negY.material, volumeRenderPaths, contactPatches),
this.createFaceMaterial(brush, "posZ", brush.faces.posZ.material, volumeRenderPaths, contactPatches),
this.createFaceMaterial(brush, "negZ", brush.faces.negZ.material, volumeRenderPaths, contactPatches)
];
const mesh = new Mesh(geometry, materials);
@@ -593,20 +610,32 @@ export class RuntimeHost {
brush: RuntimeBoxBrushInstance,
faceId: "posX" | "negX" | "posY" | "negY" | "posZ" | "negZ",
material: RuntimeBoxBrushInstance["faces"]["posX"]["material"],
volumeRenderPaths: { fog: "performance" | "quality"; water: "performance" | "quality" }
volumeRenderPaths: { fog: "performance" | "quality"; water: "performance" | "quality" },
contactPatches: ReturnType<typeof collectWaterContactPatches>
): Material {
if (brush.volume.mode === "water") {
if (volumeRenderPaths.water === "quality") {
return this.createWaterQualityMaterial(brush.volume.water, faceId);
}
// Performance fallback: simple transparent material
const baseOpacity = Math.max(0.05, Math.min(1, brush.volume.water.surfaceOpacity));
return new MeshBasicMaterial({
color: brush.volume.water.colorHex,
transparent: true,
const waterMaterial = createWaterMaterial({
colorHex: brush.volume.water.colorHex,
surfaceOpacity: brush.volume.water.surfaceOpacity,
waveStrength: brush.volume.water.waveStrength,
opacity: faceId === "posY" ? Math.min(1, baseOpacity + 0.18) : baseOpacity * 0.5,
depthWrite: false
quality: volumeRenderPaths.water === "quality",
wireframe: false,
isTopFace: faceId === "posY",
time: this.volumeTime,
halfSize: {
x: brush.size.x * 0.5,
z: brush.size.z * 0.5
},
contactPatches
});
if (waterMaterial.animationUniform !== null) {
this.volumeAnimatedUniforms.push(waterMaterial.animationUniform);
}
return waterMaterial.material;
}
if (brush.volume.mode === "fog") {