import * as THREE from 'three/webgpu'; import type { Renderer } from 'three/webgpu'; import type { SplatVideoTextureMesh } from './SplatVideoTextureSource.cjs'; import type { SplatSequencePrepareOptions, SplatSequenceRenderer, SplatSequenceRenderRange } from './SplatSequence.cjs'; import type { SplatVideoVideoManifest } from './SplatVideoManifest.cjs'; import type { ExpandedSplatSourceData } from './SplatSourceResource.cjs'; /** Promise with its settlement functions retained for clip readiness. */ export interface SplatVideoClipDeferred extends Promise { resolve(value: TValue | PromiseLike): void; reject(reason?: unknown): void; } /** Encoded attribute-video tracks owned by a clip. */ export interface SplatVideoClipTracks { geometry: ArrayBuffer; appearance: ArrayBuffer; } /** Construction and playback options for the full-GPU video runtime. */ export interface SplatVideoClipOptions { loop?: boolean | undefined; autoplay?: boolean | undefined; playbackSpeed?: number | undefined; /** `sequential` presents every authored frame; `realtime` may skip ahead to follow wall time. */ framePacing?: 'realtime' | 'sequential' | undefined; /** Inner GaussianSplats options. Clip coordinates are preserved (`coordinateSystem: 'source'`) by default. */ splats?: Record | undefined; hardwareAcceleration?: HardwareAcceleration | undefined; /** Full resident R8 plane-cache budget. Defaults to 192 MiB; 0 disables the cache. */ cacheBudgetBytes?: number | undefined; } /** Pinned empty attribute payload used to size the video-backed Gaussian mesh. */ export interface SplatVideoClipData extends ExpandedSplatSourceData { positions: Float32Array; scales: Float32Array; rotations: Float32Array; colors: Float32Array; count: number; shDegree: 0; chunkBounds: Float32Array; chunkSize: number; chunkBoundsMaxStdDev: number; } /** Event fired before the inner mesh enters its compute update. */ export interface SplatVideoClipMeshEventMap extends THREE.Object3DEventMap { beforeupdate: { renderer: Renderer; }; } /** Lap-one paired-transport throughput compared with the authored presentation rate. */ export interface SplatVideoDecodeMargin { pairDeliveryFps: number; frameRate: number; margin: number; warning: boolean; } /** Structural GaussianSplats boundary used by the video runtime. */ export interface SplatVideoClipMesh extends THREE.Object3D, SplatVideoTextureMesh { readonly isGaussianSplats: true; count: number; readonly stats: Record; readonly renderVersion: number; uniforms: { splatCount: { value: number; }; }; setData(data: SplatVideoClipData): void; setSourceBounds(min: readonly number[], max: readonly number[]): void; readStats(): Promise>; waitForRender(options?: { afterVersion?: number | undefined; signal?: AbortSignal | undefined; timeout?: number | undefined; }): Promise; invalidate(): void; dispose(): void; _invalidateShadowLights?: (() => void) | undefined; _shResolver?: { markDirty(): void; } | null | undefined; } /** Context delivered after deterministic rendering of one video frame. */ export interface SplatVideoClipRenderContext { frame: number; splats: SplatVideoClipMesh; renderer: SplatSequenceRenderer; scene: THREE.Scene; camera: THREE.Camera; } /** Playback and lifecycle events emitted by {@link SplatVideoClip}. */ export interface SplatVideoClipEventMap extends THREE.Object3DEventMap { framechange: { frame: number; time: number; }; play: Record; pause: Record; ended: Record; decodemargin: SplatVideoDecodeMargin; dispose: Record; } /** * GPU playback runtime for USV `video` clips (GS4D-5 v2): a worker inflates byte-exact * geometry, WebCodecs decodes hardware-friendly appearance, and a per-frame unpack compute * pass writes the pinned mesh's splat buffers in place ({@link SplatVideoTextureSource}). * Memory stays bounded by encoded tracks, the small prefetch/ready queues, and pinned buffers. * * The public surface mirrors {@link SplatClip} / {@link SplatSequence} (play/pause/`time`/ * loop/playbackSpeed/`update`/`setFrame`/`prepareFrame`/`renderFrames`) so benches, goldens, * and `syncClips` drive all three interchangeably. Seeks coalesce like `SplatSequence`: while * a pair is in flight, newer targets replace queued ones (flipbook skip-ahead, never a stall). * * @class SplatVideoClip * @extends THREE.Object3D * @short Worker-inflate + WebCodecs texture-unpack player for USV video clips. * @category GaussianSplatting * @tags WebGPU, Animation */ export declare class SplatVideoClip extends THREE.Object3D { readonly isSplatVideoClip: true; readonly type: 'SplatVideoClip'; manifest: SplatVideoVideoManifest; frameRate: number; frameCount: number; loop: boolean; playbackSpeed: number; framePacing: 'realtime' | 'sequential'; playing: boolean; ready: SplatVideoClipDeferred; private _time; private _frameAccumulator; private _disposed; private _counts; private _appliedFrame; private _pending; private _readyQueue; private _parked; private _feedCursor; private _fedSinceSeek; private _appliedSinceSeek; private _tailFlushed; private _warm; private _held; private _splats; private _source; private _onBeforeUpdate; private _tracks; private _decodePreference; private _decoders; private _terminalError; private _decodeMargin; private _decodeLapStartedAt; private _decodeLapLastFrame; private _decodeLapIntervals; private _transportIdle; /** * @param {Object} manifest - Normalized `video`-encoding manifest from `parseSplatVideoManifest`. * @param {{ geometry:ArrayBuffer, appearance:ArrayBuffer }} tracks - Fetched `.af` track buffers. * @param {Object} [options] - Playback options. * @param {boolean} [options.loop=true] - Loop playback at the clip end. * @param {boolean} [options.autoplay=false] - Begin playing once ready. * @param {number} [options.playbackSpeed=1] - Playback speed multiplier. * @param {Object} [options.splats] - Extra options forwarded to the inner GaussianSplats. * @param {'prefer-hardware'|'prefer-software'|'no-preference'} [options.hardwareAcceleration] * Appearance defaults to `prefer-hardware`, with the decoder's normal no-preference * fallback when the platform rejects an explicit hardware request. */ constructor(manifest: SplatVideoVideoManifest, tracks: SplatVideoClipTracks, options?: SplatVideoClipOptions); /** The inner GaussianSplats mesh (advanced integrations). */ get splats(): SplatVideoClipMesh; /** Clip duration in seconds. */ get duration(): number; /** Active WebCodecs hardware-acceleration preference, including automatic fallback changes. */ get decodePreference(): HardwareAcceleration; /** Lap-one paired delivery throughput, once one sequential wrap has completed. */ get decodeMargin(): SplatVideoDecodeMargin | null; /** Current playback time in seconds. */ get time(): number; set time(seconds: number); /** Index of the frame currently on the GPU (-1 before the first pair lands). */ get currentFrame(): number; /** Renderer statistics of the inner mesh. */ get stats(): Record; /** Resolve current GPU stage timestamps and merge video transport/cache diagnostics. */ readStats(): Promise>; private _buildDecoders; private _onDecoderError; private _terminateWithError; private _onGeometryFrame; private _onAppearanceFrame; private _assembleReadyPairs; private _recordPairDelivery; private _applyPair; private _applyResidentFrame; private _publishFrame; private _idleTransport; private _ownedVideoFrames; private _flushReadyQueue; private _seek; /** Begin playback. */ play(): this; /** Pause playback. */ pause(): this; /** Stop playback and rewind to the first frame. */ stop(): this; /** * Seek to a frame index. Fractional positions snap down (the video tier is a flipbook — * inter-frame interpolation belongs to the `tracks` encoding). * * @param {number} frameIndex - Frame position on the clip timeline. * @returns {this} */ setFrame(frameIndex: number): this; /** @param {number} seconds Playback time in seconds. @returns {this} */ seekSeconds(seconds: number): this; private _pumpSequentialFeed; private _requestPlaybackFrame; private _updateSequentialPlayback; /** * Advance playback (call from the application render loop). Decode keeps up with the clock * rather than gating it. An in-flight pair is allowed to finish; the following update then * coalesces to the latest clock frame instead of repeatedly resetting the decoder. * * @param {number} deltaSeconds - Elapsed seconds since the previous update. */ update(deltaSeconds: number): void; /** * Seek to a frame and wait until it completes a render — the deterministic capture path * (mirrors {@link SplatClip#prepareFrame} so the golden harness drives both). * * @param {number} frameIndex - Frame position. * @param {Object} [options={}] - { renderer, camera, scene, signal, timeout }. * @returns {Promise} The render-ready inner mesh. */ prepareFrame(frameIndex: number, options?: SplatSequencePrepareOptions): Promise; /** * Deterministically render a frame range, invoking a capture callback after every frame * (mirrors {@link SplatClip#renderFrames}). * * @param {THREE.WebGPURenderer} renderer - Renderer used for each frame. * @param {THREE.Scene} scene - Scene containing this clip. * @param {THREE.Camera} camera - Render camera. * @param {Function} onFrame - Async callback receiving { frame, splats, renderer, scene, camera }. * @param {Object} [options={}] - Range and cancellation options ({ start, end, step, signal, timeout }). * @returns {Promise} */ renderFrames(renderer: SplatSequenceRenderer, scene: THREE.Scene, camera: THREE.Camera, onFrame: (context: SplatVideoClipRenderContext) => Promise | unknown, options?: SplatSequenceRenderRange): Promise; private _findScene; /** Dispose the clip: decoders, held frames, textures, and the inner mesh. */ dispose(): void; private _disposeResources; }