/** * UnifiedParticleBuffer — Shared particle buffer coupling for MLS-MPM + PBD. * * Bridges fluid (MLS-MPM), cloth (PBD), rigid, debris, and crowd particles * into a single coordinate space so they can interact via boundary coupling. * * Architecture (per P.GAPS.09): * - Each subsystem owns its particles and advances them independently * - This buffer provides a unified view for: * (a) Boundary force exchange (fluid ↔ cloth pressure) * (b) Collision detection across particle types * (c) Rendering (single draw call for all particles) * (d) Network sync (one serialization path) * * GPU path: subsystems share GPUBuffers via sub-allocation offsets. * CPU path: Float32Array views into a single ArrayBuffer. * * @module physics * @see docs/specs/pbd-solver-upgrade.md */ import { ParticleType, type IParticleAttributes } from './PhysicsTypes'; /** Registration handle returned when a subsystem adds particles. */ export interface ParticleRange { /** Particle type tag */ type: ParticleType; /** Starting index in the unified buffer */ offset: number; /** Number of particles in this range */ count: number; /** Label for debugging */ label: string; } /** Boundary coupling force between two particle types. */ export interface BoundaryCoupling { /** Source particle type exerting force */ from: ParticleType; /** Target particle type receiving force */ to: ParticleType; /** Coupling strength multiplier (default: 1.0) */ strength: number; /** Interaction radius in world units */ radius: number; } /** Stats for monitoring buffer usage. */ export interface UnifiedBufferStats { totalCapacity: number; totalActive: number; rangeCount: number; byType: Record; bufferSizeMB: number; } /** * CPU-side unified particle buffer. * * Pre-allocates a fixed-capacity buffer. Subsystems register particle ranges * and write positions/velocities into their slice. The buffer provides * read access for coupling, rendering, and serialization. */ export declare class UnifiedParticleBuffer { /** Max particles across all subsystems */ readonly capacity: number; /** Positions: [x, y, z] per particle, flat array */ readonly positions: Float32Array; /** Velocities: [vx, vy, vz] per particle, flat array */ readonly velocities: Float32Array; /** Particle attributes (type, phase, density, pressure), 4 floats each */ readonly attributes: Float32Array; /** Registered particle ranges */ private ranges; /** Boundary coupling rules */ private couplings; /** Next free index for allocation */ private nextFree; constructor(capacity?: number); /** * Register a particle range for a subsystem. * Returns a ParticleRange handle with the allocated offset. * * @throws if capacity exceeded */ registerParticles(type: ParticleType, count: number, label: string): ParticleRange; /** * Unregister a particle range. Does NOT compact — leaves a hole. * Call compact() to reclaim fragmented space. */ unregisterParticles(range: ParticleRange): void; /** * Register a boundary coupling rule between two particle types. * During solveBoundaryCoupling(), particles of type `from` exert * pressure forces on nearby particles of type `to`. */ addCoupling(coupling: BoundaryCoupling): void; /** * Solve boundary coupling forces between registered particle types. * * Uses a simple pairwise interaction: for each coupling rule, iterate * source particles and apply repulsive forces to nearby target particles. * * For GPU path, this would be a compute shader with spatial hashing. * This CPU implementation is O(N*M) and suitable for < 10K cross-type pairs. */ solveBoundaryCoupling(dt: number): void; private applyPairwiseCoupling; /** * Write positions from an external source into a registered range. * Used by subsystems to push their updated state after simulation. */ writePositions(range: ParticleRange, data: Float32Array): void; /** * Write velocities from an external source into a registered range. */ writeVelocities(range: ParticleRange, data: Float32Array): void; /** * Read positions for a specific range (returns a view, not a copy). */ readPositions(range: ParticleRange): Float32Array; /** * Read velocities for a specific range. */ readVelocities(range: ParticleRange): Float32Array; /** * Get attribute for a single particle. */ getAttributes(particleIndex: number): IParticleAttributes; /** * Update density/pressure for a range (written by fluid solver). */ writeDensityPressure(range: ParticleRange, density: Float32Array, pressure: Float32Array): void; /** Get all registered ranges. */ getRanges(): readonly ParticleRange[]; /** Get ranges of a specific particle type. */ getRangesByType(type: ParticleType): ParticleRange[]; /** Total active particles across all ranges. */ getActiveCount(): number; /** Get buffer usage stats. */ getStats(): UnifiedBufferStats; /** * Serialize all active particles into a compact binary buffer. * Layout per particle: [x, y, z, vx, vy, vz, type] = 28 bytes * * Header: [magic(4), version(2), particleCount(4), rangeCount(2)] * Then per range: [type(1), offset(4), count(4)] * Then particle data: [f32 x, f32 y, f32 z, f32 vx, f32 vy, f32 vz, u8 type] packed * * Returns an ArrayBuffer suitable for DataChannel.send(). */ serialize(): ArrayBuffer; /** * Deserialize a binary buffer (from a remote peer) into the local buffer. * Clears existing ranges and replaces with the remote state. */ deserialize(data: ArrayBuffer): void; /** Dispose all buffers. */ dispose(): void; } //# sourceMappingURL=UnifiedParticleBuffer.d.ts.map