@voluma/vlam 0.2.4 → 0.3.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/README.md +3 -2
- package/dist/budget-governor.d.ts +4 -4
- package/dist/camera-budget-governor.d.ts +11 -11
- package/dist/chunk-cache-budget.d.ts +5 -5
- package/dist/chunk-fetch-scheduler.d.ts +4 -4
- package/dist/chunk-loader-DlNwggfG.js +1859 -0
- package/dist/chunk-loader-DlNwggfG.js.map +1 -0
- package/dist/chunk-loader.d.ts +2 -2
- package/dist/compute-sorter.d.ts +1 -1
- package/dist/effects.d.ts +11 -10
- package/dist/effects.js.map +1 -1
- package/dist/formats/ksplat/index.d.ts +1 -1
- package/dist/formats/lcc/collision-mesh.d.ts +2 -2
- package/dist/formats/lcc.js +17 -16
- package/dist/formats/lcc.js.map +1 -1
- package/dist/formats/ply/index.d.ts +1 -1
- package/dist/formats/ply/parse-splat-ply.d.ts +2 -2
- package/dist/formats/ply.js +1 -1
- package/dist/formats/ply.js.map +1 -1
- package/dist/formats/rad/index.d.ts +1 -1
- package/dist/formats/rad/parse-rad.d.ts +1 -1
- package/dist/formats/rad/rad.d.ts +2 -2
- package/dist/formats/rad.js +3 -3
- package/dist/formats/rad.js.map +1 -1
- package/dist/formats/sog/index.d.ts +1 -1
- package/dist/formats/sog/parse-sog.d.ts +4 -4
- package/dist/formats/sog.js.map +1 -1
- package/dist/formats/splat/index.d.ts +1 -1
- package/dist/formats/spz/index.d.ts +1 -1
- package/dist/index.d.ts +16 -25
- package/dist/index.js +253 -9047
- package/dist/index.js.map +1 -1
- package/dist/{load-scene.d.ts → load-splat-data.d.ts} +15 -13
- package/dist/loaders.d.ts +15 -0
- package/dist/loaders.js +26 -0
- package/dist/loaders.js.map +1 -0
- package/dist/loading-wLo8vRbA.js.map +1 -1
- package/dist/loading.d.ts +8 -8
- package/dist/{splat-mesh-types-CTN_duw2.js → lod-scheduler-B0a_uBlv.js} +133 -140
- package/dist/lod-scheduler-B0a_uBlv.js.map +1 -0
- package/dist/lod-scheduler.d.ts +1 -1
- package/dist/lod-source.d.ts +5 -5
- package/dist/{splat-scene.d.ts → merged-splat-mesh.d.ts} +14 -15
- package/dist/orientation.d.ts +7 -7
- package/dist/{ply-header-BwgExCn-.js → ply-header-B9WllbPg.js} +3 -3
- package/dist/{ply-header-BwgExCn-.js.map → ply-header-B9WllbPg.js.map} +1 -1
- package/dist/radix-sorter-R0FCSqKu.js +178 -0
- package/dist/radix-sorter-R0FCSqKu.js.map +1 -0
- package/dist/relighting-CSQF3UcL.js +969 -0
- package/dist/relighting-CSQF3UcL.js.map +1 -0
- package/dist/relighting.d.ts +1 -1
- package/dist/selection.d.ts +13 -0
- package/dist/selection.js +137 -0
- package/dist/selection.js.map +1 -0
- package/dist/source-transform.d.ts +4 -4
- package/dist/{splat-budget-DXew_86c.js → splat-budget-DuVZspPQ.js} +7 -7
- package/dist/{splat-budget-DXew_86c.js.map → splat-budget-DuVZspPQ.js.map} +1 -1
- package/dist/splat-budget.d.ts +1 -1
- package/dist/splat-data-BZM5dhhx.js.map +1 -1
- package/dist/splat-data.d.ts +4 -4
- package/dist/splat-mesh-aQdcWA6v.js +1942 -0
- package/dist/splat-mesh-aQdcWA6v.js.map +1 -0
- package/dist/splat-mesh-material.d.ts +2 -2
- package/dist/splat-mesh-types-BZIko-_9.js +18 -0
- package/dist/splat-mesh-types-BZIko-_9.js.map +1 -0
- package/dist/splat-mesh-types.d.ts +14 -8
- package/dist/splat-mesh.d.ts +4 -4
- package/dist/splat-modifier.d.ts +5 -5
- package/dist/splat-partition.d.ts +1 -1
- package/dist/static-lod-entry.d.ts +12 -0
- package/dist/static-lod-splat-mesh.d.ts +2 -2
- package/dist/{static-lod-worker-D2fyqQZO.js → static-lod-worker-DktKnJkR.js} +46 -46
- package/dist/{static-lod-worker-D2fyqQZO.js.map → static-lod-worker-DktKnJkR.js.map} +1 -1
- package/dist/static-lod.js +148 -0
- package/dist/static-lod.js.map +1 -0
- package/dist/streamed-splat-mesh.d.ts +21 -27
- package/dist/streaming.d.ts +23 -0
- package/dist/streaming.js +2877 -0
- package/dist/streaming.js.map +1 -0
- package/dist/{unified-splat-renderer.d.ts → unified-splat-mesh.d.ts} +10 -10
- package/dist/unified-work-buffer.d.ts +66 -0
- package/dist/unified.d.ts +11 -0
- package/dist/unified.js +738 -0
- package/dist/unified.js.map +1 -0
- package/dist/webgpu-limits-Y3CI_6_6.js +43 -0
- package/dist/webgpu-limits-Y3CI_6_6.js.map +1 -0
- package/dist/webgpu-limits.d.ts +0 -59
- package/dist/work-buffer-gather.d.ts +1 -1
- package/package.json +24 -4
- package/dist/splat-mesh-types-CTN_duw2.js.map +0 -1
package/dist/formats/lcc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lcc.js","sources":["../../src/lib/formats/lcc/parse-lcc.ts","../../src/lib/formats/lcc/lcc2-transform.ts","../../src/lib/formats/lcc/lcc.ts","../../src/lib/formats/lcc/lcc2.ts","../../src/lib/formats/lcc/parse-mesh-ply.ts","../../src/lib/formats/lcc/parse-collision-lci.ts","../../src/lib/formats/lcc/collision-mesh.ts","../../src/lib/formats/lcc/collision-partition.ts"],"sourcesContent":["import { writeCovariance, type SplatData } from '../../splat-data';\nimport { packedRangesEqual, requantizeShWord } from '../../sh-pack';\n\n/**\n * Decoder for XGRIDS' older LCC datasets (`.lcc` / `meta.lcc`; see\n * `docs/formats/lcc-notes.md`). Manifest versions **2.x** through **5.x** share the\n * same binary layout; v2/v3 omit `fileType` and v3 often names the manifest `meta.lcc`.\n *\n * Unlike `.lcc2` - which delivers SOG/WebP tiles and reuses {@link parseSog} -\n * an LCC dataset is raw binary: a JSON manifest, an `index.bin` of per-cell\n * per-level ranges, and one large `data.bin` of fixed 32-byte splat records\n * that chunks are cut out of with HTTP range requests.\n *\n * Implemented from XGRIDS' openly published spec (Data Organization Format\n * originated from XGRIDS); no XGRIDS code is used, and their capture data is\n * never redistributed. Where the whitepaper's field table disagrees with real\n * captures the bytes win - see `docs/formats/lcc-notes.md` for the verification.\n */\n\n/** Bytes per splat in `data.bin`. */\nexport const LCC_RECORD_BYTES = 32;\n/** Bytes per splat in `shcoef.bin`, and of the SH block inside an env record. */\nexport const LCC_SH_RECORD_BYTES = 64;\n/** SH coefficients per channel in a `Quality` capture (3rd order). */\nexport const LCC_SH_COEFFICIENTS = 15;\n\n/**\n * Upper bound on a decoded chunk's splat count, mirroring {@link parseSog}'s\n * guard: the renderer cannot draw more than the 2048-wide pool textures hold,\n * so a corrupt index should be rejected rather than allocate gigabytes.\n */\nconst MAX_CHUNK_SPLATS = 1 << 24;\n\n/** A `[min, max]` dequantization range from the manifest's `attributes`. */\nexport interface LccRange {\n readonly min: readonly [number, number, number];\n readonly max: readonly [number, number, number];\n}\n\n/**\n * Where a capture's local origin sits in the real world. Purely informational\n * - the splat data is local and is rendered as-is (see `parseGeoreference`).\n */\nexport interface LccGeoreference {\n /** The local origin's coordinates in {@link epsg}, from manifest `offset`. */\n readonly origin: readonly [number, number, number];\n /** The projection's EPSG code, when the manifest names one. */\n readonly epsg?: number;\n}\n\n/** The manifest fields this loader consumes. */\nexport interface LccManifest {\n readonly totalSplats: number;\n readonly totalLevel: number;\n readonly cellLengthX: number;\n readonly cellLengthY: number;\n readonly indexDataSize: number;\n /** Per-level splat totals; index 0 is the finest level. */\n readonly splats: readonly number[];\n readonly bounds: { min: [number, number, number]; max: [number, number, number] };\n /** `Quality` captures carry `shcoef.bin`; `Portable` ones have no SH. */\n readonly fileType: 'Portable' | 'Quality';\n readonly scale: LccRange;\n readonly shcoef: LccRange;\n readonly envScale: LccRange;\n readonly envShcoef: LccRange;\n /** Set when the capture is georeferenced; absent for a purely local one. */\n readonly georeference?: LccGeoreference;\n}\n\n/** One cell's range within `data.bin` at one LOD level. */\nexport interface LccLevelRange {\n readonly count: number;\n readonly byteOffset: number;\n readonly byteSize: number;\n}\n\n/** One `index.bin` record: a cell of the 2D grid, at every LOD level. */\nexport interface LccIndexCell {\n readonly cellX: number;\n readonly cellY: number;\n /** Range per level; index 0 is the finest. */\n readonly levels: readonly LccLevelRange[];\n}\n\n/** Everything the worker needs to turn fetched bytes into splats. */\nexport interface LccChunkParams {\n /** `splats` decodes `data.bin`; `environment` decodes `environment.bin`. */\n readonly kind: 'splats' | 'environment';\n /** Byte range within the source file. */\n readonly start: number;\n readonly length: number;\n /**\n * Bytes per record. Always 32 in `data.bin`; `environment.bin` interleaves\n * a 64-byte SH block per splat in a `Quality` capture, making it 96.\n *\n * This is a property of the *file*, not of whether the SH is wanted - a\n * `Quality` environment stays 96 bytes per record even with SH disabled.\n */\n readonly stride: number;\n /** Dequantization range for the scale field (`scale` or `envscale`). */\n readonly scale: LccRange;\n /** Present when this chunk's SH should be decoded (see `LccShParams`). */\n readonly sh?: LccShParams;\n}\n\n/** How to obtain (and dequantize) a chunk's higher-order SH. */\nexport interface LccShParams {\n /** SH bands to keep: 1, 2 or 3 → 3, 8 or 15 coefficients. */\n readonly bands: 1 | 2 | 3;\n readonly range: LccRange;\n /**\n * Where the SH bytes come from. `sidecar` fetches `shcoef.bin` over the\n * given range (`data.bin` offset × 2); `inline` reads the 64-byte block\n * that follows each base record inside a 96-byte environment record.\n */\n readonly source: 'sidecar' | 'inline';\n /** Absolute `shcoef.bin` URL; only for `source: 'sidecar'`. */\n readonly url?: string;\n /**\n * Re-encode the decoded words into this range instead of {@link range}.\n *\n * An LCC capture quantizes base SH (`shcoef`) and environment SH\n * (`envshcoef`) against *different* ranges, but the shared pool decodes the\n * whole scene through one range uniform. Left alone, whichever chunk lands\n * first wins and the other's coefficients are read against the wrong span -\n * the environment range is wider, so base SH gets amplified into a\n * view-dependent \"flashlight.\" So the environment chunk is requantized into\n * the base range here, letting every chunk agree on `shcoef`. Values outside\n * the base range clip (only decorative sky highlights, and only the few\n * thousand environment splats pay the re-encode).\n */\n readonly requantizeTo?: LccRange;\n}\n\n/** Coefficients carried by each SH band count. */\nexport function lccShCoefficientCount(bands: 1 | 2 | 3): number {\n return bands === 1 ? 3 : bands === 2 ? 8 : 15;\n}\n\n/**\n * Validates an untrusted `.lcc` manifest and extracts the fields the loader\n * needs. Everything downstream (index record size, chunk byte ranges, pool\n * sizing) derives from these, so they are checked up front.\n *\n * @throws {Error} on an unsupported version or a malformed manifest.\n */\nexport function parseLccManifest(json: unknown): LccManifest {\n const raw = json as Record<string, unknown>;\n const version = String(raw['version'] ?? '');\n // 2.x, 3.x (older L2 Pro / `meta.lcc`), 4.x and 5.x share the same binary\n // layout; 2.x and 3.x omit `fileType`, 4.x and 5.x write it. 4.x was\n // verified against a real capture (conferencehall) - its only quirk is\n // manifest `splats` counts that slightly over-declare what index.bin holds,\n // which the index cross-check in `lcc.ts` tolerates. 2.x was verified\n // against a real capture (trainmuseum): base attributes only, no\n // environment records, and every field this parser reads carries the same\n // meaning as in 3.x. Other major versions have not been verified.\n if (!/^[2345]\\./.test(version)) {\n throw new Error(\n `Unsupported LCC manifest version: \"${version}\" (expected 2.x, 3.x, 4.x or 5.x).`,\n );\n }\n // The whitepaper defines only COMPRESS; anything else is a format we have\n // not seen and would silently mis-decode.\n const encoding = raw['encoding'];\n if (encoding !== undefined && encoding !== 'COMPRESS') {\n throw new Error(`Unsupported LCC encoding: \"${String(encoding)}\" (expected COMPRESS).`);\n }\n\n const totalLevel = raw['totalLevel'];\n if (!Number.isSafeInteger(totalLevel) || (totalLevel as number) < 1) {\n throw new Error(`LCC manifest declares an invalid totalLevel: ${String(totalLevel)}.`);\n }\n const levels = totalLevel as number;\n\n const indexDataSize = raw['indexDataSize'];\n const expectedIndexSize = 4 + levels * 16;\n if (indexDataSize !== expectedIndexSize) {\n throw new Error(\n `LCC manifest indexDataSize is ${String(indexDataSize)}, but ${levels} levels imply ${expectedIndexSize}.`,\n );\n }\n\n const splats = raw['splats'];\n if (!Array.isArray(splats) || splats.length !== levels) {\n throw new Error(`LCC manifest \"splats\" must list one count per level (${levels}).`);\n }\n for (const count of splats) {\n if (!Number.isSafeInteger(count) || count < 0) {\n throw new Error(`LCC manifest \"splats\" contains an invalid count: ${String(count)}.`);\n }\n }\n\n const totalSplats = raw['totalSplats'];\n if (!Number.isSafeInteger(totalSplats) || (totalSplats as number) < 0) {\n throw new Error(`LCC manifest declares an invalid totalSplats: ${String(totalSplats)}.`);\n }\n\n const cellLengthX = numberField(raw, 'cellLengthX');\n const cellLengthY = numberField(raw, 'cellLengthY');\n if (cellLengthX <= 0 || cellLengthY <= 0) {\n throw new Error('LCC manifest cell lengths must be positive.');\n }\n\n // `offset` is a georeference anchor, not a transform of the splat data: it\n // names where the local origin sits in the manifest's `epsg` projection\n // (e.g. UTM easting/northing/height), while `boundingBox` and every record\n // in `data.bin` stay local. Adding it to positions would be wrong twice\n // over - it double-places the scene, and baking hundreds of kilometres into\n // float32 destroys the precision splats are rendered with. So it is carried\n // as metadata and left out of the geometry.\n const georeference = parseGeoreference(raw);\n\n // `shift` and `scale`, unlike `offset`, would genuinely transform the data.\n // No capture has used them, so refuse rather than place the scene wrong.\n const shift = raw['shift'];\n if (Array.isArray(shift) && shift.some((v) => v !== 0)) {\n throw new Error(\n `LCC manifest \"shift\" is non-zero (${JSON.stringify(shift)}), which is not supported.`,\n );\n }\n const scaleTransform = raw['scale'];\n if (Array.isArray(scaleTransform) && scaleTransform.some((v) => v !== 1)) {\n throw new Error(\n `LCC manifest \"scale\" is non-identity (${JSON.stringify(scaleTransform)}), which is not supported.`,\n );\n }\n\n const bounds = raw['boundingBox'] as { min?: unknown; max?: unknown } | undefined;\n const min = triple(bounds?.min, 'boundingBox.min');\n const max = triple(bounds?.max, 'boundingBox.max');\n\n const attributes = raw['attributes'];\n if (!Array.isArray(attributes)) throw new Error('LCC manifest is missing \"attributes\".');\n const byName = new Map<string, LccRange>();\n for (const entry of attributes as { name?: unknown; min?: unknown; max?: unknown }[]) {\n if (typeof entry?.name !== 'string') continue;\n // Opacity is 1-D; only the 3-D ranges the decoder reads are kept.\n if (!Array.isArray(entry.min) || entry.min.length < 3) continue;\n byName.set(entry.name, {\n min: triple(entry.min, `attributes.${entry.name}.min`),\n max: triple(entry.max, `attributes.${entry.name}.max`),\n });\n }\n const require = (name: string): LccRange => {\n const range = byName.get(name);\n if (!range) throw new Error(`LCC manifest is missing the \"${name}\" attribute range.`);\n return range;\n };\n\n const shcoef = byName.get('shcoef') ?? ZERO_RANGE;\n const fileType = resolveFileType(raw['fileType'], shcoef);\n\n return {\n totalSplats: totalSplats as number,\n totalLevel: levels,\n cellLengthX,\n cellLengthY,\n indexDataSize: expectedIndexSize,\n splats: splats as number[],\n bounds: { min, max },\n fileType,\n scale: require('scale'),\n // A Portable capture has no SH at all; keep a benign range so the type\n // stays total rather than sprinkling optionality through the loader.\n shcoef,\n envScale: byName.get('envscale') ?? ZERO_RANGE,\n envShcoef: byName.get('envshcoef') ?? ZERO_RANGE,\n georeference,\n };\n}\n\n/**\n * Reads the capture's world anchor. Absent, all-zero or unprojected (`epsg`\n * missing or 0) manifests are simply local, so they report no georeference\n * rather than a meaningless origin.\n */\nfunction parseGeoreference(raw: Record<string, unknown>): LccGeoreference | undefined {\n const offset = raw['offset'];\n if (!Array.isArray(offset) || !offset.some((v) => v !== 0)) return undefined;\n const epsg = raw['epsg'];\n return {\n origin: triple(offset, 'offset'),\n epsg: Number.isSafeInteger(epsg) && (epsg as number) > 0 ? (epsg as number) : undefined,\n };\n}\n\n/**\n * Resolves `Portable` vs `Quality`. v5.0 writes the field; v3.0 omits it.\n * Portable manifests still list a stub `shcoef` of `[0,0,0]..[1,1,1]` (or\n * zeros); a real Quality capture has trained ranges outside that.\n */\nfunction resolveFileType(fileType: unknown, shcoef: LccRange): 'Portable' | 'Quality' {\n if (fileType === 'Portable' || fileType === 'Quality') return fileType;\n if (fileType === undefined) return isPlaceholderShcoef(shcoef) ? 'Portable' : 'Quality';\n throw new Error(\n `Unsupported LCC fileType: \"${String(fileType)}\" (expected Portable or Quality).`,\n );\n}\n\n/** True when `shcoef` looks like a Portable stub rather than trained ranges. */\nfunction isPlaceholderShcoef(range: LccRange): boolean {\n return range.min.every((v) => v === 0) && range.max.every((v) => v === 0 || v === 1);\n}\n\nconst ZERO_RANGE: LccRange = { min: [0, 0, 0], max: [0, 0, 0] };\n\n/**\n * Parses `index.bin` into one record per cell.\n *\n * @param buffer - The whole file (a few KB even for large captures).\n * @param manifest - Supplies the record size and level count.\n * @throws {Error} if the file length or any record is inconsistent.\n */\nexport function parseLccIndex(buffer: ArrayBuffer, manifest: LccManifest): LccIndexCell[] {\n const { indexDataSize, totalLevel } = manifest;\n if (buffer.byteLength === 0 || buffer.byteLength % indexDataSize !== 0) {\n throw new Error(\n `LCC index.bin is ${buffer.byteLength} bytes, not a multiple of indexDataSize ${indexDataSize}.`,\n );\n }\n const view = new DataView(buffer);\n const cells: LccIndexCell[] = [];\n for (let offset = 0; offset < buffer.byteLength; offset += indexDataSize) {\n const levels: LccLevelRange[] = [];\n for (let level = 0; level < totalLevel; level++) {\n const at = offset + 4 + level * 16;\n const count = view.getUint32(at, true);\n const byteOffsetBig = view.getBigUint64(at + 4, true);\n // Captures are far below 2^53 bytes; assert so a future 8 EB-addressed\n // dataset fails loudly here instead of silently losing precision.\n if (byteOffsetBig > BigInt(Number.MAX_SAFE_INTEGER)) {\n throw new Error(\n `LCC index.bin has a byte offset beyond Number.MAX_SAFE_INTEGER: ${byteOffsetBig}.`,\n );\n }\n const byteSize = view.getUint32(at + 12, true);\n if (byteSize !== count * LCC_RECORD_BYTES) {\n throw new Error(\n `LCC index.bin level ${level} declares ${count} splats but ${byteSize} bytes ` +\n `(expected ${count * LCC_RECORD_BYTES}).`,\n );\n }\n levels.push({ count, byteOffset: Number(byteOffsetBig), byteSize });\n }\n cells.push({\n cellX: view.getUint16(offset, true),\n cellY: view.getUint16(offset + 2, true),\n levels,\n });\n }\n return cells;\n}\n\n/**\n * Decodes a packed rotation into a quaternion `[w, x, y, z]`.\n *\n * Smallest-three: bits 30-31 name the omitted (largest-magnitude) component\n * as an index into `(x, y, z, w)`, and three 10-bit fields hold the others in\n * ascending index order as offset binary with bias 511, spanning `[-√½, +√½]`.\n * The omitted component is recovered from unit length.\n *\n * Note this is neither the whitepaper's `QLut`/`DecodeRotation` nor the\n * LCC-Web SDK's sign-magnitude `parseQuat` (which decodes the SDK's own\n * repacked GPU texture, not the file) - see `docs/formats/lcc-notes.md`.\n *\n * The result is unit-length for any word a real encoder produces. A corrupt\n * word whose three stored components already exceed unit length yields a\n * non-unit (but always finite) quaternion, as in {@link parseSog}'s\n * equivalent decode; {@link writeCovariance} normalizes.\n */\nexport function decodeLccRotation(encoded: number): [number, number, number, number] {\n const largest = (encoded >>> 30) & 3;\n const q = [0, 0, 0, 0]; // (x, y, z, w)\n let next = 0;\n for (let index = 0; index < 4; index++) {\n if (index === largest) continue;\n const block = (encoded >>> (10 * next)) & 0x3ff;\n q[index] = (block - 511) * QUATERNION_SCALE;\n next++;\n }\n const rest =\n (q[0] as number) ** 2 + (q[1] as number) ** 2 + (q[2] as number) ** 2 + (q[3] as number) ** 2;\n q[largest] = Math.sqrt(Math.max(0, 1 - rest));\n return [q[3] as number, q[0] as number, q[1] as number, q[2] as number];\n}\n\n/** Maps a 10-bit offset-binary field onto `[-√½, +√½]`. */\nconst QUATERNION_SCALE = 1 / (511 * Math.SQRT2);\n\n/**\n * Decodes one packed SH word into its three channel values.\n *\n * `DecodePacked_11_10_11`: R is bits 0-10, G bits 11-20, B bits 21-31, each a\n * unit fraction of its field's maximum, dequantized across `range`.\n */\nexport function decodeLccShWord(\n word: number,\n range: LccRange,\n out: Float32Array,\n at: number,\n): void {\n const r = (word & 0x7ff) / 2047;\n const g = ((word >>> 11) & 0x3ff) / 1023;\n const b = ((word >>> 21) & 0x7ff) / 2047;\n out[at + 0] = lerp(range.min[0], range.max[0], r);\n out[at + 1] = lerp(range.min[1], range.max[1], g);\n out[at + 2] = lerp(range.min[2], range.max[2], b);\n}\n\nfunction lerp(min: number, max: number, t: number): number {\n return min + (max - min) * t;\n}\n\n/**\n * Decodes a range of `data.bin` (or the base records of `environment.bin`)\n * into the flat arrays the pool uploads.\n *\n * @param buffer - Exactly the chunk's bytes: `count * 32` for `splats`, or\n * `count * stride` for `environment`.\n * @param params - Byte range and dequantization ranges from the scene builder.\n * @param sh - Optional SH bytes: the matching `shcoef.bin` range for\n * `source: 'sidecar'`, or unused when the SH is inline in `buffer`.\n * @param signal - Checked inside the decode loop so a superseded chunk can be\n * abandoned mid-decode.\n * @throws a `DOMException` named `AbortError` when cancelled.\n */\nexport async function parseLccChunk(\n buffer: ArrayBuffer,\n params: LccChunkParams,\n sh?: ArrayBuffer,\n signal?: AbortSignal,\n): Promise<SplatData> {\n const stride = params.stride;\n if (stride !== LCC_RECORD_BYTES && stride !== LCC_RECORD_BYTES + LCC_SH_RECORD_BYTES) {\n throw new Error(`LCC chunk declares an unsupported ${stride}-byte record.`);\n }\n if (params.sh?.source === 'inline' && stride === LCC_RECORD_BYTES) {\n throw new Error('LCC chunk asks for inline SH but its records have no room for it.');\n }\n if (buffer.byteLength % stride !== 0) {\n throw new Error(\n `LCC chunk is ${buffer.byteLength} bytes, not a multiple of the ${stride}-byte record.`,\n );\n }\n const count = buffer.byteLength / stride;\n if (count > MAX_CHUNK_SPLATS) {\n throw new Error(\n `LCC chunk holds ${count} splats, above the supported maximum of ${MAX_CHUNK_SPLATS}.`,\n );\n }\n\n const view = new DataView(buffer);\n const positions = new Float32Array(count * 3);\n const colors = new Uint8Array(count * 4);\n const covariances = new Float32Array(count * 6);\n const bytes = new Uint8Array(buffer);\n\n const { min, max } = params.scale;\n const scaleMinX = min[0],\n scaleMinY = min[1],\n scaleMinZ = min[2];\n const scaleSpanX = max[0] - min[0],\n scaleSpanY = max[1] - min[1],\n scaleSpanZ = max[2] - min[2];\n\n for (let i = 0; i < count; i++) {\n // The decode loop dominates chunk time (a single cell's finest level can\n // be ~2.9M splats). With a signal, yield every 65,536 splats so a pending\n // cancel can be delivered at all - the worker is single-threaded.\n if (signal && (i & 0xffff) === 0 && i > 0) {\n await new Promise((resolve) => setTimeout(resolve, 0));\n signal.throwIfAborted();\n }\n const at = i * stride;\n\n positions[i * 3 + 0] = view.getFloat32(at, true);\n positions[i * 3 + 1] = view.getFloat32(at + 4, true);\n positions[i * 3 + 2] = view.getFloat32(at + 8, true);\n\n // Colors are stored exactly as the pool wants them: RGB pre-mapped through\n // 0.5 + sh·SH_C0, and alpha already activated (no sigmoid, as in SOG).\n colors[i * 4 + 0] = bytes[at + 12] as number;\n colors[i * 4 + 1] = bytes[at + 13] as number;\n colors[i * 4 + 2] = bytes[at + 14] as number;\n colors[i * 4 + 3] = bytes[at + 15] as number;\n\n // Scale is quantized linear (already exponentiated), unlike PLY/SOG.\n const [qw, qx, qy, qz] = decodeLccRotation(view.getUint32(at + 22, true));\n writeCovariance(\n covariances,\n i,\n scaleMinX + scaleSpanX * (view.getUint16(at + 16, true) / 65535),\n scaleMinY + scaleSpanY * (view.getUint16(at + 18, true) / 65535),\n scaleMinZ + scaleSpanZ * (view.getUint16(at + 20, true) / 65535),\n qw,\n qx,\n qy,\n qz,\n );\n }\n\n signal?.throwIfAborted();\n const packed = params.sh ? decodeChunkSh(buffer, sh, params.sh, count, stride) : undefined;\n return {\n count,\n positions,\n colors,\n covariances,\n ...(packed ? { shPacked: packed } : {}),\n };\n}\n\n/**\n * Extracts a chunk's SH words, keeping only the requested bands.\n *\n * The words stay packed: the shader decodes them, so this is a copy rather\n * than a 45-float-per-splat expansion. Coefficients are ordered low-band\n * first, so a lower band count is a prefix of each 16-word record.\n */\nfunction decodeChunkSh(\n buffer: ArrayBuffer,\n sidecar: ArrayBuffer | undefined,\n params: LccShParams,\n count: number,\n stride: number,\n): NonNullable<SplatData['shPacked']> {\n const coefficients = lccShCoefficientCount(params.bands);\n const packed = new Uint32Array(count * coefficients);\n\n if (params.source === 'inline') {\n // Environment records carry their 64-byte SH block right after the base.\n const view = new DataView(buffer);\n for (let i = 0; i < count; i++) {\n const at = i * stride + LCC_RECORD_BYTES;\n for (let c = 0; c < coefficients; c++)\n packed[i * coefficients + c] = view.getUint32(at + c * 4, true);\n }\n return finishChunkSh(packed, params);\n }\n\n if (!sidecar) throw new Error('LCC chunk expected shcoef.bin bytes but none were fetched.');\n const expected = count * LCC_SH_RECORD_BYTES;\n if (sidecar.byteLength !== expected) {\n throw new Error(\n `LCC shcoef.bin range is ${sidecar.byteLength} bytes; expected ${expected} for ${count} splats.`,\n );\n }\n const view = new DataView(sidecar);\n for (let i = 0; i < count; i++) {\n const at = i * LCC_SH_RECORD_BYTES;\n for (let c = 0; c < coefficients; c++)\n packed[i * coefficients + c] = view.getUint32(at + c * 4, true);\n }\n return finishChunkSh(packed, params);\n}\n\n/**\n * Tags the packed words with their range, re-encoding into\n * {@link LccShParams.requantizeTo} first when the chunk's own range differs\n * from it (the environment case - see that field's note).\n */\nfunction finishChunkSh(\n packed: Uint32Array,\n params: LccShParams,\n): NonNullable<SplatData['shPacked']> {\n const to = params.requantizeTo;\n if (!to || packedRangesEqual(params.range, to)) {\n return { bands: params.bands, packed, range: params.range };\n }\n for (let i = 0; i < packed.length; i++) {\n packed[i] = requantizeShWord(packed[i] as number, params.range, to);\n }\n return { bands: params.bands, packed, range: to };\n}\n\nfunction numberField(raw: Record<string, unknown>, key: string): number {\n const value = raw[key];\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n throw new Error(`LCC manifest field \"${key}\" is not a finite number.`);\n }\n return value;\n}\n\nfunction triple(value: unknown, label: string): [number, number, number] {\n if (!Array.isArray(value) || value.length < 3)\n throw new Error(`LCC manifest \"${label}\" must have three numbers.`);\n const out = value.slice(0, 3).map((v) => {\n if (typeof v !== 'number' || !Number.isFinite(v))\n throw new Error(`LCC manifest \"${label}\" has a non-finite entry.`);\n return v;\n });\n return out as [number, number, number];\n}\n","import * as THREE from 'three/webgpu';\n\n/**\n * LCC2 / XGRIDS captures are Z-up. Three.js (and this package's SOG/PLY path)\n * expects Y-up. The established LCC/Spark convention maps raw LCC coordinates\n * `(x, y, z)` → `(-x, z, y)` - equivalent to −90° about X followed by 180°\n * about Y. Kept as a single matrix so consumers cannot accidentally apply only\n * half of the correction.\n *\n * Basis check: +X → −X, +Y → +Z, +Z → +Y.\n */\nexport function createLcc2ToThreeMatrix(): THREE.Matrix4 {\n // Row-major: x'=-x, y'=z, z'=y.\n return new THREE.Matrix4().set(-1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1);\n}\n\n/**\n * Applies the LCC2→Three.js format correction to a mesh. Bounds and LOD data\n * stay in source-local coordinates; `matrixWorld` / `worldToLocal` pick this up.\n * `StreamedSplatMesh.load()` applies this correction automatically for LCC2;\n * use this helper only with meshes created through another loading path.\n */\nexport function applyLcc2ToThreeTransform(target: THREE.Object3D): void {\n target.matrix.copy(createLcc2ToThreeMatrix());\n target.matrix.decompose(target.position, target.quaternion, target.scale);\n target.matrixWorldNeedsUpdate = true;\n}\n","import * as THREE from 'three/webgpu';\nimport { LodScheduler } from '../../lod-scheduler';\nimport type { LodLeaf, LodManifest, LodRange } from '../../lod-manifest';\nimport type { LodSourceOptions, StreamedChunkOptions, StreamedScene } from '../../lod-source';\nimport { createLcc2ToThreeMatrix } from './lcc2-transform';\nimport { MAX_SH_BANDS } from '../../splat-mesh';\nimport {\n LCC_RECORD_BYTES,\n LCC_SH_RECORD_BYTES,\n parseLccIndex,\n parseLccManifest,\n type LccIndexCell,\n type LccManifest,\n} from './parse-lcc';\nimport { toRequestInit, toSplatLoadError, type SplatRequestOptions } from '../../loading';\nimport type { SplatDatasetSource } from '../../dataset-source';\n\n/**\n * Scene builder for XGRIDS' older LCC datasets (`.lcc` / `meta.lcc`, manifest v3–v5;\n * format details in `docs/formats/lcc-notes.md`, record decoding in\n * {@link parseLccChunk}).\n *\n * An LCC capture is a flat 2D grid of cells, each holding an independent splat\n * set at every LOD level - not the shared-ancestor octree `.lcc2` uses. This\n * builder synthesizes a {@link LodManifest} over the cells and reuses\n * {@link LodScheduler} rather than adding a second selector.\n *\n * **The whole LOD ladder is scheduled**, so a distant or over-budget cell\n * coarsens to a smaller level rather than being dropped whole - full coverage\n * at any budget, matching the XGRIDS viewer. See the note in\n * {@link buildLccScene} for the streak-vs-coverage tradeoff an LCC LOD ladder\n * carries.\n *\n * The splats live in one large `data.bin` (~300 MB), and each `(cell, level)`\n * is one contiguous slice of it. A cell whose finest level runs to millions of\n * splats (~90 MB) is **sub-chunked**: the cell becomes K sub-leaves, each\n * level partitioned K ways at shared boundaries, every `(cell, level, i)`\n * slice its own ranged chunk. Sub-leaves from one physical cell share an\n * atomic budget group: they stream independently, but the scheduler never\n * mixes their LOD levels. Some writers preserve spatial locality in record\n * order, for which independently budgeting slices would create rectangular\n * holes as refinement settles.\n * `environment.bin` becomes one extra always-resident chunk.\n */\n\n/**\n * Target splats per sub-chunk when a cell's finest level splits. Matches the\n * scheduler's `MAX_RUN_SPLATS` (128k) so a sub-leaf's run never re-splits, and\n * keeps a slice's `data.bin` range at ~4 MB (+8 MB `shcoef.bin` on a Quality\n * capture) - small enough that fetches pipeline through the in-flight cap and\n * an abort-on-move loses little.\n */\nconst SUBCHUNK_SPLATS = 128_000;\n\n/** Options for {@link buildLccScene} beyond the shared LOD knobs. */\nexport interface LccSceneOptions extends LodSourceOptions {\n /**\n * Higher-order SH bands to stream for a `Quality` capture: 0 disables SH\n * (nothing is fetched from `shcoef.bin`), 3 is the full 3rd order.\n * Unset means every band the capture carries. Always clamped to that -\n * a `Portable` capture has none, whatever is asked for.\n */\n shBands?: 0 | 1 | 2 | 3;\n signal?: AbortSignal;\n request?: SplatRequestOptions;\n}\n\n/**\n * Builds a streamed scene from a parsed `.lcc` manifest, fetching the small\n * sidecars (`index.bin`, and `environment.bin`'s size) it needs to lay out\n * chunks. `data.bin` itself is never fetched here.\n *\n * @param json - The parsed `.lcc` manifest.\n * @param source - Dataset used to resolve and fetch the manifest's sidecars.\n * @param options - LOD, request, cancellation, and spherical-harmonic options.\n */\nexport async function buildLccScene(\n json: unknown,\n source: SplatDatasetSource,\n options: LccSceneOptions,\n): Promise<StreamedScene> {\n const manifest = parseLccManifest(json);\n const require = (name: string): string => {\n const url = source.resolve(name);\n if (url === null) throw new Error(`LCC dataset is missing \"${name}\".`);\n return url;\n };\n const dataUrl = require('data.bin');\n const environmentUrl = source.resolve('environment.bin');\n\n const index = parseLccIndex(\n await fetchBytes(require('index.bin'), options, 'manifest'),\n manifest,\n );\n assertIndexMatchesManifest(index, manifest);\n\n // A Portable capture has no shcoef.bin at all, so it caps everyone at 0 -\n // asking for SH on one must not allocate pool textures for SH that can\n // never arrive, nor fetch a file that does not exist.\n const available = manifest.fileType === 'Quality' ? MAX_SH_BANDS : 0;\n const requested = options.shBands ?? available;\n const bands = Math.min(requested, available);\n const shBands: 1 | 2 | 3 | undefined = bands === 0 ? undefined : (bands as 1 | 2 | 3);\n const shUrl = shBands === undefined ? null : require('shcoef.bin');\n\n const chunkUrls: string[] = [];\n const chunkOptions: (StreamedChunkOptions | undefined)[] = [];\n const addChunk = (label: string, chunk: StreamedChunkOptions): number => {\n chunkUrls.push(label);\n chunkOptions.push(chunk);\n return chunkUrls.length - 1;\n };\n\n const leaves: LodLeaf[] = [];\n const pinnedFiles = new Set<number>();\n let minimumCoverageSplats = 0;\n // Every cell offers its whole LOD ladder, so the scheduler coarsens a\n // distant or over-budget cell to a smaller level instead of dropping it -\n // full coverage at any budget, the way the XGRIDS viewer behaves. (An\n // earlier revision scheduled only the finest level; on a capture whose\n // finest level dwarfs the budget that made whole central cells vanish.)\n //\n // The tradeoff an LCC LOD ladder carries: a coarser level is a decimated\n // *alternative*, not a refinement of the same surface - merging widens each\n // splat into a flat disc - so a coarse level forced near the camera reads as\n // a streak. The distance model plus `fillBudget` keep near cells at their\n // finest whenever the budget allows, so coarse levels surface only at\n // distance (where they read fine) or under a very tight budget (the accepted\n // cost).\n //\n // A big cell is split into K sub-leaves (its finest level in ~128k-splat\n // slices), every level partitioned K ways at the same boundaries and every\n // (cell, level, i) slice its own chunk file. Dedicated files mean the\n // scheduler's runs never coalesce across sub-leaves. Each slice streams on\n // its own (including resolved L0): waiting for a whole Quality cell's finest\n // cut before any swap left near detail stuck on coarse while fetches\n // churned. `budgetGroup` still keeps every slice on one scheduler cut so a\n // cell does not refine mid-ladder while a sibling is still coarse. This is\n // load-bearing for captures whose record order has spatial locality: mixing\n // levels within a cell would remove a compact patch rather than merely\n // thinning density. Each sub-leaf pins its own slice of the coarsest level\n // as substitute coverage while finer data lands farther out.\n const levelCount = manifest.totalLevel;\n for (let cellIndex = 0; cellIndex < index.length; cellIndex++) {\n const cell = index[cellIndex] as LccIndexCell;\n // K is sized from the finest *present* level; coarser levels split into\n // proportionally smaller slices at the same boundaries.\n let finestCount = 0;\n for (let level = 0; level < levelCount; level++) {\n const count = (cell.levels[level] as LccIndexCell['levels'][number]).count;\n if (count > 0) {\n finestCount = count;\n break;\n }\n }\n const subLeaves = Math.max(1, Math.ceil(finestCount / SUBCHUNK_SPLATS));\n for (let sub = 0; sub < subLeaves; sub++) {\n const lods: (LodRange | undefined)[] = new Array<LodRange | undefined>(levelCount).fill(\n undefined,\n );\n let coarsestFile = -1;\n let coarsestCount = 0;\n for (let level = 0; level < levelCount; level++) {\n const range = cell.levels[level] as LccIndexCell['levels'][number];\n // A level absent from a cell leaves a hole in `lods`; the scheduler\n // resolves to the nearest present level. A wholly empty cell keeps all\n // holes (as a single empty leaf).\n if (range.count === 0) continue;\n // This sub-leaf's slice of the level: floor boundaries partition the\n // level's records exactly across the K sub-leaves. A coarse level\n // with fewer records than sub-leaves leaves some slices empty.\n const lo = Math.floor((sub * range.count) / subLeaves);\n const hi = Math.floor(((sub + 1) * range.count) / subLeaves);\n if (hi === lo) continue;\n // The URL fragment never reaches the server; it just makes the chunk\n // legible in dev tools and in the mesh's debug stats. The `.i` suffix\n // appears only on split cells, keeping unsplit labels stable.\n const label = `${dataUrl}#c${cell.cellX}_${cell.cellY}-l${level}${\n subLeaves > 1 ? `.${sub}` : ''\n }`;\n const file = addChunk(label, {\n format: 'lcc-bin',\n lcc: {\n kind: 'splats',\n start: range.byteOffset + lo * LCC_RECORD_BYTES,\n length: (hi - lo) * LCC_RECORD_BYTES,\n stride: LCC_RECORD_BYTES,\n scale: manifest.scale,\n ...(shBands === undefined\n ? {}\n : {\n sh: {\n bands: shBands,\n range: manifest.shcoef,\n source: 'sidecar' as const,\n // shcoef.bin is aligned 2:1 with data.bin - 64 bytes per\n // splat against 32 - so each slice's SH is its own doubled\n // byte range, which the loader derives from `start`/`length`.\n url: shUrl as string,\n },\n }),\n },\n });\n lods[level] = { file, offset: 0, count: hi - lo };\n // Levels ascend finest-to-coarsest, so the last written is the\n // coarsest this sub-leaf carries.\n coarsestFile = file;\n coarsestCount = hi - lo;\n }\n // Pin the coarsest present slice as always-cached substitute coverage;\n // an empty cell has none. The coverage floor sums those coarsest slices\n // (a partition, so the total equals the unsplit sum).\n if (coarsestFile >= 0) {\n pinnedFiles.add(coarsestFile);\n minimumCoverageSplats += coarsestCount;\n }\n leaves.push({ bounds: cellBounds(cell, manifest), lods, budgetGroup: cellIndex });\n }\n }\n\n const environmentCount = await environmentSplatCount(source, manifest);\n if (environmentCount > 0 && environmentUrl !== null) {\n const stride = environmentStride(manifest);\n const file = addChunk(`${environmentUrl}#env`, {\n format: 'lcc-bin',\n lcc: {\n kind: 'environment',\n start: 0,\n length: environmentCount * stride,\n // From `fileType`, never from whether SH is enabled: a Quality\n // environment record is 96 bytes even when its SH is being skipped.\n stride,\n scale: manifest.envScale,\n ...(shBands === undefined\n ? {}\n : {\n sh: {\n bands: shBands,\n range: manifest.envShcoef,\n source: 'inline' as const,\n // The environment quantizes SH against its own (wider) range;\n // re-encode into the base range so the whole scene decodes\n // through one pool uniform. See LccShParams.requantizeTo.\n requantizeTo: manifest.shcoef,\n },\n }),\n },\n });\n // Sky/background splats have no LOD ladder - a single always-resident\n // level, so its run key never churns. It stays pinned (and counts into the\n // coverage floor): a few thousand splats never worth evicting.\n pinnedFiles.add(file);\n minimumCoverageSplats += environmentCount;\n leaves.push({\n bounds: sceneBounds(manifest),\n lods: [{ file, offset: 0, count: environmentCount }],\n });\n }\n\n const lodManifest: LodManifest = {\n leaves,\n chunkUrls,\n // Per-level splat totals; index 0 is the finest. Informational here (the\n // pool and coverage floor are sized explicitly below), but kept accurate.\n counts: manifest.splats.slice(0, levelCount),\n lodLevels: levelCount,\n bounds: sceneBounds(manifest),\n };\n\n const collisionUrl = await resolveCollisionLci(source);\n\n return {\n // Classic LCC cells are broad XY tiles. Keep their priority independent of\n // frustum crossings so an orbit does not demote a sharp tile as it leaves\n // one edge, then rebuild it when the same tile re-enters the other edge.\n source: new LodScheduler(lodManifest, {\n ...options,\n // Classic LCC cells are broad XY tiles. Keep their priority independent of\n // frustum crossings so an orbit does not demote a sharp tile as it leaves\n // one edge, then rebuild it when the same tile re-enters the other edge.\n frustumAware: false,\n // Short range: distance bands pick the level (≤~9 m → L0, ≤20 m → L1,\n // …). Do not blanket-force finest then re-promote after demotion - that\n // loaded L0 for margin cells only to demote them with the camera still.\n forceFinestWithin: undefined,\n // Distance band is the fetch target. Do not fill past it.\n fillPastDistance: false,\n // XGRIDS retains a little budget headroom and does not turn a raised\n // ceiling into an all-finest distant cut. The cap limits only optional\n // nearest-first restoration after demotion; the distance-selected cut\n // can still spend the caller's full explicit budget when it needs it.\n budgetFillFraction: 0.95,\n budgetFillCap: 8_000_000,\n forceFinestWhenFits: false,\n }),\n chunkUrls,\n chunkOptions,\n shBands: shBands ?? 0,\n chunkKind: 'file',\n bounds: sceneBounds(manifest),\n pinnedFiles,\n // Worst case is every cell resident at its finest level, plus the sky.\n maxResidentSplats: (manifest.splats[0] ?? options.budget) + environmentCount,\n // Full coarse coverage is each cell's coarsest level plus the sky - the\n // budget floor below which even the substitute levels cannot all fit.\n minimumCoverageSplats: Math.max(1, minimumCoverageSplats),\n // Same Z-up frame as .lcc2; bounds stay source-local and matrixWorld\n // handles the conversion.\n formatTransform: createLcc2ToThreeMatrix(),\n // Optional classic-LCC collision mesh file. One descriptor for the whole\n // `.lci`; `loadCollisionMeshes` expands it into per-cell triangle tiles.\n ...(collisionUrl !== null ? { collision: { meshes: [{ url: collisionUrl }] } } : {}),\n };\n}\n\n/**\n * Locates an optional `collision.lci` sidecar. Captures and CDNs disagree on\n * casing (`collision.lci` vs whitepaper `Collision.lci`), so both names are\n * probed via {@link SplatDatasetSource.size} - missing is fine, same as\n * `environment.bin`.\n */\nasync function resolveCollisionLci(source: SplatDatasetSource): Promise<string | null> {\n for (const name of ['collision.lci', 'Collision.lci'] as const) {\n const size = await source.size(name);\n if (size !== null && Number.isSafeInteger(size) && size > 0) {\n return source.resolve(name);\n }\n }\n return null;\n}\n\n/** Bytes per `environment.bin` record: Quality interleaves a 64-byte SH block. */\nexport function environmentStride(manifest: LccManifest): number {\n return manifest.fileType === 'Quality'\n ? LCC_RECORD_BYTES + LCC_SH_RECORD_BYTES\n : LCC_RECORD_BYTES;\n}\n\n/**\n * A cell's bounds: cells tile X/Y on a fixed grid anchored at the scene's\n * minimum corner and span the full Z range. Clamped to the scene box so the\n * outermost cells do not claim empty space beyond it.\n */\nfunction cellBounds(cell: LccIndexCell, manifest: LccManifest): THREE.Box3 {\n const { min, max } = manifest.bounds;\n const x0 = min[0] + cell.cellX * manifest.cellLengthX;\n const y0 = min[1] + cell.cellY * manifest.cellLengthY;\n return new THREE.Box3(\n new THREE.Vector3(Math.min(x0, max[0]), Math.min(y0, max[1]), min[2]),\n new THREE.Vector3(\n Math.min(x0 + manifest.cellLengthX, max[0]),\n Math.min(y0 + manifest.cellLengthY, max[1]),\n max[2],\n ),\n );\n}\n\nfunction sceneBounds(manifest: LccManifest): THREE.Box3 {\n const { min, max } = manifest.bounds;\n return new THREE.Box3(\n new THREE.Vector3(min[0], min[1], min[2]),\n new THREE.Vector3(max[0], max[1], max[2]),\n );\n}\n\n/**\n * Cross-checks the index against the manifest before anything is sized from\n * it. The manifest may over-declare slightly: the v4 writer leaves stale\n * `splats` counts (conferencehall is short 2 and 1 splats on levels 0/1 of\n * ~6.2M/2.9M), and index.bin is what every byte range is actually read from.\n * An index that *exceeds* the manifest, or falls short by more than a sliver,\n * still means a mismatched or truncated file pair and is refused.\n */\nfunction assertIndexMatchesManifest(index: LccIndexCell[], manifest: LccManifest): void {\n for (let level = 0; level < manifest.totalLevel; level++) {\n let total = 0;\n for (const cell of index) total += (cell.levels[level] as LccIndexCell['levels'][number]).count;\n const declared = manifest.splats[level] as number;\n const shortfall = declared - total;\n const tolerance = Math.max(4, Math.ceil(declared * 0.001));\n if (shortfall < 0 || shortfall > tolerance) {\n throw new Error(\n `LCC index.bin level ${level} totals ${total} splats, but the manifest declares ${String(manifest.splats[level])}.`,\n );\n }\n }\n}\n\n/**\n * How many environment splats exist, from the file's size alone -\n * `environment.bin` has no index. A missing file is not an error: it is\n * optional, and plenty of captures have no sky.\n */\nasync function environmentSplatCount(\n source: SplatDatasetSource,\n manifest: LccManifest,\n): Promise<number> {\n const size = await source.size('environment.bin');\n if (size === null || !Number.isSafeInteger(size) || size <= 0) return 0;\n const stride = environmentStride(manifest);\n if (size % stride !== 0) {\n throw new Error(\n `LCC environment.bin is ${size} bytes, not a multiple of the ${stride}-byte ${manifest.fileType} record.`,\n );\n }\n return size / stride;\n}\n\nasync function fetchBytes(\n url: string,\n options: LccSceneOptions,\n phase: 'manifest' | 'fetch',\n): Promise<ArrayBuffer> {\n let response: Response;\n try {\n response = await fetch(url, toRequestInit(options.request, options.signal));\n } catch (error) {\n throw toSplatLoadError(error, { phase, url });\n }\n if (!response.ok) {\n throw toSplatLoadError(new Error(`Failed to load ${url}: HTTP ${response.status}`), {\n phase,\n url,\n status: response.status,\n });\n }\n return response.arrayBuffer();\n}\n","import * as THREE from 'three/webgpu';\nimport type { LodRun } from '../../lod-scheduler';\nimport type {\n CollisionMeshDescriptor,\n LodSource,\n LodSourceOptions,\n SceneCollision,\n StreamedScene,\n} from '../../lod-source';\nimport type { SplatDatasetSource } from '../../dataset-source';\nimport { createLcc2ToThreeMatrix } from './lcc2-transform';\nimport { warn } from '../../logging';\n\n/**\n * Reader for XGRIDS' `.lcc2` datasets (see `docs/formats/lcc2-notes.md`).\n *\n * An `.lcc2` manifest is a cut-based LOD octree: each node holds one splat\n * range (`{file, start, count}`) representing its spatial region at one\n * level, and every level fully tiles the scene, so rendering picks a *cut*\n * (one node per root→leaf path) rather than a level per leaf. Coarse nodes\n * are shared ancestors, so per-leaf independent selection would double-draw;\n * {@link OctreeLodSource} therefore selects a valid cut directly.\n *\n * The splat tiles are standard SOG v2 bundles (one file packs many nodes at\n * different `start` offsets), so the existing chunk cache and pool reuse\n * everything. Implemented from XGRIDS' openly published spec; no XGRIDS code\n * is used, and their capture data is never redistributed.\n */\n\n/** Out-of-frustum nodes act this many times farther away (see LodScheduler). */\nconst FRUSTUM_PENALTY = 3;\n/** Frustum test margin, as a fraction of each node's own size. */\nconst FRUSTUM_MARGIN = 0.1;\n/** Hysteresis dead-band around each LOD distance threshold. */\nconst THRESHOLD_MARGIN = 0.1;\n/** Minimum time a cell must hold a level before changing again, ms. */\nconst DWELL_MS = 500;\n\ninterface OctNode {\n file: number;\n offset: number;\n count: number;\n bounds: THREE.Box3;\n /** `totalLevels − depth`; 0 is finest. */\n level: number;\n children: number[];\n /** Finest-cell interval `[leafStart, leafEnd)` over this node's subtree. */\n leafStart: number;\n leafEnd: number;\n}\n\ninterface RawNode {\n boundingBox: { min: [number, number, number]; max: [number, number, number] };\n child?: Record<string, RawNode>;\n data?: {\n '3dgs'?: { name: number; start: number; count: number };\n /** Collision/nav mesh for this node's region; `name` indexes `meshFiles`. */\n mesh?: { name: number; vertex: number; face: number };\n /**\n * Always-resident environment/background tile; root only. `name` indexes\n * `splatFiles`. Carries no `count` - the manifest never states how many\n * env splats there are, so it is measured when the tile decodes.\n */\n env?: { name: number };\n };\n}\n\ninterface RawManifest {\n version: string;\n totalLevels: number;\n lodSplats: number[];\n root: RawNode & {\n splatFiles: string[];\n /** Triangle-mesh PLYs, referenced by nodes' `data.mesh.name`. */\n meshFiles?: string[];\n /**\n * XGRIDS' own `.btree` BVHs over `meshFiles`. Deliberately unread: the\n * format is proprietary and undocumented, and a host that wants an\n * acceleration structure builds one from the meshes itself.\n */\n bvhFiles?: string[];\n };\n}\n\n/** Builds a scene from a parsed `.lcc2` manifest (JSON already fetched). */\nexport function buildLcc2Scene(\n json: unknown,\n dataset: SplatDatasetSource,\n options: LodSourceOptions,\n shBands: 0 | 1 | 2 | 3 = 0,\n): StreamedScene {\n const raw = json as RawManifest;\n const chunkUrls = raw.root.splatFiles.map((path) => {\n const url = dataset.resolve(path);\n if (url === null) throw new Error(`LCC2 dataset is missing the tile \"${path}\".`);\n return url;\n });\n\n const nodes: OctNode[] = [];\n const cellNodes: number[] = []; // node index of each finest cell, by cell index\n let nextLeaf = 0;\n // Flatten every node that carries splat data (all but the root). Finest\n // cells are the childless nodes, numbered in DFS order; each node's\n // [leafStart, leafEnd) spans the cells in its subtree, so ancestor\n // intervals nest over descendants (what the swap grouping needs).\n const build = (rawNode: RawNode, depth: number): number => {\n const range = rawNode.data?.['3dgs'];\n if (!range) throw new Error('LCC2 node is missing 3dgs data.');\n const index = nodes.length;\n nodes.push(null as unknown as OctNode); // reserve slot (children get later indices)\n\n const leafStart = nextLeaf;\n const children: number[] = [];\n const rawChildren = rawNode.child ? Object.values(rawNode.child) : [];\n if (rawChildren.length === 0) {\n cellNodes[nextLeaf] = index; // this node is one finest cell\n nextLeaf++;\n } else {\n for (const child of rawChildren) children.push(build(child, depth + 1));\n }\n const leafEnd = rawChildren.length === 0 ? leafStart + 1 : nextLeaf;\n\n nodes[index] = {\n file: range.name,\n offset: range.start,\n count: range.count,\n bounds: boxFromRaw(rawNode.boundingBox),\n level: raw.totalLevels - depth,\n children,\n leafStart,\n leafEnd,\n };\n return index;\n };\n\n const rootChildren = Object.values(raw.root.child ?? {}).map((child) => build(child, 1));\n\n const source = new OctreeLodSource(nodes, rootChildren, cellNodes, raw.totalLevels - 1, options);\n const pinnedFiles = new Set<number>(rootChildren.map((i) => (nodes[i] as OctNode).file));\n\n // The environment tile (root `data.env`) is an always-resident background -\n // one whole `.sog`, no LOD ladder, no `count` in the manifest (measured at\n // decode). Pin its file so a fetch in flight is never cancelled by an LOD\n // reschedule, and StreamedSplatMesh loads it once and toggles it live.\n const envName = raw.root.data?.env?.name;\n const environment =\n envName !== undefined && chunkUrls[envName] !== undefined ? { file: envName } : undefined;\n if (envName !== undefined && environment === undefined) {\n warn(`LCC2 root references env tile #${envName}, which the manifest does not list.`);\n }\n if (environment) pinnedFiles.add(environment.file);\n\n const packShBands = shBands >= 1 ? (shBands as 1 | 2 | 3) : undefined;\n\n return {\n source,\n chunkUrls,\n chunkKind: 'file', // LCC2 tiles are single bundled .sog (ZIP) files\n // Explicit format: a dropped folder resolves tiles to `blob:` URLs with no\n // `.sog` extension, so the chunk loader cannot sniff the parser from the URL.\n chunkOptions: chunkUrls.map(() => ({\n format: 'sog' as const,\n ...(packShBands ? { sog: { packShBands } } : {}),\n })),\n bounds: boxFromRaw(raw.root.boundingBox),\n pinnedFiles,\n maxResidentSplats: raw.lodSplats[0] ?? options.budget,\n minimumCoverageSplats: Math.max(\n 1,\n rootChildren.reduce((sum, index) => sum + (nodes[index] as OctNode).count, 0),\n ),\n ...collisionFrom(raw, dataset),\n ...(environment ? { environment } : {}),\n ...(packShBands ? { shBands: packShBands } : {}),\n // Match the established XGRIDS LCC and Spark viewer coordinate frame.\n // Bounds remain source-local and matrixWorld handles the conversion.\n formatTransform: createLcc2ToThreeMatrix(),\n };\n}\n\n/**\n * Collects the dataset's collision-mesh tiles, if it ships any.\n *\n * Nodes name their mesh by index into `root.meshFiles`, and several nodes can\n * share one file, so tiles are deduped by that index and carry the bounds of\n * the first node that claimed them. Unlike a splat tile, a missing mesh is not\n * fatal - collision is optional, and a partial set still beats none.\n */\nfunction collisionFrom(\n raw: RawManifest,\n dataset: SplatDatasetSource,\n): { collision?: SceneCollision } {\n const meshFiles = raw.root.meshFiles;\n if (!meshFiles || meshFiles.length === 0) return {};\n\n const boundsByFile = new Map<number, THREE.Box3>();\n const visit = (node: RawNode): void => {\n const mesh = node.data?.mesh;\n if (mesh && !boundsByFile.has(mesh.name)) {\n boundsByFile.set(mesh.name, boxFromRaw(node.boundingBox));\n }\n for (const child of Object.values(node.child ?? {})) visit(child);\n };\n visit(raw.root);\n\n // Nodes are the authority on which files are collision meshes and where they\n // sit. A manifest that lists the files but never references them still gets\n // its meshes loaded, just without per-tile bounds.\n const wanted: { file: number; bounds?: THREE.Box3 }[] =\n boundsByFile.size > 0\n ? [...boundsByFile].map(([file, bounds]) => ({ file, bounds }))\n : meshFiles.map((_, file) => ({ file }));\n\n const meshes: CollisionMeshDescriptor[] = [];\n for (const { file, bounds } of wanted) {\n const path = meshFiles[file];\n if (path === undefined) {\n warn(`LCC2 node references mesh file ${file}, which the manifest does not list.`);\n continue;\n }\n const url = dataset.resolve(path);\n if (url === null) {\n warn(`LCC2 dataset is missing the collision mesh \"${path}\"; skipping it.`);\n continue;\n }\n meshes.push({ url, ...(bounds ? { bounds } : {}) });\n }\n return meshes.length > 0 ? { collision: { meshes } } : {};\n}\n\n/**\n * Selects a budget-bounded cut through an LCC2 octree with the same\n * stability as the SOG scheduler. The stable state is a per-finest-cell\n * desired level (0 = finest), updated from camera distance with a dead-band\n * and dwell so a still or slowly-panning camera does not flip levels - this\n * is what stops the \"two LODs fighting\" flicker. From those levels a valid\n * cut is built (descend into a node only while some cell in its subtree\n * wants finer than the node provides), then budget-adjusted: coarsen the\n * farthest cells if over budget, refine the nearest with any leftover.\n * Because coarse nodes are shared ancestors, the cut construction - not\n * per-cell independent selection - is what guarantees no double-draw.\n */\nclass OctreeLodSource implements LodSource {\n budget: number;\n lodBaseDistance: number;\n lodMultiplier: number;\n\n private readonly nodes: OctNode[];\n private readonly rootChildren: number[];\n private readonly cellNodes: number[];\n private readonly coarsest: number;\n /** Finest available level per cell (its own node's level). */\n private readonly cellFinestLevel: Int32Array;\n /** Stable, dwelled desired level per cell. */\n private readonly cellLevel: Int32Array;\n /** Timestamp of each cell's last level change, for dwell. */\n private readonly changedAt: Float64Array;\n /** Index into {@link rootChildren} of the subtree containing each cell. */\n private readonly cellRootChild: Uint32Array;\n\n private readonly scratchBox = new THREE.Box3();\n private readonly scratchSize = new THREE.Vector3();\n\n constructor(\n nodes: OctNode[],\n rootChildren: number[],\n cellNodes: number[],\n coarsest: number,\n options: LodSourceOptions,\n ) {\n this.nodes = nodes;\n this.rootChildren = rootChildren;\n this.cellNodes = cellNodes;\n this.coarsest = coarsest;\n this.budget = options.budget;\n this.lodBaseDistance = options.lodBaseDistance;\n this.lodMultiplier = options.lodMultiplier;\n\n const n = cellNodes.length;\n this.cellFinestLevel = new Int32Array(n);\n this.cellLevel = new Int32Array(n).fill(coarsest);\n this.changedAt = new Float64Array(n);\n for (let c = 0; c < n; c++) {\n this.cellFinestLevel[c] = (nodes[cellNodes[c] as number] as OctNode).level;\n }\n this.cellRootChild = new Uint32Array(n);\n rootChildren.forEach((nodeIndex, r) => {\n const node = nodes[nodeIndex] as OctNode;\n for (let cell = node.leafStart; cell < node.leafEnd; cell++) this.cellRootChild[cell] = r;\n });\n }\n\n computeDesiredRuns(cameraLocal: THREE.Vector3, frustum: THREE.Frustum, now: number): LodRun[] {\n const cellDistance = this.updateCellLevels(cameraLocal, frustum, now);\n\n // resolved[] starts from the dwelled per-cell levels, then is\n // budget-adjusted; keeping the dwelled base separate (like the SOG\n // scheduler's level/resolved split) prevents budget changes from\n // feeding back into the hysteresis.\n const resolved = Int32Array.from(this.cellLevel);\n // A cell's level only affects the cut inside its own root-child subtree,\n // so per-subtree contributions are cached and each adjustment step\n // re-walks one subtree - not the whole tree, which made the enforce/fill\n // loops O(cells² · depth) on heavily over-budget scenes.\n const contributions = this.rootChildren.map((index) => this.subtreeCutTotal(index, resolved));\n let total = contributions.reduce((sum, count) => sum + count, 0);\n const adjust = (cell: number, delta: number): void => {\n resolved[cell] = (resolved[cell] as number) + delta;\n const r = this.cellRootChild[cell] as number;\n const next = this.subtreeCutTotal(this.rootChildren[r] as number, resolved);\n total += next - (contributions[r] as number);\n contributions[r] = next;\n };\n\n const byDistanceDesc = [...this.cellNodes.keys()].sort(\n (a, b) => (cellDistance[b] as number) - (cellDistance[a] as number),\n );\n // Enforce: coarsen the farthest cells until the cut fits the budget.\n for (let i = 0; i < byDistanceDesc.length && total > this.budget;) {\n const c = byDistanceDesc[i] as number;\n if ((resolved[c] as number) < this.coarsest) {\n adjust(c, +1);\n } else {\n i++;\n }\n }\n // Fill: spend leftover budget refining the nearest cells, to 85% - the\n // headroom keeps region-atomic swap transients (old + new runs briefly\n // co-resident) from pushing the live count past budget on a scene far\n // larger than the budget.\n const fillTarget = this.budget * 0.85;\n for (let i = byDistanceDesc.length - 1; i >= 0 && total < fillTarget;) {\n const c = byDistanceDesc[i] as number;\n if ((resolved[c] as number) > (this.cellFinestLevel[c] as number)) {\n adjust(c, -1);\n if (total > this.budget) {\n adjust(c, +1); // overshoot; revert and stop\n break;\n }\n } else {\n i--;\n }\n }\n\n const runs: LodRun[] = [];\n this.collectCut(resolved, (index) => runs.push(runFromNode(this.nodes[index] as OctNode)));\n return runs;\n }\n\n coarsestRunsFor(from: number, to: number): LodRun[] {\n const runs: LodRun[] = [];\n for (const index of this.rootChildren) {\n const node = this.nodes[index] as OctNode;\n if (node.leafStart < to && node.leafEnd > from) runs.push(runFromNode(node));\n }\n return runs;\n }\n\n /**\n * Coarsest root-child runs covering in-view finest cells. An empty frustum\n * falls back to the nearest cell's root child so a skyward start still paints\n * something rather than releasing an empty hold.\n */\n coverageRunsFor(cameraLocal: THREE.Vector3, frustum: THREE.Frustum): LodRun[] {\n const n = this.cellNodes.length;\n if (n === 0) return [];\n const picked = new Uint8Array(this.rootChildren.length);\n let anyInView = false;\n let nearestCell = 0;\n let nearestDist = Number.POSITIVE_INFINITY;\n for (let c = 0; c < n; c++) {\n const node = this.nodes[this.cellNodes[c] as number] as OctNode;\n const d = node.bounds.distanceToPoint(cameraLocal);\n if (d < nearestDist) {\n nearestDist = d;\n nearestCell = c;\n }\n if (this.cellIntersectsFrustum(frustum, c)) {\n picked[this.cellRootChild[c] as number] = 1;\n anyInView = true;\n }\n }\n if (!anyInView) picked[this.cellRootChild[nearestCell] as number] = 1;\n const runs: LodRun[] = [];\n for (let r = 0; r < this.rootChildren.length; r++) {\n if (picked[r] !== 1) continue;\n runs.push(runFromNode(this.nodes[this.rootChildren[r] as number] as OctNode));\n }\n return runs;\n }\n\n /** Finest-cell AABB expanded by {@link FRUSTUM_MARGIN}, written into scratch. */\n private expandCellBox(cell: number): THREE.Box3 {\n const node = this.nodes[this.cellNodes[cell] as number] as OctNode;\n this.scratchBox.copy(node.bounds);\n node.bounds.getSize(this.scratchSize);\n this.scratchBox.expandByScalar(this.scratchSize.length() * FRUSTUM_MARGIN);\n return this.scratchBox;\n }\n\n private cellIntersectsFrustum(frustum: THREE.Frustum, cell: number): boolean {\n return frustum.intersectsBox(this.expandCellBox(cell));\n }\n\n /** Distance (frustum-penalized) to each finest cell's node bounds. */\n private cellDistanceOf(cameraLocal: THREE.Vector3, frustum: THREE.Frustum, cell: number): number {\n const node = this.nodes[this.cellNodes[cell] as number] as OctNode;\n const d = node.bounds.distanceToPoint(cameraLocal);\n return this.cellIntersectsFrustum(frustum, cell) ? d : d * FRUSTUM_PENALTY;\n }\n\n /** Advances each cell's dwelled level toward the distance target. */\n private updateCellLevels(\n cameraLocal: THREE.Vector3,\n frustum: THREE.Frustum,\n now: number,\n ): Float64Array {\n const { lodBaseDistance: base, lodMultiplier: m } = this;\n const distances = new Float64Array(this.cellNodes.length);\n for (let c = 0; c < this.cellNodes.length; c++) {\n const d = this.cellDistanceOf(cameraLocal, frustum, c);\n distances[c] = d;\n\n const min = this.cellFinestLevel[c] as number;\n let level = Math.min(this.coarsest, Math.max(min, this.cellLevel[c] as number));\n // Dead-band: coarsen only past threshold·(1+margin), refine only\n // within threshold·(1−margin); threshold(L) = base·m^L.\n while (level < this.coarsest && d > base * m ** level * (1 + THRESHOLD_MARGIN)) level++;\n while (level > min && d <= base * m ** (level - 1) * (1 - THRESHOLD_MARGIN)) level--;\n\n if (\n level !== (this.cellLevel[c] as number) &&\n now - (this.changedAt[c] as number) >= DWELL_MS\n ) {\n this.cellLevel[c] = level;\n this.changedAt[c] = now;\n }\n }\n return distances;\n }\n\n /** Splat total of one subtree's cut for the given per-cell levels. */\n private subtreeCutTotal(index: number, levels: Int32Array): number {\n let total = 0;\n this.visitCut(index, levels, (i) => {\n total += (this.nodes[i] as OctNode).count;\n });\n return total;\n }\n\n /**\n * Walks the tree emitting the cut for the given per-cell levels: a node is\n * selected (stop) unless some cell in its subtree wants finer than the\n * node provides, in which case its children are visited. Guarantees an\n * antichain - exactly one node per root→leaf path.\n */\n private collectCut(levels: Int32Array, emit: (index: number) => void): void {\n for (const index of this.rootChildren) this.visitCut(index, levels, emit);\n }\n\n /** {@link collectCut} restricted to one subtree. */\n private visitCut(index: number, levels: Int32Array, emit: (index: number) => void): void {\n const node = this.nodes[index] as OctNode;\n if (node.children.length > 0) {\n let minWanted = this.coarsest;\n for (let cell = node.leafStart; cell < node.leafEnd; cell++) {\n const level = levels[cell] as number;\n if (level < minWanted) minWanted = level;\n }\n if (minWanted < node.level) {\n for (const child of node.children) this.visitCut(child, levels, emit);\n return;\n }\n }\n emit(index);\n }\n}\n\nfunction runFromNode(node: OctNode): LodRun {\n return {\n file: node.file,\n level: node.level,\n offset: node.offset,\n count: node.count,\n leafStart: node.leafStart,\n leafEnd: node.leafEnd,\n };\n}\n\nfunction boxFromRaw(bound: RawNode['boundingBox']): THREE.Box3 {\n return new THREE.Box3(\n new THREE.Vector3(bound.min[0], bound.min[1], bound.min[2]),\n new THREE.Vector3(bound.max[0], bound.max[1], bound.max[2]),\n );\n}\n","import {\n assertPlyElementFits,\n assertPlyPropertyTypes,\n parsePlyHeader,\n plyPropertyOffset,\n plyTypeSize,\n type PlyElement,\n} from '../../ply-header';\n\n/**\n * Parser for plain binary triangle-mesh PLY files - vertices and faces, no\n * splat properties.\n *\n * XGRIDS `.lcc2` datasets ship these as collision/navigation companions to\n * their splat tiles (`data/mesh/*.ply`, see `docs/formats/lcc2-notes.md`): a vertex\n * element of float x/y/z and a face element of index lists. The geometry is a\n * plain triangle soup in the dataset's source-local frame - what a host needs\n * to build its own collision or spatial queries.\n *\n * Nothing here is LCC-specific: any binary little-endian PLY with a positioned\n * vertex element and an index list parses.\n */\n\n/** Names the face index list goes by, in preference order. */\nconst FACE_INDEX_PROPERTIES = ['vertex_indices', 'vertex_index'] as const;\n\n/** A plain indexed triangle mesh decoded from a PLY. */\nexport interface TriangleMeshData {\n readonly vertexCount: number;\n readonly triangleCount: number;\n /** `x, y, z` per vertex, in the file's own coordinate frame. */\n readonly positions: Float32Array;\n /** Three vertex indices per triangle, into {@link positions}. */\n readonly indices: Uint32Array;\n}\n\n/**\n * Parses a binary little-endian triangle-mesh PLY.\n *\n * Faces with more than three vertices are fan-triangulated; degenerate faces\n * (fewer than three) are dropped. Coordinates are passed through untouched, so\n * a source-local frame stays source-local - an LCC2 mesh needs the same\n * `createLcc2ToThreeMatrix` correction its splats get.\n *\n * @throws {Error} if the file is not a binary little-endian PLY, lacks a\n * usable vertex or face element, or is truncated.\n */\nexport function parseMeshPly(buffer: ArrayBuffer): TriangleMeshData {\n const header = parsePlyHeader(buffer);\n\n const vertices = header.element('vertex');\n if (!vertices) throw new Error('Not a mesh PLY: no \"vertex\" element.');\n assertPlyPropertyTypes(vertices, ['x', 'y', 'z'], ['float', 'float32'], 'Mesh PLY vertex');\n assertPlyElementFits(vertices, buffer.byteLength);\n const positions = readPositions(buffer, vertices);\n\n const faces = header.element('face');\n if (!faces) throw new Error('Not a mesh PLY: no \"face\" element.');\n const indices = readFaces(buffer, faces, vertices.count);\n\n return {\n vertexCount: vertices.count,\n triangleCount: indices.length / 3,\n positions,\n indices,\n };\n}\n\n/** Copies x/y/z out of the vertex records, honoring stride and offsets. */\nfunction readPositions(buffer: ArrayBuffer, vertices: PlyElement): Float32Array {\n const view = new DataView(buffer, vertices.offset);\n const xOffset = plyPropertyOffset(vertices, 'x');\n const yOffset = plyPropertyOffset(vertices, 'y');\n const zOffset = plyPropertyOffset(vertices, 'z');\n const positions = new Float32Array(vertices.count * 3);\n for (let i = 0; i < vertices.count; i++) {\n const record = i * vertices.stride;\n positions[i * 3] = view.getFloat32(record + xOffset, true);\n positions[i * 3 + 1] = view.getFloat32(record + yOffset, true);\n positions[i * 3 + 2] = view.getFloat32(record + zOffset, true);\n }\n return positions;\n}\n\n/**\n * Walks the variable-stride face records into a flat triangle index array.\n *\n * Each record is a count followed by that many indices, so the records can only\n * be read in order - there is no arithmetic that finds face `n`.\n */\nfunction readFaces(buffer: ArrayBuffer, faces: PlyElement, vertexCount: number): Uint32Array {\n if (faces.count === 0) return new Uint32Array(0);\n if (faces.offset < 0) {\n throw new Error(\n 'Unsupported PLY layout: the \"face\" element follows a list property, so its position ' +\n 'cannot be determined.',\n );\n }\n if (faces.properties.size > 0) {\n throw new Error(\n 'Unsupported mesh PLY: the \"face\" element mixes scalar properties with its index list.',\n );\n }\n\n const name = FACE_INDEX_PROPERTIES.find((candidate) => faces.listProperties.has(candidate));\n const list = name ? faces.listProperties.get(name) : undefined;\n if (!list) {\n throw new Error(\n `Mesh PLY \"face\" element is missing an index list (${FACE_INDEX_PROPERTIES.join(' or ')}).`,\n );\n }\n const countSize = plyTypeSize(list.countType);\n const itemSize = plyTypeSize(list.itemType);\n\n const view = new DataView(buffer);\n const end = buffer.byteLength;\n // Most faces are triangles, so this is usually exact; a fan grows it.\n let indices = new Uint32Array(faces.count * 3);\n let written = 0;\n let cursor = faces.offset;\n\n for (let face = 0; face < faces.count; face++) {\n if (cursor + countSize > end) throw new Error(truncated(face));\n const count = readInteger(view, cursor, list.countType, countSize);\n if (count < 0) {\n throw new Error(`Mesh PLY face ${face} declares a negative vertex count (${count}).`);\n }\n cursor += countSize;\n if (cursor + count * itemSize > end) throw new Error(truncated(face));\n\n // A fan needs (count - 2) triangles; skip degenerate faces but still step\n // the cursor past their data so the following records stay aligned.\n const triangles = Math.max(0, count - 2);\n if (written + triangles * 3 > indices.length) {\n const grown = new Uint32Array(Math.max(indices.length * 2, written + triangles * 3));\n grown.set(indices.subarray(0, written));\n indices = grown;\n }\n let first = 0;\n let previous = 0;\n for (let i = 0; i < count; i++) {\n const index = readInteger(view, cursor + i * itemSize, list.itemType, itemSize);\n if (index < 0 || index >= vertexCount) {\n throw new Error(\n `Mesh PLY face ${face} references vertex ${index}, but the file has ${vertexCount}.`,\n );\n }\n if (i === 0) first = index;\n else if (i >= 2) {\n indices[written++] = first;\n indices[written++] = previous;\n indices[written++] = index;\n }\n previous = index;\n }\n cursor += count * itemSize;\n }\n\n return written === indices.length ? indices : indices.slice(0, written);\n}\n\nfunction truncated(face: number): string {\n return `Truncated PLY file: the \"face\" element ends inside face ${face}.`;\n}\n\n/** Reads one little-endian integer of a PLY scalar type. */\nfunction readInteger(view: DataView, offset: number, type: string, size: number): number {\n switch (type) {\n case 'char':\n case 'int8':\n return view.getInt8(offset);\n case 'uchar':\n case 'uint8':\n return view.getUint8(offset);\n case 'short':\n case 'int16':\n return view.getInt16(offset, true);\n case 'ushort':\n case 'uint16':\n return view.getUint16(offset, true);\n case 'int':\n case 'int32':\n return view.getInt32(offset, true);\n case 'uint':\n case 'uint32':\n return view.getUint32(offset, true);\n default:\n // Sized by plyTypeSize, so this is a float type where an index belongs.\n throw new Error(`Unsupported PLY index type: \"${type}\" (${size} bytes).`);\n }\n}\n","import * as THREE from 'three/webgpu';\nimport type { TriangleMeshData } from './parse-mesh-ply';\n\n/**\n * Parser for XGRIDS classic-LCC `collision.lci` files (manifest companions to\n * `.lcc` / `meta.lcc` captures).\n *\n * Layout reverse-engineered from the\n * [LCC whitepaper](https://github.com/xgrids/LCCWhitepaper) and verified on\n * live captures (e.g. Kaiserpfalz v2). The file packs one triangle mesh per\n * occupied cell, plus a proprietary serialized BVH we deliberately skip -\n * hosts build their own acceleration structure (see the demo's\n * `collision.ts`), the same bargain `.lcc2` makes by ignoring `.btree`.\n *\n * Coordinates are Z-up source-local, matching `data.bin` / `formatTransform`.\n */\n\nconst MAGIC = 0x6c6c6f63; // 'coll' LE\nconst SUPPORTED_VERSION = 2;\nconst GLOBAL_HEADER_BYTES = 48;\nconst MESH_HEADER_BYTES = 40;\nconst FLOATS_PER_VERTEX = 3;\nconst INDICES_PER_FACE = 3;\nconst BYTES_PER_VERTEX = FLOATS_PER_VERTEX * 4;\nconst BYTES_PER_FACE = INDICES_PER_FACE * 4;\n\n/** One cell's collision mesh decoded from a `collision.lci`. */\nexport interface CollisionLciTile {\n readonly cellX: number;\n readonly cellY: number;\n readonly data: TriangleMeshData;\n /**\n * Cell-grid AABB in the file's source-local frame, clamped to the file's\n * scene bounds. Useful for culling before consulting the triangles.\n */\n readonly bounds: THREE.Box3;\n}\n\n/** Everything {@link parseCollisionLci} extracts from one file. */\nexport interface CollisionLciFile {\n readonly version: number;\n readonly bounds: THREE.Box3;\n readonly cellLengthX: number;\n readonly cellLengthY: number;\n readonly tiles: readonly CollisionLciTile[];\n}\n\n/**\n * Parses a classic-LCC `collision.lci` buffer into per-cell triangle meshes.\n *\n * @throws {Error} on bad magic, unsupported version, truncated payloads,\n * inconsistent size fields, or out-of-range face indices.\n */\nexport function parseCollisionLci(buffer: ArrayBuffer): CollisionLciFile {\n if (buffer.byteLength < GLOBAL_HEADER_BYTES) {\n throw new Error(\n `collision.lci is truncated: ${buffer.byteLength} bytes, need at least ${GLOBAL_HEADER_BYTES}.`,\n );\n }\n const view = new DataView(buffer);\n const magic = view.getUint32(0, true);\n if (magic !== MAGIC) {\n throw new Error(\n `Not a collision.lci: expected magic \"coll\", got 0x${magic.toString(16).padStart(8, '0')}.`,\n );\n }\n const version = view.getUint32(4, true);\n if (version !== SUPPORTED_VERSION) {\n throw new Error(\n `Unsupported collision.lci version ${version} (expected ${SUPPORTED_VERSION}).`,\n );\n }\n const headerLen = view.getUint32(8, true);\n const sceneMin = new THREE.Vector3(\n view.getFloat32(12, true),\n view.getFloat32(16, true),\n view.getFloat32(20, true),\n );\n const sceneMax = new THREE.Vector3(\n view.getFloat32(24, true),\n view.getFloat32(28, true),\n view.getFloat32(32, true),\n );\n const cellLengthX = view.getFloat32(36, true);\n const cellLengthY = view.getFloat32(40, true);\n const meshCount = view.getUint32(44, true);\n\n const expectedHeader = GLOBAL_HEADER_BYTES + meshCount * MESH_HEADER_BYTES;\n if (headerLen !== expectedHeader) {\n throw new Error(\n `collision.lci headerLen ${headerLen} does not match ${meshCount} mesh headers ` +\n `(expected ${expectedHeader}).`,\n );\n }\n if (buffer.byteLength < headerLen) {\n throw new Error(\n `collision.lci is truncated: header claims ${headerLen} bytes, file has ${buffer.byteLength}.`,\n );\n }\n if (!(cellLengthX > 0) || !(cellLengthY > 0)) {\n throw new Error(\n `collision.lci cell lengths must be positive (got ${cellLengthX} × ${cellLengthY}).`,\n );\n }\n\n const sceneBounds = new THREE.Box3(sceneMin.clone(), sceneMax.clone());\n const tiles: CollisionLciTile[] = [];\n let expectedOffset = headerLen;\n\n for (let i = 0; i < meshCount; i++) {\n const at = GLOBAL_HEADER_BYTES + i * MESH_HEADER_BYTES;\n const cellX = view.getUint32(at, true);\n const cellY = view.getUint32(at + 4, true);\n const dataOffset = Number(view.getBigUint64(at + 8, true));\n const dataSize = Number(view.getBigUint64(at + 16, true));\n const vertexCount = view.getUint32(at + 24, true);\n const faceCount = view.getUint32(at + 28, true);\n const bvhByteSize = view.getUint32(at + 32, true);\n\n if (!Number.isSafeInteger(dataOffset) || !Number.isSafeInteger(dataSize)) {\n throw new Error(`collision.lci mesh ${i} has an unsafe data offset/size.`);\n }\n if (dataOffset !== expectedOffset) {\n throw new Error(\n `collision.lci mesh ${i} data offset ${dataOffset} is not contiguous ` +\n `(expected ${expectedOffset}).`,\n );\n }\n const geometryBytes = vertexCount * BYTES_PER_VERTEX + faceCount * BYTES_PER_FACE;\n if (geometryBytes + bvhByteSize !== dataSize) {\n throw new Error(\n `collision.lci mesh ${i} size ${dataSize} != ` +\n `${vertexCount}*12 + ${faceCount}*12 + BVH ${bvhByteSize}.`,\n );\n }\n const end = dataOffset + dataSize;\n if (end > buffer.byteLength) {\n throw new Error(\n `collision.lci mesh ${i} extends to byte ${end}, past file length ${buffer.byteLength}.`,\n );\n }\n\n const positions = new Float32Array(buffer, dataOffset, vertexCount * FLOATS_PER_VERTEX);\n const indexOffset = dataOffset + vertexCount * BYTES_PER_VERTEX;\n const indices = new Uint32Array(buffer, indexOffset, faceCount * INDICES_PER_FACE);\n for (let t = 0; t < indices.length; t++) {\n const index = indices[t]!;\n if (index >= vertexCount) {\n throw new Error(\n `collision.lci mesh ${i} face index ${index} is out of range ` +\n `(vertexCount ${vertexCount}).`,\n );\n }\n }\n\n tiles.push({\n cellX,\n cellY,\n data: {\n vertexCount,\n triangleCount: faceCount,\n // Copy so the returned arrays outlive a transferred/detached buffer\n // and are not views into the BVH-adjacent region.\n positions: positions.slice(),\n indices: indices.slice(),\n },\n bounds: cellBounds(cellX, cellY, cellLengthX, cellLengthY, sceneBounds),\n });\n expectedOffset = end;\n }\n\n if (expectedOffset !== buffer.byteLength) {\n throw new Error(\n `collision.lci has ${buffer.byteLength - expectedOffset} trailing bytes after the last mesh.`,\n );\n }\n\n return {\n version,\n bounds: sceneBounds,\n cellLengthX,\n cellLengthY,\n tiles,\n };\n}\n\n/**\n * Cell AABB on the LCI grid, anchored at the scene minimum and clamped to the\n * scene box - same rule classic LCC uses for splat cells.\n */\nfunction cellBounds(\n cellX: number,\n cellY: number,\n cellLengthX: number,\n cellLengthY: number,\n scene: THREE.Box3,\n): THREE.Box3 {\n const x0 = scene.min.x + cellX * cellLengthX;\n const y0 = scene.min.y + cellY * cellLengthY;\n return new THREE.Box3(\n new THREE.Vector3(Math.min(x0, scene.max.x), Math.min(y0, scene.max.y), scene.min.z),\n new THREE.Vector3(\n Math.min(x0 + cellLengthX, scene.max.x),\n Math.min(y0 + cellLengthY, scene.max.y),\n scene.max.z,\n ),\n );\n}\n","import type * as THREE from 'three/webgpu';\nimport { toRequestInit, type SplatRequestOptions } from '../../loading';\nimport { parseMeshPly, type TriangleMeshData } from './parse-mesh-ply';\nimport { parseCollisionLci } from './parse-collision-lci';\nimport type { CollisionMeshDescriptor, SceneCollision } from '../../lod-source';\nimport { warn } from '../../logging';\n\n/**\n * Fetches the collision-mesh tiles a streamed scene declares.\n *\n * VLAM! is not a physics engine: this hands back plain triangle geometry and\n * stops there. Building an acceleration structure, resolving a character\n * against it, and deciding what \"collision\" means for the host's camera are\n * all the host's business (see the demo's `collision.ts` for one answer).\n *\n * Descriptors may point at `.lcc2` mesh PLYs or a classic-LCC `collision.lci`\n * (one file → many per-cell tiles).\n */\n\n/** One loaded collision tile, in the scene's source-local frame. */\nexport interface CollisionMeshTile {\n /** The URL it came from - a stable identity for caching or debugging. */\n readonly url: string;\n readonly data: TriangleMeshData;\n /** Source-local bounds from the manifest, when it declared them. */\n readonly bounds?: THREE.Box3;\n}\n\n/**\n * Fetches and parses every tile in `collision`, concurrently.\n *\n * A tile that fails to fetch or parse is warned about and skipped rather than\n * failing the batch: collision degrades to partial coverage, which is what the\n * rest of the streaming pipeline does with a bad chunk too. A classic-LCC\n * `.lci` descriptor expands into one tile per cell mesh inside the file.\n *\n * @throws a `DOMException` named `AbortError` when cancelled.\n */\nexport async function loadCollisionMeshTiles(\n collision: SceneCollision,\n options: { request?: SplatRequestOptions; signal?: AbortSignal } = {},\n): Promise<CollisionMeshTile[]> {\n const settled = await Promise.allSettled(\n collision.meshes.map((descriptor) => loadDescriptor(descriptor, options)),\n );\n\n options.signal?.throwIfAborted();\n\n const tiles: CollisionMeshTile[] = [];\n for (const result of settled) {\n if (result.status === 'fulfilled') tiles.push(...result.value);\n else warn('Skipping a collision mesh that failed to load:', result.reason);\n }\n return tiles;\n}\n\nasync function loadDescriptor(\n descriptor: CollisionMeshDescriptor,\n options: { request?: SplatRequestOptions; signal?: AbortSignal },\n): Promise<CollisionMeshTile[]> {\n const response = await fetch(descriptor.url, toRequestInit(options.request, options.signal));\n if (!response.ok) {\n throw new Error(`HTTP ${response.status} for collision mesh ${descriptor.url}`);\n }\n const buffer = await response.arrayBuffer();\n\n if (isLciUrl(descriptor.url)) {\n const parsed = parseCollisionLci(buffer);\n return parsed.tiles.map((tile) => ({\n url: `${descriptor.url}#c${tile.cellX}_${tile.cellY}`,\n data: tile.data,\n bounds: tile.bounds,\n }));\n }\n\n const data = parseMeshPly(buffer);\n return [\n {\n url: descriptor.url,\n data,\n ...(descriptor.bounds ? { bounds: descriptor.bounds } : {}),\n },\n ];\n}\n\n/** True when the descriptor points at a classic-LCC `collision.lci`. */\nfunction isLciUrl(url: string): boolean {\n try {\n return new URL(url, 'https://example.invalid/').pathname.toLowerCase().endsWith('.lci');\n } catch {\n return url.toLowerCase().includes('.lci');\n }\n}\n","import type { TriangleMeshData } from './parse-mesh-ply';\nimport type { SelectionVolume } from '../../selection-volume';\n\n/**\n * Splits a collision triangle mesh by the same {@link SelectionVolume} used to\n * separate a splat region, so the separated object keeps its collision\n * geometry and both halves stay usable by a host's collision system.\n *\n * Collision tiles arrive in the dataset's source-local frame (see\n * `parse-mesh-ply.ts`), which for `.lcc2` is the frame the mesh matrix maps to\n * world - the same frame `SplatData.positions` use. So a volume built for the\n * splats partitions the collision mesh with no extra transform, as long as the\n * host holds both in that one frame.\n */\nexport interface TriangleMeshPartition {\n /** Triangles assigned to the selection. */\n readonly inside: TriangleMeshData;\n /** Everything else. */\n readonly outside: TriangleMeshData;\n}\n\n/**\n * Partitions a triangle mesh by a selection volume.\n *\n * A triangle goes to `inside` when **any** of its vertices is inside the\n * volume. Triangles are never clipped, so the two halves tile the original\n * exactly (no cracks, no new geometry); the trade-off is that boundary\n * triangles overhang the volume by up to one triangle. Vertex winding is\n * preserved. Vertex buffers are compacted per half - shared vertices are\n * duplicated into whichever halves reference them.\n *\n * @throws {Error} if `mesh` is internally inconsistent (array lengths that\n * disagree with the declared counts, or an out-of-range index). {@link parseMeshPly}\n * already guarantees this; a host-built mesh is checked here rather than\n * silently producing `NaN` positions.\n */\nexport function partitionTriangleMesh(\n mesh: TriangleMeshData,\n volume: SelectionVolume,\n): TriangleMeshPartition {\n assertConsistent(mesh);\n const { positions, indices, vertexCount, triangleCount } = mesh;\n\n const vertexInside = new Uint8Array(vertexCount);\n for (let v = 0; v < vertexCount; v++) {\n vertexInside[v] = volume.containsPoint(\n positions[v * 3] as number,\n positions[v * 3 + 1] as number,\n positions[v * 3 + 2] as number,\n )\n ? 1\n : 0;\n }\n\n const triangleInside = new Uint8Array(triangleCount);\n let insideTriangles = 0;\n for (let t = 0; t < triangleCount; t++) {\n const a = indices[t * 3] as number;\n const b = indices[t * 3 + 1] as number;\n const c = indices[t * 3 + 2] as number;\n if (vertexInside[a] === 1 || vertexInside[b] === 1 || vertexInside[c] === 1) {\n triangleInside[t] = 1;\n insideTriangles++;\n }\n }\n\n return {\n inside: gatherTriangles(mesh, triangleInside, 1, insideTriangles),\n outside: gatherTriangles(mesh, triangleInside, 0, triangleCount - insideTriangles),\n };\n}\n\n/** Rejects a mesh whose arrays disagree with its declared counts. */\nfunction assertConsistent(mesh: TriangleMeshData): void {\n if (mesh.positions.length < mesh.vertexCount * 3) {\n throw new Error(\n `partitionTriangleMesh: positions hold ${mesh.positions.length} floats, ` +\n `too few for ${mesh.vertexCount} vertices.`,\n );\n }\n if (mesh.indices.length < mesh.triangleCount * 3) {\n throw new Error(\n `partitionTriangleMesh: indices hold ${mesh.indices.length} entries, ` +\n `too few for ${mesh.triangleCount} triangles.`,\n );\n }\n for (let k = 0; k < mesh.triangleCount * 3; k++) {\n const index = mesh.indices[k] as number;\n if (index >= mesh.vertexCount) {\n throw new Error(\n `partitionTriangleMesh: index ${index} is out of range (${mesh.vertexCount} vertices).`,\n );\n }\n }\n}\n\n/** Compacts the triangles whose flag matches `side` into a fresh mesh. */\nfunction gatherTriangles(\n mesh: TriangleMeshData,\n triangleFlags: Uint8Array,\n side: 0 | 1,\n triangleCount: number,\n): TriangleMeshData {\n const remap = new Int32Array(mesh.vertexCount).fill(-1);\n const indices = new Uint32Array(triangleCount * 3);\n // First pass: remap indices while counting the referenced vertices.\n let vertexCount = 0;\n let written = 0;\n for (let t = 0; t < mesh.triangleCount; t++) {\n if (triangleFlags[t] !== side) continue;\n for (let k = 0; k < 3; k++) {\n const old = mesh.indices[t * 3 + k] as number;\n let mapped = remap[old] as number;\n if (mapped < 0) {\n mapped = vertexCount++;\n remap[old] = mapped;\n }\n indices[written++] = mapped;\n }\n }\n const positions = new Float32Array(vertexCount * 3);\n for (let old = 0; old < mesh.vertexCount; old++) {\n const mapped = remap[old] as number;\n if (mapped < 0) continue;\n positions[mapped * 3] = mesh.positions[old * 3] as number;\n positions[mapped * 3 + 1] = mesh.positions[old * 3 + 1] as number;\n positions[mapped * 3 + 2] = mesh.positions[old * 3 + 2] as number;\n }\n return { vertexCount, triangleCount, positions, indices };\n}\n"],"names":["LCC_RECORD_BYTES","LCC_SH_RECORD_BYTES","MAX_CHUNK_SPLATS","lccShCoefficientCount","bands","parseLccManifest","json","raw","version","encoding","totalLevel","levels","indexDataSize","expectedIndexSize","splats","count","totalSplats","cellLengthX","numberField","cellLengthY","georeference","parseGeoreference","shift","v","scaleTransform","bounds","min","triple","max","attributes","byName","entry","require","name","range","shcoef","ZERO_RANGE","fileType","resolveFileType","offset","epsg","isPlaceholderShcoef","parseLccIndex","buffer","manifest","view","cells","level","at","byteOffsetBig","byteSize","decodeLccRotation","encoded","largest","q","next","index","block","QUATERNION_SCALE","rest","parseLccChunk","params","sh","signal","stride","_a","positions","colors","covariances","bytes","scaleMinX","scaleMinY","scaleMinZ","scaleSpanX","scaleSpanY","scaleSpanZ","i","resolve","qw","qx","qy","qz","writeCovariance","packed","decodeChunkSh","sidecar","coefficients","c","finishChunkSh","expected","to","packedRangesEqual","requantizeShWord","key","value","label","createLcc2ToThreeMatrix","THREE","applyLcc2ToThreeTransform","target","SUBCHUNK_SPLATS","buildLccScene","source","options","url","dataUrl","environmentUrl","fetchBytes","assertIndexMatchesManifest","available","MAX_SH_BANDS","requested","shBands","shUrl","chunkUrls","chunkOptions","addChunk","chunk","leaves","pinnedFiles","minimumCoverageSplats","levelCount","cellIndex","cell","finestCount","subLeaves","sub","lods","coarsestFile","coarsestCount","lo","hi","file","cellBounds","environmentCount","environmentSplatCount","environmentStride","sceneBounds","lodManifest","collisionUrl","resolveCollisionLci","LodScheduler","size","x0","y0","total","declared","shortfall","tolerance","phase","response","toRequestInit","error","toSplatLoadError","FRUSTUM_PENALTY","FRUSTUM_MARGIN","THRESHOLD_MARGIN","DWELL_MS","buildLcc2Scene","dataset","path","nodes","cellNodes","nextLeaf","build","rawNode","depth","leafStart","children","rawChildren","child","leafEnd","boxFromRaw","rootChildren","OctreeLodSource","envName","_b","environment","warn","packShBands","sum","collisionFrom","meshFiles","boundsByFile","visit","node","mesh","wanted","_","meshes","coarsest","__publicField","n","nodeIndex","r","cameraLocal","frustum","now","cellDistance","resolved","contributions","adjust","delta","byDistanceDesc","a","b","fillTarget","runs","runFromNode","from","picked","anyInView","nearestCell","nearestDist","d","base","m","distances","emit","minWanted","bound","FACE_INDEX_PROPERTIES","parseMeshPly","header","parsePlyHeader","vertices","assertPlyPropertyTypes","assertPlyElementFits","readPositions","faces","indices","readFaces","xOffset","plyPropertyOffset","yOffset","zOffset","record","vertexCount","candidate","list","countSize","plyTypeSize","itemSize","end","written","cursor","face","truncated","readInteger","triangles","grown","first","previous","type","MAGIC","SUPPORTED_VERSION","GLOBAL_HEADER_BYTES","MESH_HEADER_BYTES","FLOATS_PER_VERTEX","INDICES_PER_FACE","BYTES_PER_VERTEX","BYTES_PER_FACE","parseCollisionLci","magic","headerLen","sceneMin","sceneMax","meshCount","expectedHeader","tiles","expectedOffset","cellX","cellY","dataOffset","dataSize","faceCount","bvhByteSize","indexOffset","t","scene","loadCollisionMeshTiles","collision","settled","descriptor","loadDescriptor","result","isLciUrl","tile","data","partitionTriangleMesh","volume","assertConsistent","triangleCount","vertexInside","triangleInside","insideTriangles","gatherTriangles","k","triangleFlags","side","remap","old","mapped"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lcc.js","sources":["../../src/lib/formats/lcc/parse-lcc.ts","../../src/lib/formats/lcc/lcc2-transform.ts","../../src/lib/formats/lcc/lcc.ts","../../src/lib/formats/lcc/lcc2.ts","../../src/lib/formats/lcc/parse-mesh-ply.ts","../../src/lib/formats/lcc/parse-collision-lci.ts","../../src/lib/formats/lcc/collision-mesh.ts","../../src/lib/formats/lcc/collision-partition.ts"],"sourcesContent":["import { writeCovariance, type SplatData } from '../../splat-data';\nimport { packedRangesEqual, requantizeShWord } from '../../sh-pack';\n\n/**\n * Decoder for XGRIDS' older LCC datasets (`.lcc` / `meta.lcc`; see\n * `docs/formats/lcc-notes.md`). Manifest versions **2.x** through **5.x** share the\n * same binary layout; v2/v3 omit `fileType` and v3 often names the manifest `meta.lcc`.\n *\n * Unlike `.lcc2` - which delivers SOG/WebP tiles and reuses {@link parseSog} -\n * an LCC dataset is raw binary: a JSON manifest, an `index.bin` of per-cell\n * per-level ranges, and one large `data.bin` of fixed 32-byte splat records\n * that chunks are cut out of with HTTP range requests.\n *\n * Implemented from XGRIDS' openly published spec (Data Organization Format\n * originated from XGRIDS); no XGRIDS code is used, and their capture data is\n * never redistributed. Where the whitepaper's field table disagrees with real\n * captures the bytes win - see `docs/formats/lcc-notes.md` for the verification.\n */\n\n/** Bytes per splat in `data.bin`. */\nexport const LCC_RECORD_BYTES = 32;\n/** Bytes per splat in `shcoef.bin`, and of the SH block inside an env record. */\nexport const LCC_SH_RECORD_BYTES = 64;\n/** SH coefficients per channel in a `Quality` capture (3rd order). */\nexport const LCC_SH_COEFFICIENTS = 15;\n\n/**\n * Upper bound on a decoded chunk's splat count, mirroring {@link parseSog}'s\n * guard: the renderer cannot draw more than the 2048-wide pool textures hold,\n * so a corrupt index should be rejected rather than allocate gigabytes.\n */\nconst MAX_CHUNK_SPLATS = 1 << 24;\n\n/** A `[min, max]` dequantization range from the manifest's `attributes`. */\nexport interface LccRange {\n readonly min: readonly [number, number, number];\n readonly max: readonly [number, number, number];\n}\n\n/**\n * Where a capture's local origin sits in the real world. Purely informational\n * - the splat data is local and is rendered as-is (see `parseGeoreference`).\n */\nexport interface LccGeoreference {\n /** The local origin's coordinates in {@link epsg}, from manifest `offset`. */\n readonly origin: readonly [number, number, number];\n /** The projection's EPSG code, when the manifest names one. */\n readonly epsg?: number;\n}\n\n/** The manifest fields this loader consumes. */\nexport interface LccManifest {\n readonly totalSplats: number;\n readonly totalLevel: number;\n readonly cellLengthX: number;\n readonly cellLengthY: number;\n readonly indexDataSize: number;\n /** Per-level splat totals; index 0 is the finest level. */\n readonly splats: readonly number[];\n readonly bounds: { min: [number, number, number]; max: [number, number, number] };\n /** `Quality` captures carry `shcoef.bin`; `Portable` ones have no SH. */\n readonly fileType: 'Portable' | 'Quality';\n readonly scale: LccRange;\n readonly shcoef: LccRange;\n readonly envScale: LccRange;\n readonly envShcoef: LccRange;\n /** Set when the capture is georeferenced; absent for a purely local one. */\n readonly georeference?: LccGeoreference;\n}\n\n/** One cell's range within `data.bin` at one LOD level. */\nexport interface LccLevelRange {\n readonly count: number;\n readonly byteOffset: number;\n readonly byteSize: number;\n}\n\n/** One `index.bin` record: a cell of the 2D grid, at every LOD level. */\nexport interface LccIndexCell {\n readonly cellX: number;\n readonly cellY: number;\n /** Range per level; index 0 is the finest. */\n readonly levels: readonly LccLevelRange[];\n}\n\n/** Everything the worker needs to turn fetched bytes into splats. */\nexport interface LccChunkParams {\n /** `splats` decodes `data.bin`; `environment` decodes `environment.bin`. */\n readonly kind: 'splats' | 'environment';\n /** Byte range within the source file. */\n readonly start: number;\n readonly length: number;\n /**\n * Bytes per record. Always 32 in `data.bin`; `environment.bin` interleaves\n * a 64-byte SH block per splat in a `Quality` capture, making it 96.\n *\n * This is a property of the *file*, not of whether the SH is wanted - a\n * `Quality` environment stays 96 bytes per record even with SH disabled.\n */\n readonly stride: number;\n /** Dequantization range for the scale field (`scale` or `envscale`). */\n readonly scale: LccRange;\n /** Present when this chunk's SH should be decoded (see `LccShParams`). */\n readonly sh?: LccShParams;\n}\n\n/** How to obtain (and dequantize) a chunk's higher-order SH. */\nexport interface LccShParams {\n /** SH bands to keep: 1, 2 or 3 → 3, 8 or 15 coefficients. */\n readonly bands: 1 | 2 | 3;\n readonly range: LccRange;\n /**\n * Where the SH bytes come from. `sidecar` fetches `shcoef.bin` over the\n * given range (`data.bin` offset × 2); `inline` reads the 64-byte block\n * that follows each base record inside a 96-byte environment record.\n */\n readonly source: 'sidecar' | 'inline';\n /** Absolute `shcoef.bin` URL; only for `source: 'sidecar'`. */\n readonly url?: string;\n /**\n * Re-encode the decoded words into this range instead of {@link range}.\n *\n * An LCC capture quantizes base SH (`shcoef`) and environment SH\n * (`envshcoef`) against *different* ranges, but the shared pool decodes the\n * whole scene through one range uniform. Left alone, whichever chunk lands\n * first wins and the other's coefficients are read against the wrong span -\n * the environment range is wider, so base SH gets amplified into a\n * view-dependent \"flashlight.\" So the environment chunk is requantized into\n * the base range here, letting every chunk agree on `shcoef`. Values outside\n * the base range clip (only decorative sky highlights, and only the few\n * thousand environment splats pay the re-encode).\n */\n readonly requantizeTo?: LccRange;\n}\n\n/** Coefficients carried by each SH band count. */\nexport function lccShCoefficientCount(bands: 1 | 2 | 3): number {\n return bands === 1 ? 3 : bands === 2 ? 8 : 15;\n}\n\n/**\n * Validates an untrusted `.lcc` manifest and extracts the fields the loader\n * needs. Everything downstream (index record size, chunk byte ranges, pool\n * sizing) derives from these, so they are checked up front.\n *\n * @throws {Error} on an unsupported version or a malformed manifest.\n */\nexport function parseLccManifest(json: unknown): LccManifest {\n const raw = json as Record<string, unknown>;\n const version = String(raw['version'] ?? '');\n // 2.x, 3.x (older L2 Pro / `meta.lcc`), 4.x and 5.x share the same binary\n // layout; 2.x and 3.x omit `fileType`, 4.x and 5.x write it. 4.x was\n // verified against a real capture (conferencehall) - its only quirk is\n // manifest `splats` counts that slightly over-declare what index.bin holds,\n // which the index cross-check in `lcc.ts` tolerates. 2.x was verified\n // against a real capture (trainmuseum): base attributes only, no\n // environment records, and every field this parser reads carries the same\n // meaning as in 3.x. Other major versions have not been verified.\n if (!/^[2345]\\./.test(version)) {\n throw new Error(\n `Unsupported LCC manifest version: \"${version}\" (expected 2.x, 3.x, 4.x or 5.x).`,\n );\n }\n // The whitepaper defines only COMPRESS; anything else is a format we have\n // not seen and would silently mis-decode.\n const encoding = raw['encoding'];\n if (encoding !== undefined && encoding !== 'COMPRESS') {\n throw new Error(`Unsupported LCC encoding: \"${String(encoding)}\" (expected COMPRESS).`);\n }\n\n const totalLevel = raw['totalLevel'];\n if (!Number.isSafeInteger(totalLevel) || (totalLevel as number) < 1) {\n throw new Error(`LCC manifest declares an invalid totalLevel: ${String(totalLevel)}.`);\n }\n const levels = totalLevel as number;\n\n const indexDataSize = raw['indexDataSize'];\n const expectedIndexSize = 4 + levels * 16;\n if (indexDataSize !== expectedIndexSize) {\n throw new Error(\n `LCC manifest indexDataSize is ${String(indexDataSize)}, but ${levels} levels imply ${expectedIndexSize}.`,\n );\n }\n\n const splats = raw['splats'];\n if (!Array.isArray(splats) || splats.length !== levels) {\n throw new Error(`LCC manifest \"splats\" must list one count per level (${levels}).`);\n }\n for (const count of splats) {\n if (!Number.isSafeInteger(count) || count < 0) {\n throw new Error(`LCC manifest \"splats\" contains an invalid count: ${String(count)}.`);\n }\n }\n\n const totalSplats = raw['totalSplats'];\n if (!Number.isSafeInteger(totalSplats) || (totalSplats as number) < 0) {\n throw new Error(`LCC manifest declares an invalid totalSplats: ${String(totalSplats)}.`);\n }\n\n const cellLengthX = numberField(raw, 'cellLengthX');\n const cellLengthY = numberField(raw, 'cellLengthY');\n if (cellLengthX <= 0 || cellLengthY <= 0) {\n throw new Error('LCC manifest cell lengths must be positive.');\n }\n\n // `offset` is a georeference anchor, not a transform of the splat data: it\n // names where the local origin sits in the manifest's `epsg` projection\n // (e.g. UTM easting/northing/height), while `boundingBox` and every record\n // in `data.bin` stay local. Adding it to positions would be wrong twice\n // over - it double-places the scene, and baking hundreds of kilometres into\n // float32 destroys the precision splats are rendered with. So it is carried\n // as metadata and left out of the geometry.\n const georeference = parseGeoreference(raw);\n\n // `shift` and `scale`, unlike `offset`, would genuinely transform the data.\n // No capture has used them, so refuse rather than place the scene wrong.\n const shift = raw['shift'];\n if (Array.isArray(shift) && shift.some((v) => v !== 0)) {\n throw new Error(\n `LCC manifest \"shift\" is non-zero (${JSON.stringify(shift)}), which is not supported.`,\n );\n }\n const scaleTransform = raw['scale'];\n if (Array.isArray(scaleTransform) && scaleTransform.some((v) => v !== 1)) {\n throw new Error(\n `LCC manifest \"scale\" is non-identity (${JSON.stringify(scaleTransform)}), which is not supported.`,\n );\n }\n\n const bounds = raw['boundingBox'] as { min?: unknown; max?: unknown } | undefined;\n const min = triple(bounds?.min, 'boundingBox.min');\n const max = triple(bounds?.max, 'boundingBox.max');\n\n const attributes = raw['attributes'];\n if (!Array.isArray(attributes)) throw new Error('LCC manifest is missing \"attributes\".');\n const byName = new Map<string, LccRange>();\n for (const entry of attributes as { name?: unknown; min?: unknown; max?: unknown }[]) {\n if (typeof entry?.name !== 'string') continue;\n // Opacity is 1-D; only the 3-D ranges the decoder reads are kept.\n if (!Array.isArray(entry.min) || entry.min.length < 3) continue;\n byName.set(entry.name, {\n min: triple(entry.min, `attributes.${entry.name}.min`),\n max: triple(entry.max, `attributes.${entry.name}.max`),\n });\n }\n const require = (name: string): LccRange => {\n const range = byName.get(name);\n if (!range) throw new Error(`LCC manifest is missing the \"${name}\" attribute range.`);\n return range;\n };\n\n const shcoef = byName.get('shcoef') ?? ZERO_RANGE;\n const fileType = resolveFileType(raw['fileType'], shcoef);\n\n return {\n totalSplats: totalSplats as number,\n totalLevel: levels,\n cellLengthX,\n cellLengthY,\n indexDataSize: expectedIndexSize,\n splats: splats as number[],\n bounds: { min, max },\n fileType,\n scale: require('scale'),\n // A Portable capture has no SH at all; keep a benign range so the type\n // stays total rather than sprinkling optionality through the loader.\n shcoef,\n envScale: byName.get('envscale') ?? ZERO_RANGE,\n envShcoef: byName.get('envshcoef') ?? ZERO_RANGE,\n georeference,\n };\n}\n\n/**\n * Reads the capture's world anchor. Absent, all-zero or unprojected (`epsg`\n * missing or 0) manifests are simply local, so they report no georeference\n * rather than a meaningless origin.\n */\nfunction parseGeoreference(raw: Record<string, unknown>): LccGeoreference | undefined {\n const offset = raw['offset'];\n if (!Array.isArray(offset) || !offset.some((v) => v !== 0)) return undefined;\n const epsg = raw['epsg'];\n return {\n origin: triple(offset, 'offset'),\n epsg: Number.isSafeInteger(epsg) && (epsg as number) > 0 ? (epsg as number) : undefined,\n };\n}\n\n/**\n * Resolves `Portable` vs `Quality`. v5.0 writes the field; v3.0 omits it.\n * Portable manifests still list a stub `shcoef` of `[0,0,0]..[1,1,1]` (or\n * zeros); a real Quality capture has trained ranges outside that.\n */\nfunction resolveFileType(fileType: unknown, shcoef: LccRange): 'Portable' | 'Quality' {\n if (fileType === 'Portable' || fileType === 'Quality') return fileType;\n if (fileType === undefined) return isPlaceholderShcoef(shcoef) ? 'Portable' : 'Quality';\n throw new Error(\n `Unsupported LCC fileType: \"${String(fileType)}\" (expected Portable or Quality).`,\n );\n}\n\n/** True when `shcoef` looks like a Portable stub rather than trained ranges. */\nfunction isPlaceholderShcoef(range: LccRange): boolean {\n return range.min.every((v) => v === 0) && range.max.every((v) => v === 0 || v === 1);\n}\n\nconst ZERO_RANGE: LccRange = { min: [0, 0, 0], max: [0, 0, 0] };\n\n/**\n * Parses `index.bin` into one record per cell.\n *\n * @param buffer - The whole file (a few KB even for large captures).\n * @param manifest - Supplies the record size and level count.\n * @throws {Error} if the file length or any record is inconsistent.\n */\nexport function parseLccIndex(buffer: ArrayBuffer, manifest: LccManifest): LccIndexCell[] {\n const { indexDataSize, totalLevel } = manifest;\n if (buffer.byteLength === 0 || buffer.byteLength % indexDataSize !== 0) {\n throw new Error(\n `LCC index.bin is ${buffer.byteLength} bytes, not a multiple of indexDataSize ${indexDataSize}.`,\n );\n }\n const view = new DataView(buffer);\n const cells: LccIndexCell[] = [];\n for (let offset = 0; offset < buffer.byteLength; offset += indexDataSize) {\n const levels: LccLevelRange[] = [];\n for (let level = 0; level < totalLevel; level++) {\n const at = offset + 4 + level * 16;\n const count = view.getUint32(at, true);\n const byteOffsetBig = view.getBigUint64(at + 4, true);\n // Captures are far below 2^53 bytes; assert so a future 8 EB-addressed\n // dataset fails loudly here instead of silently losing precision.\n if (byteOffsetBig > BigInt(Number.MAX_SAFE_INTEGER)) {\n throw new Error(\n `LCC index.bin has a byte offset beyond Number.MAX_SAFE_INTEGER: ${byteOffsetBig}.`,\n );\n }\n const byteSize = view.getUint32(at + 12, true);\n if (byteSize !== count * LCC_RECORD_BYTES) {\n throw new Error(\n `LCC index.bin level ${level} declares ${count} splats but ${byteSize} bytes ` +\n `(expected ${count * LCC_RECORD_BYTES}).`,\n );\n }\n levels.push({ count, byteOffset: Number(byteOffsetBig), byteSize });\n }\n cells.push({\n cellX: view.getUint16(offset, true),\n cellY: view.getUint16(offset + 2, true),\n levels,\n });\n }\n return cells;\n}\n\n/**\n * Decodes a packed rotation into a quaternion `[w, x, y, z]`.\n *\n * Smallest-three: bits 30-31 name the omitted (largest-magnitude) component\n * as an index into `(x, y, z, w)`, and three 10-bit fields hold the others in\n * ascending index order as offset binary with bias 511, spanning `[-√½, +√½]`.\n * The omitted component is recovered from unit length.\n *\n * Note this is neither the whitepaper's `QLut`/`DecodeRotation` nor the\n * LCC-Web SDK's sign-magnitude `parseQuat` (which decodes the SDK's own\n * repacked GPU texture, not the file) - see `docs/formats/lcc-notes.md`.\n *\n * The result is unit-length for any word a real encoder produces. A corrupt\n * word whose three stored components already exceed unit length yields a\n * non-unit (but always finite) quaternion, as in {@link parseSog}'s\n * equivalent decode; {@link writeCovariance} normalizes.\n */\nexport function decodeLccRotation(encoded: number): [number, number, number, number] {\n const largest = (encoded >>> 30) & 3;\n const q = [0, 0, 0, 0]; // (x, y, z, w)\n let next = 0;\n for (let index = 0; index < 4; index++) {\n if (index === largest) continue;\n const block = (encoded >>> (10 * next)) & 0x3ff;\n q[index] = (block - 511) * QUATERNION_SCALE;\n next++;\n }\n const rest =\n (q[0] as number) ** 2 + (q[1] as number) ** 2 + (q[2] as number) ** 2 + (q[3] as number) ** 2;\n q[largest] = Math.sqrt(Math.max(0, 1 - rest));\n return [q[3] as number, q[0] as number, q[1] as number, q[2] as number];\n}\n\n/** Maps a 10-bit offset-binary field onto `[-√½, +√½]`. */\nconst QUATERNION_SCALE = 1 / (511 * Math.SQRT2);\n\n/**\n * Decodes one packed SH word into its three channel values.\n *\n * `DecodePacked_11_10_11`: R is bits 0-10, G bits 11-20, B bits 21-31, each a\n * unit fraction of its field's maximum, dequantized across `range`.\n */\nexport function decodeLccShWord(\n word: number,\n range: LccRange,\n out: Float32Array,\n at: number,\n): void {\n const r = (word & 0x7ff) / 2047;\n const g = ((word >>> 11) & 0x3ff) / 1023;\n const b = ((word >>> 21) & 0x7ff) / 2047;\n out[at + 0] = lerp(range.min[0], range.max[0], r);\n out[at + 1] = lerp(range.min[1], range.max[1], g);\n out[at + 2] = lerp(range.min[2], range.max[2], b);\n}\n\nfunction lerp(min: number, max: number, t: number): number {\n return min + (max - min) * t;\n}\n\n/**\n * Decodes a range of `data.bin` (or the base records of `environment.bin`)\n * into the flat arrays the pool uploads.\n *\n * @param buffer - Exactly the chunk's bytes: `count * 32` for `splats`, or\n * `count * stride` for `environment`.\n * @param params - Byte range and dequantization ranges from the scene builder.\n * @param sh - Optional SH bytes: the matching `shcoef.bin` range for\n * `source: 'sidecar'`, or unused when the SH is inline in `buffer`.\n * @param signal - Checked inside the decode loop so a superseded chunk can be\n * abandoned mid-decode.\n * @throws a `DOMException` named `AbortError` when cancelled.\n */\nexport async function parseLccChunk(\n buffer: ArrayBuffer,\n params: LccChunkParams,\n sh?: ArrayBuffer,\n signal?: AbortSignal,\n): Promise<SplatData> {\n const stride = params.stride;\n if (stride !== LCC_RECORD_BYTES && stride !== LCC_RECORD_BYTES + LCC_SH_RECORD_BYTES) {\n throw new Error(`LCC chunk declares an unsupported ${stride}-byte record.`);\n }\n if (params.sh?.source === 'inline' && stride === LCC_RECORD_BYTES) {\n throw new Error('LCC chunk asks for inline SH but its records have no room for it.');\n }\n if (buffer.byteLength % stride !== 0) {\n throw new Error(\n `LCC chunk is ${buffer.byteLength} bytes, not a multiple of the ${stride}-byte record.`,\n );\n }\n const count = buffer.byteLength / stride;\n if (count > MAX_CHUNK_SPLATS) {\n throw new Error(\n `LCC chunk holds ${count} splats, above the supported maximum of ${MAX_CHUNK_SPLATS}.`,\n );\n }\n\n const view = new DataView(buffer);\n const positions = new Float32Array(count * 3);\n const colors = new Uint8Array(count * 4);\n const covariances = new Float32Array(count * 6);\n const bytes = new Uint8Array(buffer);\n\n const { min, max } = params.scale;\n const scaleMinX = min[0],\n scaleMinY = min[1],\n scaleMinZ = min[2];\n const scaleSpanX = max[0] - min[0],\n scaleSpanY = max[1] - min[1],\n scaleSpanZ = max[2] - min[2];\n\n for (let i = 0; i < count; i++) {\n // The decode loop dominates chunk time (a single cell's finest level can\n // be ~2.9M splats). With a signal, yield every 65,536 splats so a pending\n // cancel can be delivered at all - the worker is single-threaded.\n if (signal && (i & 0xffff) === 0 && i > 0) {\n await new Promise((resolve) => setTimeout(resolve, 0));\n signal.throwIfAborted();\n }\n const at = i * stride;\n\n positions[i * 3 + 0] = view.getFloat32(at, true);\n positions[i * 3 + 1] = view.getFloat32(at + 4, true);\n positions[i * 3 + 2] = view.getFloat32(at + 8, true);\n\n // Colors are stored exactly as the pool wants them: RGB pre-mapped through\n // 0.5 + sh·SH_C0, and alpha already activated (no sigmoid, as in SOG).\n colors[i * 4 + 0] = bytes[at + 12] as number;\n colors[i * 4 + 1] = bytes[at + 13] as number;\n colors[i * 4 + 2] = bytes[at + 14] as number;\n colors[i * 4 + 3] = bytes[at + 15] as number;\n\n // Scale is quantized linear (already exponentiated), unlike PLY/SOG.\n const [qw, qx, qy, qz] = decodeLccRotation(view.getUint32(at + 22, true));\n writeCovariance(\n covariances,\n i,\n scaleMinX + scaleSpanX * (view.getUint16(at + 16, true) / 65535),\n scaleMinY + scaleSpanY * (view.getUint16(at + 18, true) / 65535),\n scaleMinZ + scaleSpanZ * (view.getUint16(at + 20, true) / 65535),\n qw,\n qx,\n qy,\n qz,\n );\n }\n\n signal?.throwIfAborted();\n const packed = params.sh ? decodeChunkSh(buffer, sh, params.sh, count, stride) : undefined;\n return {\n count,\n positions,\n colors,\n covariances,\n ...(packed ? { shPacked: packed } : {}),\n };\n}\n\n/**\n * Extracts a chunk's SH words, keeping only the requested bands.\n *\n * The words stay packed: the shader decodes them, so this is a copy rather\n * than a 45-float-per-splat expansion. Coefficients are ordered low-band\n * first, so a lower band count is a prefix of each 16-word record.\n */\nfunction decodeChunkSh(\n buffer: ArrayBuffer,\n sidecar: ArrayBuffer | undefined,\n params: LccShParams,\n count: number,\n stride: number,\n): NonNullable<SplatData['shPacked']> {\n const coefficients = lccShCoefficientCount(params.bands);\n const packed = new Uint32Array(count * coefficients);\n\n if (params.source === 'inline') {\n // Environment records carry their 64-byte SH block right after the base.\n const view = new DataView(buffer);\n for (let i = 0; i < count; i++) {\n const at = i * stride + LCC_RECORD_BYTES;\n for (let c = 0; c < coefficients; c++)\n packed[i * coefficients + c] = view.getUint32(at + c * 4, true);\n }\n return finishChunkSh(packed, params);\n }\n\n if (!sidecar) throw new Error('LCC chunk expected shcoef.bin bytes but none were fetched.');\n const expected = count * LCC_SH_RECORD_BYTES;\n if (sidecar.byteLength !== expected) {\n throw new Error(\n `LCC shcoef.bin range is ${sidecar.byteLength} bytes; expected ${expected} for ${count} splats.`,\n );\n }\n const view = new DataView(sidecar);\n for (let i = 0; i < count; i++) {\n const at = i * LCC_SH_RECORD_BYTES;\n for (let c = 0; c < coefficients; c++)\n packed[i * coefficients + c] = view.getUint32(at + c * 4, true);\n }\n return finishChunkSh(packed, params);\n}\n\n/**\n * Tags the packed words with their range, re-encoding into\n * {@link LccShParams.requantizeTo} first when the chunk's own range differs\n * from it (the environment case - see that field's note).\n */\nfunction finishChunkSh(\n packed: Uint32Array,\n params: LccShParams,\n): NonNullable<SplatData['shPacked']> {\n const to = params.requantizeTo;\n if (!to || packedRangesEqual(params.range, to)) {\n return { bands: params.bands, packed, range: params.range };\n }\n for (let i = 0; i < packed.length; i++) {\n packed[i] = requantizeShWord(packed[i] as number, params.range, to);\n }\n return { bands: params.bands, packed, range: to };\n}\n\nfunction numberField(raw: Record<string, unknown>, key: string): number {\n const value = raw[key];\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n throw new Error(`LCC manifest field \"${key}\" is not a finite number.`);\n }\n return value;\n}\n\nfunction triple(value: unknown, label: string): [number, number, number] {\n if (!Array.isArray(value) || value.length < 3)\n throw new Error(`LCC manifest \"${label}\" must have three numbers.`);\n const out = value.slice(0, 3).map((v) => {\n if (typeof v !== 'number' || !Number.isFinite(v))\n throw new Error(`LCC manifest \"${label}\" has a non-finite entry.`);\n return v;\n });\n return out as [number, number, number];\n}\n","import * as THREE from 'three/webgpu';\n\n/**\n * LCC2 / XGRIDS captures are Z-up. Three.js (and this package's SOG/PLY path)\n * expects Y-up. The established LCC/Spark convention maps raw LCC coordinates\n * `(x, y, z)` → `(-x, z, y)` - equivalent to −90° about X followed by 180°\n * about Y. Kept as a single matrix so consumers cannot accidentally apply only\n * half of the correction.\n *\n * Basis check: +X → −X, +Y → +Z, +Z → +Y.\n */\nexport function createLcc2ToThreeMatrix(): THREE.Matrix4 {\n // Row-major: x'=-x, y'=z, z'=y.\n return new THREE.Matrix4().set(-1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1);\n}\n\n/**\n * Applies the LCC2→Three.js format correction to a mesh. Bounds and LOD data\n * stay in source-local coordinates; `matrixWorld` / `worldToLocal` pick this up.\n * `StreamedSplatMesh.load()` applies this correction automatically for LCC2;\n * use this helper only with meshes created through another loading path.\n */\nexport function applyLcc2ToThreeTransform(target: THREE.Object3D): void {\n target.matrix.copy(createLcc2ToThreeMatrix());\n target.matrix.decompose(target.position, target.quaternion, target.scale);\n target.matrixWorldNeedsUpdate = true;\n}\n","import * as THREE from 'three/webgpu';\nimport { LodScheduler } from '../../lod-scheduler';\nimport type { LodLeaf, LodManifest, LodRange } from '../../lod-manifest';\nimport type { LodSourceOptions, StreamedChunkOptions, StreamedScene } from '../../lod-source';\nimport { createLcc2ToThreeMatrix } from './lcc2-transform';\nimport { MAX_SH_BANDS } from '../../splat-mesh';\nimport {\n LCC_RECORD_BYTES,\n LCC_SH_RECORD_BYTES,\n parseLccIndex,\n parseLccManifest,\n type LccIndexCell,\n type LccManifest,\n} from './parse-lcc';\nimport { toRequestInit, toSplatLoadError, type SplatRequestOptions } from '../../loading';\nimport type { SplatDatasetSource } from '../../dataset-source';\n\n/**\n * Scene builder for XGRIDS' older LCC datasets (`.lcc` / `meta.lcc`, manifest v3–v5;\n * format details in `docs/formats/lcc-notes.md`, record decoding in\n * {@link parseLccChunk}).\n *\n * An LCC capture is a flat 2D grid of cells, each holding an independent splat\n * set at every LOD level - not the shared-ancestor octree `.lcc2` uses. This\n * builder synthesizes a {@link LodManifest} over the cells and reuses\n * {@link LodScheduler} rather than adding a second selector.\n *\n * **The whole LOD ladder is scheduled**, so a distant or over-budget cell\n * coarsens to a smaller level rather than being dropped whole - full coverage\n * at any budget, matching the XGRIDS viewer. See the note in\n * {@link buildLccScene} for the streak-vs-coverage tradeoff an LCC LOD ladder\n * carries.\n *\n * The splats live in one large `data.bin` (~300 MB), and each `(cell, level)`\n * is one contiguous slice of it. A cell whose finest level runs to millions of\n * splats (~90 MB) is **sub-chunked**: the cell becomes K sub-leaves, each\n * level partitioned K ways at shared boundaries, every `(cell, level, i)`\n * slice its own ranged chunk. Sub-leaves from one physical cell share an\n * atomic budget group: they stream independently, but the scheduler never\n * mixes their LOD levels. Some writers preserve spatial locality in record\n * order, for which independently budgeting slices would create rectangular\n * holes as refinement settles.\n * `environment.bin` becomes one extra always-resident chunk.\n */\n\n/**\n * Target splats per sub-chunk when a cell's finest level splits. Matches the\n * scheduler's `MAX_RUN_SPLATS` (128k) so a sub-leaf's run never re-splits, and\n * keeps a slice's `data.bin` range at ~4 MB (+8 MB `shcoef.bin` on a Quality\n * capture) - small enough that fetches pipeline through the in-flight cap and\n * an abort-on-move loses little.\n */\nconst SUBCHUNK_SPLATS = 128_000;\n\n/** Options for {@link buildLccScene} beyond the shared LOD knobs. */\nexport interface LccSceneOptions extends LodSourceOptions {\n /**\n * Higher-order SH bands to stream for a `Quality` capture: 0 disables SH\n * (nothing is fetched from `shcoef.bin`), 3 is the full 3rd order.\n * Unset means every band the capture carries. Always clamped to that -\n * a `Portable` capture has none, whatever is asked for.\n */\n shBands?: 0 | 1 | 2 | 3;\n signal?: AbortSignal;\n request?: SplatRequestOptions;\n}\n\n/**\n * Builds a streamed scene from a parsed `.lcc` manifest, fetching the small\n * sidecars (`index.bin`, and `environment.bin`'s size) it needs to lay out\n * chunks. `data.bin` itself is never fetched here.\n *\n * @param json - The parsed `.lcc` manifest.\n * @param source - Dataset used to resolve and fetch the manifest's sidecars.\n * @param options - LOD, request, cancellation, and spherical-harmonic options.\n */\nexport async function buildLccScene(\n json: unknown,\n source: SplatDatasetSource,\n options: LccSceneOptions,\n): Promise<StreamedScene> {\n const manifest = parseLccManifest(json);\n const require = (name: string): string => {\n const url = source.resolve(name);\n if (url === null) throw new Error(`LCC dataset is missing \"${name}\".`);\n return url;\n };\n const dataUrl = require('data.bin');\n const environmentUrl = source.resolve('environment.bin');\n\n const index = parseLccIndex(\n await fetchBytes(require('index.bin'), options, 'manifest'),\n manifest,\n );\n assertIndexMatchesManifest(index, manifest);\n\n // A Portable capture has no shcoef.bin at all, so it caps everyone at 0 -\n // asking for SH on one must not allocate pool textures for SH that can\n // never arrive, nor fetch a file that does not exist.\n const available = manifest.fileType === 'Quality' ? MAX_SH_BANDS : 0;\n const requested = options.shBands ?? available;\n const bands = Math.min(requested, available);\n const shBands: 1 | 2 | 3 | undefined = bands === 0 ? undefined : (bands as 1 | 2 | 3);\n const shUrl = shBands === undefined ? null : require('shcoef.bin');\n\n const chunkUrls: string[] = [];\n const chunkOptions: (StreamedChunkOptions | undefined)[] = [];\n const addChunk = (label: string, chunk: StreamedChunkOptions): number => {\n chunkUrls.push(label);\n chunkOptions.push(chunk);\n return chunkUrls.length - 1;\n };\n\n const leaves: LodLeaf[] = [];\n const pinnedFiles = new Set<number>();\n let minimumCoverageSplats = 0;\n // Every cell offers its whole LOD ladder, so the scheduler coarsens a\n // distant or over-budget cell to a smaller level instead of dropping it -\n // full coverage at any budget, the way the XGRIDS viewer behaves. (An\n // earlier revision scheduled only the finest level; on a capture whose\n // finest level dwarfs the budget that made whole central cells vanish.)\n //\n // The tradeoff an LCC LOD ladder carries: a coarser level is a decimated\n // *alternative*, not a refinement of the same surface - merging widens each\n // splat into a flat disc - so a coarse level forced near the camera reads as\n // a streak. The distance model plus `fillBudget` keep near cells at their\n // finest whenever the budget allows, so coarse levels surface only at\n // distance (where they read fine) or under a very tight budget (the accepted\n // cost).\n //\n // A big cell is split into K sub-leaves (its finest level in ~128k-splat\n // slices), every level partitioned K ways at the same boundaries and every\n // (cell, level, i) slice its own chunk file. Dedicated files mean the\n // scheduler's runs never coalesce across sub-leaves. Each slice streams on\n // its own (including resolved L0): waiting for a whole Quality cell's finest\n // cut before any swap left near detail stuck on coarse while fetches\n // churned. `budgetGroup` still keeps every slice on one scheduler cut so a\n // cell does not refine mid-ladder while a sibling is still coarse. This is\n // load-bearing for captures whose record order has spatial locality: mixing\n // levels within a cell would remove a compact patch rather than merely\n // thinning density. Each sub-leaf pins its own slice of the coarsest level\n // as substitute coverage while finer data lands farther out.\n const levelCount = manifest.totalLevel;\n for (let cellIndex = 0; cellIndex < index.length; cellIndex++) {\n const cell = index[cellIndex] as LccIndexCell;\n // K is sized from the finest *present* level; coarser levels split into\n // proportionally smaller slices at the same boundaries.\n let finestCount = 0;\n for (let level = 0; level < levelCount; level++) {\n const count = (cell.levels[level] as LccIndexCell['levels'][number]).count;\n if (count > 0) {\n finestCount = count;\n break;\n }\n }\n const subLeaves = Math.max(1, Math.ceil(finestCount / SUBCHUNK_SPLATS));\n for (let sub = 0; sub < subLeaves; sub++) {\n const lods: (LodRange | undefined)[] = new Array<LodRange | undefined>(levelCount).fill(\n undefined,\n );\n let coarsestFile = -1;\n let coarsestCount = 0;\n for (let level = 0; level < levelCount; level++) {\n const range = cell.levels[level] as LccIndexCell['levels'][number];\n // A level absent from a cell leaves a hole in `lods`; the scheduler\n // resolves to the nearest present level. A wholly empty cell keeps all\n // holes (as a single empty leaf).\n if (range.count === 0) continue;\n // This sub-leaf's slice of the level: floor boundaries partition the\n // level's records exactly across the K sub-leaves. A coarse level\n // with fewer records than sub-leaves leaves some slices empty.\n const lo = Math.floor((sub * range.count) / subLeaves);\n const hi = Math.floor(((sub + 1) * range.count) / subLeaves);\n if (hi === lo) continue;\n // The URL fragment never reaches the server; it just makes the chunk\n // legible in dev tools and in the mesh's debug stats. The `.i` suffix\n // appears only on split cells, keeping unsplit labels stable.\n const label = `${dataUrl}#c${cell.cellX}_${cell.cellY}-l${level}${\n subLeaves > 1 ? `.${sub}` : ''\n }`;\n const file = addChunk(label, {\n format: 'lcc-bin',\n lcc: {\n kind: 'splats',\n start: range.byteOffset + lo * LCC_RECORD_BYTES,\n length: (hi - lo) * LCC_RECORD_BYTES,\n stride: LCC_RECORD_BYTES,\n scale: manifest.scale,\n ...(shBands === undefined\n ? {}\n : {\n sh: {\n bands: shBands,\n range: manifest.shcoef,\n source: 'sidecar' as const,\n // shcoef.bin is aligned 2:1 with data.bin - 64 bytes per\n // splat against 32 - so each slice's SH is its own doubled\n // byte range, which the loader derives from `start`/`length`.\n url: shUrl as string,\n },\n }),\n },\n });\n lods[level] = { file, offset: 0, count: hi - lo };\n // Levels ascend finest-to-coarsest, so the last written is the\n // coarsest this sub-leaf carries.\n coarsestFile = file;\n coarsestCount = hi - lo;\n }\n // Pin the coarsest present slice as always-cached substitute coverage;\n // an empty cell has none. The coverage floor sums those coarsest slices\n // (a partition, so the total equals the unsplit sum).\n if (coarsestFile >= 0) {\n pinnedFiles.add(coarsestFile);\n minimumCoverageSplats += coarsestCount;\n }\n leaves.push({ bounds: cellBounds(cell, manifest), lods, budgetGroup: cellIndex });\n }\n }\n\n const environmentCount = await environmentSplatCount(source, manifest);\n if (environmentCount > 0 && environmentUrl !== null) {\n const stride = environmentStride(manifest);\n const file = addChunk(`${environmentUrl}#env`, {\n format: 'lcc-bin',\n lcc: {\n kind: 'environment',\n start: 0,\n length: environmentCount * stride,\n // From `fileType`, never from whether SH is enabled: a Quality\n // environment record is 96 bytes even when its SH is being skipped.\n stride,\n scale: manifest.envScale,\n ...(shBands === undefined\n ? {}\n : {\n sh: {\n bands: shBands,\n range: manifest.envShcoef,\n source: 'inline' as const,\n // The environment quantizes SH against its own (wider) range;\n // re-encode into the base range so the whole scene decodes\n // through one pool uniform. See LccShParams.requantizeTo.\n requantizeTo: manifest.shcoef,\n },\n }),\n },\n });\n // Sky/background splats have no LOD ladder - a single always-resident\n // level, so its run key never churns. It stays pinned (and counts into the\n // coverage floor): a few thousand splats never worth evicting.\n pinnedFiles.add(file);\n minimumCoverageSplats += environmentCount;\n leaves.push({\n bounds: sceneBounds(manifest),\n lods: [{ file, offset: 0, count: environmentCount }],\n });\n }\n\n const lodManifest: LodManifest = {\n leaves,\n chunkUrls,\n // Per-level splat totals; index 0 is the finest. Informational here (the\n // pool and coverage floor are sized explicitly below), but kept accurate.\n counts: manifest.splats.slice(0, levelCount),\n lodLevels: levelCount,\n bounds: sceneBounds(manifest),\n };\n\n const collisionUrl = await resolveCollisionLci(source);\n\n return {\n // Classic LCC cells are broad XY tiles. Keep their priority independent of\n // frustum crossings so an orbit does not demote a sharp tile as it leaves\n // one edge, then rebuild it when the same tile re-enters the other edge.\n source: new LodScheduler(lodManifest, {\n ...options,\n // Classic LCC cells are broad XY tiles. Keep their priority independent of\n // frustum crossings so an orbit does not demote a sharp tile as it leaves\n // one edge, then rebuild it when the same tile re-enters the other edge.\n frustumAware: false,\n // Short range: distance bands pick the level (≤~9 m → L0, ≤20 m → L1,\n // …). Do not blanket-force finest then re-promote after demotion - that\n // loaded L0 for margin cells only to demote them with the camera still.\n forceFinestWithin: undefined,\n // Distance band is the fetch target. Do not fill past it.\n fillPastDistance: false,\n // XGRIDS retains a little budget headroom and does not turn a raised\n // ceiling into an all-finest distant cut. The cap limits only optional\n // nearest-first restoration after demotion; the distance-selected cut\n // can still spend the caller's full explicit budget when it needs it.\n budgetFillFraction: 0.95,\n budgetFillCap: 8_000_000,\n forceFinestWhenFits: false,\n }),\n chunkUrls,\n chunkOptions,\n shBands: shBands ?? 0,\n chunkKind: 'file',\n bounds: sceneBounds(manifest),\n pinnedFiles,\n // Worst case is every cell resident at its finest level, plus the sky.\n maxResidentSplats: (manifest.splats[0] ?? options.budget) + environmentCount,\n // Full coarse coverage is each cell's coarsest level plus the sky - the\n // budget floor below which even the substitute levels cannot all fit.\n minimumCoverageSplats: Math.max(1, minimumCoverageSplats),\n // Same Z-up frame as .lcc2; bounds stay source-local and matrixWorld\n // handles the conversion.\n formatTransform: createLcc2ToThreeMatrix(),\n // Optional classic-LCC collision mesh file. One descriptor for the whole\n // `.lci`; `loadCollisionMeshes` expands it into per-cell triangle tiles.\n ...(collisionUrl !== null ? { collision: { meshes: [{ url: collisionUrl }] } } : {}),\n };\n}\n\n/**\n * Locates an optional `collision.lci` sidecar. Captures and CDNs disagree on\n * casing (`collision.lci` vs whitepaper `Collision.lci`), so both names are\n * probed via {@link SplatDatasetSource.size} - missing is fine, same as\n * `environment.bin`.\n */\nasync function resolveCollisionLci(source: SplatDatasetSource): Promise<string | null> {\n for (const name of ['collision.lci', 'Collision.lci'] as const) {\n const size = await source.size(name);\n if (size !== null && Number.isSafeInteger(size) && size > 0) {\n return source.resolve(name);\n }\n }\n return null;\n}\n\n/** Bytes per `environment.bin` record: Quality interleaves a 64-byte SH block. */\nexport function environmentStride(manifest: LccManifest): number {\n return manifest.fileType === 'Quality'\n ? LCC_RECORD_BYTES + LCC_SH_RECORD_BYTES\n : LCC_RECORD_BYTES;\n}\n\n/**\n * A cell's bounds: cells tile X/Y on a fixed grid anchored at the scene's\n * minimum corner and span the full Z range. Clamped to the scene box so the\n * outermost cells do not claim empty space beyond it.\n */\nfunction cellBounds(cell: LccIndexCell, manifest: LccManifest): THREE.Box3 {\n const { min, max } = manifest.bounds;\n const x0 = min[0] + cell.cellX * manifest.cellLengthX;\n const y0 = min[1] + cell.cellY * manifest.cellLengthY;\n return new THREE.Box3(\n new THREE.Vector3(Math.min(x0, max[0]), Math.min(y0, max[1]), min[2]),\n new THREE.Vector3(\n Math.min(x0 + manifest.cellLengthX, max[0]),\n Math.min(y0 + manifest.cellLengthY, max[1]),\n max[2],\n ),\n );\n}\n\nfunction sceneBounds(manifest: LccManifest): THREE.Box3 {\n const { min, max } = manifest.bounds;\n return new THREE.Box3(\n new THREE.Vector3(min[0], min[1], min[2]),\n new THREE.Vector3(max[0], max[1], max[2]),\n );\n}\n\n/**\n * Cross-checks the index against the manifest before anything is sized from\n * it. The manifest may over-declare slightly: the v4 writer leaves stale\n * `splats` counts (conferencehall is short 2 and 1 splats on levels 0/1 of\n * ~6.2M/2.9M), and index.bin is what every byte range is actually read from.\n * An index that *exceeds* the manifest, or falls short by more than a sliver,\n * still means a mismatched or truncated file pair and is refused.\n */\nfunction assertIndexMatchesManifest(index: LccIndexCell[], manifest: LccManifest): void {\n for (let level = 0; level < manifest.totalLevel; level++) {\n let total = 0;\n for (const cell of index) total += (cell.levels[level] as LccIndexCell['levels'][number]).count;\n const declared = manifest.splats[level] as number;\n const shortfall = declared - total;\n const tolerance = Math.max(4, Math.ceil(declared * 0.001));\n if (shortfall < 0 || shortfall > tolerance) {\n throw new Error(\n `LCC index.bin level ${level} totals ${total} splats, but the manifest declares ${String(manifest.splats[level])}.`,\n );\n }\n }\n}\n\n/**\n * How many environment splats exist, from the file's size alone -\n * `environment.bin` has no index. A missing file is not an error: it is\n * optional, and plenty of captures have no sky.\n */\nasync function environmentSplatCount(\n source: SplatDatasetSource,\n manifest: LccManifest,\n): Promise<number> {\n const size = await source.size('environment.bin');\n if (size === null || !Number.isSafeInteger(size) || size <= 0) return 0;\n const stride = environmentStride(manifest);\n if (size % stride !== 0) {\n throw new Error(\n `LCC environment.bin is ${size} bytes, not a multiple of the ${stride}-byte ${manifest.fileType} record.`,\n );\n }\n return size / stride;\n}\n\nasync function fetchBytes(\n url: string,\n options: LccSceneOptions,\n phase: 'manifest' | 'fetch',\n): Promise<ArrayBuffer> {\n let response: Response;\n try {\n response = await fetch(url, toRequestInit(options.request, options.signal));\n } catch (error) {\n throw toSplatLoadError(error, { phase, url });\n }\n if (!response.ok) {\n throw toSplatLoadError(new Error(`Failed to load ${url}: HTTP ${response.status}`), {\n phase,\n url,\n status: response.status,\n });\n }\n return response.arrayBuffer();\n}\n","import * as THREE from 'three/webgpu';\nimport type { LodRun } from '../../lod-scheduler';\nimport type {\n CollisionMeshDescriptor,\n LodSource,\n LodSourceOptions,\n SplatCollisionData,\n StreamedScene,\n} from '../../lod-source';\nimport type { SplatDatasetSource } from '../../dataset-source';\nimport { createLcc2ToThreeMatrix } from './lcc2-transform';\nimport { warn } from '../../logging';\n\n/**\n * Reader for XGRIDS' `.lcc2` datasets (see `docs/formats/lcc2-notes.md`).\n *\n * An `.lcc2` manifest is a cut-based LOD octree: each node holds one splat\n * range (`{file, start, count}`) representing its spatial region at one\n * level, and every level fully tiles the scene, so rendering picks a *cut*\n * (one node per root→leaf path) rather than a level per leaf. Coarse nodes\n * are shared ancestors, so per-leaf independent selection would double-draw;\n * {@link OctreeLodSource} therefore selects a valid cut directly.\n *\n * The splat tiles are standard SOG v2 bundles (one file packs many nodes at\n * different `start` offsets), so the existing chunk cache and pool reuse\n * everything. Implemented from XGRIDS' openly published spec; no XGRIDS code\n * is used, and their capture data is never redistributed.\n */\n\n/** Out-of-frustum nodes act this many times farther away (see LodScheduler). */\nconst FRUSTUM_PENALTY = 3;\n/** Frustum test margin, as a fraction of each node's own size. */\nconst FRUSTUM_MARGIN = 0.1;\n/** Hysteresis dead-band around each LOD distance threshold. */\nconst THRESHOLD_MARGIN = 0.1;\n/** Minimum time a cell must hold a level before changing again, ms. */\nconst DWELL_MS = 500;\n\ninterface OctNode {\n file: number;\n offset: number;\n count: number;\n bounds: THREE.Box3;\n /** `totalLevels − depth`; 0 is finest. */\n level: number;\n children: number[];\n /** Finest-cell interval `[leafStart, leafEnd)` over this node's subtree. */\n leafStart: number;\n leafEnd: number;\n}\n\ninterface RawNode {\n boundingBox: { min: [number, number, number]; max: [number, number, number] };\n child?: Record<string, RawNode>;\n data?: {\n '3dgs'?: { name: number; start: number; count: number };\n /** Collision/nav mesh for this node's region; `name` indexes `meshFiles`. */\n mesh?: { name: number; vertex: number; face: number };\n /**\n * Always-resident environment/background tile; root only. `name` indexes\n * `splatFiles`. Carries no `count` - the manifest never states how many\n * env splats there are, so it is measured when the tile decodes.\n */\n env?: { name: number };\n };\n}\n\ninterface RawManifest {\n version: string;\n totalLevels: number;\n lodSplats: number[];\n root: RawNode & {\n splatFiles: string[];\n /** Triangle-mesh PLYs, referenced by nodes' `data.mesh.name`. */\n meshFiles?: string[];\n /**\n * XGRIDS' own `.btree` BVHs over `meshFiles`. Deliberately unread: the\n * format is proprietary and undocumented, and a host that wants an\n * acceleration structure builds one from the meshes itself.\n */\n bvhFiles?: string[];\n };\n}\n\n/** Builds a scene from a parsed `.lcc2` manifest (JSON already fetched). */\nexport function buildLcc2Scene(\n json: unknown,\n dataset: SplatDatasetSource,\n options: LodSourceOptions,\n shBands: 0 | 1 | 2 | 3 = 0,\n): StreamedScene {\n const raw = json as RawManifest;\n const chunkUrls = raw.root.splatFiles.map((path) => {\n const url = dataset.resolve(path);\n if (url === null) throw new Error(`LCC2 dataset is missing the tile \"${path}\".`);\n return url;\n });\n\n const nodes: OctNode[] = [];\n const cellNodes: number[] = []; // node index of each finest cell, by cell index\n let nextLeaf = 0;\n // Flatten every node that carries splat data (all but the root). Finest\n // cells are the childless nodes, numbered in DFS order; each node's\n // [leafStart, leafEnd) spans the cells in its subtree, so ancestor\n // intervals nest over descendants (what the swap grouping needs).\n const build = (rawNode: RawNode, depth: number): number => {\n const range = rawNode.data?.['3dgs'];\n if (!range) throw new Error('LCC2 node is missing 3dgs data.');\n const index = nodes.length;\n nodes.push(null as unknown as OctNode); // reserve slot (children get later indices)\n\n const leafStart = nextLeaf;\n const children: number[] = [];\n const rawChildren = rawNode.child ? Object.values(rawNode.child) : [];\n if (rawChildren.length === 0) {\n cellNodes[nextLeaf] = index; // this node is one finest cell\n nextLeaf++;\n } else {\n for (const child of rawChildren) children.push(build(child, depth + 1));\n }\n const leafEnd = rawChildren.length === 0 ? leafStart + 1 : nextLeaf;\n\n nodes[index] = {\n file: range.name,\n offset: range.start,\n count: range.count,\n bounds: boxFromRaw(rawNode.boundingBox),\n level: raw.totalLevels - depth,\n children,\n leafStart,\n leafEnd,\n };\n return index;\n };\n\n const rootChildren = Object.values(raw.root.child ?? {}).map((child) => build(child, 1));\n\n const source = new OctreeLodSource(nodes, rootChildren, cellNodes, raw.totalLevels - 1, options);\n const pinnedFiles = new Set<number>(rootChildren.map((i) => (nodes[i] as OctNode).file));\n\n // The environment tile (root `data.env`) is an always-resident background -\n // one whole `.sog`, no LOD ladder, no `count` in the manifest (measured at\n // decode). Pin its file so a fetch in flight is never cancelled by an LOD\n // reschedule, and StreamedSplatMesh loads it once and toggles it live.\n const envName = raw.root.data?.env?.name;\n const environment =\n envName !== undefined && chunkUrls[envName] !== undefined ? { file: envName } : undefined;\n if (envName !== undefined && environment === undefined) {\n warn(`LCC2 root references env tile #${envName}, which the manifest does not list.`);\n }\n if (environment) pinnedFiles.add(environment.file);\n\n const packShBands = shBands >= 1 ? (shBands as 1 | 2 | 3) : undefined;\n\n return {\n source,\n chunkUrls,\n chunkKind: 'file', // LCC2 tiles are single bundled .sog (ZIP) files\n // Explicit format: a dropped folder resolves tiles to `blob:` URLs with no\n // `.sog` extension, so the chunk loader cannot sniff the parser from the URL.\n chunkOptions: chunkUrls.map(() => ({\n format: 'sog' as const,\n ...(packShBands ? { sog: { packShBands } } : {}),\n })),\n bounds: boxFromRaw(raw.root.boundingBox),\n pinnedFiles,\n maxResidentSplats: raw.lodSplats[0] ?? options.budget,\n minimumCoverageSplats: Math.max(\n 1,\n rootChildren.reduce((sum, index) => sum + (nodes[index] as OctNode).count, 0),\n ),\n ...collisionFrom(raw, dataset),\n ...(environment ? { environment } : {}),\n ...(packShBands ? { shBands: packShBands } : {}),\n // Match the established XGRIDS LCC and Spark viewer coordinate frame.\n // Bounds remain source-local and matrixWorld handles the conversion.\n formatTransform: createLcc2ToThreeMatrix(),\n };\n}\n\n/**\n * Collects the dataset's collision-mesh tiles, if it ships any.\n *\n * Nodes name their mesh by index into `root.meshFiles`, and several nodes can\n * share one file, so tiles are deduped by that index and carry the bounds of\n * the first node that claimed them. Unlike a splat tile, a missing mesh is not\n * fatal - collision is optional, and a partial set still beats none.\n */\nfunction collisionFrom(\n raw: RawManifest,\n dataset: SplatDatasetSource,\n): { collision?: SplatCollisionData } {\n const meshFiles = raw.root.meshFiles;\n if (!meshFiles || meshFiles.length === 0) return {};\n\n const boundsByFile = new Map<number, THREE.Box3>();\n const visit = (node: RawNode): void => {\n const mesh = node.data?.mesh;\n if (mesh && !boundsByFile.has(mesh.name)) {\n boundsByFile.set(mesh.name, boxFromRaw(node.boundingBox));\n }\n for (const child of Object.values(node.child ?? {})) visit(child);\n };\n visit(raw.root);\n\n // Nodes are the authority on which files are collision meshes and where they\n // sit. A manifest that lists the files but never references them still gets\n // its meshes loaded, just without per-tile bounds.\n const wanted: { file: number; bounds?: THREE.Box3 }[] =\n boundsByFile.size > 0\n ? [...boundsByFile].map(([file, bounds]) => ({ file, bounds }))\n : meshFiles.map((_, file) => ({ file }));\n\n const meshes: CollisionMeshDescriptor[] = [];\n for (const { file, bounds } of wanted) {\n const path = meshFiles[file];\n if (path === undefined) {\n warn(`LCC2 node references mesh file ${file}, which the manifest does not list.`);\n continue;\n }\n const url = dataset.resolve(path);\n if (url === null) {\n warn(`LCC2 dataset is missing the collision mesh \"${path}\"; skipping it.`);\n continue;\n }\n meshes.push({ url, ...(bounds ? { bounds } : {}) });\n }\n return meshes.length > 0 ? { collision: { meshes } } : {};\n}\n\n/**\n * Selects a budget-bounded cut through an LCC2 octree with the same\n * stability as the SOG scheduler. The stable state is a per-finest-cell\n * desired level (0 = finest), updated from camera distance with a dead-band\n * and dwell so a still or slowly-panning camera does not flip levels - this\n * is what stops the \"two LODs fighting\" flicker. From those levels a valid\n * cut is built (descend into a node only while some cell in its subtree\n * wants finer than the node provides), then budget-adjusted: coarsen the\n * farthest cells if over budget, refine the nearest with any leftover.\n * Because coarse nodes are shared ancestors, the cut construction - not\n * per-cell independent selection - is what guarantees no double-draw.\n */\nclass OctreeLodSource implements LodSource {\n budget: number;\n lodBaseDistance: number;\n lodMultiplier: number;\n\n private readonly nodes: OctNode[];\n private readonly rootChildren: number[];\n private readonly cellNodes: number[];\n private readonly coarsest: number;\n /** Finest available level per cell (its own node's level). */\n private readonly cellFinestLevel: Int32Array;\n /** Stable, dwelled desired level per cell. */\n private readonly cellLevel: Int32Array;\n /** Timestamp of each cell's last level change, for dwell. */\n private readonly changedAt: Float64Array;\n /** Index into {@link rootChildren} of the subtree containing each cell. */\n private readonly cellRootChild: Uint32Array;\n\n private readonly scratchBox = new THREE.Box3();\n private readonly scratchSize = new THREE.Vector3();\n\n constructor(\n nodes: OctNode[],\n rootChildren: number[],\n cellNodes: number[],\n coarsest: number,\n options: LodSourceOptions,\n ) {\n this.nodes = nodes;\n this.rootChildren = rootChildren;\n this.cellNodes = cellNodes;\n this.coarsest = coarsest;\n this.budget = options.budget;\n this.lodBaseDistance = options.lodBaseDistance;\n this.lodMultiplier = options.lodMultiplier;\n\n const n = cellNodes.length;\n this.cellFinestLevel = new Int32Array(n);\n this.cellLevel = new Int32Array(n).fill(coarsest);\n this.changedAt = new Float64Array(n);\n for (let c = 0; c < n; c++) {\n this.cellFinestLevel[c] = (nodes[cellNodes[c] as number] as OctNode).level;\n }\n this.cellRootChild = new Uint32Array(n);\n rootChildren.forEach((nodeIndex, r) => {\n const node = nodes[nodeIndex] as OctNode;\n for (let cell = node.leafStart; cell < node.leafEnd; cell++) this.cellRootChild[cell] = r;\n });\n }\n\n computeDesiredRuns(cameraLocal: THREE.Vector3, frustum: THREE.Frustum, now: number): LodRun[] {\n const cellDistance = this.updateCellLevels(cameraLocal, frustum, now);\n\n // resolved[] starts from the dwelled per-cell levels, then is\n // budget-adjusted; keeping the dwelled base separate (like the SOG\n // scheduler's level/resolved split) prevents budget changes from\n // feeding back into the hysteresis.\n const resolved = Int32Array.from(this.cellLevel);\n // A cell's level only affects the cut inside its own root-child subtree,\n // so per-subtree contributions are cached and each adjustment step\n // re-walks one subtree - not the whole tree, which made the enforce/fill\n // loops O(cells² · depth) on heavily over-budget scenes.\n const contributions = this.rootChildren.map((index) => this.subtreeCutTotal(index, resolved));\n let total = contributions.reduce((sum, count) => sum + count, 0);\n const adjust = (cell: number, delta: number): void => {\n resolved[cell] = (resolved[cell] as number) + delta;\n const r = this.cellRootChild[cell] as number;\n const next = this.subtreeCutTotal(this.rootChildren[r] as number, resolved);\n total += next - (contributions[r] as number);\n contributions[r] = next;\n };\n\n const byDistanceDesc = [...this.cellNodes.keys()].sort(\n (a, b) => (cellDistance[b] as number) - (cellDistance[a] as number),\n );\n // Enforce: coarsen the farthest cells until the cut fits the budget.\n for (let i = 0; i < byDistanceDesc.length && total > this.budget;) {\n const c = byDistanceDesc[i] as number;\n if ((resolved[c] as number) < this.coarsest) {\n adjust(c, +1);\n } else {\n i++;\n }\n }\n // Fill: spend leftover budget refining the nearest cells, to 85% - the\n // headroom keeps region-atomic swap transients (old + new runs briefly\n // co-resident) from pushing the live count past budget on a scene far\n // larger than the budget.\n const fillTarget = this.budget * 0.85;\n for (let i = byDistanceDesc.length - 1; i >= 0 && total < fillTarget;) {\n const c = byDistanceDesc[i] as number;\n if ((resolved[c] as number) > (this.cellFinestLevel[c] as number)) {\n adjust(c, -1);\n if (total > this.budget) {\n adjust(c, +1); // overshoot; revert and stop\n break;\n }\n } else {\n i--;\n }\n }\n\n const runs: LodRun[] = [];\n this.collectCut(resolved, (index) => runs.push(runFromNode(this.nodes[index] as OctNode)));\n return runs;\n }\n\n coarsestRunsFor(from: number, to: number): LodRun[] {\n const runs: LodRun[] = [];\n for (const index of this.rootChildren) {\n const node = this.nodes[index] as OctNode;\n if (node.leafStart < to && node.leafEnd > from) runs.push(runFromNode(node));\n }\n return runs;\n }\n\n /**\n * Coarsest root-child runs covering in-view finest cells. An empty frustum\n * falls back to the nearest cell's root child so a skyward start still paints\n * something rather than releasing an empty hold.\n */\n coverageRunsFor(cameraLocal: THREE.Vector3, frustum: THREE.Frustum): LodRun[] {\n const n = this.cellNodes.length;\n if (n === 0) return [];\n const picked = new Uint8Array(this.rootChildren.length);\n let anyInView = false;\n let nearestCell = 0;\n let nearestDist = Number.POSITIVE_INFINITY;\n for (let c = 0; c < n; c++) {\n const node = this.nodes[this.cellNodes[c] as number] as OctNode;\n const d = node.bounds.distanceToPoint(cameraLocal);\n if (d < nearestDist) {\n nearestDist = d;\n nearestCell = c;\n }\n if (this.cellIntersectsFrustum(frustum, c)) {\n picked[this.cellRootChild[c] as number] = 1;\n anyInView = true;\n }\n }\n if (!anyInView) picked[this.cellRootChild[nearestCell] as number] = 1;\n const runs: LodRun[] = [];\n for (let r = 0; r < this.rootChildren.length; r++) {\n if (picked[r] !== 1) continue;\n runs.push(runFromNode(this.nodes[this.rootChildren[r] as number] as OctNode));\n }\n return runs;\n }\n\n /** Finest-cell AABB expanded by {@link FRUSTUM_MARGIN}, written into scratch. */\n private expandCellBox(cell: number): THREE.Box3 {\n const node = this.nodes[this.cellNodes[cell] as number] as OctNode;\n this.scratchBox.copy(node.bounds);\n node.bounds.getSize(this.scratchSize);\n this.scratchBox.expandByScalar(this.scratchSize.length() * FRUSTUM_MARGIN);\n return this.scratchBox;\n }\n\n private cellIntersectsFrustum(frustum: THREE.Frustum, cell: number): boolean {\n return frustum.intersectsBox(this.expandCellBox(cell));\n }\n\n /** Distance (frustum-penalized) to each finest cell's node bounds. */\n private cellDistanceOf(cameraLocal: THREE.Vector3, frustum: THREE.Frustum, cell: number): number {\n const node = this.nodes[this.cellNodes[cell] as number] as OctNode;\n const d = node.bounds.distanceToPoint(cameraLocal);\n return this.cellIntersectsFrustum(frustum, cell) ? d : d * FRUSTUM_PENALTY;\n }\n\n /** Advances each cell's dwelled level toward the distance target. */\n private updateCellLevels(\n cameraLocal: THREE.Vector3,\n frustum: THREE.Frustum,\n now: number,\n ): Float64Array {\n const { lodBaseDistance: base, lodMultiplier: m } = this;\n const distances = new Float64Array(this.cellNodes.length);\n for (let c = 0; c < this.cellNodes.length; c++) {\n const d = this.cellDistanceOf(cameraLocal, frustum, c);\n distances[c] = d;\n\n const min = this.cellFinestLevel[c] as number;\n let level = Math.min(this.coarsest, Math.max(min, this.cellLevel[c] as number));\n // Dead-band: coarsen only past threshold·(1+margin), refine only\n // within threshold·(1−margin); threshold(L) = base·m^L.\n while (level < this.coarsest && d > base * m ** level * (1 + THRESHOLD_MARGIN)) level++;\n while (level > min && d <= base * m ** (level - 1) * (1 - THRESHOLD_MARGIN)) level--;\n\n if (\n level !== (this.cellLevel[c] as number) &&\n now - (this.changedAt[c] as number) >= DWELL_MS\n ) {\n this.cellLevel[c] = level;\n this.changedAt[c] = now;\n }\n }\n return distances;\n }\n\n /** Splat total of one subtree's cut for the given per-cell levels. */\n private subtreeCutTotal(index: number, levels: Int32Array): number {\n let total = 0;\n this.visitCut(index, levels, (i) => {\n total += (this.nodes[i] as OctNode).count;\n });\n return total;\n }\n\n /**\n * Walks the tree emitting the cut for the given per-cell levels: a node is\n * selected (stop) unless some cell in its subtree wants finer than the\n * node provides, in which case its children are visited. Guarantees an\n * antichain - exactly one node per root→leaf path.\n */\n private collectCut(levels: Int32Array, emit: (index: number) => void): void {\n for (const index of this.rootChildren) this.visitCut(index, levels, emit);\n }\n\n /** {@link collectCut} restricted to one subtree. */\n private visitCut(index: number, levels: Int32Array, emit: (index: number) => void): void {\n const node = this.nodes[index] as OctNode;\n if (node.children.length > 0) {\n let minWanted = this.coarsest;\n for (let cell = node.leafStart; cell < node.leafEnd; cell++) {\n const level = levels[cell] as number;\n if (level < minWanted) minWanted = level;\n }\n if (minWanted < node.level) {\n for (const child of node.children) this.visitCut(child, levels, emit);\n return;\n }\n }\n emit(index);\n }\n}\n\nfunction runFromNode(node: OctNode): LodRun {\n return {\n file: node.file,\n level: node.level,\n offset: node.offset,\n count: node.count,\n leafStart: node.leafStart,\n leafEnd: node.leafEnd,\n };\n}\n\nfunction boxFromRaw(bound: RawNode['boundingBox']): THREE.Box3 {\n return new THREE.Box3(\n new THREE.Vector3(bound.min[0], bound.min[1], bound.min[2]),\n new THREE.Vector3(bound.max[0], bound.max[1], bound.max[2]),\n );\n}\n","import {\n assertPlyElementFits,\n assertPlyPropertyTypes,\n parsePlyHeader,\n plyPropertyOffset,\n plyTypeSize,\n type PlyElement,\n} from '../../ply-header';\n\n/**\n * Parser for plain binary triangle-mesh PLY files - vertices and faces, no\n * splat properties.\n *\n * XGRIDS `.lcc2` datasets ship these as collision/navigation companions to\n * their splat tiles (`data/mesh/*.ply`, see `docs/formats/lcc2-notes.md`): a vertex\n * element of float x/y/z and a face element of index lists. The geometry is a\n * plain triangle soup in the dataset's source-local frame - what a host needs\n * to build its own collision or spatial queries.\n *\n * Nothing here is LCC-specific: any binary little-endian PLY with a positioned\n * vertex element and an index list parses.\n */\n\n/** Names the face index list goes by, in preference order. */\nconst FACE_INDEX_PROPERTIES = ['vertex_indices', 'vertex_index'] as const;\n\n/** A plain indexed triangle mesh decoded from a PLY. */\nexport interface TriangleMeshData {\n readonly vertexCount: number;\n readonly triangleCount: number;\n /** `x, y, z` per vertex, in the file's own coordinate frame. */\n readonly positions: Float32Array;\n /** Three vertex indices per triangle, into {@link positions}. */\n readonly indices: Uint32Array;\n}\n\n/**\n * Parses a binary little-endian triangle-mesh PLY.\n *\n * Faces with more than three vertices are fan-triangulated; degenerate faces\n * (fewer than three) are dropped. Coordinates are passed through untouched, so\n * a source-local frame stays source-local - an LCC2 mesh needs the same\n * `createLcc2ToThreeMatrix` correction its splats get.\n *\n * @throws {Error} if the file is not a binary little-endian PLY, lacks a\n * usable vertex or face element, or is truncated.\n */\nexport function parseMeshPly(buffer: ArrayBuffer): TriangleMeshData {\n const header = parsePlyHeader(buffer);\n\n const vertices = header.element('vertex');\n if (!vertices) throw new Error('Not a mesh PLY: no \"vertex\" element.');\n assertPlyPropertyTypes(vertices, ['x', 'y', 'z'], ['float', 'float32'], 'Mesh PLY vertex');\n assertPlyElementFits(vertices, buffer.byteLength);\n const positions = readPositions(buffer, vertices);\n\n const faces = header.element('face');\n if (!faces) throw new Error('Not a mesh PLY: no \"face\" element.');\n const indices = readFaces(buffer, faces, vertices.count);\n\n return {\n vertexCount: vertices.count,\n triangleCount: indices.length / 3,\n positions,\n indices,\n };\n}\n\n/** Copies x/y/z out of the vertex records, honoring stride and offsets. */\nfunction readPositions(buffer: ArrayBuffer, vertices: PlyElement): Float32Array {\n const view = new DataView(buffer, vertices.offset);\n const xOffset = plyPropertyOffset(vertices, 'x');\n const yOffset = plyPropertyOffset(vertices, 'y');\n const zOffset = plyPropertyOffset(vertices, 'z');\n const positions = new Float32Array(vertices.count * 3);\n for (let i = 0; i < vertices.count; i++) {\n const record = i * vertices.stride;\n positions[i * 3] = view.getFloat32(record + xOffset, true);\n positions[i * 3 + 1] = view.getFloat32(record + yOffset, true);\n positions[i * 3 + 2] = view.getFloat32(record + zOffset, true);\n }\n return positions;\n}\n\n/**\n * Walks the variable-stride face records into a flat triangle index array.\n *\n * Each record is a count followed by that many indices, so the records can only\n * be read in order - there is no arithmetic that finds face `n`.\n */\nfunction readFaces(buffer: ArrayBuffer, faces: PlyElement, vertexCount: number): Uint32Array {\n if (faces.count === 0) return new Uint32Array(0);\n if (faces.offset < 0) {\n throw new Error(\n 'Unsupported PLY layout: the \"face\" element follows a list property, so its position ' +\n 'cannot be determined.',\n );\n }\n if (faces.properties.size > 0) {\n throw new Error(\n 'Unsupported mesh PLY: the \"face\" element mixes scalar properties with its index list.',\n );\n }\n\n const name = FACE_INDEX_PROPERTIES.find((candidate) => faces.listProperties.has(candidate));\n const list = name ? faces.listProperties.get(name) : undefined;\n if (!list) {\n throw new Error(\n `Mesh PLY \"face\" element is missing an index list (${FACE_INDEX_PROPERTIES.join(' or ')}).`,\n );\n }\n const countSize = plyTypeSize(list.countType);\n const itemSize = plyTypeSize(list.itemType);\n\n const view = new DataView(buffer);\n const end = buffer.byteLength;\n // Most faces are triangles, so this is usually exact; a fan grows it.\n let indices = new Uint32Array(faces.count * 3);\n let written = 0;\n let cursor = faces.offset;\n\n for (let face = 0; face < faces.count; face++) {\n if (cursor + countSize > end) throw new Error(truncated(face));\n const count = readInteger(view, cursor, list.countType, countSize);\n if (count < 0) {\n throw new Error(`Mesh PLY face ${face} declares a negative vertex count (${count}).`);\n }\n cursor += countSize;\n if (cursor + count * itemSize > end) throw new Error(truncated(face));\n\n // A fan needs (count - 2) triangles; skip degenerate faces but still step\n // the cursor past their data so the following records stay aligned.\n const triangles = Math.max(0, count - 2);\n if (written + triangles * 3 > indices.length) {\n const grown = new Uint32Array(Math.max(indices.length * 2, written + triangles * 3));\n grown.set(indices.subarray(0, written));\n indices = grown;\n }\n let first = 0;\n let previous = 0;\n for (let i = 0; i < count; i++) {\n const index = readInteger(view, cursor + i * itemSize, list.itemType, itemSize);\n if (index < 0 || index >= vertexCount) {\n throw new Error(\n `Mesh PLY face ${face} references vertex ${index}, but the file has ${vertexCount}.`,\n );\n }\n if (i === 0) first = index;\n else if (i >= 2) {\n indices[written++] = first;\n indices[written++] = previous;\n indices[written++] = index;\n }\n previous = index;\n }\n cursor += count * itemSize;\n }\n\n return written === indices.length ? indices : indices.slice(0, written);\n}\n\nfunction truncated(face: number): string {\n return `Truncated PLY file: the \"face\" element ends inside face ${face}.`;\n}\n\n/** Reads one little-endian integer of a PLY scalar type. */\nfunction readInteger(view: DataView, offset: number, type: string, size: number): number {\n switch (type) {\n case 'char':\n case 'int8':\n return view.getInt8(offset);\n case 'uchar':\n case 'uint8':\n return view.getUint8(offset);\n case 'short':\n case 'int16':\n return view.getInt16(offset, true);\n case 'ushort':\n case 'uint16':\n return view.getUint16(offset, true);\n case 'int':\n case 'int32':\n return view.getInt32(offset, true);\n case 'uint':\n case 'uint32':\n return view.getUint32(offset, true);\n default:\n // Sized by plyTypeSize, so this is a float type where an index belongs.\n throw new Error(`Unsupported PLY index type: \"${type}\" (${size} bytes).`);\n }\n}\n","import * as THREE from 'three/webgpu';\nimport type { TriangleMeshData } from './parse-mesh-ply';\n\n/**\n * Parser for XGRIDS classic-LCC `collision.lci` files (manifest companions to\n * `.lcc` / `meta.lcc` captures).\n *\n * Layout reverse-engineered from the\n * [LCC whitepaper](https://github.com/xgrids/LCCWhitepaper) and verified on\n * live captures (e.g. Kaiserpfalz v2). The file packs one triangle mesh per\n * occupied cell, plus a proprietary serialized BVH we deliberately skip -\n * hosts build their own acceleration structure (see the demo's\n * `collision.ts`), the same bargain `.lcc2` makes by ignoring `.btree`.\n *\n * Coordinates are Z-up source-local, matching `data.bin` / `formatTransform`.\n */\n\nconst MAGIC = 0x6c6c6f63; // 'coll' LE\nconst SUPPORTED_VERSION = 2;\nconst GLOBAL_HEADER_BYTES = 48;\nconst MESH_HEADER_BYTES = 40;\nconst FLOATS_PER_VERTEX = 3;\nconst INDICES_PER_FACE = 3;\nconst BYTES_PER_VERTEX = FLOATS_PER_VERTEX * 4;\nconst BYTES_PER_FACE = INDICES_PER_FACE * 4;\n\n/** One cell's collision mesh decoded from a `collision.lci`. */\nexport interface CollisionLciTile {\n readonly cellX: number;\n readonly cellY: number;\n readonly data: TriangleMeshData;\n /**\n * Cell-grid AABB in the file's source-local frame, clamped to the file's\n * scene bounds. Useful for culling before consulting the triangles.\n */\n readonly bounds: THREE.Box3;\n}\n\n/** Everything {@link parseCollisionLci} extracts from one file. */\nexport interface CollisionLciFile {\n readonly version: number;\n readonly bounds: THREE.Box3;\n readonly cellLengthX: number;\n readonly cellLengthY: number;\n readonly tiles: readonly CollisionLciTile[];\n}\n\n/**\n * Parses a classic-LCC `collision.lci` buffer into per-cell triangle meshes.\n *\n * @throws {Error} on bad magic, unsupported version, truncated payloads,\n * inconsistent size fields, or out-of-range face indices.\n */\nexport function parseCollisionLci(buffer: ArrayBuffer): CollisionLciFile {\n if (buffer.byteLength < GLOBAL_HEADER_BYTES) {\n throw new Error(\n `collision.lci is truncated: ${buffer.byteLength} bytes, need at least ${GLOBAL_HEADER_BYTES}.`,\n );\n }\n const view = new DataView(buffer);\n const magic = view.getUint32(0, true);\n if (magic !== MAGIC) {\n throw new Error(\n `Not a collision.lci: expected magic \"coll\", got 0x${magic.toString(16).padStart(8, '0')}.`,\n );\n }\n const version = view.getUint32(4, true);\n if (version !== SUPPORTED_VERSION) {\n throw new Error(\n `Unsupported collision.lci version ${version} (expected ${SUPPORTED_VERSION}).`,\n );\n }\n const headerLen = view.getUint32(8, true);\n const sceneMin = new THREE.Vector3(\n view.getFloat32(12, true),\n view.getFloat32(16, true),\n view.getFloat32(20, true),\n );\n const sceneMax = new THREE.Vector3(\n view.getFloat32(24, true),\n view.getFloat32(28, true),\n view.getFloat32(32, true),\n );\n const cellLengthX = view.getFloat32(36, true);\n const cellLengthY = view.getFloat32(40, true);\n const meshCount = view.getUint32(44, true);\n\n const expectedHeader = GLOBAL_HEADER_BYTES + meshCount * MESH_HEADER_BYTES;\n if (headerLen !== expectedHeader) {\n throw new Error(\n `collision.lci headerLen ${headerLen} does not match ${meshCount} mesh headers ` +\n `(expected ${expectedHeader}).`,\n );\n }\n if (buffer.byteLength < headerLen) {\n throw new Error(\n `collision.lci is truncated: header claims ${headerLen} bytes, file has ${buffer.byteLength}.`,\n );\n }\n if (!(cellLengthX > 0) || !(cellLengthY > 0)) {\n throw new Error(\n `collision.lci cell lengths must be positive (got ${cellLengthX} × ${cellLengthY}).`,\n );\n }\n\n const sceneBounds = new THREE.Box3(sceneMin.clone(), sceneMax.clone());\n const tiles: CollisionLciTile[] = [];\n let expectedOffset = headerLen;\n\n for (let i = 0; i < meshCount; i++) {\n const at = GLOBAL_HEADER_BYTES + i * MESH_HEADER_BYTES;\n const cellX = view.getUint32(at, true);\n const cellY = view.getUint32(at + 4, true);\n const dataOffset = Number(view.getBigUint64(at + 8, true));\n const dataSize = Number(view.getBigUint64(at + 16, true));\n const vertexCount = view.getUint32(at + 24, true);\n const faceCount = view.getUint32(at + 28, true);\n const bvhByteSize = view.getUint32(at + 32, true);\n\n if (!Number.isSafeInteger(dataOffset) || !Number.isSafeInteger(dataSize)) {\n throw new Error(`collision.lci mesh ${i} has an unsafe data offset/size.`);\n }\n if (dataOffset !== expectedOffset) {\n throw new Error(\n `collision.lci mesh ${i} data offset ${dataOffset} is not contiguous ` +\n `(expected ${expectedOffset}).`,\n );\n }\n const geometryBytes = vertexCount * BYTES_PER_VERTEX + faceCount * BYTES_PER_FACE;\n if (geometryBytes + bvhByteSize !== dataSize) {\n throw new Error(\n `collision.lci mesh ${i} size ${dataSize} != ` +\n `${vertexCount}*12 + ${faceCount}*12 + BVH ${bvhByteSize}.`,\n );\n }\n const end = dataOffset + dataSize;\n if (end > buffer.byteLength) {\n throw new Error(\n `collision.lci mesh ${i} extends to byte ${end}, past file length ${buffer.byteLength}.`,\n );\n }\n\n const positions = new Float32Array(buffer, dataOffset, vertexCount * FLOATS_PER_VERTEX);\n const indexOffset = dataOffset + vertexCount * BYTES_PER_VERTEX;\n const indices = new Uint32Array(buffer, indexOffset, faceCount * INDICES_PER_FACE);\n for (let t = 0; t < indices.length; t++) {\n const index = indices[t]!;\n if (index >= vertexCount) {\n throw new Error(\n `collision.lci mesh ${i} face index ${index} is out of range ` +\n `(vertexCount ${vertexCount}).`,\n );\n }\n }\n\n tiles.push({\n cellX,\n cellY,\n data: {\n vertexCount,\n triangleCount: faceCount,\n // Copy so the returned arrays outlive a transferred/detached buffer\n // and are not views into the BVH-adjacent region.\n positions: positions.slice(),\n indices: indices.slice(),\n },\n bounds: cellBounds(cellX, cellY, cellLengthX, cellLengthY, sceneBounds),\n });\n expectedOffset = end;\n }\n\n if (expectedOffset !== buffer.byteLength) {\n throw new Error(\n `collision.lci has ${buffer.byteLength - expectedOffset} trailing bytes after the last mesh.`,\n );\n }\n\n return {\n version,\n bounds: sceneBounds,\n cellLengthX,\n cellLengthY,\n tiles,\n };\n}\n\n/**\n * Cell AABB on the LCI grid, anchored at the scene minimum and clamped to the\n * scene box - same rule classic LCC uses for splat cells.\n */\nfunction cellBounds(\n cellX: number,\n cellY: number,\n cellLengthX: number,\n cellLengthY: number,\n scene: THREE.Box3,\n): THREE.Box3 {\n const x0 = scene.min.x + cellX * cellLengthX;\n const y0 = scene.min.y + cellY * cellLengthY;\n return new THREE.Box3(\n new THREE.Vector3(Math.min(x0, scene.max.x), Math.min(y0, scene.max.y), scene.min.z),\n new THREE.Vector3(\n Math.min(x0 + cellLengthX, scene.max.x),\n Math.min(y0 + cellLengthY, scene.max.y),\n scene.max.z,\n ),\n );\n}\n","import type * as THREE from 'three/webgpu';\nimport { toRequestInit, type SplatRequestOptions } from '../../loading';\nimport { parseMeshPly, type TriangleMeshData } from './parse-mesh-ply';\nimport { parseCollisionLci } from './parse-collision-lci';\nimport type { CollisionMeshDescriptor, SplatCollisionData } from '../../lod-source';\nimport { warn } from '../../logging';\n\n/**\n * Fetches the collision-mesh tiles a streamed scene declares.\n *\n * VLAM! is not a physics engine: this hands back plain triangle geometry and\n * stops there. Building an acceleration structure, resolving a character\n * against it, and deciding what \"collision\" means for the host's camera are\n * all the host's business (see the demo's `collision.ts` for one answer).\n *\n * Descriptors may point at `.lcc2` mesh PLYs or a classic-LCC `collision.lci`\n * (one file → many per-cell tiles).\n */\n\n/** One loaded collision tile, in the scene's source-local frame. */\nexport interface CollisionMeshTile {\n /** The URL it came from - a stable identity for caching or debugging. */\n readonly url: string;\n readonly data: TriangleMeshData;\n /** Source-local bounds from the manifest, when it declared them. */\n readonly bounds?: THREE.Box3;\n}\n\n/**\n * Fetches and parses every tile in `collision`, concurrently.\n *\n * A tile that fails to fetch or parse is warned about and skipped rather than\n * failing the batch: collision degrades to partial coverage, which is what the\n * rest of the streaming pipeline does with a bad chunk too. A classic-LCC\n * `.lci` descriptor expands into one tile per cell mesh inside the file.\n *\n * @throws a `DOMException` named `AbortError` when cancelled.\n */\nexport async function loadCollisionMeshTiles(\n collision: SplatCollisionData,\n options: { request?: SplatRequestOptions; signal?: AbortSignal } = {},\n): Promise<CollisionMeshTile[]> {\n const settled = await Promise.allSettled(\n collision.meshes.map((descriptor) => loadDescriptor(descriptor, options)),\n );\n\n options.signal?.throwIfAborted();\n\n const tiles: CollisionMeshTile[] = [];\n for (const result of settled) {\n if (result.status === 'fulfilled') tiles.push(...result.value);\n else warn('Skipping a collision mesh that failed to load:', result.reason);\n }\n return tiles;\n}\n\nasync function loadDescriptor(\n descriptor: CollisionMeshDescriptor,\n options: { request?: SplatRequestOptions; signal?: AbortSignal },\n): Promise<CollisionMeshTile[]> {\n const response = await fetch(descriptor.url, toRequestInit(options.request, options.signal));\n if (!response.ok) {\n throw new Error(`HTTP ${response.status} for collision mesh ${descriptor.url}`);\n }\n const buffer = await response.arrayBuffer();\n\n if (isLciUrl(descriptor.url)) {\n const parsed = parseCollisionLci(buffer);\n return parsed.tiles.map((tile) => ({\n url: `${descriptor.url}#c${tile.cellX}_${tile.cellY}`,\n data: tile.data,\n bounds: tile.bounds,\n }));\n }\n\n const data = parseMeshPly(buffer);\n return [\n {\n url: descriptor.url,\n data,\n ...(descriptor.bounds ? { bounds: descriptor.bounds } : {}),\n },\n ];\n}\n\n/** True when the descriptor points at a classic-LCC `collision.lci`. */\nfunction isLciUrl(url: string): boolean {\n try {\n return new URL(url, 'https://example.invalid/').pathname.toLowerCase().endsWith('.lci');\n } catch {\n return url.toLowerCase().includes('.lci');\n }\n}\n","import type { TriangleMeshData } from './parse-mesh-ply';\nimport type { SelectionVolume } from '../../selection-volume';\n\n/**\n * Splits a collision triangle mesh by the same {@link SelectionVolume} used to\n * separate a splat region, so the separated object keeps its collision\n * geometry and both halves stay usable by a host's collision system.\n *\n * Collision tiles arrive in the dataset's source-local frame (see\n * `parse-mesh-ply.ts`), which for `.lcc2` is the frame the mesh matrix maps to\n * world - the same frame `SplatData.positions` use. So a volume built for the\n * splats partitions the collision mesh with no extra transform, as long as the\n * host holds both in that one frame.\n */\nexport interface TriangleMeshPartition {\n /** Triangles assigned to the selection. */\n readonly inside: TriangleMeshData;\n /** Everything else. */\n readonly outside: TriangleMeshData;\n}\n\n/**\n * Partitions a triangle mesh by a selection volume.\n *\n * A triangle goes to `inside` when **any** of its vertices is inside the\n * volume. Triangles are never clipped, so the two halves tile the original\n * exactly (no cracks, no new geometry); the trade-off is that boundary\n * triangles overhang the volume by up to one triangle. Vertex winding is\n * preserved. Vertex buffers are compacted per half - shared vertices are\n * duplicated into whichever halves reference them.\n *\n * @throws {Error} if `mesh` is internally inconsistent (array lengths that\n * disagree with the declared counts, or an out-of-range index). {@link parseMeshPly}\n * already guarantees this; a host-built mesh is checked here rather than\n * silently producing `NaN` positions.\n */\nexport function partitionTriangleMesh(\n mesh: TriangleMeshData,\n volume: SelectionVolume,\n): TriangleMeshPartition {\n assertConsistent(mesh);\n const { positions, indices, vertexCount, triangleCount } = mesh;\n\n const vertexInside = new Uint8Array(vertexCount);\n for (let v = 0; v < vertexCount; v++) {\n vertexInside[v] = volume.containsPoint(\n positions[v * 3] as number,\n positions[v * 3 + 1] as number,\n positions[v * 3 + 2] as number,\n )\n ? 1\n : 0;\n }\n\n const triangleInside = new Uint8Array(triangleCount);\n let insideTriangles = 0;\n for (let t = 0; t < triangleCount; t++) {\n const a = indices[t * 3] as number;\n const b = indices[t * 3 + 1] as number;\n const c = indices[t * 3 + 2] as number;\n if (vertexInside[a] === 1 || vertexInside[b] === 1 || vertexInside[c] === 1) {\n triangleInside[t] = 1;\n insideTriangles++;\n }\n }\n\n return {\n inside: gatherTriangles(mesh, triangleInside, 1, insideTriangles),\n outside: gatherTriangles(mesh, triangleInside, 0, triangleCount - insideTriangles),\n };\n}\n\n/** Rejects a mesh whose arrays disagree with its declared counts. */\nfunction assertConsistent(mesh: TriangleMeshData): void {\n if (mesh.positions.length < mesh.vertexCount * 3) {\n throw new Error(\n `partitionTriangleMesh: positions hold ${mesh.positions.length} floats, ` +\n `too few for ${mesh.vertexCount} vertices.`,\n );\n }\n if (mesh.indices.length < mesh.triangleCount * 3) {\n throw new Error(\n `partitionTriangleMesh: indices hold ${mesh.indices.length} entries, ` +\n `too few for ${mesh.triangleCount} triangles.`,\n );\n }\n for (let k = 0; k < mesh.triangleCount * 3; k++) {\n const index = mesh.indices[k] as number;\n if (index >= mesh.vertexCount) {\n throw new Error(\n `partitionTriangleMesh: index ${index} is out of range (${mesh.vertexCount} vertices).`,\n );\n }\n }\n}\n\n/** Compacts the triangles whose flag matches `side` into a fresh mesh. */\nfunction gatherTriangles(\n mesh: TriangleMeshData,\n triangleFlags: Uint8Array,\n side: 0 | 1,\n triangleCount: number,\n): TriangleMeshData {\n const remap = new Int32Array(mesh.vertexCount).fill(-1);\n const indices = new Uint32Array(triangleCount * 3);\n // First pass: remap indices while counting the referenced vertices.\n let vertexCount = 0;\n let written = 0;\n for (let t = 0; t < mesh.triangleCount; t++) {\n if (triangleFlags[t] !== side) continue;\n for (let k = 0; k < 3; k++) {\n const old = mesh.indices[t * 3 + k] as number;\n let mapped = remap[old] as number;\n if (mapped < 0) {\n mapped = vertexCount++;\n remap[old] = mapped;\n }\n indices[written++] = mapped;\n }\n }\n const positions = new Float32Array(vertexCount * 3);\n for (let old = 0; old < mesh.vertexCount; old++) {\n const mapped = remap[old] as number;\n if (mapped < 0) continue;\n positions[mapped * 3] = mesh.positions[old * 3] as number;\n positions[mapped * 3 + 1] = mesh.positions[old * 3 + 1] as number;\n positions[mapped * 3 + 2] = mesh.positions[old * 3 + 2] as number;\n }\n return { vertexCount, triangleCount, positions, indices };\n}\n"],"names":["LCC_RECORD_BYTES","LCC_SH_RECORD_BYTES","MAX_CHUNK_SPLATS","lccShCoefficientCount","bands","parseLccManifest","json","raw","version","encoding","totalLevel","levels","indexDataSize","expectedIndexSize","splats","count","totalSplats","cellLengthX","numberField","cellLengthY","georeference","parseGeoreference","shift","v","scaleTransform","bounds","min","triple","max","attributes","byName","entry","require","name","range","shcoef","ZERO_RANGE","fileType","resolveFileType","offset","epsg","isPlaceholderShcoef","parseLccIndex","buffer","manifest","view","cells","level","at","byteOffsetBig","byteSize","decodeLccRotation","encoded","largest","q","next","index","block","QUATERNION_SCALE","rest","parseLccChunk","params","sh","signal","stride","_a","positions","colors","covariances","bytes","scaleMinX","scaleMinY","scaleMinZ","scaleSpanX","scaleSpanY","scaleSpanZ","i","resolve","qw","qx","qy","qz","writeCovariance","packed","decodeChunkSh","sidecar","coefficients","c","finishChunkSh","expected","to","packedRangesEqual","requantizeShWord","key","value","label","createLcc2ToThreeMatrix","THREE","applyLcc2ToThreeTransform","target","SUBCHUNK_SPLATS","buildLccScene","source","options","url","dataUrl","environmentUrl","fetchBytes","assertIndexMatchesManifest","available","MAX_SH_BANDS","requested","shBands","shUrl","chunkUrls","chunkOptions","addChunk","chunk","leaves","pinnedFiles","minimumCoverageSplats","levelCount","cellIndex","cell","finestCount","subLeaves","sub","lods","coarsestFile","coarsestCount","lo","hi","file","cellBounds","environmentCount","environmentSplatCount","environmentStride","sceneBounds","lodManifest","collisionUrl","resolveCollisionLci","LodScheduler","size","x0","y0","total","declared","shortfall","tolerance","phase","response","toRequestInit","error","toSplatLoadError","FRUSTUM_PENALTY","FRUSTUM_MARGIN","THRESHOLD_MARGIN","DWELL_MS","buildLcc2Scene","dataset","path","nodes","cellNodes","nextLeaf","build","rawNode","depth","leafStart","children","rawChildren","child","leafEnd","boxFromRaw","rootChildren","OctreeLodSource","envName","_b","environment","warn","packShBands","sum","collisionFrom","meshFiles","boundsByFile","visit","node","mesh","wanted","_","meshes","coarsest","__publicField","n","nodeIndex","r","cameraLocal","frustum","now","cellDistance","resolved","contributions","adjust","delta","byDistanceDesc","a","b","fillTarget","runs","runFromNode","from","picked","anyInView","nearestCell","nearestDist","d","base","m","distances","emit","minWanted","bound","FACE_INDEX_PROPERTIES","parseMeshPly","header","parsePlyHeader","vertices","assertPlyPropertyTypes","assertPlyElementFits","readPositions","faces","indices","readFaces","xOffset","plyPropertyOffset","yOffset","zOffset","record","vertexCount","candidate","list","countSize","plyTypeSize","itemSize","end","written","cursor","face","truncated","readInteger","triangles","grown","first","previous","type","MAGIC","SUPPORTED_VERSION","GLOBAL_HEADER_BYTES","MESH_HEADER_BYTES","FLOATS_PER_VERTEX","INDICES_PER_FACE","BYTES_PER_VERTEX","BYTES_PER_FACE","parseCollisionLci","magic","headerLen","sceneMin","sceneMax","meshCount","expectedHeader","tiles","expectedOffset","cellX","cellY","dataOffset","dataSize","faceCount","bvhByteSize","indexOffset","t","scene","loadCollisionMeshTiles","collision","settled","descriptor","loadDescriptor","result","isLciUrl","tile","data","partitionTriangleMesh","volume","assertConsistent","triangleCount","vertexInside","triangleInside","insideTriangles","gatherTriangles","k","triangleFlags","side","remap","old","mapped"],"mappings":"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"}
|