@voluma/vlam 0.3.4 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/compute-sorter.d.ts +7 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/radix-sorter.d.ts +6 -0
- package/dist/core/sort-worker-protocol.d.ts +2 -0
- package/dist/core/sorter.d.ts +4 -4
- package/dist/core/splat-mesh-types.d.ts +22 -5
- package/dist/core/splat-mesh.d.ts +11 -2
- package/dist/core/splat-sort-bounds.d.ts +17 -0
- package/dist/core/worker-sorter.d.ts +7 -5
- package/dist/index.js +3 -3
- package/dist/radix-sorter-tSffmerk.js +181 -0
- package/dist/radix-sorter-tSffmerk.js.map +1 -0
- package/dist/relighting-qaANKC4Z.js +997 -0
- package/dist/relighting-qaANKC4Z.js.map +1 -0
- package/dist/{splat-mesh-CsLOQb08.js → splat-mesh-B1KlWHvx.js} +545 -513
- package/dist/splat-mesh-B1KlWHvx.js.map +1 -0
- package/dist/splat-mesh-types-BZIko-_9.js.map +1 -1
- package/dist/static-lod.js +1 -1
- package/dist/streaming.js +17 -17
- package/dist/unified.js +17 -17
- package/package.json +1 -1
- package/dist/radix-sorter-BrbUg_CV.js +0 -178
- package/dist/radix-sorter-BrbUg_CV.js.map +0 -1
- package/dist/relighting-Tiwep8yd.js +0 -977
- package/dist/relighting-Tiwep8yd.js.map +0 -1
- package/dist/splat-mesh-CsLOQb08.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"relighting-Tiwep8yd.js","sources":["../src/lib/core/splat-modifier-stack.ts","../src/lib/core/splat-mesh-pool.ts","../src/lib/core/depth-of-field.ts","../src/lib/core/splat-mesh-material.ts","../src/lib/core/splat-sort-bounds.ts","../src/lib/core/storage-attribute-mirror.ts","../src/lib/core/compute-sorter.ts","../src/lib/core/sort-scheduler.ts","../src/lib/core/xr-view.ts","../src/lib/core/relighting.ts"],"sourcesContent":["/** Modifier-stack folding for the splat material graph. */\nimport * as THREE from 'three/webgpu';\nimport { bool, float, mat3, modelViewMatrix, modelWorldMatrix, vec3, vec4 } from 'three/tsl';\nimport type { SplatContext, SplatModifier, SplatOutputs } from './splat-modifier';\nimport type { Vec3Uniform } from './splat-mesh-material';\n\nfunction asNode<T extends string>(node: unknown): THREE.Node<T> {\n return node as THREE.Node<T>;\n}\n\n/**\n * Builds the per-splat {@link SplatContext}, folds the modifier stack\n * over it in order, and returns the graph fragments the renderer applies.\n * `null` fragments mean \"untouched\": with an empty stack every fragment\n * is null and the caller emits exactly the unhooked graph.\n *\n * Modifiers are called once, here, at material build time - they are\n * graph builders, not per-frame callbacks. Derived context fields\n * (worldCenter, viewCenter, normal) are memoized lazily so only the\n * modifiers that read them add their nodes to the graph.\n */\nexport function foldSplatModifierStack(\n modifierList: readonly SplatModifier[],\n localCameraPosition: Vec3Uniform,\n inputs: {\n index: THREE.Node<'int'>;\n localCenter: THREE.Node<'vec3'>;\n /** Pre-placement pool position; defaults to {@link localCenter}. */\n sourceCenter?: THREE.Node<'vec3'>;\n /** Source-data-frame → mesh-local linear transform; defaults to identity. */\n sourceToLocal?: THREE.Node<'mat3'>;\n color: THREE.Node<'vec4'>;\n makeNormal: () => THREE.Node<'vec3'>;\n makeChannel: (name: string) => THREE.Node<'float'>;\n /** Optional coordinate-space adapters for compute-time modifier folding. */\n makeWorldCenter?: () => THREE.Node<'vec3'>;\n makeViewCenter?: () => THREE.Node<'vec3'>;\n },\n): {\n color: THREE.Node<'vec4'>;\n offset: THREE.Node<'vec3'> | null;\n scaleSquared: THREE.Node<'float'> | null;\n rotation: THREE.Node<'mat3'> | null;\n visible: THREE.Node<'bool'> | null;\n isotropicCovarianceMix: THREE.Node<'float'> | null;\n isotropicVarianceScale: THREE.Node<'float'> | null;\n isotropicScreenRadiusPx: THREE.Node<'float'> | null;\n} {\n if (modifierList.length === 0) {\n return {\n color: inputs.color,\n offset: null,\n scaleSquared: null,\n rotation: null,\n visible: null,\n isotropicCovarianceMix: null,\n isotropicVarianceScale: null,\n isotropicScreenRadiusPx: null,\n };\n }\n\n const cameraLocal = asNode<'vec3'>(localCameraPosition);\n const state = {\n color: inputs.color,\n offset: asNode<'vec3'>(vec3(0.0, 0.0, 0.0)),\n scale: asNode<'float'>(float(1.0)),\n rotation: asNode<'mat3'>(mat3(vec3(1, 0, 0), vec3(0, 1, 0), vec3(0, 0, 1))),\n visible: asNode<'bool'>(bool(true)),\n };\n const used = { offset: false, scale: false, rotation: false, visible: false };\n let isotropicCovarianceMix: THREE.Node<'float'> | null = null;\n let isotropicVarianceScale: THREE.Node<'float'> | null = null;\n let isotropicScreenRadiusPx: THREE.Node<'float'> | null = null;\n\n let worldCenter: THREE.Node<'vec3'> | null = null;\n let viewCenter: THREE.Node<'vec3'> | null = null;\n let normal: THREE.Node<'vec3'> | null = null;\n const channelNodes = new Map<string, THREE.Node<'float'>>();\n const context: SplatContext = {\n index: inputs.index,\n localCenter: inputs.localCenter,\n sourceCenter: inputs.sourceCenter ?? inputs.localCenter,\n sourceToLocal: inputs.sourceToLocal ?? state.rotation,\n cameraLocal,\n baseColor: inputs.color,\n get worldCenter() {\n return (worldCenter ??= asNode<'vec3'>(\n inputs.makeWorldCenter\n ? inputs.makeWorldCenter()\n : modelWorldMatrix.mul(vec4(inputs.localCenter, 1.0)).xyz,\n ));\n },\n get viewCenter() {\n return (viewCenter ??= asNode<'vec3'>(\n inputs.makeViewCenter\n ? inputs.makeViewCenter()\n : modelViewMatrix.mul(vec4(inputs.localCenter, 1.0)).xyz,\n ));\n },\n get normal() {\n return (normal ??= inputs.makeNormal());\n },\n channel(name: string) {\n let node = channelNodes.get(name);\n if (!node) {\n node = inputs.makeChannel(name);\n channelNodes.set(name, node);\n }\n return node;\n },\n get color() {\n return state.color;\n },\n get offset() {\n return state.offset;\n },\n get scale() {\n return state.scale;\n },\n get rotation() {\n return state.rotation;\n },\n get visible() {\n return state.visible;\n },\n };\n\n for (const modifier of modifierList) {\n const outputs: SplatOutputs = modifier(context);\n if (outputs.color) state.color = outputs.color;\n if (outputs.offset) {\n state.offset = outputs.offset;\n used.offset = true;\n }\n if (outputs.scale) {\n state.scale = outputs.scale;\n used.scale = true;\n }\n if (outputs.rotation) {\n state.rotation = outputs.rotation;\n used.rotation = true;\n }\n if (outputs.visible) {\n state.visible = outputs.visible;\n used.visible = true;\n }\n if (outputs.isotropicCovarianceMix !== undefined) {\n isotropicCovarianceMix = outputs.isotropicCovarianceMix;\n }\n if (outputs.isotropicVarianceScale !== undefined) {\n isotropicVarianceScale = outputs.isotropicVarianceScale;\n }\n if (outputs.isotropicScreenRadiusPx !== undefined) {\n isotropicScreenRadiusPx = outputs.isotropicScreenRadiusPx;\n }\n }\n\n return {\n color: state.color,\n offset: used.offset ? state.offset : null,\n scaleSquared: used.scale ? asNode<'float'>(state.scale.mul(state.scale)) : null,\n rotation: used.rotation ? state.rotation : null,\n visible: used.visible ? state.visible : null,\n isotropicCovarianceMix,\n isotropicVarianceScale,\n isotropicScreenRadiusPx,\n };\n}\n","/**\n * Texture-pool primitives behind `SplatMesh`.\n *\n * Splat attributes live in pool data textures, a fixed number of texels wide,\n * and every range the mesh hands out is a whole number of rows in them. This\n * module holds the parts of that scheme with no dependency on the mesh's own\n * state: creating the textures, measuring the backend's limits, and the row-span\n * bookkeeping the allocator runs on.\n *\n * {@link SplatPool} is public - a host builds one to let several meshes draw\n * from a shared memory envelope. The rest is internal.\n */\nimport * as THREE from 'three/webgpu';\n\n/**\n * Texels per row in every pool data texture.\n *\n * A range is row-aligned, so this also sets the allocation granularity: a\n * 10-splat chunk still occupies a whole 2048-texel row. Wider rows waste more\n * on small chunks; narrower rows need more rows than a backend may allow.\n */\nexport const SPLAT_DATA_TEXTURE_WIDTH = 2048;\n\n/** A free span of rows in the pool, as a row index and a row count. */\nexport interface RowSpan {\n start: number;\n count: number;\n}\n\n/**\n * Takes `rowCount` contiguous rows out of `spans`, best-fit.\n *\n * Best-fit rather than first-fit: a chunk pool churns ranges of very different\n * sizes, and spending the smallest span that fits keeps the large ones intact\n * for the large chunks that have nowhere else to go.\n *\n * Mutates `spans` in place. Throws when nothing contiguous is left, which is\n * the caller's cue to compact.\n *\n * @param poolRows - Total rows in the pool; named in the failure only.\n * @returns The starting row of the allocated span.\n */\nexport function allocateRowSpan(spans: RowSpan[], rowCount: number, poolRows: number): number {\n let bestIndex = -1;\n for (let i = 0; i < spans.length; i++) {\n const span = spans[i] as RowSpan;\n if (span.count < rowCount) continue;\n if (bestIndex === -1 || span.count < (spans[bestIndex] as RowSpan).count) {\n bestIndex = i;\n }\n if (span.count === rowCount) break;\n }\n if (bestIndex !== -1) {\n const span = spans[bestIndex] as RowSpan;\n const start = span.start;\n span.start += rowCount;\n span.count -= rowCount;\n if (span.count === 0) spans.splice(bestIndex, 1);\n return start;\n }\n throw new Error(\n `SplatMesh capacity exceeded: no contiguous space for ${rowCount} rows ` +\n `(${SPLAT_DATA_TEXTURE_WIDTH} splats each) in a ${poolRows}-row pool.`,\n );\n}\n\n/**\n * Returns a span to the free list, coalescing it with any neighbours.\n *\n * Merging on release is what keeps {@link allocateRowSpan} able to satisfy a\n * large request after many small ranges have come and gone; without it the\n * free list fragments into unusable slivers.\n *\n * @returns The new free list, sorted by start row.\n */\nexport function releaseRowSpan(spans: RowSpan[], start: number, count: number): RowSpan[] {\n spans.push({ start, count });\n spans.sort((a, b) => a.start - b.start);\n const merged: RowSpan[] = [];\n for (const span of spans) {\n const last = merged[merged.length - 1];\n if (last && last.start + last.count > span.start) {\n // Overlapping free spans mean a double (or overlapping) release - left\n // silent, the same rows would be handed out twice by allocateRowSpan.\n throw new Error(\n `SplatMesh pool released rows [${start}, ${start + count}) overlap an already-free span.`,\n );\n }\n if (last && last.start + last.count === span.start) last.count += span.count;\n else merged.push(span);\n }\n return merged;\n}\n\n/** CPU-side mirror of the pool textures, kept for partial uploads. */\nexport interface SplatPoolBacking {\n centers: Float32Array;\n colors: Uint8Array;\n covarianceA: Float32Array;\n covarianceB: Float32Array;\n /** Per-splat packed SH; texture t holds coefficients 4t..4t+3. */\n shPacked: Uint32Array[];\n}\n\n/** A tenant's allocation in the pool, as a row index and a row count. */\nexport interface SplatPoolRange {\n startRow: number;\n rowCount: number;\n}\n\n/**\n * A mesh that draws from a {@link SplatPool}.\n *\n * Only compaction needs this: packing the pool relocates rows belonging to\n * every tenant, so each must be able to enumerate its ranges, follow them to\n * their new rows, and rebuild whatever it keyed by pool index.\n */\nexport interface SplatPoolTenant {\n /** The tenant's current allocations. */\n poolRanges(): Iterable<SplatPoolRange>;\n /**\n * Adopts a new start row for one of this tenant's ranges. The pool has\n * already moved the splat data; the tenant moves anything else keyed by pool\n * row (per-splat channels) and records that those rows need re-uploading.\n */\n relocatePoolRange(range: SplatPoolRange, targetRow: number): void;\n /** Called once after a compaction, so the tenant can rebuild draw state. */\n onPoolCompacted(): void;\n}\n\n/** Options for {@link SplatPool}. */\nexport interface SplatPoolOptions {\n /** Splats the pool must hold; rounded up to whole rows. */\n capacity: number;\n /**\n * Texture precision for centers and covarianceA. `'float16'` halves their\n * VRAM; covarianceB stays float32 either way because it packs integer IDs\n * (SOG palette label, `.rad` frontier parent) that halves cannot represent\n * exactly above 2048.\n */\n floatTextures?: 'float32' | 'float16';\n /** Bands of per-splat (non-palette) SH to allocate for; 0 disables. */\n packedShBands?: 0 | 1 | 2 | 3;\n /** Packed-SH coefficient count for `packedShBands`, supplied by the caller\n * so this module stays free of the SH packing tables. */\n packedShTextureCount?: number;\n /**\n * The device's `maxTextureDimension2D`, so a pool too tall for it fails here\n * rather than at first draw. Pass {@link deviceMaxTextureSize}.\n *\n * `SplatMesh.update` also checks this, but only once and only if it is ever\n * reached: a pool created past the limit and drawn through a path that\n * swallows that first throw renders forever against invalid textures, which\n * surfaces as an unreadable cascade of WebGPU \"invalid due to a previous\n * error\" validation failures and a black canvas. Checking at construction is\n * both earlier (before the allocation) and unskippable.\n *\n * Omitted or 0 skips the check, so a caller that cannot read the limit is not\n * falsely rejected.\n */\n maxTextureSize?: number;\n}\n\n/**\n * The splat storage a mesh draws from: the data textures, their CPU backing,\n * and the row allocator that hands out space in them.\n *\n * Split out of `SplatMesh` because storage and drawing have different\n * lifetimes and, ultimately, different owners. A mesh's *draw identity* is\n * already independent of where its splats sit - `splatIndex` maps each\n * instance to a pool index and the sorter rewrites it freely - so the pool\n * behind those indices is a separate concern, and one pool can in principle\n * back several meshes (each keeping its own active list of pool indices).\n *\n * Allocation is row-aligned and best-fit; see {@link allocateRowSpan}.\n */\nexport class SplatPool {\n readonly width = SPLAT_DATA_TEXTURE_WIDTH;\n readonly rows: number;\n readonly floatTextures: 'float32' | 'float16';\n readonly packedShBands: 0 | 1 | 2 | 3;\n readonly backing: SplatPoolBacking;\n readonly centersTexture: THREE.DataTexture;\n readonly colorsTexture: THREE.DataTexture;\n readonly covarianceATexture: THREE.DataTexture;\n readonly covarianceBTexture: THREE.DataTexture;\n readonly shPackedTextures: readonly THREE.DataTexture[];\n /** Free row spans, sorted by start row. Mutated in place by the allocator. */\n freeRowSpans: RowSpan[];\n private readonly tenants = new Set<SplatPoolTenant>();\n /**\n * Pool index → its owner's packed active-list slot, valid only while that\n * pool index is active.\n *\n * Pool-owned rather than per-mesh because it is keyed by *pool* index: a\n * per-mesh copy would have to span the whole pool, so N meshes sharing one\n * pool would each pay 4 B per pool splat (a 4 M-splat pool shared by 13\n * meshes = ~200 MB of reverse maps). One array is safe because a row belongs\n * to exactly one tenant at a time, so no two tenants ever write the same\n * entry - the slot numbers in it are simply read back by whoever owns\n * the row.\n */\n readonly activeSlotByPoolIndex: Uint32Array;\n private poolIndexTemplate: Uint32Array | null = null;\n\n constructor(options: SplatPoolOptions) {\n if (!(options.capacity > 0)) throw new Error('SplatPool capacity must be positive.');\n this.rows = Math.ceil(options.capacity / this.width);\n assertPoolRowsFitDevice(this.rows, options.maxTextureSize ?? 0, this.width);\n const texelCount = this.width * this.rows;\n this.floatTextures = options.floatTextures === 'float16' ? 'float16' : 'float32';\n this.packedShBands = options.packedShBands ?? 0;\n const floatType = this.floatTextures === 'float16' ? THREE.HalfFloatType : THREE.FloatType;\n\n this.backing = {\n centers: new Float32Array(texelCount * 4),\n colors: new Uint8Array(texelCount * 4),\n covarianceA: new Float32Array(texelCount * 4),\n covarianceB: new Float32Array(texelCount * 4),\n // One RGBA32UI texel per splat per texture; sharing the pool's row\n // geometry lets the whole partial-upload/compaction path reuse the\n // existing row arithmetic unchanged.\n shPacked: Array.from(\n { length: options.packedShTextureCount ?? 0 },\n () => new Uint32Array(texelCount * 4),\n ),\n };\n // Under float16 the textures get their own half-encoded images; the float32\n // backing above stays authoritative and is re-encoded on upload.\n const centersImage: Float32Array | Uint16Array =\n this.floatTextures === 'float16' ? new Uint16Array(texelCount * 4) : this.backing.centers;\n const covarianceAImage: Float32Array | Uint16Array =\n this.floatTextures === 'float16' ? new Uint16Array(texelCount * 4) : this.backing.covarianceA;\n\n this.centersTexture = createDataTexture(centersImage, this.width, this.rows, floatType);\n this.colorsTexture = createDataTexture(\n this.backing.colors,\n this.width,\n this.rows,\n THREE.UnsignedByteType,\n );\n this.covarianceATexture = createDataTexture(covarianceAImage, this.width, this.rows, floatType);\n this.covarianceBTexture = createDataTexture(\n this.backing.covarianceB,\n this.width,\n this.rows,\n THREE.FloatType,\n );\n this.shPackedTextures = this.backing.shPacked.map((data) =>\n createIntegerDataTexture(data, this.width, this.rows),\n );\n this.freeRowSpans = [{ start: 0, count: this.rows }];\n this.activeSlotByPoolIndex = new Uint32Array(texelCount);\n }\n\n /**\n * A reusable `0, 1, 2, …` ramp over the pool, used to fill runs of pool\n * indices and active-list slots without a per-element loop. Pool-owned so\n * tenants sharing a pool share the one copy.\n */\n indexTemplate(): Uint32Array {\n if (this.poolIndexTemplate) return this.poolIndexTemplate;\n const indices = new Uint32Array(this.capacity);\n for (let index = 0; index < indices.length; index++) indices[index] = index;\n this.poolIndexTemplate = indices;\n return indices;\n }\n\n /** Splats the pool holds, always a whole number of rows. */\n get capacity(): number {\n return this.rows * this.width;\n }\n\n /** Whether `texture` belongs to this pool (and so outlives any one tenant). */\n isPoolTexture(texture: THREE.Texture): boolean {\n return (\n texture === this.centersTexture ||\n texture === this.colorsTexture ||\n texture === this.covarianceATexture ||\n texture === this.covarianceBTexture ||\n this.shPackedTextures.includes(texture as THREE.DataTexture)\n );\n }\n\n /** The four core textures, in the order the upload path indexes them. */\n get coreTextures(): readonly THREE.DataTexture[] {\n return [\n this.centersTexture,\n this.colorsTexture,\n this.covarianceATexture,\n this.covarianceBTexture,\n ];\n }\n\n /** Rows still free, whether or not they are contiguous. */\n get freeRows(): number {\n let rows = 0;\n for (const span of this.freeRowSpans) rows += span.count;\n return rows;\n }\n\n /** Takes `rowCount` contiguous rows; throws when none are left (compact cue). */\n allocateRows(rowCount: number): number {\n return allocateRowSpan(this.freeRowSpans, rowCount, this.rows);\n }\n\n /** Returns rows to the free list, coalescing neighbours. */\n releaseRows(start: number, count: number): void {\n this.freeRowSpans = releaseRowSpan(this.freeRowSpans, start, count);\n }\n\n /** Resets the free list to one span covering the whole pool. */\n resetFreeRows(fromRow = 0): void {\n this.freeRowSpans = fromRow < this.rows ? [{ start: fromRow, count: this.rows - fromRow }] : [];\n }\n\n /**\n * Adds a mesh that draws from this pool. Registration exists for\n * {@link compact}: packing the pool moves rows that belong to *every*\n * tenant, so each has to be told where its ranges went.\n */\n register(tenant: SplatPoolTenant): void {\n this.tenants.add(tenant);\n }\n\n /**\n * Drops a tenant. Release its ranges first: an unregistered tenant is\n * invisible to {@link compact}, which would then treat rows it still holds\n * as free and hand them to someone else. `compact` throws rather than let\n * that happen silently.\n */\n unregister(tenant: SplatPoolTenant): void {\n this.tenants.delete(tenant);\n }\n\n /** Number of meshes drawing from this pool. */\n get tenantCount(): number {\n return this.tenants.size;\n }\n\n /**\n * Packs every tenant's ranges toward row 0, removing the gaps that add /\n * remove churn leaves behind and restoring a single contiguous free span.\n * Callers reach this when {@link allocateRows} throws despite enough total\n * free rows - row-alignment fragmentation.\n *\n * The CPU backing arrays are authoritative, so nothing is re-fetched; moved\n * rows are re-uploaded on the tenant's next update.\n *\n * Note this is a whole-pool stall: with several tenants, one mesh's\n * fragmentation relocates the others' rows too, and every tenant rebuilds\n * its draw state. (Spark sidesteps the equivalent by allocating fixed-size,\n * interchangeable pages that never need packing; vlam's ranges are variable\n * runs, so it packs instead.)\n */\n compact(): void {\n const width = this.width;\n // Move in ascending row order so each target row is <= its current row and\n // no not-yet-moved range is ever overwritten. Ordering is global across\n // tenants, because they interleave in one address space.\n const placements: { tenant: SplatPoolTenant; range: SplatPoolRange }[] = [];\n let heldRows = 0;\n for (const tenant of this.tenants) {\n for (const range of tenant.poolRanges()) {\n placements.push({ tenant, range });\n heldRows += range.rowCount;\n }\n }\n // Every row is either free or held by a registered tenant. A shortfall\n // means rows are allocated to something the pool cannot see - an\n // unregistered tenant, or a range the tenant forgot to report - and\n // packing would hand those rows out a second time.\n if (heldRows + this.freeRows !== this.rows) {\n throw new Error(\n `SplatPool compaction found ${this.rows - heldRows - this.freeRows} unaccounted row(s): ` +\n `${heldRows} held by ${this.tenants.size} tenant(s), ${this.freeRows} free, ${this.rows} total. ` +\n `Release a tenant's ranges before unregistering it.`,\n );\n }\n placements.sort((a, b) => a.range.startRow - b.range.startRow);\n\n let targetRow = 0;\n for (const { tenant, range } of placements) {\n if (range.startRow !== targetRow) {\n const from = range.startRow * width * 4;\n const to = targetRow * width * 4;\n const length = range.rowCount * width * 4;\n this.backing.centers.copyWithin(to, from, from + length);\n this.backing.colors.copyWithin(to, from, from + length);\n this.backing.covarianceA.copyWithin(to, from, from + length);\n this.backing.covarianceB.copyWithin(to, from, from + length);\n // Packed SH shares the pool's row geometry and its RGBA texel stride,\n // so it relocates on exactly the same arithmetic.\n for (const group of this.backing.shPacked) group.copyWithin(to, from, from + length);\n // The tenant moves whatever else is keyed by pool row (channels) and\n // adopts the new start.\n tenant.relocatePoolRange(range, targetRow);\n }\n targetRow += range.rowCount;\n }\n this.resetFreeRows(targetRow);\n for (const tenant of this.tenants) tenant.onPoolCompacted();\n }\n\n dispose(): void {\n for (const texture of this.coreTextures) texture.dispose();\n for (const texture of this.shPackedTextures) texture.dispose();\n this.tenants.clear();\n }\n}\n\nexport function createDataTexture(\n data: Float32Array | Uint8Array | Uint16Array,\n width: number,\n height: number,\n type: THREE.TextureDataType,\n): THREE.DataTexture {\n const texture = new THREE.DataTexture(data, width, height, THREE.RGBAFormat, type);\n texture.needsUpdate = true;\n return texture;\n}\n\n/**\n * An RGBA32UI data texture: four raw `uint32` per texel, delivered to the\n * shader as a `uvec4` with no filtering or normalization. Packed SH words go\n * through this rather than a float texture so they survive bit-exact.\n */\nexport function createIntegerDataTexture(\n data: Uint32Array,\n width: number,\n height: number,\n): THREE.DataTexture {\n const texture = new THREE.DataTexture(\n data,\n width,\n height,\n THREE.RGBAIntegerFormat,\n THREE.UnsignedIntType,\n );\n // Integer textures cannot be filtered; nearest is the only legal choice and\n // is what a per-texel lookup wants anyway. The backend derives the concrete\n // format (`rgba32uint` on WebGPU, `RGBA32UI` on WebGL2) from format + type,\n // so `internalFormat` is deliberately left alone.\n texture.magFilter = THREE.NearestFilter;\n texture.minFilter = THREE.NearestFilter;\n texture.generateMipmaps = false;\n texture.needsUpdate = true;\n return texture;\n}\n\n/**\n * The renderer backend's maximum 2D texture dimension, or 0 when it can't be\n * read (in which case callers skip any limit check rather than falsely reject).\n * WebGPU exposes `device.limits.maxTextureDimension2D`; WebGL2 exposes\n * `gl.MAX_TEXTURE_SIZE`.\n */\nexport function deviceMaxTextureSize(renderer: THREE.WebGPURenderer): number {\n const backend = renderer.backend as\n | {\n device?: { limits?: { maxTextureDimension2D?: number } };\n gl?: WebGL2RenderingContext;\n }\n | undefined;\n const wgpu = backend?.device?.limits?.maxTextureDimension2D;\n if (typeof wgpu === 'number' && wgpu > 0) return wgpu;\n const gl = backend?.gl;\n if (gl) {\n const max = gl.getParameter(gl.MAX_TEXTURE_SIZE) as unknown;\n if (typeof max === 'number' && max > 0) return max;\n }\n return 0;\n}\n\n/**\n * Throws when a pool's data textures would be taller than the device allows.\n *\n * `maxTextureSize <= 0` means \"limit unknown\" and skips the check rather than\n * risk a false rejection; see {@link deviceMaxTextureSize}.\n *\n * @param rows - Rows the pool needs.\n * @param maxTextureSize - The device's `maxTextureDimension2D`.\n * @param width - Texels per row, for the splat count in the message.\n */\nexport function assertPoolRowsFitDevice(\n rows: number,\n maxTextureSize: number,\n width: number = SPLAT_DATA_TEXTURE_WIDTH,\n): void {\n if (!(maxTextureSize > 0) || rows <= maxTextureSize) return;\n throw new Error(\n `SplatPool: this scene needs a ${width}×${rows} data texture, but this device ` +\n `caps texture dimensions at ${maxTextureSize} (about ` +\n `${(maxTextureSize * width).toLocaleString('en-US')} pool splats). Lower the ` +\n `splat budget, or raise maxTextureDimension2D in the renderer's requiredLimits ` +\n `if the adapter advertises more.`,\n );\n}\n\n/**\n * Three's WebGPU backend submits one `queue.writeBuffer` per update range.\n * Merge touching mutations here so a region-atomic commit does not turn a\n * handful of contiguous backfills into several main-thread submissions.\n */\nexport function addMergedUpdateRange(\n attribute: THREE.BufferAttribute,\n start: number,\n count: number,\n): void {\n let mergedStart = start;\n let mergedEnd = start + count;\n const disjoint: { start: number; count: number }[] = [];\n for (const range of attribute.updateRanges) {\n const rangeEnd = range.start + range.count;\n if (rangeEnd < mergedStart || range.start > mergedEnd) {\n disjoint.push(range);\n continue;\n }\n mergedStart = Math.min(mergedStart, range.start);\n mergedEnd = Math.max(mergedEnd, rangeEnd);\n }\n disjoint.push({ start: mergedStart, count: mergedEnd - mergedStart });\n disjoint.sort((a, b) => a.start - b.start);\n attribute.clearUpdateRanges();\n for (const range of disjoint) attribute.addUpdateRange(range.start, range.count);\n}\n","/**\n * Core projected-2D depth of field (post-EWA isotropic CoC).\n *\n * Spark-style pinhole CoC:\n * focusBlur = |depth − focus| / depth\n * apertureRadius = focalPx · tan(0.5 · apertureAngle)\n * cocRadius = focusBlur · apertureRadius\n * blurVariance = clamp(cocRadius², …, maxRadius²)\n *\n * `/ depth` is Spark's exact falloff (`splatVertex.glsl`), restored for visual\n * parity: scenes authored against Spark's foreground defocus rendered flat here\n * under the previous `/ max(depth, focus)`, which capped focusBlur at 1 and so\n * limited the CoC to a single apertureRadius - sub-pixel at the aperture sizes\n * ModifyCamera produces. Behind the focus plane the two forms are identical\n * (`max(depth, focus) === depth`), so only the near side changes.\n *\n * Note `/ depth` is unbounded as depth → 0. The fill-rate guard is the same one\n * Spark uses: {@link MAX_DOF_RADIUS_PX} clamps the CoC. Do not \"fix\" the near\n * side by dividing by focus instead - that pushes every *background* splat to\n * the cap when zooming out with a near focus plane (huge quads → fill-rate\n * death), which is what the bounded form was originally guarding against.\n *\n * Voluma's ModifyCamera `apertureSize` maps to an angle through the *live*\n * focus distance - `apertureAngle = 2·atan(0.5·size / focus)` - matching the\n * host helper Voluma ships for Spark. A fixed reference distance was tried\n * instead to stop near focus planes from\n * ballooning every splat, but it decouples aperture from focus and so flattens\n * the intended effect: racking focus toward the camera is exactly how authors\n * get the whole scene to go soft. Keep the mapping focus-relative; the CoC cap\n * is what bounds fill-rate.\n *\n * Unlike the M13 `depthOfFieldPreset` modifier (3D scale), this path adds\n * `coc²·I` to the projected 2D covariance with `√(det)` opacity fade.\n */\n\n/**\n * Soft CoC radius cap in pixels (Spark clamps with `maxPixelRadius`).\n * Kept modest for interactive fill-rate; raise only if cinematic stills need it.\n */\nexport const MAX_DOF_RADIUS_PX = 24;\n\n/** Cap on CoC variance (px²) added to the projected 2D covariance diagonal. */\nexport const MAX_DOF_VARIANCE = MAX_DOF_RADIUS_PX * MAX_DOF_RADIUS_PX;\n\n/** Live camera depth-of-field settings for {@link SplatMesh.setDepthOfField}. */\nexport type DepthOfFieldSettings = {\n /** View-space distance to the focal plane (world units). Must stay positive. */\n focusDistance: number;\n /** Aperture size in the same units as Voluma `DofSettings.apertureSize`. `0` = off. */\n aperture: number;\n};\n\n/**\n * Maps Voluma aperture size → full aperture angle (radians) through the focus\n * distance, identical to the host helper Voluma ships for Spark:\n * `2·atan(0.5·size / focus)`. Pulling focus toward the camera widens the angle,\n * which is what makes a near focus plane soften the whole scene.\n */\nexport function apertureAngleFromSize(aperture: number, focusDistance: number): number {\n const size = Math.max(0, aperture);\n if (size <= 0) return 0;\n const focus = Math.max(1e-4, focusDistance);\n return 2 * Math.atan((0.5 * size) / focus);\n}\n\n/**\n * Circle-of-confusion variance in px² for a splat at view-space depth.\n * Returns `0` when aperture is disabled. Mirrors Spark's vertex DoF branch.\n */\nexport function computeDofCocVariancePx2(options: {\n depth: number;\n focusDistance: number;\n aperture: number;\n focalPx: number;\n maxVariance?: number;\n}): number {\n const depth = Math.max(1e-4, options.depth);\n const focus = Math.max(1e-4, options.focusDistance);\n const apertureAngle = apertureAngleFromSize(options.aperture, focus);\n if (apertureAngle <= 0 || !(options.focalPx > 0)) return 0;\n // Spark's falloff. Unbounded as depth → 0; the px cap below is the guard.\n const focusBlur = Math.abs(depth - focus) / depth;\n const apertureRadius = options.focalPx * Math.tan(0.5 * apertureAngle);\n const cocRadiusPx = focusBlur * apertureRadius;\n const variance = cocRadiusPx * cocRadiusPx;\n const maxVariance = options.maxVariance ?? MAX_DOF_VARIANCE;\n return Math.min(variance, maxVariance);\n}\n\n/**\n * Opacity fade that conserves Gaussian mass after isotropic screen-space\n * dilation: `√(detRaw / detBlur)`.\n */\nexport function computeDofOpacityFade(detRaw: number, detBlur: number): number {\n return Math.sqrt(Math.max(0, detRaw) / Math.max(1e-9, detBlur));\n}\n\n/** Clamps host-supplied DoF settings before writing live uniforms. */\nexport function clampDepthOfFieldSettings(\n settings: Partial<DepthOfFieldSettings>,\n previous: DepthOfFieldSettings = { focusDistance: 10, aperture: 0 },\n): DepthOfFieldSettings {\n const focusDistance =\n typeof settings.focusDistance === 'number' && Number.isFinite(settings.focusDistance)\n ? Math.max(1e-4, settings.focusDistance)\n : previous.focusDistance;\n const aperture =\n typeof settings.aperture === 'number' && Number.isFinite(settings.aperture)\n ? Math.max(0, settings.aperture)\n : previous.aperture;\n return { focusDistance, aperture };\n}\n","/**\n * The TSL material graph behind `SplatMesh`.\n *\n * One graph serves both of the mesh's materials: `display` writes premultiplied\n * color, `pick` encodes linear view depth. They must stay one builder - a pick\n * that disagreed with the display about projection, covariance or visibility\n * would return hits for splats the viewer cannot see.\n *\n * Everything here runs once, at material build time, and reads only what the\n * mesh hands it, so these are free functions rather than methods. Note the two\n * inputs that must be passed live rather than copied: the uniform *node\n * instances* (display and pick share them, so a frame updates both at once) and\n * the channels map (a rebuild after `defineChannel` has to see the new entry).\n *\n * Internal. Nothing here is exported from `index.ts`.\n */\nimport * as THREE from 'three/webgpu';\nimport {\n Discard,\n Fn,\n If,\n attribute,\n colorSpaceToWorking,\n float,\n int,\n ivec2,\n mat3,\n mix,\n modelViewMatrix,\n cameraProjectionMatrix,\n positionGeometry,\n screenUV,\n texture as tslTexture,\n textureLoad,\n uint,\n uniform,\n uniformArray,\n varying,\n vec2,\n vec3,\n vec4,\n} from 'three/tsl';\nimport type { SplatModifier } from './splat-modifier';\nimport { foldSplatModifierStack } from './splat-modifier-stack';\nimport type { SplatPerformanceProfile } from './splat-mesh';\nimport { SPLAT_DATA_TEXTURE_WIDTH } from './splat-mesh-pool';\nimport { MAX_DOF_VARIANCE } from './depth-of-field';\n\n/**\n * Largest on-screen radius a splat quad may reach, in pixels. A splat crossing\n * the near plane projects to an unbounded size; clamping keeps one degenerate\n * splat from covering the screen and stalling the rasterizer.\n */\nconst MAX_SPLAT_RADIUS_PX = 1024;\n\n/**\n * Stand-in for an infinite `parent_size` (a root LOD node, or a splat whose\n * parent has not decoded yet) in the frontier cut: any finite `limit·distance`\n * is below it, so the node always passes the \"parent too big\" half of the test.\n * A large finite value avoids `Infinity` arithmetic in the shader.\n */\nconst FRONTIER_ROOT_SIZE = 1e30;\n\n/** Default variance scale for isotropic point mode: (0.35 × min-axis)². */\nexport const DEFAULT_ISOTROPIC_VARIANCE_SCALE = 0.35 * 0.35;\n/** Default isotropic screen-space sigma radius (px); only used when a modifier opts in. */\nexport const DEFAULT_ISOTROPIC_SCREEN_RADIUS_PX = 1.0;\n\n/**\n * Blends `Σ` toward σ²·I where σ² = λ_min(Σ) · varianceScale. Spark point\n * mode sets every scale axis to min(scale)·0.35; since λ_min(Σ) = min(scale)²,\n * that is exactly σ² = (min·0.35)².\n *\n * λ_min is estimated without `Σ⁻¹` (near-singular on flat Gaussians, which\n * made n·Σ·n blow up into large blobs): take the min of the three axis\n * Rayleigh quotients and the Rayleigh along the longest row-cross of Σ (a\n * stable thin-axis hint when Σ is rank-deficient). The `normal` argument is\n * kept for call-site compatibility and ignored.\n */\nexport function applyIsotropicCovarianceOverride(\n covariance: THREE.Node<'mat3'>,\n _normal: THREE.Node<'vec3'>,\n mixFactor: THREE.Node<'float'>,\n varianceScale: THREE.Node<'float'>,\n): THREE.Node<'mat3'> {\n const e0 = vec3(1, 0, 0);\n const e1 = vec3(0, 1, 0);\n const e2 = vec3(0, 0, 1);\n const row0 = covariance.mul(e0);\n const row1 = covariance.mul(e1);\n const row2 = covariance.mul(e2);\n const r0 = asNode<'float'>(row0.x);\n const r1 = asNode<'float'>(row1.y);\n const r2 = asNode<'float'>(row2.z);\n const crossA = row0.cross(row1);\n const crossB = row1.cross(row2);\n const crossC = row2.cross(row0);\n const lenA = crossA.length();\n const lenB = crossB.length();\n const lenC = crossC.length();\n const useA = lenA.greaterThanEqual(lenB).and(lenA.greaterThanEqual(lenC));\n const useB = lenB.greaterThan(lenA).and(lenB.greaterThanEqual(lenC));\n const axis = useA.select(crossA, useB.select(crossB, crossC));\n const thin = axis.div(axis.length().max(1e-12));\n const rThin = asNode<'float'>(thin.dot(covariance.mul(thin)));\n const minVar = asNode<'float'>(r0.min(r1).min(r2).min(rThin).max(1e-12));\n const isoVar = minVar.mul(varianceScale);\n const isoCov = mat3(vec3(isoVar, 0, 0), vec3(0, isoVar, 0), vec3(0, 0, isoVar));\n return asNode<'mat3'>(covariance.mul(float(1).sub(mixFactor)).add(isoCov.mul(mixFactor)));\n}\n\n/** Sets both screen-space eigenvalues to min(λ1, λ2) for a circular footprint. */\nexport function equalizeProjectedEigenvalues(\n lambda1: THREE.Node<'float'>,\n lambda2: THREE.Node<'float'>,\n mixFactor: THREE.Node<'float'>,\n): { lambda1: THREE.Node<'float'>; lambda2: THREE.Node<'float'> } {\n const circle = lambda1.min(lambda2);\n return {\n lambda1: asNode<'float'>(mix(lambda1, circle, mixFactor)),\n lambda2: asNode<'float'>(mix(lambda2, circle, mixFactor)),\n };\n}\n\n/**\n * Caps isotropic λ to a screen-space sigma radius while blending by mix.\n * `screenRadiusPx ≤ 0` is a no-op (opt-in only; 0 must not shrink λ toward zero).\n */\nexport function capProjectedEigenvaluesToScreenRadius(\n lambda1: THREE.Node<'float'>,\n lambda2: THREE.Node<'float'>,\n mixFactor: THREE.Node<'float'>,\n screenRadiusPx: THREE.Node<'float'>,\n maxStdDev: THREE.Node<'float'>,\n): { lambda1: THREE.Node<'float'>; lambda2: THREE.Node<'float'> } {\n const targetVariance = screenRadiusPx.div(maxStdDev).pow(2);\n const circle = lambda1.min(lambda2).min(targetVariance);\n const capped1 = asNode<'float'>(mix(lambda1, circle, mixFactor));\n const capped2 = asNode<'float'>(mix(lambda2, circle, mixFactor));\n const enabled = screenRadiusPx.greaterThan(0);\n return {\n lambda1: asNode<'float'>(enabled.select(capped1, lambda1)),\n lambda2: asNode<'float'>(enabled.select(capped2, lambda2)),\n };\n}\n\n/** The pool data textures a graph samples per splat. */\nexport interface SplatMaterialTextures {\n centersTexture: THREE.DataTexture;\n colorsTexture: THREE.DataTexture;\n covarianceATexture: THREE.DataTexture;\n covarianceBTexture: THREE.DataTexture;\n}\n\n/** Narrow a TSL expression to the typed {@link THREE.Node} our hook contract\n * expects. Identity at runtime - satisfies TypeScript only. */\nexport function asNode<T extends string>(node: unknown): THREE.Node<T> {\n return node as THREE.Node<T>;\n}\n\n/** A `vec3` uniform, named so its type can be referred to in field decls. */\nexport function vec3Uniform() {\n return uniform(new THREE.Vector3());\n}\nexport type Vec3Uniform = ReturnType<typeof vec3Uniform>;\n\nfunction vec2Uniform() {\n return uniform(new THREE.Vector2());\n}\n/** A `vec2` uniform (focal, viewport). */\nexport type Vec2Uniform = ReturnType<typeof vec2Uniform>;\n\nfunction floatUniform() {\n return uniform(0);\n}\n/** A scalar uniform (pick thresholds and planes). */\nexport type FloatUniform = ReturnType<typeof floatUniform>;\n\n/**\n * How the material reads a splat's higher-order SH coefficients. The two\n * sources differ only in where a coefficient comes from - the band\n * accumulation and view-direction math are shared:\n *\n * - `palette`: SOG/`.lcc2` shN. Coefficients live in a per-file codebook and\n * each splat stores a label; only a static mesh can use it, because two\n * files' palettes cannot be merged into one pool.\n * - `packed`: LCC `Quality`, `.rad`, etc. Each splat carries its own coefficients as packed\n * words in pool-shaped textures, so appended ranges keep their SH.\n */\nexport type SplatShInputs =\n | { mode: 'palette'; bands: number; paletteTexture: THREE.DataTexture }\n | {\n mode: 'packed';\n bands: 1 | 2 | 3;\n textures: readonly THREE.DataTexture[];\n range: { min: Vec3Uniform; max: Vec3Uniform };\n };\n\n/**\n * Per-source placement inputs for a unified pool ({@link MergedSplatMesh}): the\n * splat's source id plus the shared array of source matrices. Drives two things\n * at once - the splat's mesh-local position (`M · poolCenter`, applied before\n * the modifier stack) and the frame view-dependent SH is evaluated in.\n */\nexport interface SplatSourcePlacement {\n /** Pool-aligned source id channel. */\n sourceIdTexture: THREE.DataTexture;\n /** Four column vectors per source world matrix. */\n columns: ReturnType<typeof uniformArray>;\n}\n\n/**\n * The `mat4 · (poolCenter, 1)` placed center for source id `s`, plus the linear\n * 3×3 part as a `mat3`, read from the shared column array (three.js\n * `Matrix4.elements` are column-major, so a column maps to\n * `elements[k*4 .. k*4+3]`).\n *\n * Lives here rather than in `source-transform.ts` because the material graph and\n * the sorter must place a splat identically. Keeping it on the material side\n * also avoids a runtime dependency from `source-transform.ts` back into the\n * material graph.\n */\nexport function sourceWorldTransform(\n columns: ReturnType<typeof uniformArray>,\n sourceId: THREE.Node<'int'>,\n localCenter: THREE.Node<'vec3'>,\n): { worldCenter: THREE.Node<'vec3'>; linear: THREE.Node<'mat3'> } {\n const base = sourceId.mul(int(4));\n const c0 = asNode<'vec4'>(columns.element(base));\n const c1 = asNode<'vec4'>(columns.element(base.add(int(1))));\n const c2 = asNode<'vec4'>(columns.element(base.add(int(2))));\n const c3 = asNode<'vec4'>(columns.element(base.add(int(3))));\n const worldCenter = asNode<'vec3'>(\n c0.xyz\n .mul(localCenter.x)\n .add(c1.xyz.mul(localCenter.y))\n .add(c2.xyz.mul(localCenter.z))\n .add(c3.xyz),\n );\n const linear = asNode<'mat3'>(mat3(c0.xyz, c1.xyz, c2.xyz));\n return { worldCenter, linear };\n}\n\n/** Coefficients per channel for a band count (0 → none, 3 → 3rd order). */\nexport function shCoefficientCount(bands: number): number {\n return [0, 3, 8, 15][bands] ?? 0;\n}\n\n/**\n * Builds the per-coefficient accessor for whichever SH source the mesh has -\n * the only part of the SH graph that differs between them.\n *\n * `palette` indirects through the splat's codebook label; `packed` reads the\n * splat's own words straight out of the pool-shaped integer textures and\n * unpacks them (R: bits 0-10, G: 11-20, B: 21-31, each a unit fraction of its\n * field, dequantized across the scene's range). The packed texel loads are\n * hoisted into variables so all 15 coefficients cost at most four fetches.\n */\nexport function shCoefficientReader(\n sh: SplatShInputs,\n textures: { covarianceBTexture: THREE.DataTexture },\n splatTexel: THREE.Node<'ivec2'>,\n): (c: number) => THREE.Node<'vec3'> {\n if (sh.mode === 'palette') {\n const label = textureLoad(textures.covarianceBTexture, splatTexel).z.toInt();\n const column = label.mod(int(64)).mul(int(shCoefficientCount(sh.bands)));\n const row = label.div(int(64));\n return (c) => textureLoad(sh.paletteTexture, ivec2(column.add(int(c)), row)).xyz;\n }\n\n // An RGBA32UI fetch is a uvec4; TSL's types describe textureLoad as vec4,\n // so the element type is asserted rather than inferred. `toVar` hoists each\n // fetch, so all 15 coefficients cost at most four texture reads.\n const groups = sh.textures.map((texture) =>\n asNode<'uvec4'>(textureLoad(texture, splatTexel).toVar()),\n );\n const span = sh.range.max.sub(sh.range.min);\n return (c) => {\n const group = groups[c >> 2] as THREE.Node<'uvec4'>;\n const word = asNode<'uint'>([group.x, group.y, group.z, group.w][c & 3]);\n const channels = vec3(\n word.bitAnd(uint(0x7ff)).toFloat().div(2047),\n word.shiftRight(uint(11)).bitAnd(uint(0x3ff)).toFloat().div(1023),\n word.shiftRight(uint(21)).bitAnd(uint(0x7ff)).toFloat().div(2047),\n );\n return sh.range.min.add(span.mul(channels));\n };\n}\n\n/** Spherical harmonics basis constants for bands 1–3 (3DGS convention). */\nconst SH_C1 = 0.4886025119029199;\nconst SH_C2 = [\n 1.0925484305920792, -1.0925484305920792, 0.31539156525252005, -1.0925484305920792,\n 0.5462742152960396,\n] as const;\nconst SH_C3 = [\n -0.5900435899266435, 2.890611442640554, -0.4570457994644658, 0.3731763325901154,\n -0.4570457994644658, 1.445305721320277, -0.5900435899266435,\n] as const;\n\n/** Evaluates a source's higher-order SH contribution for a local view direction. */\nexport function evaluateSplatSh(\n sh: SplatShInputs,\n textures: { covarianceBTexture: THREE.DataTexture },\n splatTexel: THREE.Node<'ivec2'>,\n direction: THREE.Node<'vec3'>,\n): THREE.Node<'vec3'> {\n const x = direction.x;\n const y = direction.y;\n const z = direction.z;\n const coefficient = shCoefficientReader(sh, textures, splatTexel);\n const band1 = coefficient(0)\n .mul(y.mul(-SH_C1))\n .add(coefficient(1).mul(z.mul(SH_C1)))\n .add(coefficient(2).mul(x.mul(-SH_C1)));\n if (sh.bands === 1) return band1;\n\n const xx = x.mul(x);\n const yy = y.mul(y);\n const zz = z.mul(z);\n const band2 = band1\n .add(coefficient(3).mul(x.mul(y).mul(SH_C2[0])))\n .add(coefficient(4).mul(y.mul(z).mul(SH_C2[1])))\n .add(coefficient(5).mul(zz.mul(2.0).sub(xx).sub(yy).mul(SH_C2[2])))\n .add(coefficient(6).mul(x.mul(z).mul(SH_C2[3])))\n .add(coefficient(7).mul(xx.sub(yy).mul(SH_C2[4])));\n if (sh.bands === 2) return band2;\n\n return band2\n .add(coefficient(8).mul(y.mul(xx.mul(3.0).sub(yy)).mul(SH_C3[0])))\n .add(coefficient(9).mul(x.mul(y).mul(z).mul(SH_C3[1])))\n .add(coefficient(10).mul(y.mul(zz.mul(4.0).sub(xx).sub(yy)).mul(SH_C3[2])))\n .add(coefficient(11).mul(z.mul(zz.mul(2.0).sub(xx.mul(3.0)).sub(yy.mul(3.0))).mul(SH_C3[3])))\n .add(coefficient(12).mul(x.mul(zz.mul(4.0).sub(xx).sub(yy)).mul(SH_C3[4])))\n .add(coefficient(13).mul(z.mul(xx.sub(yy)).mul(SH_C3[5])))\n .add(coefficient(14).mul(x.mul(xx.sub(yy.mul(3.0))).mul(SH_C3[6])));\n}\n\n/** Everything the material graph reads, gathered by the mesh. */\nexport interface SplatMaterialBuildInputs {\n textures: SplatMaterialTextures;\n sh: SplatShInputs | null;\n /**\n * Per-source placement for a unified pool, or `null` for a plain mesh. When\n * present, a splat's mesh-local center is `M · poolCenter` and SH is\n * evaluated in the source's own frame.\n */\n sourcePlacement: SplatSourcePlacement | null;\n /**\n * The mesh's uniform *node instances*, not their values: display and pick\n * share them, so a per-frame write reaches both graphs.\n */\n uniforms: {\n focal: Vec2Uniform;\n viewport: Vec2Uniform;\n localCameraPosition: Vec3Uniform;\n /** Frontier-cut limit on `own_size / distance` (`foveationMode: 'frontier'`). */\n pixelScaleLimit: FloatUniform;\n /**\n * Core projected-2D DoF focus plane (world/view units). Live uniform -\n * racking focus does not rebuild the material. See `depth-of-field.ts`.\n */\n dofFocusDistance: FloatUniform;\n /** Core DoF aperture; `0` disables. Live uniform. */\n dofAperture: FloatUniform;\n /**\n * Screen-radius band bounds in px, live so a foveated mesh can follow its\n * own LOD cut. Whether the band exists at all is still decided at build\n * time from `settings.minScreenRadiusPx` / `maxScreenRadiusPx`; only the\n * bounds move.\n *\n * They have to move: the band spans one LOD level, so a mesh that refines\n * its cut to spend spare budget would have exactly that new detail culled\n * for being smaller than a bound chosen for the coarser cut.\n */\n screenBandMin: FloatUniform;\n screenBandMax: FloatUniform;\n /**\n * Proxy-mesh relight map (RGB = lit, A = coverage). Live texture binding -\n * swap via the TextureNode / rebuild when the mesh updates `setRelighting`.\n * Display fragment only; pick ignores this.\n */\n relightMap: THREE.Texture;\n /** `0` = baked color only; `1` = full modulate. Live uniform. */\n relightBlend: FloatUniform;\n /** Scales lit RGB (PlayCanvas default ~2 for 0.5 gray proxy). Live. */\n relightBrightness: FloatUniform;\n /** Multiplier where coverage alpha is 0 (sky). Live. */\n relightBackground: FloatUniform;\n /**\n * Coverage soft edge in screen pixels. Live; `0` = hard mask.\n * See {@link RelightingSettings.softness}.\n */\n relightSoftness: FloatUniform;\n };\n /** Pick-only uniforms. Required when building in `'pick'` mode. */\n pick: {\n alphaThreshold: FloatUniform;\n near: FloatUniform;\n far: FloatUniform;\n } | null;\n /** Baked at build time; changing any of these needs a rebuild. */\n settings: {\n maxStdDev: number;\n /** Screen-space floor on each quad axis, px (0/undefined = off). */\n minSplatSizePx?: number;\n antialias: boolean;\n /** Classic LCC uses XGRIDS' smaller, always-compensated low-pass. */\n projectedFilterProfile: 'default' | 'lcc';\n srgbOutput: boolean;\n performanceProfile: SplatPerformanceProfile;\n /** Cull splats whose projected radius exceeds this many px (0 = off). */\n maxScreenRadiusPx?: number;\n /** Foveation band lower bound: cull splats *below* this many px (0 = off). */\n minScreenRadiusPx?: number;\n /**\n * `.rad` foveation cut. `'band'` (default/undefined) uses the screen-radius\n * band above; `'frontier'` uses Spark's exact per-splat tree cut driven by\n * `own_size` (from the covariance) and the `parent_size` packed in\n * `covarianceB.w`. See `docs/formats/rad-notes.md` M14.6.\n */\n foveationMode?: 'band' | 'frontier' | 'page-table';\n /**\n * Cap on a rendered splat's major/minor axis ratio (0/undefined = off). Tames\n * far-field needle/spike artifacts from very anisotropic Gaussians and\n * expansion-enlarged coarse LOD nodes. Baked into the material graph.\n */\n maxAspect?: number;\n /**\n * Spark's LOD alpha encoding (`.rad`): the stored opacity byte is `alpha/2`,\n * so the shader multiplies by 2 to recover `alpha ∈ [0,2]`; `alpha > 1` marks\n * a merged node whose σ-cutoff grows (`+0.7·(remap−1)`) and whose falloff\n * becomes a super-Gaussian, covering its subtree without scaling covariance.\n */\n lodAlpha?: boolean;\n };\n /**\n * The mesh's live channel map, not a copy: `defineChannel` adds to it and\n * then rebuilds, and the graph must resolve names against the new entry.\n */\n channels: ReadonlyMap<string, { texture: THREE.DataTexture }>;\n modifiers: readonly SplatModifier[];\n}\n\n/**\n * Shared TSL graph for display and pick materials: projection, covariance,\n * modifiers, visibility, and Gaussian falloff. Display writes premultiplied\n * color; pick encodes linear view depth into RGB with alpha as the hit flag.\n *\n * @param inputs.pick - Required when `mode` is `'pick'`.\n */\nexport function applySplatMaterialGraph(\n material: THREE.NodeMaterial,\n mode: 'display' | 'pick',\n inputs: SplatMaterialBuildInputs,\n): void {\n const { textures, sh, uniforms, settings, pick } = inputs;\n const textureWidth = int(SPLAT_DATA_TEXTURE_WIDTH);\n const maxRadius = float(MAX_SPLAT_RADIUS_PX);\n // The quad spans ±maxStdDev σ, so |quadPosition| = 1 sits at maxStdDev σ\n // and the Gaussian exponent -½·(maxStdDev·|q|)² folds to this constant.\n // Vertex extent and this exponent must agree or the falloff rescales.\n const gaussianExponent = -0.5 * settings.maxStdDev * settings.maxStdDev;\n\n // Per-instance index -> texel coordinate in the data textures.\n const splatIndex = attribute<'float'>('splatIndex', 'float').toInt();\n const splatTexel = ivec2(splatIndex.mod(textureWidth), splatIndex.div(textureWidth));\n\n // Varyings (computed in the vertex stage, constant across each quad).\n // With SH data, the view-dependent contribution - the higher SH bands\n // evaluated with the local view direction (Kerbl et al. convention,\n // coefficients read from the SOG palette) - is added to the base color.\n const baseColor = textureLoad(textures.colorsTexture, splatTexel);\n /** The splat's center as stored in the pool - its own source's data frame. */\n const poolCenter = textureLoad(textures.centersTexture, splatTexel).xyz;\n // Per-source placement is resolved here, ahead of everything else, so the\n // rest of the graph - modifier stack included - sees the splat where it\n // visually is. In a `MergedSplatMesh` the pool frame is an internal storage\n // detail; the splat's real mesh-local position is `M · poolCenter`. Applying\n // it outside the fold (rather than as modifier #0, which is what this used to\n // be) also makes it impossible for a host modifier's `offset`/`rotation` to\n // overwrite the placement - the fold replaces those fields, it does not\n // accumulate them.\n const placement = inputs.sourcePlacement;\n const placed = placement\n ? sourceWorldTransform(\n placement.columns,\n asNode<'int'>(textureLoad(placement.sourceIdTexture, splatTexel).r.toInt()),\n asNode<'vec3'>(poolCenter),\n )\n : null;\n /** Splat center in mesh-local space: the pool texel, or its placed position. */\n const localCenter = placed ? placed.worldCenter : asNode<'vec3'>(poolCenter);\n const shSum =\n sh === null\n ? null\n : (() => {\n const direction = (() => {\n if (!placed) return localCenter.sub(uniforms.localCameraPosition).normalize();\n // SH coefficients stay in their source frame. Transforming the\n // camera ray by the inverse linear placement is equivalent to\n // rotating every l=1..3 coefficient band, without re-uploading\n // per-splat packed coefficients when a source moves.\n return placed.linear\n .inverse()\n .mul(localCenter.sub(uniforms.localCameraPosition))\n .normalize();\n })();\n const x = direction.x;\n const y = direction.y;\n const z = direction.z;\n\n /** Coefficient c of this splat, per color channel. */\n const coefficient = shCoefficientReader(sh, textures, splatTexel);\n\n const band1 = coefficient(0)\n .mul(y.mul(-SH_C1))\n .add(coefficient(1).mul(z.mul(SH_C1)))\n .add(coefficient(2).mul(x.mul(-SH_C1)));\n if (sh.bands === 1) return band1;\n\n const xx = x.mul(x);\n const yy = y.mul(y);\n const zz = z.mul(z);\n const band2 = band1\n .add(coefficient(3).mul(x.mul(y).mul(SH_C2[0])))\n .add(coefficient(4).mul(y.mul(z).mul(SH_C2[1])))\n .add(coefficient(5).mul(zz.mul(2.0).sub(xx).sub(yy).mul(SH_C2[2])))\n .add(coefficient(6).mul(x.mul(z).mul(SH_C2[3])))\n .add(coefficient(7).mul(xx.sub(yy).mul(SH_C2[4])));\n if (sh.bands === 2) return band2;\n\n return (\n band2\n .add(coefficient(8).mul(y.mul(xx.mul(3.0).sub(yy)).mul(SH_C3[0])))\n .add(coefficient(9).mul(x.mul(y).mul(z).mul(SH_C3[1])))\n .add(coefficient(10).mul(y.mul(zz.mul(4.0).sub(xx).sub(yy)).mul(SH_C3[2])))\n .add(\n coefficient(11).mul(\n z.mul(zz.mul(2.0).sub(xx.mul(3.0)).sub(yy.mul(3.0))).mul(SH_C3[3]),\n ),\n )\n .add(coefficient(12).mul(x.mul(zz.mul(4.0).sub(xx).sub(yy)).mul(SH_C3[4])))\n .add(coefficient(13).mul(z.mul(xx.sub(yy)).mul(SH_C3[5])))\n // l=3, m=3 is x(x² − 3y²) - the mirror of m=−3's y(3x² − y²)\n // above. This read x(x² − y²) until 2026-07-17, tinting the\n // band-3 lobes of every shN scene.\n .add(coefficient(14).mul(x.mul(xx.sub(yy.mul(3.0))).mul(SH_C3[6])))\n );\n })();\n const colorAfterSh =\n shSum === null ? baseColor : vec4(baseColor.rgb.add(shSum).clamp(0.0, 1.0), baseColor.a);\n /** Approximate surface normal for lighting hooks: Σ⁻¹ amplifies the\n * least-variance axis (inverse iteration, two applications), oriented\n * toward the camera. Built only when a modifier reads `ctx.normal`. */\n const makeNormal = (): THREE.Node<'vec3'> => {\n const covA = textureLoad(textures.covarianceATexture, splatTexel);\n const covB = textureLoad(textures.covarianceBTexture, splatTexel);\n const poolSigma = mat3(\n vec3(covA.x.add(1e-8), covA.y, covA.z),\n vec3(covA.y, covA.w.add(1e-8), covB.x),\n vec3(covA.z, covB.x, covB.y.add(1e-8)),\n );\n // Run the inverse iteration in the *placed* frame, so a rotated source is\n // shaded consistently with the rest of the scene. Regularizing before the\n // placement (rather than adding εI to A·Σ·Aᵀ) avoids mat3 diagonal surgery\n // in TSL and still yields A·Σ·Aᵀ + ε·A·Aᵀ, positive-definite for any\n // nonsingular A - it degrades only for a source scaled to ~0, which is\n // degenerate everywhere else too.\n //\n // Note the A⁻ᵀ·n \"normal transform\" shortcut does not apply here: that\n // identity is for a level-set gradient, and the least-variance eigenvector\n // of A·Σ·Aᵀ is not A⁻ᵀ times the least-variance eigenvector of Σ.\n const sigma = placed\n ? asNode<'mat3'>(placed.linear.mul(poolSigma).mul(placed.linear.transpose()))\n : asNode<'mat3'>(poolSigma);\n const inverse = sigma.inverse();\n const toCamera = uniforms.localCameraPosition.sub(localCenter);\n const axis = inverse.mul(inverse.mul(toCamera)).normalize();\n return asNode<'vec3'>(axis.mul(axis.dot(toCamera).sign()));\n };\n\n /** Reads a per-splat channel (M7.3). Undefined name is a build error, not\n * a silent zero. `.r` holds the value (normalized for byte channels). */\n const makeChannel = (name: string): THREE.Node<'float'> => {\n const channel = inputs.channels.get(name);\n if (!channel) {\n throw new Error(\n `SplatMesh: a modifier reads channel \"${name}\", which is not defined. ` +\n `Call defineChannel(\"${name}\") before assigning the modifier.`,\n );\n }\n return asNode<'float'>(textureLoad(channel.texture, splatTexel).r);\n };\n\n // Fold the modifier stack (empty stack ⇒ all fragments null and the\n // graph below is emitted exactly as the unhooked renderer).\n const stack = foldSplatModifierStack(inputs.modifiers, uniforms.localCameraPosition, {\n index: asNode<'int'>(splatIndex),\n localCenter,\n sourceCenter: asNode<'vec3'>(poolCenter),\n sourceToLocal: placed?.linear,\n color: asNode<'vec4'>(colorAfterSh),\n makeNormal,\n makeChannel,\n });\n\n // Source formats store display-ready sRGB colors while a regular Three.js\n // scene renders in its linear working space. Keep that conversion local to\n // the splat material so standard meshes can share the renderer without\n // forcing the entire canvas into LinearSRGBColorSpace.\n // With `srgbOutput` the stored sRGB color is emitted as-is: the renderer is\n // expected to skip output conversion, so splats alpha-composite on\n // gamma-encoded values (3DGS training / WebGL-viewer semantics).\n // Pick only needs opacity (a); keep the same color path so modifiers that\n // tint/fade alpha stay consistent with the display pass.\n const splatColor = varying(\n settings.srgbOutput\n ? asNode<'vec4'>(stack.color)\n : asNode<'vec4'>(colorSpaceToWorking(stack.color, THREE.SRGBColorSpace)),\n );\n const quadPosition = varying(positionGeometry.xy);\n const viewDepthVarying = mode === 'pick' ? varying(float(0), 'pickViewDepth') : null;\n // Opacity compensation for screen-space dilation (mip antialias and/or\n // core projected-2D DoF). Always present so DoF can fade opacity when\n // antialias is off; with both disabled the fade stays 1.\n const opacityCompensation = varying(float(1), 'vOpacityCompensation');\n // Spark LOD alpha (`.rad`): per-splat σ-cutoff and the recovered `alpha ∈ [0,2]`,\n // computed in the vertex stage and used by both the quad extent and the\n // fragment falloff. Only present with `lodAlpha`, so other formats are byte-\n // identical.\n const vAdjustedStdDev = settings.lodAlpha\n ? varying(float(settings.maxStdDev), 'vAdjustedStdDev')\n : null;\n const vAlpha2 = settings.lodAlpha ? varying(float(1), 'vAlpha2') : null;\n // Visual fade (modifier alpha / original encoded alpha). Applied after LOD\n // falloff so a marker crossfade cannot reclassify a merged node as a leaf.\n const vVisualOpacity = settings.lodAlpha ? varying(float(1), 'vVisualOpacity') : null;\n\n material.vertexNode = Fn(() => {\n const center = stack.offset === null ? localCenter : localCenter.add(stack.offset);\n const viewCenter = modelViewMatrix.mul(vec4(center, 1.0)).toVar();\n const clipCenter = cameraProjectionMatrix.mul(viewCenter).toVar();\n\n // Default: outside clip space, so culled splats emit no fragments.\n const clipPosition = vec4(0.0, 0.0, 2.0, 1.0).toVar();\n\n // Skip splats behind the camera or far outside the frustum - and\n // splats a modifier hides. Display uses a 1.2 NDC pad around the\n // center. Pick crops one source pixel onto the full NDC cube (1 px =\n // 2 NDC), so the same pad would keep only centers inside ~0.6 px of\n // the cursor. Expand by the screen-radius cap so a splat that covers\n // the clicked pixel still emits a quad.\n const frustumNdc = mode === 'pick' ? 1.2 + 2 * MAX_SPLAT_RADIUS_PX : 1.2;\n const margin = clipCenter.w.mul(frustumNdc);\n const inFrustum = clipCenter.z\n .greaterThan(margin.negate())\n .and(clipCenter.x.abs().lessThan(margin))\n .and(clipCenter.y.abs().lessThan(margin));\n const isVisible = stack.visible === null ? inFrustum : inFrustum.and(stack.visible);\n\n If(isVisible, () => {\n if (viewDepthVarying) viewDepthVarying.assign(viewCenter.z.negate());\n\n const covA = textureLoad(textures.covarianceATexture, splatTexel);\n const covB = textureLoad(textures.covarianceBTexture, splatTexel);\n const covarianceBase = mat3(\n vec3(covA.x, covA.y, covA.z),\n vec3(covA.y, covA.w, covB.x),\n vec3(covA.z, covB.x, covB.y),\n );\n // Placement first, outside the fold: Σ_placed = A·Σ·Aᵀ = (A·M)(A·M)ᵀ,\n // exact for any linear A (rotation, non-uniform scale, shear) because Σ\n // is by definition an outer product of a linear map. A maps the source's\n // data frame to mesh-local, so it is innermost; the host stack's rigid\n // rotation is authored in mesh-local and wraps it.\n const placedCovariance = placed\n ? asNode<'mat3'>(placed.linear.mul(covarianceBase).mul(placed.linear.transpose()))\n : asNode<'mat3'>(covarianceBase);\n // Rigid rotation is covariance-exact: Σ' = R·Σ·Rᵀ.\n let covariance3d: THREE.Node<'mat3'> =\n stack.rotation === null\n ? placedCovariance\n : asNode<'mat3'>(stack.rotation.mul(placedCovariance).mul(stack.rotation.transpose()));\n if (stack.isotropicCovarianceMix !== null) {\n const varianceScale =\n stack.isotropicVarianceScale ?? float(DEFAULT_ISOTROPIC_VARIANCE_SCALE);\n covariance3d = applyIsotropicCovarianceOverride(\n covariance3d,\n vec3(0, 0, 1),\n stack.isotropicCovarianceMix,\n varianceScale,\n );\n }\n\n // EWA splatting: project Σ to screen space, Σ' = J·W·Σ·Wᵀ·Jᵀ, with\n // J the Jacobian of the perspective projection. Written as dot\n // products: Σ'ₐᵦ = uₐᵀ·Σ·uᵦ where uₐ = Wᵀ·jₐ and jₐ are J's rows.\n const invZ = float(1.0).div(viewCenter.z);\n const invZ2 = invZ.mul(invZ);\n const j1 = vec3(\n uniforms.focal.x.mul(invZ),\n 0.0,\n uniforms.focal.x.negate().mul(viewCenter.x).mul(invZ2),\n );\n const j2 = vec3(\n 0.0,\n uniforms.focal.y.mul(invZ),\n uniforms.focal.y.negate().mul(viewCenter.y).mul(invZ2),\n );\n const viewRotationT = modelViewMatrix.toMat3().transpose();\n const u1 = viewRotationT.mul(j1);\n const u2 = viewRotationT.mul(j2);\n\n // 2×2 screen covariance [[a, b], [b, d]], low-pass filtered so every\n // splat covers at least about one pixel (3DGS paper). Uniform splat\n // scaling factors out of the quadratic form: Σ' = s²·Σ.\n const aQ = u1.dot(covariance3d.mul(u1));\n const dQ = u2.dot(covariance3d.mul(u2));\n const bQ = u1.dot(covariance3d.mul(u2));\n const aRaw = stack.scaleSquared === null ? aQ : aQ.mul(stack.scaleSquared);\n const dRaw = stack.scaleSquared === null ? dQ : dQ.mul(stack.scaleSquared);\n const bRaw = stack.scaleSquared === null ? bQ : bQ.mul(stack.scaleSquared);\n\n // Core projected-2D DoF (Spark splatVertex.glsl). Aperture maps through\n // the live focus plane, so pulling focus toward the camera widens the\n // angle and softens the whole scene - the authored effect.\n const depth = viewCenter.z.negate().max(1e-4);\n const focus = uniforms.dofFocusDistance.max(1e-4);\n const halfApertureAngle = uniforms.dofAperture.max(0).mul(0.5).div(focus).atan();\n // Spark's falloff (`/depth`). Unbounded near the camera; MAX_DOF_VARIANCE\n // below is the fill-rate guard, exactly as Spark's maxPixelRadius is.\n const focusBlur = depth.sub(focus).abs().div(depth);\n const apertureRadius = uniforms.focal.x.mul(halfApertureAngle.tan());\n const cocRadiusPx = focusBlur.mul(apertureRadius);\n const cocVar = cocRadiusPx.mul(cocRadiusPx).min(float(MAX_DOF_VARIANCE));\n\n // XGRIDS classic LCC uses a 0.1 px² low-pass with integral-preserving\n // opacity compensation. Other formats retain the 3DGS 0.3 px² path.\n const lowPassVariance = settings.projectedFilterProfile === 'lcc' ? 0.1 : 0.3;\n const a = aRaw.add(lowPassVariance).add(cocVar);\n const d = dRaw.add(lowPassVariance).add(cocVar);\n const b = bRaw;\n\n // Mass conservation: √(det Σ / det(Σ + dilation)). Classic LCC always\n // compensates its low-pass; the standard path retains the existing\n // antialias-controlled compatibility behavior. Screen-capped isotropic\n // points skip the fade.\n const detBlur = a.mul(d).sub(b.mul(b)).max(1e-9);\n const detForFade =\n settings.antialias || settings.projectedFilterProfile === 'lcc'\n ? aRaw.mul(dRaw).sub(bRaw.mul(bRaw)).max(0.0)\n : aRaw.add(lowPassVariance).mul(dRaw.add(lowPassVariance)).sub(bRaw.mul(bRaw)).max(1e-9);\n const mipFade = detForFade.div(detBlur).sqrt();\n opacityCompensation.assign(\n stack.isotropicCovarianceMix === null\n ? mipFade\n : mix(mipFade, float(1), stack.isotropicCovarianceMix),\n );\n\n // Eigen-decomposition of the 2×2 covariance gives the ellipse axes.\n // λ is variance in px², so √λ is the standard deviation in pixels;\n // the quad reaches `maxStdDev` σ per axis (3 = the reference 3DGS\n // rasterizer). The epsilon keeps the axis-aligned case finite.\n const mid = a.add(d).mul(0.5);\n const radius = vec2(a.sub(d).mul(0.5), b).length();\n let lambda1 = mid.add(radius);\n let lambda2 = mid.sub(radius).max(0.0);\n if (stack.isotropicCovarianceMix !== null) {\n const equalized = equalizeProjectedEigenvalues(\n lambda1,\n lambda2,\n stack.isotropicCovarianceMix,\n );\n lambda1 = equalized.lambda1;\n lambda2 = equalized.lambda2;\n // Screen-radius cap is opt-in; Spark-style point mode omits it so\n // world-space dots grow mildly on zoom-in instead of locking to 1 px.\n if (stack.isotropicScreenRadiusPx !== null) {\n const capped = capProjectedEigenvaluesToScreenRadius(\n lambda1,\n lambda2,\n stack.isotropicCovarianceMix,\n stack.isotropicScreenRadiusPx,\n float(settings.maxStdDev),\n );\n lambda1 = capped.lambda1;\n lambda2 = capped.lambda2;\n }\n }\n const eigenvector1 = vec2(b, lambda1.sub(a)).add(vec2(1e-6, 0.0)).normalize();\n // Spark's LOD alpha: recover `alpha ∈ [0,2]` from the *original* texture\n // channel (stored ÷2), never from modifier-resolved opacity. A merged node\n // (`alpha > 1`) grows the σ-cutoff `maxStdDev + 0.7·(remap−1)` (remap maps\n // 1..2 → 1..5) so it covers its subtree; the covariance is untouched. A leaf\n // keeps the base cutoff. Off (`lodAlpha` false) → the plain constant cutoff.\n let stdDev: THREE.Node<'float'> = float(settings.maxStdDev);\n if (settings.lodAlpha && vAdjustedStdDev && vAlpha2 && vVisualOpacity) {\n const encodedOriginal = asNode<'float'>(colorAfterSh.a);\n const alpha2 = asNode<'float'>(encodedOriginal.mul(2.0));\n const remap = alpha2.mul(4.0).sub(3.0).min(5.0);\n stdDev = asNode<'float'>(\n alpha2\n .greaterThan(1.0)\n .select(\n float(settings.maxStdDev).add(remap.sub(1.0).mul(0.7)),\n float(settings.maxStdDev),\n ),\n );\n vAdjustedStdDev.assign(stdDev);\n vAlpha2.assign(alpha2);\n vVisualOpacity.assign(\n encodedOriginal\n .greaterThan(0)\n .select(stack.color.a.div(encodedOriginal.max(1e-8)), float(1)),\n );\n }\n\n // Optional aspect-ratio clamp (off by default; Spark does not clamp aspect).\n const majorLambda =\n settings.maxAspect && settings.maxAspect > 0\n ? lambda1.min(lambda2.mul(settings.maxAspect * settings.maxAspect))\n : lambda1;\n // A screen-space *minimum* on each axis, the counterpart to `maxRadius`.\n // A splat that projects below the floor - distant, or the whole scene\n // zoomed out - grows to it so its Gaussian tiles with its neighbours\n // instead of leaving the background visible between them; the falloff\n // normalizes to the quad, so the bigger quad just renders a bigger soft\n // splat. `.max` after `.min` so an already-large splat is untouched (no\n // extra fill where no gap can open), and `minSplat <= maxRadius` keeps the\n // clamp order well-defined. Composes with the isotropic-point screen cap\n // above as long as the floor stays below it.\n const minSplat = float(settings.minSplatSizePx ?? 0);\n const majorAxis = eigenvector1.mul(\n majorLambda.sqrt().mul(stdDev).min(maxRadius).max(minSplat),\n );\n const minorAxis = vec2(eigenvector1.y, eigenvector1.x.negate()).mul(\n lambda2.sqrt().mul(stdDev).min(maxRadius).max(minSplat),\n );\n\n const writePosition = (): void => {\n const pixelOffset = majorAxis\n .mul(positionGeometry.x)\n .add(minorAxis.mul(positionGeometry.y));\n const ndcCenter = clipCenter.xy.div(clipCenter.w);\n clipPosition.assign(\n vec4(\n ndcCenter.add(pixelOffset.mul(2.0).div(uniforms.viewport)),\n clipCenter.z.div(clipCenter.w),\n 1.0,\n ),\n );\n };\n // Per-splat LOD cut. `notBlob` is the \"keep this splat\" predicate (named\n // for the historical blob cull); null means \"no cull, always draw\".\n let notBlob: THREE.Node<'bool'> | null;\n if (settings.foveationMode === 'page-table') {\n // Spark's selected-index model: the CPU frontier already picked exactly\n // one node per root→leaf ray, and only those splats are paged into the\n // slab. Draw them all - any screen-size band here would re-cull the\n // selection (e.g. a 1.6–4px band reduces the scene to point dust and\n // makes splats *vanish* as the camera approaches and they outgrow it).\n notBlob = null;\n } else if (settings.foveationMode === 'frontier') {\n // Spark's exact tree cut (see `docs/formats/rad-notes.md` M14.6): draw splat i\n // iff its parent is too big on screen but it is small enough -\n // parent_size / distance > limit ≥ own_size / distance\n // - so exactly one node per root→leaf ray survives (full coverage, no\n // band leapfrogging). A leaf has no finer level, so it draws whenever\n // its parent does.\n //\n // `own_size` (world) is recovered from the covariance: Σ = R·S²·Rᵀ, so\n // trace(Σ) = Σ scaleᵢ², and with the merged-node-expanded scales this\n // already carries the expansion - own_size = 2·√(trace/3) ≈\n // 2·expansion·rms(scale), the same measure `parent_size` uses.\n // `parent_size` is packed in `covarianceB.w`, sign-encoding leaf-ness:\n // >0 internal, <0 leaf, |v| = parent size (`FRONTIER_ROOT_SIZE` ≈ ∞ for a\n // root), and 0 (unwritten) is treated as a root so a splat never vanishes.\n const distance = viewCenter.z.negate();\n const trace = covA.x.add(covA.w).add(covB.y);\n const ownSize = trace\n .max(0.0)\n .mul(1 / 3)\n .sqrt()\n .mul(2.0);\n const packedParent = covB.w;\n const isLeaf = packedParent.lessThan(0.0);\n const absParent = packedParent.abs();\n const parentSize = absParent.equal(0.0).select(float(FRONTIER_ROOT_SIZE), absParent);\n const limitDist = uniforms.pixelScaleLimit.mul(distance);\n const ownCut = isLeaf.select(float(0.0), ownSize);\n notBlob = asNode<'bool'>(\n parentSize.greaterThan(limitDist).and(ownCut.lessThanEqual(limitDist)),\n );\n } else {\n // Optional screen-radius cull, using the *unclamped* projected radius\n // (`majorAxis`/`minorAxis` cap at maxRadius, so every big splat would look\n // identically sized). Two uses:\n // - Blob cull (`maxScreenRadiusPx` only): drop splats bigger than the\n // upper bound - a coarse merged `.rad` node near the camera hides,\n // leaving a hole, without touching fine detail.\n // - Foveation band (`minScreenRadiusPx` too): keep only splats whose\n // on-screen radius is in `(min, max]`.\n const maxScreen = settings.maxScreenRadiusPx ?? 0;\n const minScreen = settings.minScreenRadiusPx ?? 0;\n const projRadius = lambda1.sqrt().mul(settings.maxStdDev);\n let inBand: THREE.Node<'bool'> | null = null;\n // The bounds are uniforms, not constants: a foveated mesh moves them\n // with its LOD cut (see `uniforms.screenBandMin`). The build-time\n // numbers only decide *whether* each side of the band exists.\n if (maxScreen > 0)\n inBand = asNode<'bool'>(projRadius.lessThanEqual(uniforms.screenBandMax));\n if (minScreen > 0) {\n const above = projRadius.greaterThan(uniforms.screenBandMin);\n inBand = asNode<'bool'>(inBand ? inBand.and(above) : above);\n }\n notBlob = inBand;\n }\n\n if (settings.performanceProfile === 'smooth') {\n // PlayCanvas-compatible contribution rejection. The library's\n // default quality profile bypasses this branch entirely.\n // Screen-capped isotropic points (~1 px) fail opacity·major·minor ≥ 3\n // once the camera approaches - skip the cull while mix > 0 so point\n // mode does not vanish on zoom-in (hosts typically default to `smooth`).\n const majorRadius = majorAxis.length();\n const minorRadius = minorAxis.length();\n const opacity = stack.color.a;\n const contributionOk = opacity\n .greaterThanEqual(1 / 255)\n .and(majorRadius.max(minorRadius).mul(2).greaterThanEqual(2))\n .and(opacity.mul(majorRadius).mul(minorRadius).greaterThanEqual(3));\n const passes =\n stack.isotropicCovarianceMix === null\n ? contributionOk\n : asNode<'bool'>(contributionOk.or(stack.isotropicCovarianceMix.greaterThan(0)));\n If(notBlob ? passes.and(notBlob) : passes, writePosition);\n } else if (notBlob) {\n If(notBlob, writePosition);\n } else {\n writePosition();\n }\n });\n\n return clipPosition;\n })();\n\n if (mode === 'display') {\n material.fragmentNode = Fn(() => {\n const squaredDistance = quadPosition.dot(quadPosition);\n Discard(squaredDistance.greaterThan(1.0));\n let opacity: THREE.Node<'float'>;\n if (settings.lodAlpha && vAdjustedStdDev && vAlpha2 && vVisualOpacity) {\n // Spark's LOD falloff. `g = exp(-½·adjustedStdDev²·|q|²)` is the Gaussian\n // at this fragment. A leaf composites `g · alpha`. A merged node\n // (`alpha > 1`) uses a super-Gaussian plateau `1 − (1 − g)^a`,\n // `a = exp((remap² − 1)/e)`, so a single coarse splat fills the footprint\n // of the subtree it stands in for - no covariance inflation.\n // Visual opacity (fade / alpha modifiers) scales the completed falloff.\n const g = squaredDistance.mul(vAdjustedStdDev.mul(vAdjustedStdDev).mul(-0.5)).exp();\n const remap = vAlpha2.mul(4.0).sub(3.0).min(5.0);\n const aExp = remap\n .mul(remap)\n .sub(1.0)\n .mul(1 / Math.E)\n .exp();\n const merged = g.oneMinus().pow(aExp).oneMinus();\n opacity = asNode<'float'>(\n vAlpha2.greaterThan(1.0).select(merged, g.mul(vAlpha2)).mul(vVisualOpacity),\n );\n } else {\n // True Gaussian falloff. |quadPosition| = 1 is `maxStdDev` σ from center.\n opacity = asNode<'float'>(squaredDistance.mul(gaussianExponent).exp().mul(splatColor.a));\n }\n const alpha = opacity.mul(opacityCompensation);\n // PlayCanvas-style screen-space relight: sample the host's lit proxy RT\n // at this fragment, then multiply baked RGB. `blend === 0` leaves color\n // unchanged (placeholder map is fine). Pick path does not use this.\n // Softness > 0 box-filters the map. Average RGB **weighted by alpha** so\n // uncovered clear pixels (A=0) do not pull coverage edges toward black —\n // that used to draw a dark outline of every collision triangle.\n const litCenter = tslTexture(uniforms.relightMap, screenUV);\n const ox = uniforms.relightSoftness.div(uniforms.viewport.x.max(1));\n const oy = uniforms.relightSoftness.div(uniforms.viewport.y.max(1));\n const s0 = litCenter;\n const s1 = tslTexture(uniforms.relightMap, screenUV.add(vec2(ox, 0)));\n const s2 = tslTexture(uniforms.relightMap, screenUV.add(vec2(ox.negate(), 0)));\n const s3 = tslTexture(uniforms.relightMap, screenUV.add(vec2(0, oy)));\n const s4 = tslTexture(uniforms.relightMap, screenUV.add(vec2(0, oy.negate())));\n const wSum = s0.a.add(s1.a).add(s2.a).add(s3.a).add(s4.a).max(1e-4);\n const rgbSoft = s0.rgb\n .mul(s0.a)\n .add(s1.rgb.mul(s1.a))\n .add(s2.rgb.mul(s2.a))\n .add(s3.rgb.mul(s3.a))\n .add(s4.rgb.mul(s4.a))\n .div(wSum);\n const aSoft = wSum.mul(0.2);\n const litSoft = vec4(rgbSoft, aSoft);\n const lit = uniforms.relightSoftness.greaterThan(0.5).select(litSoft, litCenter);\n const factor = mix(\n vec3(uniforms.relightBackground),\n lit.rgb.mul(uniforms.relightBrightness),\n lit.a,\n );\n const rgb = mix(splatColor.rgb, splatColor.rgb.mul(factor), uniforms.relightBlend);\n return vec4(rgb.mul(alpha), alpha); // premultiplied alpha\n })();\n\n // Premultiplied \"over\" compositing; splats are sorted back-to-front.\n material.transparent = true;\n material.depthTest = true;\n material.depthWrite = false;\n material.side = THREE.DoubleSide;\n material.blending = THREE.CustomBlending;\n material.blendSrc = THREE.OneFactor;\n material.blendDst = THREE.OneMinusSrcAlphaFactor;\n material.blendSrcAlpha = THREE.OneFactor;\n material.blendDstAlpha = THREE.OneMinusSrcAlphaFactor;\n // Splat colors are already authored for display; scene exposure should not\n // re-grade them, but the renderer still performs the final sRGB encoding.\n material.toneMapped = false;\n } else {\n if (!pick) {\n throw new Error(\"applySplatMaterialGraph in 'pick' mode requires pick uniforms.\");\n }\n material.fragmentNode = Fn(() => {\n const squaredDistance = quadPosition.dot(quadPosition);\n Discard(squaredDistance.greaterThan(1.0));\n let gaussian: THREE.Node<'float'>;\n if (settings.lodAlpha && vAdjustedStdDev && vAlpha2 && vVisualOpacity) {\n // Same LOD classification as display (original alpha); modifiers still\n // scale the hit threshold through the visual-opacity multiplier.\n const g = squaredDistance.mul(vAdjustedStdDev.mul(vAdjustedStdDev).mul(-0.5)).exp();\n const remap = vAlpha2.mul(4.0).sub(3.0).min(5.0);\n const aExp = remap\n .mul(remap)\n .sub(1.0)\n .mul(1 / Math.E)\n .exp();\n const merged = g.oneMinus().pow(aExp).oneMinus();\n gaussian = asNode<'float'>(\n vAlpha2.greaterThan(1.0).select(merged, g.mul(vAlpha2)).mul(vVisualOpacity),\n );\n } else {\n gaussian = asNode<'float'>(squaredDistance.mul(gaussianExponent).exp().mul(splatColor.a));\n }\n const alpha = gaussian.mul(opacityCompensation);\n Discard(alpha.lessThan(pick.alphaThreshold));\n\n // 24-bit normalized linear view depth → RGB; alpha marks a hit.\n const normalized = viewDepthVarying!\n .sub(pick.near)\n .div(pick.far.sub(pick.near))\n .clamp(0.0, 1.0);\n const depth = normalized.mul(16777215.0);\n const r = depth.div(65536.0).floor();\n const g = depth.mod(65536.0).div(256.0).floor();\n const b = depth.mod(256.0).floor();\n return vec4(r.div(255.0), g.div(255.0), b.div(255.0), 1.0);\n })();\n\n material.transparent = false;\n material.depthTest = true;\n material.depthWrite = true;\n material.side = THREE.DoubleSide;\n material.blending = THREE.NoBlending;\n material.toneMapped = false;\n }\n}\n\nexport { foldSplatModifierStack };\n","import type * as THREE from 'three/webgpu';\n\n/**\n * The greatest view-space depth displacement of a local bounding sphere.\n *\n * View depth is the dot product of the local point and row 2 of `modelView`.\n * A sphere with radius `r` therefore spans exactly `r · ||row2.xyz||` either\n * side of its transformed center. Unlike a maximum-axis-scale estimate, this\n * remains correct when a non-uniformly scaled ancestor and a rotated child\n * compose into a shear.\n */\nexport function viewDepthRadius(modelView: THREE.Matrix4, radius: number): number {\n const m = modelView.elements;\n return radius * Math.hypot(m[2], m[6], m[10]);\n}\n","/**\n * Frees the JS-heap mirror of storage buffers only the GPU ever reads.\n *\n * ## The gap\n *\n * Every `THREE.StorageBufferAttribute` is constructed around a JS typed array,\n * and three's WebGPU backend never releases it. `WebGPUAttributeUtils`'s\n * `createAttribute` copies the array into a `mappedAtCreation` GPU buffer,\n * unmaps it, stores the buffer - and never calls `bufferAttribute\n * .onUploadCallback()`, the hook that exists for exactly this and that the\n * legacy WebGL renderer does call (`WebGLAttributes.js`). So a buffer of N\n * slots costs N bytes on the GPU *and the same again on the JS heap,\n * permanently*, even when nothing ever reads the CPU copy back.\n *\n * For VLAM that is not a rounding error. The unified work buffer alone is\n * 72 B/slot, gathered entirely on the GPU and never read back - at a 10M-slot\n * scene that is ~720 MB of JS heap holding zeroes, next to the ~720 MB of GPU\n * memory doing the actual work. Add the sort scratch and it is most of a\n * gigabyte that no on-screen number accounts for.\n *\n * ## Why replacing `.array` is safe\n *\n * Four properties of three 0.185 hold simultaneously, and this module is only\n * correct while they all do:\n *\n * 1. `createAttribute` is guarded by `if (buffer === undefined)` - once the GPU\n * buffer exists, every later call short-circuits without reading `.array`.\n * 2. `updateAttribute` *does* re-read `.array`, but `Attributes.update` only\n * reaches it when `data.version < attribute.version` or the attribute uses\n * `DynamicDrawUsage`. VLAM sets neither on the buffers released here - which\n * is what {@link StorageMirrorReleaser}'s drift guard verifies at runtime.\n * 3. The vertex-layout paths read `.array.constructor` and\n * `.array.BYTES_PER_ELEMENT`, never the contents - and only for *geometry*\n * attributes, which these are not. Both survive a zero-length array of the\n * same constructor.\n * 4. Device loss is terminal: `Renderer._onDeviceLost` sets a flag and every\n * later render/compute returns early. There is no re-upload-from-`.array`\n * path to break.\n *\n * A zero-length array rather than a detached buffer, deliberately: if a future\n * change ever does bump `version` on a released attribute, a zero-length array\n * degrades to a legal 0-byte `writeBuffer` (silent, and caught by the drift\n * guard) where a detached one would throw inside the render loop.\n *\n * Peak memory is unchanged - the array has to exist for the upload copy. Only\n * steady state improves, which is the number that was hurting.\n *\n * ## Relationship to `releaseRendererAttributes`\n *\n * They are complements, and compose in either order.\n * `releaseRendererAttributes` frees the **GPU** buffer at dispose; this frees\n * the **CPU** mirror during the first frame. If dispose somehow ran first, the\n * upload probe simply reports \"not uploaded\" and the mirror is never released -\n * fail-safe.\n *\n * Upstream fix: a one-line `bufferAttribute.onUploadCallback()` in\n * `WebGPUAttributeUtils.createAttribute` would make the documented\n * `BufferAttribute.onUpload()` contract work on WebGPU, and this module would\n * become a shim for older three.\n */\nimport type * as THREE from 'three/webgpu';\n\nimport { warn } from './logging';\n\n/** The internal shape this module probes for; absent on WebGL2 and in test doubles. */\ninterface AttributeBackend {\n isWebGPUBackend?: boolean;\n has?(object: object): boolean;\n get?(object: object): { buffer?: unknown };\n}\n\n/**\n * Whether three has already created this attribute's GPU buffer.\n *\n * `has` before `get` is not optional: the backend's `DataMap.get` *creates* an\n * empty record on a miss, so probing with `get` alone would both pollute the\n * map and always report \"uploaded\".\n */\nfunction hasUploaded(renderer: THREE.WebGPURenderer, attribute: THREE.BufferAttribute): boolean {\n const backend = (renderer as unknown as { backend?: AttributeBackend }).backend;\n if (!backend || backend.isWebGPUBackend !== true) return false;\n if (typeof backend.has !== 'function' || typeof backend.get !== 'function') return false;\n if (!backend.has(attribute)) return false;\n return backend.get(attribute).buffer !== undefined;\n}\n\n/** Swaps in a zero-length array of the same type, keeping `count` intact. */\nfunction dropMirror(attribute: THREE.BufferAttribute): number {\n const array = attribute.array;\n const bytes = array.byteLength;\n if (bytes === 0) return 0;\n const Ctor = array.constructor as new (length: number) => typeof array;\n attribute.array = new Ctor(0);\n return bytes;\n}\n\n/**\n * Drops the JS mirrors of storage attributes three has already uploaded.\n *\n * Owners construct one over the attributes they own **outright** and call\n * {@link release} after each dispatch; it settles on the first frame and costs\n * one WeakMap probe per pending attribute until then. Attributes the owner only\n * borrows (a mesh's `splatIndex`/`sourceIndex` passed into a sorter) must never\n * be handed to it - the same ownership line `releaseRendererAttributes` draws.\n *\n * Only pass attributes that are written from the CPU *at most once*, at\n * construction. Anything the CPU rewrites per frame will silently stop\n * uploading; the drift guard turns that into a warning rather than a mystery.\n */\nexport class StorageMirrorReleaser {\n private readonly pending: THREE.BufferAttribute[];\n /** Released attributes and the `version` they carried when released. */\n private readonly released = new Map<THREE.BufferAttribute, number>();\n private releasedBytesValue = 0;\n private warnedDrift = false;\n\n constructor(attributes: readonly THREE.BufferAttribute[]) {\n this.pending = [...attributes];\n }\n\n /** Nothing left to release. */\n get settled(): boolean {\n return this.pending.length === 0;\n }\n\n /** Bytes still mirrored on the JS heap. */\n get pendingBytes(): number {\n let bytes = 0;\n for (const attribute of this.pending) bytes += attribute.array.byteLength;\n return bytes;\n }\n\n /** Bytes freed so far. Diagnostic. */\n get releasedBytes(): number {\n return this.releasedBytesValue;\n }\n\n /**\n * Releases every pending mirror three has uploaded, and checks the ones\n * already released for version drift. Idempotent and cheap once settled.\n *\n * @returns Bytes freed by this call.\n */\n release(renderer: THREE.WebGPURenderer): number {\n this.checkDrift();\n if (this.pending.length === 0) return 0;\n let freed = 0;\n // Backwards so a splice cannot skip the next entry.\n for (let i = this.pending.length - 1; i >= 0; i--) {\n const attribute = this.pending[i] as THREE.BufferAttribute;\n if (!hasUploaded(renderer, attribute)) continue;\n freed += dropMirror(attribute);\n this.released.set(attribute, attribute.version);\n this.pending.splice(i, 1);\n }\n this.releasedBytesValue += freed;\n return freed;\n }\n\n /**\n * A released attribute whose `version` moved means someone added a CPU write\n * to a buffer this class promised was GPU-only. Three would then upload the\n * zero-length array - a legal no-op - and the data would simply never reach\n * the GPU. Warn once, so that failure is visible rather than a silent wrong\n * render.\n */\n private checkDrift(): void {\n if (this.warnedDrift || this.released.size === 0) return;\n for (const [attribute, version] of this.released) {\n if (attribute.version === version) continue;\n this.warnedDrift = true;\n warn(\n `StorageMirrorReleaser: storage attribute \"${attribute.name || 'unnamed'}\" was written ` +\n `from the CPU after its mirror was released; that write will not reach the GPU. ` +\n `Remove it from the releaser's attribute list.`,\n );\n return;\n }\n }\n}\n","import * as THREE from 'three/webgpu';\nimport {\n Fn,\n If,\n Loop,\n atomicAdd,\n atomicLoad,\n atomicStore,\n float,\n instanceIndex,\n int,\n ivec2,\n storage,\n textureLoad,\n uint,\n uniform,\n} from 'three/tsl';\nimport type { SplatSorter } from './sorter';\nimport { sourceWorldTransform } from './splat-mesh-material';\nimport { viewDepthRadius } from './splat-sort-bounds';\nimport { StorageMirrorReleaser } from './storage-attribute-mirror';\nimport type { uniformArray } from 'three/tsl';\n\n/**\n * Optional per-source world transform for a unified {@link MergedSplatMesh} pool.\n * When present, each splat's center is transformed to world space by its\n * source's matrix (selected by a per-splat id read from `sourceIdTexture`)\n * before its depth is computed, so clouds with different world transforms sort\n * against each other correctly. Absent for an ordinary single-transform mesh.\n */\nexport interface PerSourceSortTransform {\n /** RedFormat float texture holding each pool slot's source id (the channel). */\n sourceIdTexture: THREE.DataTexture;\n /** Shared array of source-matrix columns; see `SourceMatrixArray`. */\n columns: ReturnType<typeof uniformArray>;\n /** CPU mirror of the source-id channel, used by the WebGL2 worker sorter. */\n sourceIds: Float32Array;\n /** Column-major source matrices, used by the WebGL2 worker sorter. */\n matrices: Float32Array;\n}\n\n/**\n * GPU depth sorter: a single-pass counting sort over 2²² (~4M) depth\n * buckets, running entirely in TSL compute passes on the WebGPU backend.\n *\n * 1. clear - zero the histogram\n * 2. histogram - depth per active splat → bucket id; count per bucket\n * 3. scanBlocks - exclusive prefix sum within each 256-bucket block\n * 4. scanBlockSums - parallel exclusive scans over groups of block totals\n * 5. scanSuperSums - exclusive scan over the remaining super-block totals\n * 6. addBlockOffsets - combine both block-sum scan levels\n * 7. addOffsets - combine block and bucket offsets\n * 8. scatter - order[offset[bucket]++] = pool index (atomic)\n *\n * Why ~4M buckets, not 64K: on a large scene, 16-bit buckets made a\n * near-camera bucket span thousands of overlapping grass/leaf splats whose\n * arbitrary intra-bucket order reshuffled as the camera moved - visible\n * popping. Each depth bucket is now a sub-splat-width slice, so splats that\n * share a bucket are effectively coplanar and their (still arbitrary) order\n * cannot be seen. (A multi-pass radix would give exact order but needs a\n * *stable* scatter, which the parallel `atomicAdd` scatter is not - hence a\n * single wide pass instead.)\n *\n * Dynamic capacity: the sorter walks the mesh's `sourceIndex` buffer (pool\n * indices of the active splats) and sorts only the first `activeCount`\n * entries, so ranges can be appended and removed without rebuilding\n * pipelines - the per-splat passes are dispatched with a dynamic invocation\n * count and guarded by an `activeCount` uniform.\n *\n * Portable by construction: cross-workgroup communication only happens\n * through the implicit synchronization WebGPU guarantees between dispatches\n * - no spin-waits or scheduling assumptions.\n *\n * The depth range for bucket quantization comes from the mesh's bounding\n * sphere transformed to view space, so no GPU min/max reduction is needed.\n */\nexport class ComputeSorter implements SplatSorter {\n readonly kind = 'counting' as const;\n private static readonly BUCKET_COUNT = 1 << 22;\n private static readonly BLOCK_SIZE = 256;\n\n /**\n * Smallest bucket count a sort will dispatch. Below this the fixed passes\n * are already cheap, and the floor keeps every block-scan index exact.\n */\n private static readonly MIN_BUCKET_COUNT = 1 << 16;\n\n private readonly renderer: THREE.WebGPURenderer;\n private readonly clearPass: THREE.ComputeNode;\n private readonly histogramPass: THREE.ComputeNode;\n private readonly scanBlocksPass: THREE.ComputeNode;\n private readonly scanBlockSumsPass: THREE.ComputeNode;\n private readonly addBlockSumOffsetsPass: THREE.ComputeNode;\n private readonly addOffsetsPass: THREE.ComputeNode;\n private readonly scatterPass: THREE.ComputeNode;\n /** All stages in dependency order, submitted as one WebGPU compute pass. */\n private readonly sortPasses: THREE.ComputeNode[];\n /** Sorter-owned working buffers, released on {@link dispose}. */\n private readonly workingAttributes: THREE.StorageBufferAttribute[];\n /** Frees the JS mirrors three keeps behind the GPU-only working buffers. */\n private readonly mirrors: StorageMirrorReleaser;\n /** Set by {@link dispose}; makes a second dispose a no-op. */\n private disposed = false;\n\n /** Row 2 of the model-view matrix; view-space z = row2 · (position, 1). */\n private readonly viewRow2 = uniform(new THREE.Vector4());\n private readonly depthMin = uniform(0);\n private readonly depthScale = uniform(0);\n /** Active splat count; a float compares exactly for counts < 2²⁴. */\n private readonly activeCount = uniform(0);\n /** Highest bucket index this sort uses; see {@link effectiveBucketCount}. */\n private readonly bucketMax = uniform(0);\n\n private readonly viewCenter = new THREE.Vector3();\n\n constructor(options: {\n renderer: THREE.WebGPURenderer;\n /** Pool capacity in splats; all buffers are sized once from this. */\n capacity: number;\n /** RGBA32F texture holding splat centers (xyz), pool-indexed. */\n centersTexture?: THREE.DataTexture;\n /** Width of the centers texture, to map pool index → texel. */\n dataTextureWidth?: number;\n /** Work-buffer centers (xyzw per splat), as written by a gather pass. */\n centersBuffer?: THREE.StorageBufferAttribute;\n /** The float `splatIndex` buffer the render material reads. */\n splatIndexAttribute: THREE.StorageInstancedBufferAttribute;\n /** Pool indices of the active splats (first `activeCount` entries). */\n sourceIndexAttribute: THREE.StorageBufferAttribute;\n /** Per-source world transform for a unified pool; omit for a single mesh. */\n perSource?: PerSourceSortTransform;\n }) {\n const { renderer, capacity, centersTexture, dataTextureWidth, centersBuffer, perSource } =\n options;\n const { BUCKET_COUNT, BLOCK_SIZE } = ComputeSorter;\n if (!centersTexture && !centersBuffer) {\n throw new Error('ComputeSorter: provide centersTexture or centersBuffer.');\n }\n if (centersTexture && dataTextureWidth === undefined) {\n throw new Error('ComputeSorter: dataTextureWidth is required with centersTexture.');\n }\n if (centersBuffer && perSource) {\n throw new Error('ComputeSorter: work-buffer centers are already in world space.');\n }\n\n this.renderer = renderer;\n\n // GPU-only working buffers. The histogram is accessed atomically in\n // every pass so all pipelines see one consistent buffer declaration;\n // after `addOffsets` it holds the global write offsets that `scatter`\n // consumes (and destroys - it is rebuilt on every sort).\n const histogramAttribute = new THREE.StorageBufferAttribute(new Uint32Array(BUCKET_COUNT), 1);\n const blockSumsAttribute = new THREE.StorageBufferAttribute(\n new Uint32Array(BUCKET_COUNT / BLOCK_SIZE),\n 1,\n );\n const superBlockSumsAttribute = new THREE.StorageBufferAttribute(\n new Uint32Array(BUCKET_COUNT / BLOCK_SIZE / BLOCK_SIZE),\n 1,\n );\n const bucketsAttribute = new THREE.StorageBufferAttribute(new Uint32Array(capacity), 1);\n this.workingAttributes = [\n histogramAttribute,\n blockSumsAttribute,\n superBlockSumsAttribute,\n bucketsAttribute,\n ];\n this.mirrors = new StorageMirrorReleaser(this.workingAttributes);\n const histogram = storage(histogramAttribute, 'uint', BUCKET_COUNT).toAtomic();\n const blockSums = storage(blockSumsAttribute, 'uint', BUCKET_COUNT / BLOCK_SIZE);\n const superBlockSums = storage(\n superBlockSumsAttribute,\n 'uint',\n BUCKET_COUNT / BLOCK_SIZE / BLOCK_SIZE,\n );\n const buckets = storage(bucketsAttribute, 'uint', capacity);\n const sourceIndex = storage(options.sourceIndexAttribute, 'uint', capacity);\n const order = storage(options.splatIndexAttribute, 'float', capacity);\n const workCenters = centersBuffer ? storage(centersBuffer, 'vec4', capacity) : null;\n\n this.clearPass = Fn(() => {\n atomicStore(histogram.element(instanceIndex), uint(0));\n })().compute(BUCKET_COUNT, [BLOCK_SIZE]);\n\n this.histogramPass = Fn(() => {\n If(float(instanceIndex).lessThan(this.activeCount), () => {\n const poolIndex = int(sourceIndex.element(instanceIndex));\n const texel = centersTexture\n ? ivec2(\n poolIndex.mod(int(dataTextureWidth as number)),\n poolIndex.div(int(dataTextureWidth as number)),\n )\n : null;\n const center = workCenters\n ? workCenters.element(poolIndex).xyz\n : textureLoad(centersTexture, texel as THREE.Node<'ivec2'>).xyz;\n\n // In a unified pool each splat lives in its source's local frame; move\n // it to world space by that source's matrix before measuring depth, so\n // clouds with different transforms share one back-to-front order. The\n // branch is resolved at build time, so a single-transform mesh compiles\n // exactly the original local-center path.\n const depthPoint = perSource\n ? sourceWorldTransform(\n perSource.columns,\n int(textureLoad(perSource.sourceIdTexture, texel as THREE.Node<'ivec2'>).r),\n center,\n ).worldCenter\n : center;\n const depth = this.viewRow2.xyz.dot(depthPoint).add(this.viewRow2.w);\n const bucket = depth\n .sub(this.depthMin)\n .mul(this.depthScale)\n .clamp(0, this.bucketMax)\n .toUint();\n\n buckets.element(instanceIndex).assign(bucket);\n atomicAdd(histogram.element(bucket), uint(1));\n });\n })().compute(capacity, [BLOCK_SIZE]);\n\n // One thread serially scans one 256-bucket block: threads never share\n // data within a dispatch, so no barriers are needed anywhere.\n this.scanBlocksPass = Fn(() => {\n const base = instanceIndex.mul(uint(BLOCK_SIZE)).toVar();\n const runningTotal = uint(0).toVar();\n Loop(BLOCK_SIZE, ({ i }) => {\n const slot = histogram.element(base.add(uint(i)));\n const bucketCount = atomicLoad(slot).toVar();\n atomicStore(slot, runningTotal);\n runningTotal.addAssign(bucketCount);\n });\n blockSums.element(instanceIndex).assign(runningTotal);\n })().compute(BUCKET_COUNT / BLOCK_SIZE, [64]);\n\n // Scan 256 block totals per invocation, then scan the resulting 64\n // super-block totals. The old single invocation walked all 16,384 totals\n // serially; this keeps every dependency chain at 256 additions or fewer.\n this.scanBlockSumsPass = Fn(() => {\n const base = instanceIndex.mul(uint(BLOCK_SIZE)).toVar();\n const runningTotal = uint(0).toVar();\n Loop(BLOCK_SIZE, ({ i }) => {\n const slot = blockSums.element(base.add(uint(i)));\n const blockTotal = slot.toVar();\n slot.assign(runningTotal);\n runningTotal.addAssign(blockTotal);\n });\n superBlockSums.element(instanceIndex).assign(runningTotal);\n })().compute(BUCKET_COUNT / BLOCK_SIZE / BLOCK_SIZE, [64]);\n\n const scanSuperBlockSums = Fn(() => {\n If(instanceIndex.equal(uint(0)), () => {\n const runningTotal = uint(0).toVar();\n Loop(BUCKET_COUNT / BLOCK_SIZE / BLOCK_SIZE, ({ i }) => {\n const superBlockTotal = superBlockSums.element(i).toVar();\n superBlockSums.element(i).assign(runningTotal);\n runningTotal.addAssign(superBlockTotal);\n });\n });\n })().compute(1, [64]);\n\n this.addBlockSumOffsetsPass = Fn(() => {\n blockSums\n .element(instanceIndex)\n .addAssign(superBlockSums.element(instanceIndex.div(uint(BLOCK_SIZE))));\n })().compute(BUCKET_COUNT / BLOCK_SIZE, [BLOCK_SIZE]);\n\n this.addOffsetsPass = Fn(() => {\n const slot = histogram.element(instanceIndex);\n const offset = blockSums.element(instanceIndex.shiftRight(uint(Math.log2(BLOCK_SIZE))));\n atomicStore(slot, atomicLoad(slot).add(offset));\n })().compute(BUCKET_COUNT, [BLOCK_SIZE]);\n\n // Ascending view-space z puts the most negative (farthest) splats\n // first: back-to-front, matching the CPU sorter.\n this.scatterPass = Fn(() => {\n If(float(instanceIndex).lessThan(this.activeCount), () => {\n const poolIndex = sourceIndex.element(instanceIndex);\n const bucket = buckets.element(instanceIndex);\n const destination = atomicAdd(histogram.element(bucket), uint(1));\n order.element(destination).assign(float(poolIndex));\n });\n })().compute(capacity, [BLOCK_SIZE]);\n\n this.sortPasses = [\n this.clearPass,\n this.histogramPass,\n this.scanBlocksPass,\n this.scanBlockSumsPass,\n scanSuperBlockSums,\n this.addBlockSumOffsetsPass,\n this.addOffsetsPass,\n this.scatterPass,\n ];\n }\n\n /**\n * Buckets to actually use for `activeCount` splats, rounded up to a power of\n * two so block indexing stays exact.\n *\n * The histogram is allocated once for the pool's worst case, but clearing and\n * scanning all 2²² buckets costs the same whether one splat is resident or\n * four million - a fixed per-sort bill that dominates on a mobile GPU when\n * the pool is only partly filled (a streaming scene ramping up, or any small\n * scene). Sizing the dispatch to the live splat count keeps the design's\n * ~1-bucket-per-splat depth resolution, so ordering is unaffected, while\n * skipping work on buckets no splat can land in.\n */\n private effectiveBucketCount(activeCount: number): number {\n const exponent = Math.ceil(Math.log2(Math.max(1, activeCount)));\n const rounded = 2 ** exponent;\n return Math.min(Math.max(rounded, ComputeSorter.MIN_BUCKET_COUNT), ComputeSorter.BUCKET_COUNT);\n }\n\n sort(modelView: THREE.Matrix4, activeCount: number, bounds: THREE.Sphere): boolean {\n if (activeCount === 0) return true;\n\n const m = modelView.elements;\n this.viewRow2.value.set(m[2], m[6], m[10], m[14]);\n this.activeCount.value = activeCount;\n\n const buckets = this.effectiveBucketCount(activeCount);\n this.viewCenter.copy(bounds.center).applyMatrix4(modelView);\n // `bounds` is in mesh-local space, but the keys are view-space z. Its\n // exact z extent is the radius times the norm of modelView's depth row;\n // that remains correct for any linear transform, including hierarchy-\n // induced shear from a rotated child under a non-uniformly scaled parent.\n const viewRadius = viewDepthRadius(modelView, bounds.radius);\n const near = this.viewCenter.z - viewRadius;\n const far = this.viewCenter.z + viewRadius;\n this.depthMin.value = near;\n this.depthScale.value = (buckets - 1) / (far - near || 1);\n this.bucketMax.value = buckets - 1;\n\n // Keep every stage limited to the buckets and splats in play while batching\n // all dependent stages into one command encoder, compute pass, and submit.\n const blockCount = buckets / ComputeSorter.BLOCK_SIZE;\n this.histogramPass.count = activeCount;\n this.scatterPass.count = activeCount;\n this.clearPass.count = buckets;\n this.scanBlocksPass.count = blockCount;\n this.scanBlockSumsPass.count = blockCount / ComputeSorter.BLOCK_SIZE;\n this.addBlockSumOffsetsPass.count = blockCount;\n this.addOffsetsPass.count = buckets;\n this.renderer.compute(this.sortPasses);\n // The working buffers are GPU-only: the histogram is zeroed by `clearPass`\n // and read atomically on the GPU, and `buckets` never leaves it. Three keeps\n // a JS mirror of each anyway - 16 MiB for the histogram alone, plus 4 B per\n // splat of capacity - so drop it once the dispatch has uploaded them.\n // Deliberately *not* `sourceIndex`/`splatIndex`: those belong to the mesh\n // and are rewritten from the CPU every frame. Same ownership line as\n // `releaseRendererAttributes` draws below.\n if (!this.mirrors.settled) this.mirrors.release(this.renderer);\n return true;\n }\n\n dispose(): void {\n // Idempotent: a mesh disposed twice (or a unified renderer torn down after\n // its owner already released the sorter) must not double-free GPU state.\n if (this.disposed) return;\n this.disposed = true;\n // Disposing each compute node releases its pipeline, bind groups and\n // node state from the renderer - without this, every scene switch on a\n // long-lived renderer leaked the sorter's working set (~32 MB GPU + CPU).\n for (const pass of this.sortPasses) pass.dispose();\n // Only the sorter-owned working buffers are freed here -\n // sourceIndex/splatIndex belong to the mesh (see releaseRendererAttributes).\n releaseRendererAttributes(this.renderer, this.workingAttributes);\n }\n}\n\n/**\n * Frees the GPU buffers of storage attributes that never sat in a geometry.\n * The renderer has no public API for this, so it reaches for the renderer's\n * attribute map directly; a harmless no-op if the internal shape changes, and\n * idempotent (the map's `delete` ignores unknown entries). Shared by the\n * sorters and by owners of mesh-level storage attributes (`sourceIndex`, the\n * unified work buffer) on dispose.\n */\nexport function releaseRendererAttributes(\n renderer: THREE.WebGPURenderer,\n attributes: readonly THREE.BufferAttribute[],\n): void {\n const map = (renderer as unknown as { _attributes?: { delete(a: object): void } })._attributes;\n if (!map) return;\n for (const attribute of attributes) map.delete(attribute);\n}\n","import * as THREE from 'three/webgpu';\n\nconst TWO_MILLION = 2_000_000;\nconst FIVE_MILLION = 5_000_000;\nconst EIGHT_MILLION = 8_000_000;\nconst MODEL_VIEW_EPSILON = 1e-6;\n/** Validates the public fixed sort-interval override. */\nexport function validateSortIntervalMs(value: number | undefined): number | undefined {\n if (value !== undefined && (!Number.isFinite(value) || value < 0)) {\n throw new RangeError(\n 'SplatMesh sortIntervalMs must be a finite number greater than or equal to 0.',\n );\n }\n return value;\n}\n\n/**\n * Returns the automatic WebGPU sort interval for the current active splat count.\n *\n * Mobile keeps a floor even for small scenes. A sort is never free: the\n * counting sorter's clear and scan passes cost the same whatever the splat\n * count, so on a mobile GPU an every-changed-frame sort stalls the frame\n * (measured on an Adreno 750: a few frames, then a freeze, repeating). Two\n * frames of sort latency while the camera moves is imperceptible next to that\n * - and {@link WebGpuSortScheduler.shouldSubmit} still sorts immediately once\n * the camera settles, so a stationary view is always exactly ordered.\n */\nexport function automaticSortIntervalMs(activeCount: number, isMobile = false): number {\n if (activeCount < TWO_MILLION) return isMobile ? 33 : 0;\n if (activeCount < FIVE_MILLION) return isMobile ? 66 : 50;\n if (activeCount < EIGHT_MILLION) return isMobile ? 133 : 100;\n return 1000 / 6;\n}\n\n/**\n * Gates WebGPU sort submissions while retaining content swaps and the\n * final camera pose. Accepted-sort state is committed separately so sorter\n * backpressure never causes a request to be forgotten.\n */\nexport class WebGpuSortScheduler {\n private readonly sortIntervalMs: number | undefined;\n private readonly isMobile: boolean;\n private readonly previousModelView = new THREE.Matrix4();\n private hasPreviousModelView = false;\n private legacyWasMoving = false;\n private forcePending = true;\n private lastAcceptedAt = -Infinity;\n private acceptedCount = 0;\n\n constructor(sortIntervalMs?: number, isMobile = false) {\n this.sortIntervalMs = validateSortIntervalMs(sortIntervalMs);\n this.isMobile = isMobile;\n }\n\n /** Forces the next changed or content-invalidated pose to bypass throttling. */\n invalidate(): void {\n this.forcePending = true;\n }\n\n /**\n * Content changes replace pool indices underneath the current draw order.\n * They must bypass cadence so a newly active range never renders through a\n * stale sort while the camera is moving.\n */\n invalidateContent(): void {\n this.forcePending = true;\n }\n\n /**\n * Whether an invalidation is still waiting for an accepted sort. The WebGL2\n * worker path consults this directly: it has no cadence, but a content swap\n * under a stationary camera must still trigger a re-sort - the swap reset\n * the draw list to unsorted active order, and without this the scene would\n * render in that unsorted order until the camera next moved (visible\n * blend-order flicker on every streaming swap).\n */\n hasPendingForce(): boolean {\n return this.forcePending;\n }\n\n /**\n * Returns whether a sort should be submitted at this timestamp.\n */\n shouldSubmit(\n modelView: THREE.Matrix4,\n lastAcceptedModelView: THREE.Matrix4,\n activeCount: number,\n now: number,\n ): boolean {\n return this.shouldSubmitLegacy(modelView, lastAcceptedModelView, activeCount, now);\n }\n\n /**\n * Commits timing state only after the sorter accepts a submission.\n */\n markAccepted(now: number): void {\n this.lastAcceptedAt = now;\n this.acceptedCount++;\n this.forcePending = false;\n }\n\n /** Internal diagnostics for the demo HUD; it does not alter scheduling. */\n snapshot(): { acceptedCount: number; lastAcceptedAt: number } {\n return { acceptedCount: this.acceptedCount, lastAcceptedAt: this.lastAcceptedAt };\n }\n\n private shouldSubmitLegacy(\n modelView: THREE.Matrix4,\n lastAcceptedModelView: THREE.Matrix4,\n activeCount: number,\n now: number,\n ): boolean {\n let settled = false;\n if (this.hasPreviousModelView) {\n const moved = maximumElementDelta(modelView, this.previousModelView) > MODEL_VIEW_EPSILON;\n settled = this.legacyWasMoving && !moved;\n this.legacyWasMoving = moved;\n } else {\n this.hasPreviousModelView = true;\n }\n this.previousModelView.copy(modelView);\n\n const interval = this.sortIntervalMs ?? automaticSortIntervalMs(activeCount, this.isMobile);\n if (this.forcePending) return true;\n if (modelView.equals(lastAcceptedModelView)) return false;\n if (settled) return true;\n return interval === 0 || now - this.lastAcceptedAt >= interval;\n }\n}\n\nfunction maximumElementDelta(a: THREE.Matrix4, b: THREE.Matrix4): number {\n let maximum = 0;\n for (let i = 0; i < 16; i++) {\n maximum = Math.max(maximum, Math.abs((a.elements[i] as number) - (b.elements[i] as number)));\n }\n return maximum;\n}\n","/**\n * Resolving the active WebXR view for a presenting renderer.\n *\n * Every render path (a static {@link SplatMesh}, a streamed one, the unified\n * renderer) needs the same three things while an immersive session presents -\n * a cyclopean camera to sort and place SH by, one eye camera to take the\n * projection from, and the *per-eye* viewport in pixels - so they all resolve\n * them here rather than each reading `renderer.xr` its own way.\n *\n * Internal. Nothing here is exported from `index.ts`.\n */\nimport * as THREE from 'three/webgpu';\n\n/** The cameras and pixel size describing one frame of a presenting XR session. */\nexport interface XrView {\n /**\n * The XR array camera: the head pose, midway between the eyes. Depth order\n * and the SH view direction come from here - at a ~63 mm IPD the per-eye\n * difference is imperceptible except within the near plane, so one\n * cyclopean sort serves both eyes instead of doubling the frame's dominant\n * cost. Its `projectionMatrix` is the union frustum covering both eyes,\n * which is also the right frustum for LOD scheduling.\n */\n head: THREE.Camera;\n /**\n * The first eye camera, for projection-derived quantities. Left and right\n * XR projections differ only in their center offset, not their focal\n * length, so a focal length taken from one eye is exact for both - it is\n * not an approximation.\n */\n eye: THREE.PerspectiveCamera;\n /** Per-eye viewport width in device pixels. */\n width: number;\n /** Per-eye viewport height in device pixels. */\n height: number;\n}\n\n/**\n * Structural view of the bits of three's XR manager this module uses. Every\n * member is optional: the renderer may be a stub in tests, and older three\n * versions predate parts of this surface.\n */\ninterface XrManagerLike {\n isPresenting?: boolean;\n cameraAutoUpdate?: boolean;\n getCamera?: () => THREE.Camera;\n updateCamera?: (camera: THREE.PerspectiveCamera) => void;\n}\n\nconst _drawingBufferSize = new THREE.Vector2();\n\n/**\n * The active XR view, or `null` when the renderer is not presenting one.\n *\n * **Freshens the XR camera before reading it.** three splits its per-frame XR\n * work in two: the sub-cameras' *local* `matrix`, `projectionMatrix` and\n * `viewport` are set in the XR manager's own animation-frame callback (which\n * runs before the host's loop body), but their `matrixWorld` /\n * `matrixWorldInverse`, the head's world matrices, and the two-eye union\n * projection are only computed in `XRManager.updateCamera()` - which the\n * renderer calls from inside `render()`. A `SplatMesh.update()` that runs\n * before `render()` would therefore read last frame's world pose, and on the\n * first presenting frame an identity one (sorting the scene from the world\n * origin). Calling `updateCamera` here closes that gap; it is idempotent and\n * cheap (a handful of matrix products - `session.updateRenderState` only\n * fires when near/far actually change), so the renderer's own call moments\n * later costs nothing.\n *\n * @param camera - The *application* camera. `updateCamera` reads its near/far,\n * layers and parent, so the app camera is required here - not the head.\n * @returns The resolved view, or `null` when not presenting (or before the\n * session has produced its first viewer pose).\n */\nexport function resolveXrView(\n camera: THREE.PerspectiveCamera,\n renderer: THREE.WebGPURenderer,\n): XrView | null {\n const xr = (renderer as { xr?: XrManagerLike }).xr;\n if (xr?.isPresenting !== true || typeof xr.getCamera !== 'function') return null;\n // A host that drives the XR camera itself sets `cameraAutoUpdate = false`;\n // freshening it here would fight that.\n if (xr.cameraAutoUpdate !== false && typeof xr.updateCamera === 'function') {\n xr.updateCamera(camera);\n }\n const head = xr.getCamera();\n const eye = (head as { cameras?: THREE.PerspectiveCamera[] }).cameras?.[0];\n if (!eye) return null;\n\n // The eye viewport is authoritative: the drawing buffer spans both eyes, so\n // using it would halve every splat's apparent horizontal size. It is only\n // the fallback for the frames before the XR layer reports viewports.\n const viewport = (eye as { viewport?: THREE.Vector4 }).viewport;\n let width = viewport?.z ?? 0;\n let height = viewport?.w ?? 0;\n if (!(width > 0) || !(height > 0)) {\n renderer.getDrawingBufferSize(_drawingBufferSize);\n width = _drawingBufferSize.x;\n height = _drawingBufferSize.y;\n }\n return { head, eye, width, height };\n}\n\n/**\n * Whether `camera` is an XR array camera. Code that needs a single frustum -\n * picking, anything unprojecting depth through `near`/`far` - must reject one\n * rather than silently read the defaults off a camera that has neither.\n */\nexport function isXrArrayCamera(camera: THREE.Camera): boolean {\n return (camera as { isArrayCamera?: boolean }).isArrayCamera === true;\n}\n\n/**\n * Session options for `navigator.xr.requestSession('immersive-vr', …)` that\n * match the renderer's backend.\n *\n * three's XR manager **throws** out of `setSession` when a WebGPU-backed\n * renderer meets a session that did not enable the `webgpu` feature - it has a\n * genuine WebGPU XR path (an `XRGPUBinding` projection layer) and refuses to\n * silently fall back to WebGL. three's own `VRButton` never asks for that\n * feature, so a host pairing it with a `WebGPURenderer` fails to enter VR on\n * exactly the browsers that support XR best. This returns the init that avoids\n * the mismatch:\n *\n * ```js\n * const session = await navigator.xr.requestSession('immersive-vr', xrSessionInit(renderer));\n * await renderer.xr.setSession(session);\n * ```\n *\n * `webgpu` is marked **required** rather than optional deliberately: an\n * optional feature the browser declines still yields a session, which is the\n * very mismatch that throws. Required turns it into a `requestSession`\n * rejection the host can catch and explain - typically by falling back to a\n * WebGL2 renderer, since three's own `WebGLXRFallback` helper swaps the entire\n * renderer and is unusable once GPU resources are bound to the current one.\n *\n * @param base - Extra options to merge. Its `requiredFeatures` and\n * `optionalFeatures` are concatenated, not replaced.\n */\nexport function xrSessionInit(\n renderer: THREE.WebGPURenderer,\n base: XRSessionInit = {},\n): XRSessionInit {\n const isWebGPU = (renderer.backend as { isWebGPUBackend?: boolean }).isWebGPUBackend === true;\n const required = [...(base.requiredFeatures ?? []), ...(isWebGPU ? ['webgpu'] : [])];\n const optional = base.optionalFeatures ?? [];\n return {\n ...base,\n ...(required.length > 0 ? { requiredFeatures: required } : {}),\n ...(optional.length > 0 ? { optionalFeatures: optional } : {}),\n };\n}\n","/**\n * PlayCanvas-style proxy-mesh splat relighting (screen-space modulate).\n *\n * Host lights a proxy mesh into an RGBA render target (RGB = lit color,\n * A = coverage), then {@link SplatMesh.setRelighting} /\n * {@link UnifiedSplatMesh.setRelighting} multiplies baked splat color in\n * the display fragment. Not a `SplatModifier` - coverage is per-pixel.\n *\n * See `docs/guide/relighting.md`.\n */\nimport * as THREE from 'three/webgpu';\n\n/** Defaults match PlayCanvas `GsplatRelighting` (0.5 gray proxy albedo → brightness 2). */\nexport const DEFAULT_RELIGHT_BLEND = 1;\nexport const DEFAULT_RELIGHT_BRIGHTNESS = 2;\nexport const DEFAULT_RELIGHT_BACKGROUND = 1;\n/** Screen-space soft edge on coverage (px). Softens coarse proxy silhouettes. */\nexport const DEFAULT_RELIGHT_SOFTNESS = 0;\n\n/** Live screen-space relighting settings for {@link SplatMesh.setRelighting}. */\nexport type RelightingSettings = {\n /** Lit proxy render: RGB = lighting, A = mesh coverage (0 = sky / uncovered). */\n map: THREE.Texture;\n /** How much the map affects splat color (`0` = baked only, `1` = full modulate). */\n blend?: number;\n /** Scales `map.rgb` before multiply; `2` compensates a 0.5 gray proxy albedo. */\n brightness?: number;\n /** Multiplier for splats where `map.a ≈ 0` (sky / uncovered). */\n background?: number;\n /**\n * Softens the coverage mask over this many screen pixels (box filter). Use\n * `2`–`4` when the proxy is a coarse collision mesh so triangle silhouettes\n * do not read as a static shadow. `0` = hard PlayCanvas-style edges.\n */\n softness?: number;\n};\n\n/** Resolved numeric fields after clamping (map omitted - still the host texture). */\nexport type RelightingUniforms = {\n blend: number;\n brightness: number;\n background: number;\n softness: number;\n};\n\n/**\n * Clamps blend / brightness / background / softness.\n */\nexport function clampRelightingSettings(\n partial: Partial<Pick<RelightingSettings, 'blend' | 'brightness' | 'background' | 'softness'>>,\n previous: RelightingUniforms = {\n blend: DEFAULT_RELIGHT_BLEND,\n brightness: DEFAULT_RELIGHT_BRIGHTNESS,\n background: DEFAULT_RELIGHT_BACKGROUND,\n softness: DEFAULT_RELIGHT_SOFTNESS,\n },\n): RelightingUniforms {\n const blend = partial.blend !== undefined ? partial.blend : previous.blend;\n const brightness = partial.brightness !== undefined ? partial.brightness : previous.brightness;\n const background = partial.background !== undefined ? partial.background : previous.background;\n const softness = partial.softness !== undefined ? partial.softness : previous.softness;\n return {\n blend: Number.isFinite(blend) ? Math.min(1, Math.max(0, blend)) : previous.blend,\n brightness: Number.isFinite(brightness) ? Math.max(0, brightness) : previous.brightness,\n background: Number.isFinite(background) ? Math.max(0, background) : previous.background,\n softness: Number.isFinite(softness) ? Math.min(8, Math.max(0, softness)) : previous.softness,\n };\n}\n\n/**\n * 1×1 opaque white placeholder so the fragment graph can always sample a map\n * when relighting is off (`blend === 0`).\n */\nexport function createPlaceholderRelightTexture(): THREE.DataTexture {\n const data = new Uint8Array([255, 255, 255, 255]);\n const texture = new THREE.DataTexture(data, 1, 1);\n texture.needsUpdate = true;\n texture.magFilter = THREE.NearestFilter;\n texture.minFilter = THREE.NearestFilter;\n texture.generateMipmaps = false;\n return texture;\n}\n"],"names":["foldSplatModifierStack","modifierList","localCameraPosition","inputs","cameraLocal","state","vec3","float","mat3","bool","used","isotropicCovarianceMix","isotropicVarianceScale","isotropicScreenRadiusPx","worldCenter","viewCenter","normal","channelNodes","context","modelWorldMatrix","vec4","modelViewMatrix","name","node","modifier","outputs","SPLAT_DATA_TEXTURE_WIDTH","allocateRowSpan","spans","rowCount","poolRows","bestIndex","i","span","start","releaseRowSpan","count","b","merged","last","SplatPool","options","__publicField","assertPoolRowsFitDevice","texelCount","floatType","THREE","centersImage","covarianceAImage","createDataTexture","data","createIntegerDataTexture","indices","index","texture","rows","fromRow","tenant","width","placements","heldRows","range","a","targetRow","from","to","length","group","height","type","deviceMaxTextureSize","renderer","backend","wgpu","_b","_a","gl","max","maxTextureSize","addMergedUpdateRange","attribute","mergedStart","mergedEnd","disjoint","rangeEnd","MAX_DOF_RADIUS_PX","MAX_DOF_VARIANCE","apertureAngleFromSize","aperture","focusDistance","size","focus","computeDofCocVariancePx2","depth","apertureAngle","focusBlur","apertureRadius","cocRadiusPx","variance","maxVariance","computeDofOpacityFade","detRaw","detBlur","clampDepthOfFieldSettings","settings","previous","MAX_SPLAT_RADIUS_PX","FRONTIER_ROOT_SIZE","DEFAULT_ISOTROPIC_VARIANCE_SCALE","applyIsotropicCovarianceOverride","covariance","_normal","mixFactor","varianceScale","e0","e1","e2","row0","row1","row2","r0","r1","r2","crossA","crossB","crossC","lenA","lenB","lenC","useA","useB","axis","thin","rThin","isoVar","isoCov","equalizeProjectedEigenvalues","lambda1","lambda2","circle","mix","capProjectedEigenvaluesToScreenRadius","screenRadiusPx","maxStdDev","targetVariance","capped1","capped2","enabled","asNode","vec3Uniform","uniform","sourceWorldTransform","columns","sourceId","localCenter","base","int","c0","c1","c2","c3","linear","shCoefficientCount","bands","shCoefficientReader","sh","textures","splatTexel","label","textureLoad","column","row","c","ivec2","groups","word","channels","uint","SH_C1","SH_C2","SH_C3","evaluateSplatSh","direction","x","y","z","coefficient","band1","xx","yy","zz","band2","applySplatMaterialGraph","material","mode","uniforms","pick","textureWidth","maxRadius","gaussianExponent","splatIndex","baseColor","poolCenter","placement","placed","shSum","colorAfterSh","makeNormal","covA","covB","poolSigma","inverse","toCamera","makeChannel","channel","stack","splatColor","varying","colorSpaceToWorking","quadPosition","positionGeometry","viewDepthVarying","opacityCompensation","vAdjustedStdDev","vAlpha2","vVisualOpacity","Fn","center","clipCenter","cameraProjectionMatrix","clipPosition","frustumNdc","margin","inFrustum","isVisible","If","covarianceBase","placedCovariance","covariance3d","invZ","invZ2","j1","j2","viewRotationT","u1","u2","aQ","dQ","bQ","aRaw","dRaw","bRaw","halfApertureAngle","cocVar","lowPassVariance","d","mipFade","mid","radius","vec2","equalized","capped","eigenvector1","stdDev","encodedOriginal","alpha2","remap","majorLambda","minSplat","majorAxis","minorAxis","writePosition","pixelOffset","ndcCenter","notBlob","distance","ownSize","packedParent","isLeaf","absParent","parentSize","limitDist","ownCut","maxScreen","minScreen","projRadius","inBand","above","majorRadius","minorRadius","opacity","contributionOk","passes","squaredDistance","Discard","g","aExp","alpha","litCenter","tslTexture","screenUV","ox","oy","s0","s1","s2","s3","s4","wSum","rgbSoft","aSoft","litSoft","lit","factor","rgb","gaussian","r","viewDepthRadius","modelView","m","hasUploaded","dropMirror","array","bytes","Ctor","StorageMirrorReleaser","attributes","freed","version","warn","_ComputeSorter","capacity","centersTexture","dataTextureWidth","centersBuffer","perSource","BUCKET_COUNT","BLOCK_SIZE","histogramAttribute","blockSumsAttribute","superBlockSumsAttribute","bucketsAttribute","histogram","storage","blockSums","superBlockSums","buckets","sourceIndex","order","workCenters","atomicStore","instanceIndex","poolIndex","texel","depthPoint","bucket","atomicAdd","runningTotal","Loop","slot","bucketCount","atomicLoad","blockTotal","scanSuperBlockSums","superBlockTotal","offset","destination","activeCount","rounded","bounds","viewRadius","near","far","blockCount","pass","releaseRendererAttributes","ComputeSorter","map","TWO_MILLION","FIVE_MILLION","EIGHT_MILLION","MODEL_VIEW_EPSILON","validateSortIntervalMs","value","automaticSortIntervalMs","isMobile","WebGpuSortScheduler","sortIntervalMs","lastAcceptedModelView","now","settled","moved","maximumElementDelta","interval","maximum","_drawingBufferSize","resolveXrView","camera","xr","head","eye","viewport","isXrArrayCamera","xrSessionInit","isWebGPU","required","optional","DEFAULT_RELIGHT_BLEND","DEFAULT_RELIGHT_BRIGHTNESS","DEFAULT_RELIGHT_BACKGROUND","DEFAULT_RELIGHT_SOFTNESS","clampRelightingSettings","partial","blend","brightness","background","softness","createPlaceholderRelightTexture"],"mappings":"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"}
|