Add water volume utility functions
This commit is contained in:
108
src/runtime-three/water-volume-utils.ts
Normal file
108
src/runtime-three/water-volume-utils.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { Euler, Quaternion, Vector3 } from "three";
|
||||
|
||||
import type { RuntimeWaterVolume } from "./runtime-scene-build";
|
||||
|
||||
export interface RuntimeWaterContact {
|
||||
volume: RuntimeWaterVolume;
|
||||
localPoint: {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
};
|
||||
surfaceHeight: number;
|
||||
}
|
||||
|
||||
function getWaterVolumeQuaternion(volume: RuntimeWaterVolume): Quaternion {
|
||||
return new Quaternion().setFromEuler(
|
||||
new Euler(
|
||||
(volume.rotationDegrees.x * Math.PI) / 180,
|
||||
(volume.rotationDegrees.y * Math.PI) / 180,
|
||||
(volume.rotationDegrees.z * Math.PI) / 180,
|
||||
"XYZ"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function getWaterVolumeLocalPoint(
|
||||
point: { x: number; y: number; z: number },
|
||||
volume: RuntimeWaterVolume
|
||||
) {
|
||||
const offset = new Vector3(
|
||||
point.x - volume.center.x,
|
||||
point.y - volume.center.y,
|
||||
point.z - volume.center.z
|
||||
);
|
||||
const inverseRotation = getWaterVolumeQuaternion(volume).invert();
|
||||
|
||||
offset.applyQuaternion(inverseRotation);
|
||||
|
||||
return {
|
||||
x: offset.x,
|
||||
y: offset.y,
|
||||
z: offset.z
|
||||
};
|
||||
}
|
||||
|
||||
export function isPointInsideWaterVolume(
|
||||
point: { x: number; y: number; z: number },
|
||||
volume: RuntimeWaterVolume
|
||||
): boolean {
|
||||
const localPoint = getWaterVolumeLocalPoint(point, volume);
|
||||
|
||||
return (
|
||||
Math.abs(localPoint.x) <= volume.size.x * 0.5 &&
|
||||
Math.abs(localPoint.y) <= volume.size.y * 0.5 &&
|
||||
Math.abs(localPoint.z) <= volume.size.z * 0.5
|
||||
);
|
||||
}
|
||||
|
||||
export function resolveWaterSurfaceHeightAtPoint(
|
||||
volume: RuntimeWaterVolume,
|
||||
point: { x: number; y: number; z: number }
|
||||
): number {
|
||||
const rotation = getWaterVolumeQuaternion(volume);
|
||||
const topCenter = new Vector3(0, volume.size.y * 0.5, 0)
|
||||
.applyQuaternion(rotation)
|
||||
.add(new Vector3(volume.center.x, volume.center.y, volume.center.z));
|
||||
const normal = new Vector3(0, 1, 0).applyQuaternion(rotation);
|
||||
|
||||
if (Math.abs(normal.y) <= 1e-5) {
|
||||
return topCenter.y;
|
||||
}
|
||||
|
||||
return (
|
||||
topCenter.y -
|
||||
(normal.x * (point.x - topCenter.x) +
|
||||
normal.z * (point.z - topCenter.z)) /
|
||||
normal.y
|
||||
);
|
||||
}
|
||||
|
||||
export function resolveWaterContact(
|
||||
point: { x: number; y: number; z: number },
|
||||
volumes: RuntimeWaterVolume[]
|
||||
): RuntimeWaterContact | null {
|
||||
let bestContact: RuntimeWaterContact | null = null;
|
||||
|
||||
for (const volume of volumes) {
|
||||
if (!isPointInsideWaterVolume(point, volume)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const surfaceHeight = resolveWaterSurfaceHeightAtPoint(volume, point);
|
||||
const contact: RuntimeWaterContact = {
|
||||
volume,
|
||||
localPoint: getWaterVolumeLocalPoint(point, volume),
|
||||
surfaceHeight
|
||||
};
|
||||
|
||||
if (
|
||||
bestContact === null ||
|
||||
contact.surfaceHeight > bestContact.surfaceHeight
|
||||
) {
|
||||
bestContact = contact;
|
||||
}
|
||||
}
|
||||
|
||||
return bestContact;
|
||||
}
|
||||
Reference in New Issue
Block a user