import * as THREE from 'three/webgpu'; import type { Camera, ComputeNode, Renderer, StorageBufferNode } from 'three/webgpu'; import type { TSLMat4Node, TSLStorageNode, TSLUintNode, TSLUniformNode, TSLVec3Node, TSLVec4Node } from '../types/tsl.cjs'; import type { SplatSHReadNodes } from './SplatSHResource.cjs'; import type { GaussianSplatPositionNode, GaussianSplatSHCoefficientNode } from './GaussianSplatsMaterial.cjs'; /** Camera-motion thresholds controlling cached SH recomputation. */ export interface SplatSHResolverOptions { shColorMode?: 'direct' | 'cached'; shColorUpdateAngle?: number; shColorUpdateDistance?: number; } /** Optional GPU cache allocation returned by {@link SplatSHResolver.createGPUResources}. */ export interface SplatSHResolverGPUResources { resolvedSH?: StorageBufferNode<'vec4'>; } /** Buffer collection required by the resolver, with room for source-specific allocations. */ export interface SplatSHResolverBuffers { resolvedSH?: StorageBufferNode<'vec4'> | null; projected?: StorageBufferNode<'vec4'> | null; [name: string]: object | null | undefined; } /** * Dense visible prefix produced by the projection pass: `indices[0, live)` are the splats * drawn this frame, `count` holds `live` (atomic uint) and `dispatch` its 256-lane * workgroup count. The record resolve then touches only visible splats. */ export interface SplatSHResolverVisiblePrefix { indices: TSLStorageNode<'uint'>; count: TSLStorageNode<'uint'>; dispatch: THREE.IndirectStorageBufferAttribute; } /** Source-position reader consumed by the resolver compute graph. */ export interface SplatSHResolverSourceReadNodes { readPosition: (splatIndex: TSLUintNode) => TSLVec3Node; readColor?: (splatIndex: TSLUintNode) => TSLVec4Node; } /** Source resource surface used to build the position reader. */ export interface SplatSHResolverSourceResource { buildReadNodes(buffers: SplatSHResolverBuffers): SplatSHResolverSourceReadNodes; } /** SH resource surface used to build the coefficient reader. */ export interface SplatSHResolverCoefficientResource { buildReadNodes(buffers: SplatSHResolverBuffers): SplatSHReadNodes; } /** Gaussian renderer state required to build and dispatch the cached SH pass. */ export interface SplatSHResolverTarget { buffers: SplatSHResolverBuffers | null; _sourceResource: SplatSHResolverSourceResource | null; _shResource: SplatSHResolverCoefficientResource | null; _shDegree: number; _maxDataSHDegree: number; _projectedRecordCount: number; _colorRecordIndex: number; count: number; material?: { customPositionNode?: GaussianSplatPositionNode | null; customSHCoefficientNode?: GaussianSplatSHCoefficientNode | null; } | null; uniforms: { modelMatrix: TSLMat4Node; cameraPosition: TSLVec3Node; inverseModelMatrix: TSLMat4Node; shStrength: TSLUniformNode<'float', number>; }; } /** * Incrementally resolves view-dependent Gaussian splat SH deltas into a source-indexed GPU cache. * * @class SplatSHResolver * @short GPU SH color cache refreshed after meaningful camera movement. * @category GaussianSplatting */ export declare class SplatSHResolver { readonly splats: SplatSHResolverTarget; readonly enabled: boolean; readonly updateAngle: number; readonly updateDistance: number; refreshCount: number; private _dirty; private readonly _lastPosition; private readonly _lastDirection; private _compute; private _recordMode; private _visibilityGated; constructor(splats: SplatSHResolverTarget, options?: SplatSHResolverOptions); /** True when the built compute writes the projected COLOR record instead of the SH cache. */ get recordModeActive(): boolean; /** * True when the built compute resolves only the visible prefix. Such records are only * valid for the projection that produced the prefix, so the owner must dispatch it with * every projection instead of on the camera-motion gate. */ get visibilityGated(): boolean; /** The compute node to dispatch, for owners that batch it with their projection pass. */ get computeNode(): ComputeNode | null; createGPUResources(count: number, shCoeffCount: number): SplatSHResolverGPUResources; buildCompute(recordEligible?: boolean, visible?: SplatSHResolverVisiblePrefix): void; markDirty(): void; private _shouldRefresh; /** * Decide whether the resolve is due this frame and record the camera state when it is. * The owner then dispatches {@link computeNode} itself (batched with its own passes). */ claimRefresh(camera: Camera): boolean; update(renderer: Renderer, camera: Camera): boolean; dispose(): void; }