Update water contact patch collection and handling in rendering and runtime modules

This commit is contained in:
2026-04-07 04:58:48 +02:00
parent 30ddb565db
commit 0e4bd01e69
4 changed files with 86 additions and 12 deletions

View File

@@ -100,7 +100,7 @@ function createInverseVolumeRotation(rotationDegrees: Vec3) {
.invert(); .invert();
} }
export function collectWaterContactPatches(volume: OrientedWaterVolume, contactBounds: WaterContactBounds[]): WaterContactPatch[] { export function collectWaterContactPatches(volume: OrientedWaterVolume, contactBounds: WaterContactSource[]): WaterContactPatch[] {
const inverseRotation = createInverseVolumeRotation(volume.rotationDegrees); const inverseRotation = createInverseVolumeRotation(volume.rotationDegrees);
const halfX = Math.max(volume.size.x * 0.5, WATER_CONTACT_EPSILON); const halfX = Math.max(volume.size.x * 0.5, WATER_CONTACT_EPSILON);
const halfY = Math.max(volume.size.y * 0.5, WATER_CONTACT_EPSILON); const halfY = Math.max(volume.size.y * 0.5, WATER_CONTACT_EPSILON);

View File

@@ -38,7 +38,7 @@ import {
resolveBoxVolumeRenderPaths, resolveBoxVolumeRenderPaths,
type ResolvedBoxVolumeRenderPaths type ResolvedBoxVolumeRenderPaths
} from "../rendering/advanced-rendering"; } from "../rendering/advanced-rendering";
import { collectWaterContactPatches, createWaterContactPatchUniformValue, createWaterMaterial } from "../rendering/water-material"; import { collectWaterContactPatches, createWaterContactPatchAxisUniformValue, createWaterContactPatchUniformValue, createWaterMaterial } from "../rendering/water-material";
import { import {
areAdvancedRenderingSettingsEqual, areAdvancedRenderingSettingsEqual,
cloneAdvancedRenderingSettings, cloneAdvancedRenderingSettings,
@@ -71,6 +71,7 @@ interface LocalLightRenderObjects {
interface RuntimeWaterContactUniformBinding { interface RuntimeWaterContactUniformBinding {
brush: RuntimeBoxBrushInstance; brush: RuntimeBoxBrushInstance;
uniform: { value: import("three").Vector4[] }; uniform: { value: import("three").Vector4[] };
axisUniform: { value: import("three").Vector2[] };
staticContactPatches: ReturnType<typeof collectWaterContactPatches>; staticContactPatches: ReturnType<typeof collectWaterContactPatches>;
} }
@@ -631,10 +632,11 @@ export class RuntimeHost {
this.volumeAnimatedUniforms.push(waterMaterial.animationUniform); this.volumeAnimatedUniforms.push(waterMaterial.animationUniform);
} }
if (faceId === "posY" && waterMaterial.contactPatchesUniform !== null) { if (faceId === "posY" && waterMaterial.contactPatchesUniform !== null && waterMaterial.contactPatchAxesUniform !== null) {
this.runtimeWaterContactUniforms.push({ this.runtimeWaterContactUniforms.push({
brush, brush,
uniform: waterMaterial.contactPatchesUniform, uniform: waterMaterial.contactPatchesUniform,
axisUniform: waterMaterial.contactPatchAxesUniform,
staticContactPatches staticContactPatches
}); });
} }
@@ -811,10 +813,31 @@ export class RuntimeHost {
} }
private collectRuntimeStaticWaterContactPatches(brush: RuntimeBoxBrushInstance) { private collectRuntimeStaticWaterContactPatches(brush: RuntimeBoxBrushInstance) {
const contactBounds = this.runtimeScene?.colliders.map((collider) => ({ const contactBounds: Parameters<typeof collectWaterContactPatches>[1] = [];
min: collider.worldBounds.min,
max: collider.worldBounds.max for (const otherBrush of this.runtimeScene?.brushes ?? []) {
})) ?? []; if (otherBrush.id === brush.id || otherBrush.volume.mode !== "none") {
continue;
}
contactBounds.push({
kind: "orientedBox",
center: otherBrush.center,
rotationDegrees: otherBrush.rotationDegrees,
size: otherBrush.size
});
}
for (const collider of this.runtimeScene?.colliders ?? []) {
if (collider.source !== "modelInstance") {
continue;
}
contactBounds.push({
min: collider.worldBounds.min,
max: collider.worldBounds.max
});
}
return collectWaterContactPatches( return collectWaterContactPatches(
{ {
@@ -852,9 +875,9 @@ export class RuntimeHost {
private updateRuntimeWaterContactUniforms() { private updateRuntimeWaterContactUniforms() {
for (const binding of this.runtimeWaterContactUniforms) { for (const binding of this.runtimeWaterContactUniforms) {
binding.uniform.value = createWaterContactPatchUniformValue( const mergedPatches = this.mergeRuntimeWaterContactPatches(binding.staticContactPatches, this.collectRuntimePlayerWaterContactPatches(binding.brush));
this.mergeRuntimeWaterContactPatches(binding.staticContactPatches, this.collectRuntimePlayerWaterContactPatches(binding.brush)) binding.uniform.value = createWaterContactPatchUniformValue(mergedPatches);
); binding.axisUniform.value = createWaterContactPatchAxisUniformValue(mergedPatches);
} }
} }

View File

@@ -3007,14 +3007,19 @@ export class ViewportHost {
rotationDegrees: Vec3, rotationDegrees: Vec3,
size: Vec3 size: Vec3
) { ) {
const contactBounds: GeneratedColliderBounds[] = []; const contactBounds: Parameters<typeof collectWaterContactPatches>[1] = [];
for (const brush of Object.values(document.brushes)) { for (const brush of Object.values(document.brushes)) {
if (brush.id === excludedBrushId || brush.volume.mode !== "none") { if (brush.id === excludedBrushId || brush.volume.mode !== "none") {
continue; continue;
} }
contactBounds.push(getBoxBrushBounds(brush)); contactBounds.push({
kind: "orientedBox",
center: brush.center,
rotationDegrees: brush.rotationDegrees,
size: brush.size
});
} }
for (const modelInstance of getModelInstances(document.modelInstances)) { for (const modelInstance of getModelInstances(document.modelInstances)) {

View File

@@ -84,6 +84,52 @@ describe("water material helpers", () => {
expect(patches).toHaveLength(0); expect(patches).toHaveLength(0);
}); });
it("preserves oriented contact regions for rotated boxes", () => {
const patches = collectWaterContactPatches(
{
center: {
x: 0,
y: 0,
z: 0
},
rotationDegrees: {
x: 0,
y: 0,
z: 0
},
size: {
x: 10,
y: 2,
z: 10
}
},
[
{
kind: "orientedBox",
center: {
x: 0,
y: 1,
z: 0
},
rotationDegrees: {
x: 0,
y: 45,
z: 0
},
size: {
x: 2,
y: 1,
z: 1
}
}
]
);
expect(patches).toHaveLength(1);
expect(Math.abs(patches[0]?.axisX ?? 0)).toBeGreaterThan(0.65);
expect(Math.abs(patches[0]?.axisZ ?? 0)).toBeGreaterThan(0.65);
});
it("builds a shared quality shader material for visible tinted water", () => { it("builds a shared quality shader material for visible tinted water", () => {
const result = createWaterMaterial({ const result = createWaterMaterial({
colorHex: "#4da6d9", colorHex: "#4da6d9",