import * as THREE from 'three/webgpu'; import type { Renderer } from 'three/webgpu'; import { SplatSequence } from './SplatSequence.cjs'; import type { SplatSequenceOptions, SplatSequencePrepareOptions, SplatSequenceRenderer, SplatSequenceRenderRange } from './SplatSequence.cjs'; import type { SpacetimeSplatData, SpacetimeSplatMesh } from './SpacetimeSplatSource.cjs'; import type { SplatVideoTracksManifest } from './SplatVideoManifest.cjs'; import type { SplatVideoPackedWindow } from './SplatVideoCodec.cjs'; import type { SplatVideoClip } from './SplatVideoClip.cjs'; /** Binary payload returned by clip asset fetchers. */ export type SplatClipBinary = ArrayBuffer | Uint8Array; /** Asset fetch seam used by manifest directories and `.usv` bundles. */ export type SplatClipFetcher = (file: string, signal?: AbortSignal) => Promise; /** On-demand keyframe-window fetch used by bounded residency. */ export type SplatClipWindowFetcher = (windowIndex: number) => Promise; /** In-memory or custom-transport source accepted by {@link SplatClip.load}. */ export interface SplatClipManifestSource { manifest: unknown; fetch: SplatClipFetcher; } /** Source accepted by {@link SplatClip.load}. */ export type SplatClipLoadSource = string | ArrayBuffer | SplatClipManifestSource; /** Playback and loading options shared by native-track and attribute-video clips. */ export interface SplatClipOptions { loop?: boolean | undefined; autoplay?: boolean | undefined; playbackSpeed?: number | undefined; /** `sequential` presents every authored video 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; sequence?: SplatSequenceOptions | undefined; /** * @deprecated Diagnostic-only reference-path override. Normal product code should omit * this option and let the manifest-selected runtime publish and unpack on the GPU. */ gpuUnpack?: boolean | undefined; residency?: 'full' | 'windowed' | undefined; windowFetch?: SplatClipWindowFetcher | null | undefined; hardwareAcceleration?: HardwareAcceleration | undefined; /** Full resident R8 plane-cache budget for `video` clips. Defaults to 192 MiB. */ cacheBudgetBytes?: number | undefined; } /** Fully decoded expanded payload installed into the temporal source. */ export interface SplatClipPayload extends SpacetimeSplatData { count: number; shDegree: 0; chunkBounds: Float32Array; chunkSize: number; chunkBoundsMaxStdDev: number; } /** A render-complete wait accepted by the inner Gaussian mesh. */ export interface SplatClipWaitOptions { afterVersion?: number | undefined; signal?: AbortSignal | undefined; timeout?: number | undefined; } /** Custom mesh event fired before GaussianSplats updates its compute graph. */ export interface SplatClipMeshEventMap extends THREE.Object3DEventMap { beforeupdate: { renderer: Renderer; }; } /** Structural GaussianSplats boundary consumed by clip playback. */ export interface SplatClipMesh extends THREE.Object3D, SpacetimeSplatMesh { readonly isGaussianSplats: true; readonly stats: Record; readonly renderVersion: number; setData(data: SplatClipPayload): void; setSourceBounds(min: readonly number[], max: readonly number[]): void; waitForRender(options?: SplatClipWaitOptions): Promise; invalidate(): void; dispose(): void; _invalidateShadowLights?: (() => void) | undefined; _shResolver?: { markDirty(): void; } | null | undefined; } /** Context delivered after deterministic rendering of one clip frame. */ export interface SplatClipRenderContext { frame: number; splats: SplatClipMesh; renderer: SplatSequenceRenderer; scene: THREE.Scene; camera: THREE.Camera; } /** Clip lifecycle and playback events. */ export interface SplatClipEventMap extends THREE.Object3DEventMap { framechange: { frame: number; time: number; }; play: Record; pause: Record; ended: Record; dispose: Record; } /** Runtime returned by any supported manifest encoding. */ export type SplatClipLoadResult = SplatClip | SplatSequence | SplatVideoClip; /** * Plays a USV (`utsubo-splat-video`) clip: a pinned `GaussianSplats` whose dynamic tail is * driven by a `SpacetimeSplatSource` resolve pass. The public playback surface mirrors * {@link SplatSequence} (play/pause/`time`/loop/playbackSpeed/`update`/`prepareFrame`) so the * existing bench + golden harness drive both interchangeably. * * Time changes: `setTime` on the source (uniforms + resolve-dirty), `invalidate()` on the mesh * (projection + sort re-run), shadow + SH invalidation — the static-position-leak checklist in * `.ai/SPLAT_VIDEO_GUIDE.md` §5. The resolve dispatch itself rides the mesh's `beforeupdate` * event (fires inside `GaussianSplats.update` with the renderer in hand, before the compute gate). * * @class SplatClip * @extends THREE.Object3D * @short Playback runtime for USV temporal gaussian-splat clips. * @category GaussianSplatting * @tags WebGPU, Animation */ export declare class SplatClip extends THREE.Object3D { /** Runtime type guard that is always `true` for splat clips. */ readonly isSplatClip: true; readonly type: 'SplatClip'; /** Validated track manifest driving this clip. */ manifest: SplatVideoTracksManifest; /** Authored playback frame rate. */ frameRate: number; /** Number of authored frames. */ frameCount: number; /** Whether playback wraps at the end of the clip. */ loop: boolean; /** Playback-speed multiplier. */ playbackSpeed: number; /** Whether calls to {@link update} advance playback time. */ playing: boolean; private _time; private _lastIntegerFrame; private _disposed; private _windowFetch; private _windowPromises; private _pendingTime; private _activeWindow; private _splats; private _source; private _onBeforeUpdate; /** * Load a clip from a manifest URL (directory layout), a `.usv` bundle (URL or ArrayBuffer), * or a pre-parsed manifest + fetcher pair. `ply-sequence` manifests route to a * {@link SplatSequence} (the V0 dev encoding); `tracks` manifests return a `SplatClip`. * * @param {string|ArrayBuffer|Object} source - Manifest/bundle URL, `.usv` ArrayBuffer, or * `{ manifest, fetch }` for an in-memory/custom transport. * @param {Object} [options] - See constructor options. * @returns {Promise} The ready playback object. */ static load(source: SplatClipLoadSource, options?: SplatClipOptions): Promise; static _fetchTracksPayload(manifest: SplatVideoTracksManifest, fetcher: SplatClipFetcher, residency?: 'full' | 'windowed'): Promise; /** * Create a clip from a normalized manifest and decoded payload. * * @param {Object} manifest - A normalized manifest from {@link parseSplatVideoManifest} (`tracks` encoding). * @param {Object} payload - Decoded clip payload (static arrays + `spacetime`, see `_fetchTracksPayload`). * @param {Object} [options] - Clip options. * @param {boolean} [options.loop=true] - Loop playback at the clip end. * @param {boolean} [options.autoplay=false] - Begin playing immediately. * @param {number} [options.playbackSpeed=1] - Playback speed multiplier. * @param {Object} [options.splats] - Extra options forwarded to the inner GaussianSplats. */ constructor(manifest: SplatVideoTracksManifest, payload: SplatClipPayload, options?: SplatClipOptions); /** The inner GaussianSplats mesh (advanced integrations). */ get splats(): SplatClipMesh; /** Clip duration in seconds. */ get duration(): number; /** Current playback time in seconds. */ get time(): number; set time(seconds: number); /** Current continuous frame position. */ get currentFrame(): number; /** Renderer statistics of the inner mesh. */ get stats(): Record; private _isWindowed; private _ensureWindow; private _applyTime; /** Begin playback. */ play(): this; /** Pause playback. */ pause(): this; /** Stop playback and rewind to the first frame. */ stop(): this; /** * Seek to a frame index (fractional frames allowed). * * @param {number} frameIndex - Frame position on the clip timeline. * @returns {this} */ setFrame(frameIndex: number): this; /** Seek to a playback time in seconds. */ seekSeconds(seconds: number): this; /** * Advance playback (call from the application render loop). * * @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 SplatSequence#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 SplatSequence#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: SplatClipRenderContext) => Promise | unknown, options?: SplatSequenceRenderRange): Promise; private _findScene; /** Dispose the clip and its inner mesh. */ dispose(): void; } /** * Drive several lock-stepped clips to the same time in one call (the splat counterpart of * `syncFrame` for `.af` video tracks). * * @param {SplatClip[]} clips - Clips to scrub together. * @param {number} seconds - Playback time in seconds. */ export declare function syncClips(clips: readonly SplatClip[], seconds: number): void;