import * as THREE from 'three/webgpu'; import { GaussianSplats } from './GaussianSplats.js'; import type { SplatStreamBounds, SplatStreamLOD, SplatStreamManifest } from './SplatStreamCodec.js'; /** Binary fetch seam used by directory and bundle-backed streams. */ export type GaussianSplatsStreamFetcher = (file: string, signal?: AbortSignal) => Promise; /** Construction/loading options for {@link GaussianSplatsStream}. */ export interface GaussianSplatsStreamOptions { budget?: number | undefined; pageSize?: number | undefined; concurrency?: number | undefined; hysteresis?: number | undefined; evictionCooldown?: number | undefined; lod?: Partial | undefined; splats?: Readonly> | undefined; } /** Mutable residency record for one manifest cell. */ export interface GaussianSplatsStreamCellState { index: number; bounds: SplatStreamBounds; counts: number[]; files: string[]; distance: number; targetLevel: number; residentLevel: number; residentPages: number[]; pendingLevel: number; pendingAbort: AbortController | null; residentSince: number; } /** Streaming progress payload dispatched after residency changes. */ export interface GaussianSplatsStreamProgressEvent { type: 'streamprogress'; residentSplats: number; residentCells: number; satisfied: number; wanted: number; inflight: number; } /** Dispatched once every wanted cell has reached its target LOD. */ export interface GaussianSplatsStreamIdleEvent { type: 'streamidle'; } export interface GaussianSplatsStreamEventMap extends THREE.Object3DEventMap { streamprogress: Omit; streamidle: Omit; } /** Streaming statistics layered over the inner renderer's statistics. */ export interface GaussianSplatsStreamStats extends Record { streamCapacity: number; streamPageSize: number; streamFreePages: number; streamResidentSplats: number; streamInflight: number; streamQueued: number; streamCells: number; } /** * Budget-driven streaming renderer for very large Gaussian splatting scenes (USS v1). * * Owns one pinned {@link GaussianSplats} whose expanded attribute buffers are treated as a page * arena. Each frame (hooked on the splats' `beforeupdate` event) it selects a LOD level per * spatial cell from the camera distance, balances the selection against the splat budget * (degrading the farthest cells first), streams missing cell files with nearest-first priority, * uploads them with partial buffer writes, and evicts with a cooldown. Coarse levels load before * fine ones, so scenes appear quickly and refine. * * Format + runtime contract: `packages/core/.ai/SPLAT_STREAM_FORMAT.md`. * * ```javascript * const stream = await GaussianSplatsStream.load( 'scene/manifest.json', { budget: 2_000_000 } ); * scene.add( stream ); * ``` * * @class GaussianSplatsStream * @short Streaming LOD residency manager over a pinned GaussianSplats page arena. * @category GaussianSplatting * @tags WebGPU, Streaming, LOD */ export declare class GaussianSplatsStream extends THREE.Object3D { readonly isGaussianSplatsStream: true; manifest: SplatStreamManifest; pageSize: number; pageCount: number; capacity: number; concurrency: number; hysteresis: number; evictionCooldown: number; lod: SplatStreamLOD; splats: GaussianSplats; private _fetcher; private _freePages; private _cells; private _frame; private _lastSelectionAt; private _lastCameraPosition; private _deferredFrees; private _inflight; private _queue; private _disposed; private _onBeforeUpdate; /** * Load a stream from a manifest URL (directory layout) or a `.uss` bundle (URL or ArrayBuffer). * * @param {string|ArrayBuffer} source Manifest/bundle URL, or a `.uss` ArrayBuffer. * @param {Object} [options] See constructor options. * @returns {Promise} Ready-to-add stream (initial fetches already queued). */ static load(source: string | ArrayBuffer, options?: GaussianSplatsStreamOptions): Promise; /** * @param {Object} manifest Parsed USS manifest (validated here). * @param {Function} fetcher `(relativePath, abortSignal) => Promise`. * @param {Object} [options] Streaming options. * @param {number} [options.budget=2000000] Resident splat budget (arena capacity). * @param {number} [options.pageSize=8192] Arena page size in splats (multiple of 256). * @param {number} [options.concurrency=4] Parallel cell fetches. * @param {number} [options.hysteresis=0.15] LOD band hysteresis fraction. * @param {number} [options.evictionCooldown=1500] Minimum ms a cell level stays resident. * @param {Object} [options.lod] Overrides for manifest lod (baseDistance, multiplier, behindPenalty). * @param {Object} [options.splats] Extra options forwarded to the inner GaussianSplats. */ constructor(manifest: unknown, fetcher: GaussianSplatsStreamFetcher, options?: GaussianSplatsStreamOptions); /** Largest single-cell level-0 count — the arena must fit at least one full cell. @private */ private _largestLevelZeroCount; /** * Allocate the pinned arena: zeroed splats (alpha 0 — projection-culled) and far-away chunk * bounds so hierarchical culling skips empty pages. Projection cost then scales with RESIDENT * splats, not arena capacity. * @private */ private _initArena; /** * Per-frame driver (splats `beforeupdate`): throttled target selection, fetch scheduling, * and deferred page frees. * @private */ private _update; /** * Distance-banded level selection with FOV compensation, behind-camera penalty, hysteresis, * and page-budget balancing (degrade farthest first, then drop farthest). * @private */ private _selectTargets; /** Start queued fetches up to the concurrency limit. @private */ private _pump; /** Fetch + decode + activate one (cell, level). Stale results are dropped. @private */ private _fetchCell; /** * Upload a decoded level into freshly allocated pages, publish culling bounds, then retire the * previous level's pages after a 2-frame overlap (a brief double-density blip instead of a hole). * @private */ private _activate; /** Remove a cell's resident level immediately (bounds → far, pages → free list). @private */ private _evict; /** Park pages behind far-away culling bounds and return them to the free list. @private */ private _releasePages; /** @private */ private _emitProgress; /** * Streaming statistics (extends the inner splats stats). * @type {Object} * @readonly */ get stats(): GaussianSplatsStreamStats; /** Dispose GPU resources and stop all streaming activity. */ dispose(): void; }