/** * Morton (Z-order) ordering utilities for Gaussian Splatting. * * Morton ordering improves GPU cache locality by ensuring spatially-adjacent * splats have adjacent indices in memory. This can significantly improve * performance when the GPU compute shader is memory-bound. * * @module MortonOrdering */ /** The position bounds used to quantize coordinates onto the 10-bit Morton grid. */ export interface MortonBounds { minX: number; minY: number; minZ: number; maxX: number; maxY: number; maxZ: number; } /** Six Float32 values per chunk: minimum xyz followed by maximum xyz. */ export type SplatChunkBounds = Float32Array; /** Output-to-source splat indices accepted by the permutation helpers. */ export type SplatPermutation = Uint32Array | readonly number[]; /** A freshly computed, stable output-to-source Morton permutation. */ export type ComputedSplatPermutation = Uint32Array; /** * Float32 splat attributes shared by loaders, packers, and sequence frames. * * Callers may carry additional format-specific properties. Generic ordering helpers retain those * properties while replacing only the arrays whose splat axis is reordered. */ export interface SanitizableSplatData { positions: Float32Array; scales: Float32Array; rotations: Float32Array; colors: Float32Array; count: number; shCoefficients?: Float32Array | null; normals?: Float32Array | null; } /** A complete splat frame whose spherical-harmonic layout is known for reordering. */ export interface SplatData extends SanitizableSplatData { shDegree: number; } /** A splat record after every per-splat Float32 attribute has been copied into a new order. */ export type PermutedSplatData = Omit & { positions: Float32Array; scales: Float32Array; rotations: Float32Array; colors: Float32Array; shCoefficients: Float32Array | null; normals: Float32Array | null; count: number; shDegree: number; }; /** Morton ordering either preserves a small input object or returns a copied, permuted record. */ export type MortonOrderingResult = TData | PermutedSplatData; /** Selects which frame supplies the sequence-wide Morton order. */ export interface StableSequenceOrderOptions { referenceFrame?: number; } /** Result for an empty sequence, for which no permutation can be computed. */ export interface EmptyStableSequenceOrderResult { permutation: null; frames: TData[]; } /** Result for a non-empty sequence after one shared permutation is copied across every frame. */ export interface OrderedStableSequenceOrderResult { permutation: ComputedSplatPermutation; frames: PermutedSplatData[]; } /** Empty or ordered sequence result, discriminated by the computed permutation. */ export type StableSequenceOrderResult = EmptyStableSequenceOrderResult | OrderedStableSequenceOrderResult; /** * Compute a Morton (Z-order) permutation for a set of splat positions. * * This is the per-asset spatial sort used by dynamic sequences: compute the permutation ONCE from a * reference frame, then apply it to every frame with {@link applySplatPermutation} so that output * index `p` maps to the same Gaussian across all frames. Stable per-asset order is what makes a * frame-to-frame depth sort reusable and (for video transport) gives codec temporal coherence — the * pixel `(u,v)` carrying splat `p`'s attributes is the same Gaussian in every frame. * * @param {Float32Array} positions - Positions array (count * 3). * @param {number} count - Number of splats. * @returns {Uint32Array} Permutation indices sorted by Morton code. */ export declare function computeMortonPermutation(positions: Float32Array, count: number): ComputedSplatPermutation; /** * Compute AABB bounds for each chunk of splats. * Used for hierarchical frustum culling at runtime. * * @param {Float32Array} positions - Positions array (count * 3) * @param {number} count - Number of splats * @param {number} [chunkSize=256] - Number of splats per chunk (should match workgroup size) * @param {Float32Array} [scales] - Optional scales array (count * 3, log-space) for AABB padding * @param {number} [maxStdDev=2.83] - Maximum std devs for Gaussian extent (default sqrt(8)) * @returns {Float32Array} Chunk bounds array (numChunks * 6): [minX, minY, minZ, maxX, maxY, maxZ] per chunk */ export declare function computeChunkBounds(positions: Float32Array, count: number, chunkSize?: number, scales?: Float32Array | null, maxStdDev?: number): SplatChunkBounds; /** * Sanitize parsed Gaussian splat arrays in place before ordering and upload. * Invalid positions become transparent origin splats so they cannot contaminate * bounds, covariance projection, or sort keys. * * @param {Object} data Parsed splat data. * @returns {Object} The same data object after sanitization. */ export declare function sanitizeSplatData(data: TData): TData; /** GPU-layout arrays produced by {@link packExpandedSplatData}. */ export interface PackedExpandedSplatArrays { /** Interleaved [position.xyz, pad][scale.xyz, pad] vec4 pairs — the expanded GPU source layout. */ packedPositionsScales: Float32Array; /** Interleaved [rotation.xyzw][color.rgba] vec4 pairs — the expanded GPU source layout. */ packedRotationsColors: Float32Array; /** * Packed half-float SH in SOURCE (file) order: two uint32 (four halves: r, g, b, pad) per * coefficient, splat-major. Reordering to the packed splat order happens on the GPU via * {@link PackedExpandedSplatArrays.shPermutation} — a CPU-side gather of the SH payload is * the single most expensive loader pass at multi-million-splat sizes. */ shCoefficientsHalf: Uint32Array | null; /** Output→source splat permutation the GPU SH scatter applies (null = file order is final). */ shPermutation: Uint32Array | null; /** Reordered float32 SH when the caller requested shFormat 'float'. */ shCoefficients: Float32Array | null; /** Reordered baked normals, when present. */ normals: Float32Array | null; chunkBounds: SplatChunkBounds; sanitizedCount: number; } /** Options for {@link packExpandedSplatData}. */ export interface PackExpandedSplatOptions { chunkSize?: number; maxStdDev?: number; shFormat?: 'half' | 'float'; } /** * Convert a float32 value to IEEE 754 binary16 bits with round-to-nearest-even — * bit-identical to native Float16Array conversion, so packed payloads are deterministic * whether or not the environment provides Float16Array. */ export declare function toHalfBits(value: number): number; /** Minimal indexable surface of a Float16Array (kept structural: the type is not in all TS libs). */ export interface Float16ArrayLike { [index: number]: number; } /** * View a buffer as half floats when the environment provides Float16Array, else null. * Callers fall back to {@link toHalfBits}, which produces bit-identical results. */ export declare function float16ViewOf(buffer: ArrayBufferLike): Float16ArrayLike | null; /** * Pack float32 SH coefficients into half floats in their existing (file) order. * * Sequential reads and writes make this pass memory-bandwidth trivial (~20 ms per million * splats at degree 3), unlike a permutation gather. Non-finite values become zero. * * @param {Float32Array} shCoefficients Planar float32 SH (count × coefficientCount × 3). * @param {number} count Splat count. * @param {number} coefficientCount SH coefficients per splat. * @returns {Uint32Array} Two uint32 (four halves: r, g, b, pad) per coefficient, splat-major. */ export declare function packSHHalfFileOrder(shCoefficients: Float32Array, count: number, coefficientCount: number): Uint32Array; /** * Sanitize only positions (and the opacity of position-invalid splats) in place. * * This is the minimal pre-pass {@link computeMortonPermutation} needs so NaN positions * cannot poison Morton codes. All remaining attribute sanitization happens inline in * {@link packExpandedSplatData}, which touches every value anyway. * * @param {Object} data Parsed splat data. * @returns {number} Number of sanitized positions. */ export declare function sanitizeSplatPositions(data: SanitizableSplatData): number; /** * Fused permute + sanitize + GPU-layout pack + chunk-bounds pass for expanded splat data. * * Replaces the legacy sanitize → five-array permutation gather → chunk-bounds sequence with a * single pass over the source arrays that writes the exact expanded GPU buffer layouts * (interleaved vec4 pairs, packed half-float SH) so the main thread can wrap the arrays as * storage attributes without any further copying. * * Callers must run {@link sanitizeSplatPositions} before computing the permutation. * * @param {Object} data Parsed splat data (positions already sanitized). * @param {Uint32Array|null} permutation Output→source Morton permutation, or null for identity. * @param {Object} [options] Chunk sizing, radius padding, and SH format options. * @returns {Object} GPU-layout arrays plus chunk bounds. */ export declare function packExpandedSplatData(data: SplatData, permutation: SplatPermutation | null, options?: PackExpandedSplatOptions): PackedExpandedSplatArrays; /** * Apply Morton ordering to parsed Gaussian Splatting data. * * Reorders all splat arrays (positions, scales, rotations, colors, shCoefficients) * by Morton code to improve GPU cache locality during rendering. * * @param {Object} data - Parsed splat data from loader * @param {Float32Array} data.positions - Positions (count * 3) * @param {Float32Array} data.scales - Scales (count * 3) * @param {Float32Array} data.rotations - Rotations (count * 4) * @param {Float32Array} data.colors - Colors (count * 4) * @param {Float32Array} [data.shCoefficients] - SH coefficients (optional) * @param {number} data.count - Number of splats * @param {number} data.shDegree - SH degree * @returns {Object} Reordered data with same structure */ export declare function applyMortonOrdering(data: TData): MortonOrderingResult; /** * Reorder all of a parsed splat frame's attribute arrays by a precomputed permutation. * * Unlike {@link applyMortonOrdering} (which derives a fresh permutation from this frame's own * positions), this applies a permutation computed elsewhere — typically once, from a sequence's * reference frame — so a clip's frames stay index-aligned. There is no count threshold: the caller * decides when ordering is worthwhile. * * @param {Object} data - Parsed splat data (positions/scales/rotations/colors[/shCoefficients]). * @param {Uint32Array|number[]} permutation - Output→source index mapping (length === count). * @returns {Object} A new data object with the same structure and reordered arrays. */ export declare function applySplatPermutation(data: TData, permutation: SplatPermutation): PermutedSplatData; /** * Compute a single stable Morton order for a whole sequence and apply it to every frame. * * The order is derived ONCE from a reference frame and reused across all frames, so output index `p` * addresses the same Gaussian in every frame. This is the encode-time spatial sort a dynamic clip * needs: a reusable depth-sort order and (for the video track) codec temporal coherence. * * Correctness precondition: the frames must already share a per-index Gaussian correspondence (splat * `i` is the same Gaussian in every frame). Independently-ordered per-frame captures must be made * correspondent first (an encoder responsibility); applying one order to non-correspondent frames * would scramble identities. * * @param {Object[]} frames - Parsed splat frames sharing a per-index correspondence. * @param {Object} [options={}] - Options. * @param {number} [options.referenceFrame=0] - Index of the frame whose positions seed the order. * @returns {{ permutation: Uint32Array|null, frames: Object[] }} The shared order and reordered frames. */ export declare function computeStableSequenceOrder(frames: TData[] | null | undefined, options?: StableSequenceOrderOptions): StableSequenceOrderResult;