Add water contact patch uniform handling in RuntimeHost

This commit is contained in:
2026-04-06 18:04:29 +02:00
parent c7433820a4
commit c3d28b11ab

View File

@@ -38,7 +38,7 @@ import {
resolveBoxVolumeRenderPaths, resolveBoxVolumeRenderPaths,
type ResolvedBoxVolumeRenderPaths type ResolvedBoxVolumeRenderPaths
} from "../rendering/advanced-rendering"; } from "../rendering/advanced-rendering";
import { collectWaterContactPatches, createWaterMaterial } from "../rendering/water-material"; import { collectWaterContactPatches, createWaterContactPatchUniformValue, createWaterMaterial } from "../rendering/water-material";
import { import {
areAdvancedRenderingSettingsEqual, areAdvancedRenderingSettingsEqual,
cloneAdvancedRenderingSettings, cloneAdvancedRenderingSettings,
@@ -68,6 +68,11 @@ interface LocalLightRenderObjects {
group: Group; group: Group;
} }
interface RuntimeWaterContactUniformBinding {
brush: RuntimeBoxBrushInstance;
uniform: { value: import("three").Vector4[] };
}
const FALLBACK_FACE_COLOR = 0x747d89; const FALLBACK_FACE_COLOR = 0x747d89;
export class RuntimeHost { export class RuntimeHost {
@@ -90,6 +95,7 @@ export class RuntimeHost {
private volumeTime = 0; private volumeTime = 0;
private readonly volumeAnimatedMaterials: ShaderMaterial[] = []; private readonly volumeAnimatedMaterials: ShaderMaterial[] = [];
private readonly volumeAnimatedUniforms: Array<{ value: number }> = []; private readonly volumeAnimatedUniforms: Array<{ value: number }> = [];
private readonly runtimeWaterContactUniforms: RuntimeWaterContactUniformBinding[] = [];
private readonly localLightObjects = new Map<string, Group>(); private readonly localLightObjects = new Map<string, Group>();
private readonly modelRenderObjects = new Map<string, Group>(); private readonly modelRenderObjects = new Map<string, Group>();
private readonly materialTextureCache = new Map<string, CachedMaterialTexture>(); private readonly materialTextureCache = new Map<string, CachedMaterialTexture>();
@@ -324,6 +330,7 @@ export class RuntimeHost {
} }
this.materialTextureCache.clear(); this.materialTextureCache.clear();
this.renderer?.forceContextLoss();
this.renderer?.dispose(); this.renderer?.dispose();
this.domElement.removeEventListener("click", this.handleRuntimeClick); this.domElement.removeEventListener("click", this.handleRuntimeClick);
this.domElement.removeEventListener("pointerdown", this.handleRuntimePointerDown); this.domElement.removeEventListener("pointerdown", this.handleRuntimePointerDown);
@@ -520,24 +527,10 @@ export class RuntimeHost {
this.clearBrushMeshes(); this.clearBrushMeshes();
const volumeRenderPaths: ResolvedBoxVolumeRenderPaths = const volumeRenderPaths: ResolvedBoxVolumeRenderPaths =
this.currentWorld === null ? { fog: "performance", water: "performance" } : resolveBoxVolumeRenderPaths(this.currentWorld.advancedRendering); 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) { for (const brush of brushes) {
const geometry = buildBoxBrushDerivedMeshData(brush).geometry; const geometry = buildBoxBrushDerivedMeshData(brush).geometry;
const contactPatches = const contactPatches = brush.volume.mode === "water" ? this.collectRuntimeWaterContactPatches(brush) : [];
brush.volume.mode === "water"
? collectWaterContactPatches(
{
center: brush.center,
rotationDegrees: brush.rotationDegrees,
size: brush.size
},
colliderBounds
)
: [];
const materials = [ const materials = [
this.createFaceMaterial(brush, "posX", brush.faces.posX.material, volumeRenderPaths, contactPatches), this.createFaceMaterial(brush, "posX", brush.faces.posX.material, volumeRenderPaths, contactPatches),
@@ -635,6 +628,13 @@ export class RuntimeHost {
this.volumeAnimatedUniforms.push(waterMaterial.animationUniform); this.volumeAnimatedUniforms.push(waterMaterial.animationUniform);
} }
if (faceId === "posY" && waterMaterial.contactPatchesUniform !== null) {
this.runtimeWaterContactUniforms.push({
brush,
uniform: waterMaterial.contactPatchesUniform
});
}
return waterMaterial.material; return waterMaterial.material;
} }
@@ -763,6 +763,74 @@ export class RuntimeHost {
this.brushMeshes.clear(); this.brushMeshes.clear();
this.volumeAnimatedMaterials.length = 0; this.volumeAnimatedMaterials.length = 0;
this.volumeAnimatedUniforms.length = 0; this.volumeAnimatedUniforms.length = 0;
this.runtimeWaterContactUniforms.length = 0;
}
private createPlayerWaterContactBounds() {
if (this.runtimeScene === null || this.currentFirstPersonTelemetry === null) {
return null;
}
const feetPosition = this.currentFirstPersonTelemetry.feetPosition;
const playerShape = this.runtimeScene.playerCollider;
switch (playerShape.mode) {
case "capsule":
return {
min: {
x: feetPosition.x - playerShape.radius,
y: feetPosition.y,
z: feetPosition.z - playerShape.radius
},
max: {
x: feetPosition.x + playerShape.radius,
y: feetPosition.y + playerShape.height,
z: feetPosition.z + playerShape.radius
}
};
case "box":
return {
min: {
x: feetPosition.x - playerShape.size.x * 0.5,
y: feetPosition.y,
z: feetPosition.z - playerShape.size.z * 0.5
},
max: {
x: feetPosition.x + playerShape.size.x * 0.5,
y: feetPosition.y + playerShape.size.y,
z: feetPosition.z + playerShape.size.z * 0.5
}
};
case "none":
return null;
}
}
private collectRuntimeWaterContactPatches(brush: RuntimeBoxBrushInstance) {
const contactBounds = this.runtimeScene?.colliders.map((collider) => ({
min: collider.worldBounds.min,
max: collider.worldBounds.max
})) ?? [];
const playerBounds = this.createPlayerWaterContactBounds();
if (playerBounds !== null) {
contactBounds.push(playerBounds);
}
return collectWaterContactPatches(
{
center: brush.center,
rotationDegrees: brush.rotationDegrees,
size: brush.size
},
contactBounds
);
}
private updateRuntimeWaterContactUniforms() {
for (const binding of this.runtimeWaterContactUniforms) {
binding.uniform.value = createWaterContactPatchUniformValue(this.collectRuntimeWaterContactPatches(binding.brush));
}
} }
private clearModelInstances() { private clearModelInstances() {
@@ -840,6 +908,10 @@ export class RuntimeHost {
this.setInteractionPrompt(null); this.setInteractionPrompt(null);
} }
if (this.runtimeWaterContactUniforms.length > 0) {
this.updateRuntimeWaterContactUniforms();
}
if (this.advancedRenderingComposer !== null) { if (this.advancedRenderingComposer !== null) {
this.advancedRenderingComposer.render(dt); this.advancedRenderingComposer.render(dt);
return; return;