import * as THREE from 'three/webgpu'; import type { Object3D, Renderer } from 'three/webgpu'; import { OceanWaves } from '../../TSL/OceanWaves.js'; import type { OceanWaveComponentOptions } from '../../TSL/OceanWaves.js'; import { DiffuseParticles } from './DiffuseParticles.js'; import type { DiffuseParticlesMesh, DiffuseParticlesMeshOptions, DiffuseParticlesOptions } from './DiffuseParticles.js'; import type { MPMFluidModelOptions } from './MPMFluidModel.js'; import { MPMSolver } from './MPMSolver.js'; import type { MPMSeedState, MPMSolverOptions, MPMStepStats } from './MPMSolver.js'; import { SurfaceField } from './SurfaceField.js'; import type { SurfaceFieldFoamMeshOptions, SurfaceFieldOptions } from './SurfaceField.js'; import type { WaterPresetName } from './WaterPresets.js'; import type { GPUInteractionWorld } from '../Interaction/GPUInteractionWorld.js'; import type { MPMBoundaryOptions, MPMGridForce, MPMParticleForce, MPMParticleUpdate } from './compute/MPMComputeContracts.js'; import type { TSLStorageNode, TSLUintNode, TSLUniformArrayNode, TSLUniformNode, TSLVec3Node } from '../../types/tsl.js'; export interface WaterVolumeWavesOptions { energy?: number | undefined; components?: readonly OceanWaveComponentOptions[] | undefined; nudgeRate?: number | undefined; seedScale?: number | undefined; } export interface WaterVolumeMaterialOptions extends MPMFluidModelOptions { } export type WaterVolumeBoundaryMode = 'absorbing' | 'reflective'; export interface WaterVolumeBoundaryOptions extends Partial { mode?: WaterVolumeBoundaryMode | undefined; rimWidth?: number | undefined; rimDamping?: number | undefined; } export interface WaterVolumeResolvedBoundaryOptions extends MPMBoundaryOptions { mode: WaterVolumeBoundaryMode; rimWidth: number; rimDamping: number; } export interface WaterVolumeLattice { x: number; y: number; z: number; } export interface WaterVolumeSeedModifierContext { index: TSLUintNode; position: TSLVec3Node; velocity: TSLVec3Node; } export type WaterVolumeSeedModifier = (context: WaterVolumeSeedModifierContext) => MPMSeedState | null | undefined | void; export interface WaterVolumeSeedOptions { profile?: boolean | undefined; waves?: boolean | undefined; lattice?: WaterVolumeLattice | null | undefined; modifier?: WaterVolumeSeedModifier | null | undefined; } export interface WaterVolumeResolvedSeedOptions { profile: boolean; waves: boolean; lattice: WaterVolumeLattice | null; modifier: WaterVolumeSeedModifier | null; } export interface WaterVolumePointerOptions { radius?: number | undefined; impulse?: number | undefined; decay?: number | undefined; verticalFalloff?: number | undefined; lift?: number | undefined; } export interface WaterVolumeResolvedPointerOptions { radius: number; impulse: number; decay: number; verticalFalloff: number; lift: number; } export interface WaterVolumeSplashOptions { slots?: number | undefined; } export interface WaterVolumeResolvedSplashOptions { slots: number; } export interface WaterVolumeInteractionOptions { enabled?: boolean | undefined; layer?: number | undefined; mask?: number | undefined; friction?: number | undefined; restitution?: number | undefined; particleRadius?: number | undefined; pushRate?: number | undefined; maxSeparationSpeed?: number | undefined; } export interface WaterVolumeResolvedInteractionOptions { enabled: boolean; layer: number; mask: number; friction: number; restitution: number; particleRadius: number; pushRate: number; maxSeparationSpeed: number; } export type WaterVolumeSolverOptions = Omit; export type WaterVolumeResolvedSolverOptions = WaterVolumeSolverOptions & { maxVelocity: number; substeps: number; }; export interface WaterVolumeOptions { capacity?: number | undefined; gridSize?: THREE.Vector3 | undefined; domainSize?: THREE.Vector3 | undefined; fillHeight?: number | undefined; preset?: WaterPresetName | undefined; gravity?: number | undefined; waves?: WaterVolumeWavesOptions | null | undefined; material?: WaterVolumeMaterialOptions | undefined; boundary?: WaterVolumeBoundaryOptions | undefined; seed?: WaterVolumeSeedOptions | undefined; pointer?: WaterVolumePointerOptions | undefined; splash?: WaterVolumeSplashOptions | undefined; surfaceField?: SurfaceFieldOptions | boolean | undefined; foam?: DiffuseParticlesOptions | boolean | undefined; interactionWorld?: GPUInteractionWorld | null | undefined; interaction?: WaterVolumeInteractionOptions | undefined; solver?: WaterVolumeSolverOptions | undefined; gridForce?: MPMGridForce | null | undefined; particleForce?: MPMParticleForce | null | undefined; onParticleUpdate?: MPMParticleUpdate | null | undefined; } export type WaterVolumeSplashUniformArray = TSLUniformArrayNode<'vec4'> & { array: THREE.Vector4[]; }; export interface WaterVolumeUniforms { domainWorldSize: TSLUniformNode<'vec3', THREE.Vector3>; cellWorld: TSLUniformNode<'vec3', THREE.Vector3>; volumeToWorld: TSLUniformNode<'mat4', THREE.Matrix4>; worldToVolume: TSLUniformNode<'mat4', THREE.Matrix4>; floorLevel: TSLUniformNode<'float', number>; fillHeight: TSLUniformNode<'float', number>; nudgeRate: TSLUniformNode<'float', number>; seedWaveScale: TSLUniformNode<'float', number>; rimWidth: TSLUniformNode<'float', number>; rimDamping: TSLUniformNode<'float', number>; pointerPosition: TSLUniformNode<'vec3', THREE.Vector3>; pointerStrength: TSLUniformNode<'float', number>; pointerRadius: TSLUniformNode<'float', number>; pointerImpulse: TSLUniformNode<'float', number>; splashPositions: WaterVolumeSplashUniformArray; splashImpulses: WaterVolumeSplashUniformArray; } export interface WaterVolumeBindDomainOptions { autoUpdate?: boolean | undefined; } export interface WaterVolumeStepStats extends MPMStepStats { preset: WaterPresetName; restDensity: number; profileBeta: number; probeCount: number; } export type WaterVolumeFoamMeshOptions = Omit; export type WaterVolumeSurfaceFoamMeshOptions = Omit; export type WaterVolumeSurfaceFoamMesh = ReturnType; /** * Ocean/water simulation block: an {@link MPMSolver} MLS-MPM/APIC fluid * composed with a dispersion-matched swell wavemaker ({@link OceanWaves}), * hydrostatic-equilibrium seeding, absorbing open-water boundaries, pointer * and splash emitters, shared interaction-world colliders, and a top-down * {@link SurfaceField} with batched height probes. * * The block owns the world mapping: the solver's normalized `[0,1]³` domain * is bound to a world-space box (SmokeVolume's domain contract — a unit cube * centered at the origin transformed by a matrix), gravity is specified in * world m/s² and mapped through the per-axis cell size, and wave components * are world wavelengths. Everything stays in uniforms, so moving or resizing * the domain never rebuilds kernels. * * ```js * import * as THREE from 'three/webgpu'; * import { WaterVolume } from 'three-blocks/water'; * * const water = new WaterVolume( { * capacity: 1_048_576, * gridSize: new THREE.Vector3( 128, 64, 128 ), * domainSize: new THREE.Vector3( 44, 8, 44 ), * preset: 'ocean-swell', * } ); * water.seed( renderer ); * * // per frame * water.step( renderer, 1 / 120 ); * * // render from the stable particle buffer * const particle = water.particleBuffer.element( instanceIndex ); * * const worldY = await water.getHeightAt( 4, - 3 ); * water.dispose(); * ``` * * Presets are calibrated option groups (`pool`, `calm`, `ocean-swell`, * `storm`); explicit constructor options override them. Disabling the ocean * systems (`waves: null`, `surfaceField: false`) leaves a plain tank driven * by the base solver only. * * @short MLS-MPM ocean block: swell wavemaker + equilibrium seed + surface field over MPMSolver. * @category Simulation * @tags WebGPU, TSL, Water, Ocean, MLS-MPM */ export declare class WaterVolume { preset: WaterPresetName; capacity: number; gridSize: THREE.Vector3; domainSize: THREE.Vector3; fillHeight: number; gravityWorld: number; floorLevel: number; particleCountSeeded: number; seedBulkDensity: number; profileBeta: number; restDensity: number; waves: OceanWaves | null; uniforms: WaterVolumeUniforms; domainMatrix: THREE.Matrix4; domainMatrixInverse: THREE.Matrix4; volumeToWorldMatrix: THREE.Matrix4; worldToVolumeMatrix: THREE.Matrix4; domainObject: Object3D | null; domainAutoUpdate: boolean; solver: MPMSolver | null; surface: SurfaceField | null; foam: DiffuseParticles | null; interactionWorld: GPUInteractionWorld | null; interaction: WaterVolumeResolvedInteractionOptions | null; private _wavesConfig; private _materialConfig; private _boundaryConfig; private _seedConfig; private _pointerConfig; private _splashConfig; private _surfaceFieldConfig; private _foamConfig; private _solverConfig; private _interactionOptions; private _userGridForce; private _userParticleForce; private _userOnParticleUpdate; private _lattice; private _seedFn; private _volumeTranslation; private _gravityVector; private _hasSeeded; private _pointerFresh; private _pointerActive; private _splashCursor; private _renderer; private _disposed; /** * @param {Object} [options] * @param {number} [options.capacity=1048576] Particle pool size. * @param {THREE.Vector3} [options.gridSize=(128,64,128)] MPM grid resolution. * @param {THREE.Vector3} [options.domainSize=(44,8,44)] World size of the domain box. * @param {number} [options.fillHeight=0.39] Water column height as a fraction of domain height. * @param {string} [options.preset='ocean-swell'] Named tuning preset. * @param {number} [options.gravity] World gravity magnitude (m/s²). * @param {?Object} [options.waves] Swell config `{ energy, components, nudgeRate, seedScale }`; `null` disables. * @param {Object} [options.material] Fluid material `{ stiffness, viscosity, restDensity }`; omitted * `restDensity` derives from the hydrostatic seed profile. * @param {Object} [options.boundary] `{ margin, mode: 'absorbing'|'reflective', floorFriction, * wallPushback, velocityDamping, rimWidth, rimDamping }`. * @param {Object} [options.seed] `{ profile, waves, lattice, modifier }` seeding controls. * `modifier` receives the generated TSL `{ index, position, velocity }` state and may * replace either node before it is written to the particle buffer. * @param {Object} [options.pointer] Pointer force `{ radius, impulse, decay, verticalFalloff, lift }`. * @param {Object} [options.splash] `{ slots }` splash emitter pool. * @param {Object|boolean} [options.surfaceField] SurfaceField options, or `false` to disable. * @param {Object|boolean} [options.foam=false] Whitewater (`DiffuseParticles`) options; `false` disables * (disabled whitewater never changes base motion). * @param {?Object} [options.interactionWorld] Shared `GPUInteractionWorld` colliders. * @param {Object} [options.interaction] Collider response `{ friction, restitution, particleRadius, * mask, layer, pushRate, maxSeparationSpeed }`. * @param {Object} [options.solver] Forwarded `MPMSolver` extras (formulation, substeps, cfl, * sorting, p2gMode, densityPrediction, packedGridMirror, diagnostics, workgroupSize, maxVelocity). * @param {?Function} [options.gridForce] Extra per-cell TSL hook, applied after the wavemaker. * @param {?Function} [options.particleForce] Extra per-particle TSL hook, applied last. * @param {?Function} [options.onParticleUpdate] Per-particle render hook (see `MPMSolver`). */ constructor({ capacity, gridSize, domainSize, fillHeight, preset, gravity, waves, material, boundary, seed, pointer, splash, surfaceField, foam, interactionWorld, interaction, solver, gridForce, particleForce, onParticleUpdate, }?: WaterVolumeOptions); private _resolveLattice; private _resolveDensityProfile; private _gravityGridMagnitude; private _createUniforms; private _buildSolver; private _collectPostPasses; private _composedGridForce; private _composedParticleForce; private _buildSeedInitializer; /** * GPU particle initialization (hydrostatic profile + in-phase swell). * Safe to call again to reset the water. * * @param {THREE.WebGPURenderer} renderer Active renderer. * @returns {this} */ seed(renderer: Renderer): this; /** * Advance the simulation. All solver substeps plus the surface-field and * probe passes go out in one compute submission. * * @param {THREE.WebGPURenderer} renderer Active renderer. * @param {number} dt Frame delta time in seconds. * @returns {this} */ step(renderer: Renderer, dt: number): this; /** Simulation time in seconds (drives the swell phase). */ get time(): number; set time(value: number); /** Stable particle storage node (`position`, `velocity`, `C` fields). */ get particleBuffer(): TSLStorageNode<'struct'>; /** Active particle count (≤ capacity). */ get particleCount(): number; set particleCount(value: number); /** Global swell amplitude multiplier; `0` gates the wavemaker to inert. */ get waveEnergy(): number; set waveEnergy(value: number); /** * Drive the pointer force. Call every frame the pointer moves; strength * decays automatically on frames without a refresh. * * @param {?THREE.Vector3} worldPosition Pointer position in world space, or `null` to release. * @param {number} [strength=1] Impulse strength for this refresh. * @returns {this} */ setPointer(worldPosition: THREE.Vector3 | null, strength?: number): this; /** * Emit a splash impulse: a decaying radial velocity kick around a world * position (emitter parity with `SmokeVolume.addSplat`). * * @param {number} x World X. * @param {number} y World Y. * @param {number} z World Z. * @param {number} [vx=0] Impulse X (world units/s). * @param {number} [vy=0] Impulse Y (world units/s). * @param {number} [vz=0] Impulse Z (world units/s). * @param {number} [radius=1.5] Influence radius (world units). * @param {number} [strength=1] Initial strength; decays over a few frames. * @returns {this} */ addSplash(x: number, y: number, z: number, vx?: number, vy?: number, vz?: number, radius?: number, strength?: number): this; /** * Bind the domain to an object (a unit-cube mesh transformed in world * space — the SmokeVolume contract). The world matrix is re-read every * step while bound. * * @param {THREE.Object3D} object Domain object whose local space is the unit cube. * @param {Object} [options] * @param {boolean} [options.autoUpdate=true] Re-sync the matrix every step. * @returns {this} */ bindDomain(object: Object3D, { autoUpdate }?: WaterVolumeBindDomainOptions): this; /** * Set the domain transform directly (unit cube centered at the origin → * world). Translation, rotation, and per-axis scale are supported; shear * is rejected. * * @param {THREE.Matrix4} matrixWorld World transform of the unit-cube domain. * @returns {this} */ setDomainTransform(matrixWorld: THREE.Matrix4): this; /** Refresh matrices for a bound domain object. */ syncDomainTransform(force?: boolean): this; private _applyDomainTransform; /** * Set the world gravity magnitude (m/s²). Mapped into grid units through * the domain transform; also re-derives the swell dispersion. * * @param {number} magnitude World gravity magnitude. * @returns {this} */ setGravity(magnitude: number): this; private _updateGravity; /** * Bind a shared collider world (parity with the other simulation blocks). * Must be called before the first `seed()`/`step()` — the collider hook * compiles into the solver kernels. * * @param {?Object} world `GPUInteractionWorld` or `null` to clear. * @param {Object} [options] Response overrides (friction, restitution, mask, layer, particleRadius). * @returns {this} */ setInteractionWorld(world: GPUInteractionWorld | null, options?: WaterVolumeInteractionOptions): this; /** * Sample the water surface height at a world XZ position. * * Inside the domain this reads the GPU {@link SurfaceField} through the * batched probe buffer (one asynchronous readback per frame; resolves * after the next completed one). Outside the domain — or when the surface * field is disabled — it falls back to the analytic swell height, which * shares the same spectrum and phase. * * @param {number} worldX World X. * @param {number} worldZ World Z. * @returns {Promise} Water surface height (world Y). */ getHeightAt(worldX: number, worldZ: number): Promise; private _volumeYToWorld; /** Still-water surface level in world space (domain center column). */ getStillWaterLevel(): number; /** Merged step statistics (solver passes plus block state). */ getLastStepStats(): WaterVolumeStepStats; /** * Build the whitewater sprite mesh (requires the `foam` option). Add the * returned mesh to the scene; its draw count is GPU-driven. * * @param {Object} [options] Forwarded to `DiffuseParticles.createMesh` (e.g. `baseSize`). * @returns {THREE.Mesh} Indirect-drawn whitewater sprites. */ createFoamMesh(options?: WaterVolumeFoamMeshOptions): DiffuseParticlesMesh; /** * Build the deposited SurfaceField foam splats used by the independent * whitewater accumulation path. This is separate from the free diffuse * particles returned by {@link createFoamMesh}. * * @param {Object} [options] Forwarded to {@link SurfaceField#createFoamMesh}. * @returns {THREE.Mesh} Fixed-count, depth-aware surface-foam splat mesh. */ createSurfaceFoamMesh(options?: WaterVolumeSurfaceFoamMeshOptions): WaterVolumeSurfaceFoamMesh; dispose(): void; }