import { Vector2 } from 'three/webgpu'; import type { NodeFrame, Renderer } from 'three/webgpu'; import type { TSLTextureNode, TSLUniformNode, TSLVec4Node } from '../types/tsl.js'; /** One queued velocity/density injection in normalized texture coordinates. */ export interface SmokeSplat { x: number; y: number; dx: number; dy: number; } /** Pointer values may be supplied directly or through a mutable vec2 uniform. */ export type SmokePointerInput = Vector2 | TSLUniformNode<'vec2', Vector2> | null; /** Shared configuration accepted by the storage and RTT smoke implementations. */ export interface SmokeSimulationOptions { pointer?: SmokePointerInput | undefined; simRes?: number; dyeRes?: number; iterations?: number; densityDissipation?: number; velocityDissipation?: number; pressureDissipation?: number; curlStrength?: number; pressureFactor?: number; radius?: number; useBoundaries?: boolean; pointerScale?: number; neighborStride?: number; speedFactor?: number; } /** Public uniform controls shared by both smoke simulation nodes. */ export interface SmokeSimulationUniforms { readonly simRes: TSLUniformNode<'vec2', Vector2>; readonly dyeRes: TSLUniformNode<'vec2', Vector2>; readonly deltaTime: TSLUniformNode<'float', number>; readonly pressureDissipation: TSLUniformNode<'float', number>; readonly pressureFactor: TSLUniformNode<'float', number>; readonly curlStrength: TSLUniformNode<'float', number>; readonly point: TSLUniformNode<'vec2', Vector2>; readonly force: TSLUniformNode<'vec2', Vector2>; readonly radius: TSLUniformNode<'float', number>; readonly pointer: TSLUniformNode<'vec2', Vector2>; readonly neighborStride: TSLUniformNode<'float', number>; } /** Public resource lifecycle shared by storage and render-target results. */ export interface SmokeSimulationResources { getTextureNode(): TSLTextureNode; setSpeedFactor(value: number): this; setPressureIterations(iterations: number): this; setPointerScale(scale: number): this; setSize(): void; dispose(): void; } /** Common node, controls, passes, and resource surface returned by both factories. */ export type SmokeSimulationResult = TSLVec4Node & SmokeSimulationUniforms & SmokeSimulationResources & { iterations: number; densityDissipation: number; velocityDissipation: number; splats: SmokeSplat[]; useBoundaries: boolean; setup(): TSLTextureNode; splat(renderer: Renderer): void; curl(renderer: Renderer): void; vorticity(renderer: Renderer): void; divergence(renderer: Renderer): void; clearPressure(renderer: Renderer): void; pressure(renderer: Renderer): void; gradientSubtract(renderer: Renderer): void; advectionVelocity(renderer: Renderer): void; advectionDensity(renderer: Renderer): void; updateBefore(frame: NodeFrame): undefined; }; /** Compute/storage-specific result, including its current splat source and batched projection pass. */ export type SmokeNodeResult = SmokeSimulationResult & { readonly splatTextureNode: TSLTextureNode; project(renderer: Renderer): void; }; /** * Smoke Simulation (TSL) – a 2D velocity–pressure solver that runs in screen space. * * Turns a fullscreen pass into a stylized, real-time smoke/ink flow using WebGPU compute through Three.js TSL. * Uses StorageTexture resources and takes advantage of Tier2 read–write access whenever the adapter exposes it. * It owns and ping-pongs its internal textures (velocity, pressure, density, curl, divergence) and updates * itself every frame when used in a RenderPipeline chain. * * **Features** * - Semi-Lagrangian advection for velocity and density * - Vorticity confinement (curl) and divergence-free projection (pressure solve) * - Configurable dissipation, pressure iterations and curl strength * - Optional pointer-driven splats via a `vec2` uniform (no global dispatcher) * - Drop-in for a render pipeline: `renderPipeline.outputNode = smoke(pointer, 128, 512, ...)` * * **Example: Basic render pipeline setup** * * ```js * import * as THREE from 'three/webgpu'; * import { smoke } from 'three-blocks/smoke'; * * const renderer = new THREE.WebGPURenderer(); * await renderer.init(); * * const renderPipeline = new THREE.RenderPipeline(renderer); * const pointer = new THREE.Vector2(); * * // smoke(pointer, simRes, dyeRes, iterations, densityDissipation, velocityDissipation, * // pressureDissipation, curlStrength, pressureFactor, radius, useBoundaries, pointerScale) * const fluidNode = smoke(pointer, 128, 512, 3, 0.97, 0.98, 0.8, 20, 0.2, 0.1, true, 45); * * renderPipeline.outputNode = fluidNode; * * renderer.setAnimationLoop(() => renderPipeline.render()); * ``` * * **Example: Interactive pointer-driven splats** * * ```js * import * as THREE from 'three/webgpu'; * import { smoke } from 'three-blocks/smoke'; * * const pointer = new THREE.Vector2(); * * // Pass pointer as first argument - motion automatically injects splats * const fluidNode = smoke(pointer, 128, 512, 3, 0.97, 0.98, 0.8, 20, 0.2, 0.1, true, 45); * * // Update pointer on mouse move (NDC coordinates: -1 to +1) * document.addEventListener('mousemove', (e) => { * pointer.x = (e.clientX / window.innerWidth) * 2 - 1; * pointer.y = -(e.clientY / window.innerHeight) * 2 + 1; * }); * ``` * * @function smoke * @short Screen-space 2D fluid smoke sim node (advection, pressure solve, vorticity) that outputs a dye texture. * @category TSL * @tsl * @tags WebGPU * @demo docs/demos/fluid.html * * @param {THREE.Vector2} [pointer] - NDC-like pointer in [-1, 1]; when present, motion injects splats. * @param {number} [simRes=128] - Square resolution for velocity/pressure buffers. * @param {number} [dyeRes=512] - Square resolution for density buffer. * @param {number} [iterations=3] - Pressure Jacobi iterations per solve. * @param {number} [densityDissipation=0.97] - Density dissipation factor in [0, 1]. * @param {number} [velocityDissipation=0.98] - Velocity dissipation factor in [0, 1]. * @param {number} [pressureDissipation=0.8] - Damp factor for pressure clear step. * @param {number} [curlStrength=20] - Vorticity confinement scale. * @param {number} [pressureFactor=0.2] - Pressure factor constant in Jacobi updates. * @param {number} [radius=0.1] - Gaussian splat radius in UV^2. Increase for larger splats. * @param {boolean} [useBoundaries=true] - Mirror-velocity boundary conditions at screen edges. * @param {number} [pointerScale=45] - Multiplier applied to pointer delta when generating splats. * @param {number} [neighborStride=1] - Multiplier for curl/divergence/pressure neighbor texel offsets. * @param {number} [speedFactor=1] - Scales sub-stepping relative to frame time (lower = more substeps). * @returns {Node} Color node sampling the current dye texture of the fluid simulation. */ export declare const smoke: (pointer?: SmokePointerInput | undefined, simRes?: number, dyeRes?: number, iterations?: number, densityDissipation?: number, velocityDissipation?: number, pressureDissipation?: number, curlStrength?: number, pressureFactor?: number, radius?: number, useBoundaries?: boolean, pointerScale?: number, neighborStride?: number, speedFactor?: number) => SmokeNodeResult;