@voluma/vlam 0.1.0 → 0.2.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.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +60 -0
  3. package/THIRD_PARTY_NOTICES.md +86 -0
  4. package/dist/budget-governor.d.ts +154 -0
  5. package/dist/camera-budget-governor.d.ts +234 -0
  6. package/dist/chunk-cache-budget.d.ts +156 -0
  7. package/dist/chunk-fetch-scheduler.d.ts +168 -0
  8. package/dist/chunk-loader.d.ts +85 -0
  9. package/dist/compute-sorter.d.ts +131 -0
  10. package/dist/create-splat-renderer.d.ts +99 -0
  11. package/dist/dataset-source.d.ts +55 -0
  12. package/dist/depth-of-field.d.ts +73 -0
  13. package/dist/effects.d.ts +337 -0
  14. package/dist/effects.js +180 -0
  15. package/dist/effects.js.map +1 -0
  16. package/dist/formats/ksplat/index.d.ts +8 -0
  17. package/dist/formats/ksplat/parse-ksplat.d.ts +9 -0
  18. package/dist/formats/ksplat.js +167 -0
  19. package/dist/formats/ksplat.js.map +1 -0
  20. package/dist/formats/lcc/collision-mesh.d.ts +37 -0
  21. package/dist/formats/lcc/collision-partition.d.ts +35 -0
  22. package/dist/formats/lcc/index.d.ts +16 -0
  23. package/dist/formats/lcc/lcc.d.ts +28 -0
  24. package/dist/formats/lcc/lcc2-transform.d.ts +18 -0
  25. package/dist/formats/lcc/lcc2.d.ts +4 -0
  26. package/dist/formats/lcc/parse-collision-lci.d.ts +28 -0
  27. package/dist/formats/lcc/parse-lcc.d.ts +177 -0
  28. package/dist/formats/lcc/parse-mesh-ply.d.ts +21 -0
  29. package/dist/formats/lcc.js +969 -0
  30. package/dist/formats/lcc.js.map +1 -0
  31. package/dist/formats/ply/index.d.ts +12 -0
  32. package/dist/formats/ply/parse-compressed-ply.d.ts +16 -0
  33. package/dist/formats/ply/parse-splat-ply.d.ts +46 -0
  34. package/dist/formats/ply.js +230 -0
  35. package/dist/formats/ply.js.map +1 -0
  36. package/dist/formats/rad/frontier-worker-protocol.d.ts +177 -0
  37. package/dist/formats/rad/index.d.ts +10 -0
  38. package/dist/formats/rad/parse-rad.d.ts +88 -0
  39. package/dist/formats/rad/rad-column-decoders.d.ts +15 -0
  40. package/dist/formats/rad/rad-foveated-source.d.ts +71 -0
  41. package/dist/formats/rad/rad-parent-sizes.d.ts +45 -0
  42. package/dist/formats/rad/rad.d.ts +132 -0
  43. package/dist/formats/rad.js +1098 -0
  44. package/dist/formats/rad.js.map +1 -0
  45. package/dist/formats/sog/index.d.ts +9 -0
  46. package/dist/formats/sog/parse-sog.d.ts +45 -0
  47. package/dist/formats/sog.js +257 -0
  48. package/dist/formats/sog.js.map +1 -0
  49. package/dist/formats/splat/index.d.ts +8 -0
  50. package/dist/formats/splat/parse-splat.d.ts +10 -0
  51. package/dist/formats/splat.js +39 -0
  52. package/dist/formats/splat.js.map +1 -0
  53. package/dist/formats/spz/index.d.ts +8 -0
  54. package/dist/formats/spz/parse-spz.d.ts +10 -0
  55. package/dist/formats/spz.js +218 -0
  56. package/dist/formats/spz.js.map +1 -0
  57. package/dist/frontier-worker-CAnK_Cmu.js +566 -0
  58. package/dist/frontier-worker-CAnK_Cmu.js.map +1 -0
  59. package/dist/half-float-Cs4FDq8Q.js +30 -0
  60. package/dist/half-float-Cs4FDq8Q.js.map +1 -0
  61. package/dist/half-float.d.ts +15 -0
  62. package/dist/index.d.ts +54 -0
  63. package/dist/index.js +9054 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/load-scene.d.ts +50 -0
  66. package/dist/load-worker-protocol.d.ts +82 -0
  67. package/dist/loading-wLo8vRbA.js +102 -0
  68. package/dist/loading-wLo8vRbA.js.map +1 -0
  69. package/dist/loading.d.ts +150 -0
  70. package/dist/lod-manifest.d.ts +61 -0
  71. package/dist/lod-scheduler.d.ts +229 -0
  72. package/dist/lod-source.d.ts +207 -0
  73. package/dist/logging-BfPdd7NJ.js +19 -0
  74. package/dist/logging-BfPdd7NJ.js.map +1 -0
  75. package/dist/logging.d.ts +37 -0
  76. package/dist/one-shot-worker-G80btnth.js +664 -0
  77. package/dist/one-shot-worker-G80btnth.js.map +1 -0
  78. package/dist/orientation.d.ts +35 -0
  79. package/dist/ply-header-BwgExCn-.js +125 -0
  80. package/dist/ply-header-BwgExCn-.js.map +1 -0
  81. package/dist/ply-header.d.ts +87 -0
  82. package/dist/radix-sort.d.ts +28 -0
  83. package/dist/radix-sorter.d.ts +46 -0
  84. package/dist/relighting.d.ts +50 -0
  85. package/dist/selection-volume.d.ts +87 -0
  86. package/dist/sh-pack-D5wAe5gg.js +54 -0
  87. package/dist/sh-pack-D5wAe5gg.js.map +1 -0
  88. package/dist/sh-pack.d.ts +49 -0
  89. package/dist/sort-scheduler.d.ts +62 -0
  90. package/dist/sort-worker-protocol.d.ts +37 -0
  91. package/dist/sorter.d.ts +31 -0
  92. package/dist/source-transform.d.ts +57 -0
  93. package/dist/splat-budget-PSojLJPO.js +157 -0
  94. package/dist/splat-budget-PSojLJPO.js.map +1 -0
  95. package/dist/splat-budget.d.ts +404 -0
  96. package/dist/splat-data-BZM5dhhx.js +10 -0
  97. package/dist/splat-data-BZM5dhhx.js.map +1 -0
  98. package/dist/splat-data.d.ts +150 -0
  99. package/dist/splat-depth-pack.d.ts +39 -0
  100. package/dist/splat-mesh-material.d.ts +255 -0
  101. package/dist/splat-mesh-picking.d.ts +87 -0
  102. package/dist/splat-mesh-pool.d.ts +241 -0
  103. package/dist/splat-mesh-types-8QDjTbZO.js +390 -0
  104. package/dist/splat-mesh-types-8QDjTbZO.js.map +1 -0
  105. package/dist/splat-mesh-types.d.ts +390 -0
  106. package/dist/splat-mesh.d.ts +737 -0
  107. package/dist/splat-modifier-stack.d.ts +38 -0
  108. package/dist/splat-modifier.d.ts +156 -0
  109. package/dist/splat-partition.d.ts +37 -0
  110. package/dist/splat-query.d.ts +51 -0
  111. package/dist/splat-scene.d.ts +127 -0
  112. package/dist/splat-sort-bounds.d.ts +11 -0
  113. package/dist/static-lod-splat-mesh.d.ts +53 -0
  114. package/dist/static-lod-worker-D2fyqQZO.js +474 -0
  115. package/dist/static-lod-worker-D2fyqQZO.js.map +1 -0
  116. package/dist/static-lod-worker-protocol.d.ts +37 -0
  117. package/dist/static-lod.d.ts +24 -0
  118. package/dist/storage-attribute-mirror.d.ts +103 -0
  119. package/dist/streamed-splat-mesh-utils.d.ts +146 -0
  120. package/dist/streamed-splat-mesh.d.ts +985 -0
  121. package/dist/unified-splat-renderer.d.ts +205 -0
  122. package/dist/webgpu-limits.d.ts +133 -0
  123. package/dist/work-buffer-gather.d.ts +119 -0
  124. package/dist/work-buffer-material.d.ts +42 -0
  125. package/dist/worker-sorter.d.ts +57 -0
  126. package/dist/xr-view.d.ts +92 -0
  127. package/package.json +135 -21
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sog.js","sources":["../../src/lib/formats/sog/parse-sog.ts"],"sourcesContent":["import { SH_C0, writeCovariance, type SplatData, type SplatShData } from '../../splat-data';\nimport {\n isAbortError,\n toRequestInit,\n toSplatLoadError,\n type SplatRequestOptions,\n} from '../../loading';\n\n/**\n * Decoder for PlayCanvas's SOG format (v2), Voluma's canonical delivery\n * format: `meta.json` plus WebP-encoded attribute images, delivered either\n * as a ZIP bundle (`.sog` file) or as an unbundled directory (the form\n * Streamed SOG chunks use).\n *\n * Spec: https://developer.playcanvas.com/user-manual/gaussian-splatting/formats/sog/\n * (an open specification; PlayCanvas explicitly invites third-party\n * implementations). The decoder below is an original implementation of that\n * spec. Splat i lives at pixel (i % width, ⌊i / width⌋) in every image.\n *\n * Note: this prototype decodes on the CPU for simplicity. SOG is designed\n * to be GPU-ready - a later optimization is uploading the WebP images as\n * textures and dequantizing in the shader, as PlayCanvas does.\n */\n\n/** meta.json schema (the subset this decoder needs). */\ninterface SogMeta {\n version: number;\n count: number;\n /** Optional: the scene was exported with antialiasing (Mip-Splatting). */\n antialias?: boolean;\n means: { mins: [number, number, number]; maxs: [number, number, number]; files: string[] };\n scales: { codebook: number[]; files: string[] };\n quats: { files: string[] };\n sh0: { codebook: number[]; files: string[] };\n shN?: { count: number; bands: number; codebook: number[]; files: string[] };\n}\n\ninterface DecodedImage {\n width: number;\n height: number;\n /** RGBA bytes, row-major. */\n data: Uint8Array;\n}\n\n/**\n * Upper bound on `meta.count` before any allocation happens. The renderer\n * cannot draw more anyway: 2048-wide pool textures cap at ~16.7M splats\n * (ROADMAP M4.2) and the float32 `splatIndex` is exact only up to 2²⁴, so\n * anything larger in an untrusted meta.json is rejected rather than\n * ballooning into a multi-gigabyte allocation.\n */\nconst MAX_SPLAT_COUNT = 1 << 24;\n\n/**\n * Parses a bundled `.sog` file into flat, GPU-friendly arrays, including\n * the palette-compressed higher-order SH coefficients (`shN`) if present.\n *\n * @param options.signal - Optional abort signal; the decode stages check it\n * between steps and inside the dequantization loop.\n * @throws {Error} if the archive or its `meta.json` is malformed;\n * a `DOMException` named `AbortError` when cancelled.\n *\n * A direct parser call sits outside the `SplatLoadError`-or-`AbortError`\n * contract of the worker-mediated loaders ({@link loadScene},\n * {@link loadSceneFile}, `ChunkLoader`, `StreamedSplatMesh`): malformed input\n * surfaces here as a plain `Error`.\n */\nexport async function parseSog(\n buffer: ArrayBuffer,\n options: { signal?: AbortSignal } = {},\n): Promise<SplatData> {\n const archive = readZip(buffer);\n return parseSogFromEntries(async (name) => {\n const entry = archive.get(name);\n if (!entry) throw new Error(`SOG bundle is missing \"${name}\".`);\n return entry();\n }, options.signal);\n}\n\n/**\n * Parses an unbundled SOG directory (`meta.json` + WebP files fetched\n * individually) into flat, GPU-friendly arrays - the layout Streamed SOG\n * chunks use.\n *\n * @param baseUrl - Absolute URL of the directory containing `meta.json`.\n * @param options.signal - Optional abort signal; in-flight fetches are\n * cancelled when it fires, and the decode stages check it between steps.\n * @throws {Error} on fetch failures or a malformed `meta.json`;\n * a `DOMException` named `AbortError` when cancelled.\n *\n * A direct parser call sits outside the `SplatLoadError`-or-`AbortError`\n * contract of the worker-mediated loaders ({@link loadScene},\n * {@link loadSceneFile}, `ChunkLoader`, `StreamedSplatMesh`): malformed input\n * surfaces here as a plain `Error`.\n */\nexport async function parseSogDirectory(\n baseUrl: string,\n options: {\n signal?: AbortSignal;\n request?: SplatRequestOptions;\n /**\n * The directory's files as `name → URL`, for a chunk that has no\n * directory URL to resolve against - a folder dropped into the page,\n * whose files are reached through `blob:` URLs.\n */\n files?: Readonly<Record<string, string>>;\n } = {},\n): Promise<SplatData> {\n const base = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;\n return parseSogFromEntries(async (name) => {\n assertPlainRelativeName(name);\n const mapped = options.files?.[name];\n if (options.files && mapped === undefined) {\n throw new Error(`SOG chunk is missing \"${name}\".`);\n }\n const url = mapped === undefined ? new URL(name, base) : new URL(mapped);\n let response: Response;\n try {\n response = await fetch(url, toRequestInit(options.request, options.signal));\n } catch (error) {\n if (isAbortError(error)) throw error;\n throw toSplatLoadError(error, { phase: 'fetch', url: url.href });\n }\n if (!response.ok) {\n throw toSplatLoadError(new Error(`Failed to load ${url.href}: HTTP ${response.status}`), {\n phase: 'fetch',\n url: url.href,\n status: response.status,\n });\n }\n return new Uint8Array(await response.arrayBuffer());\n }, options.signal);\n}\n\n/**\n * Shared decode core; `readEntry` supplies file contents by name. The\n * abort signal is checked between decode stages so a cancellation skips\n * the expensive dequantization even when all fetches already finished.\n */\nasync function parseSogFromEntries(\n readEntry: (name: string) => Promise<Uint8Array>,\n signal?: AbortSignal,\n): Promise<SplatData> {\n const meta = JSON.parse(new TextDecoder().decode(await readEntry('meta.json'))) as SogMeta;\n validateMeta(meta);\n signal?.throwIfAborted();\n\n const [meansL, meansU, quats, scales, sh0] = await Promise.all([\n readEntry(meta.means.files[0] ?? 'means_l.webp').then(decodeWebp),\n readEntry(meta.means.files[1] ?? 'means_u.webp').then(decodeWebp),\n readEntry(meta.quats.files[0] ?? 'quats.webp').then(decodeWebp),\n readEntry(meta.scales.files[0] ?? 'scales.webp').then(decodeWebp),\n readEntry(meta.sh0.files[0] ?? 'sh0.webp').then(decodeWebp),\n ]);\n signal?.throwIfAborted();\n\n const count = meta.count;\n assertImageHoldsCount(meansL, 'means_l', count);\n assertImageHoldsCount(meansU, 'means_u', count);\n assertImageHoldsCount(quats, 'quats', count);\n assertImageHoldsCount(scales, 'scales', count);\n assertImageHoldsCount(sh0, 'sh0', count);\n const positions = new Float32Array(count * 3);\n const colors = new Uint8Array(count * 4);\n const covariances = new Float32Array(count * 6);\n\n // Positions: 16 bit per axis, normalized over [mins, maxs] in a\n // symmetric-log domain: unlog(n) = sign(n)·(eⁿ − 1).\n const { mins, maxs } = meta.means;\n const unlog = (n: number): number => Math.sign(n) * (Math.exp(Math.abs(n)) - 1);\n\n // Quaternions: \"smallest three\" encoding. RGB hold three components in\n // [-√½, +√½]; alpha − 252 says which component was omitted (the largest,\n // reconstructed from unit length). Component order is (w, x, y, z).\n const quatComponent = (byte: number): number => (byte / 255 - 0.5) * Math.SQRT2;\n\n const toColorByte = (sh: number): number =>\n Math.max(0, Math.min(255, Math.round((0.5 + sh * SH_C0) * 255)));\n\n for (let i = 0; i < count; i++) {\n // The dequantization loop dominates decode time. With a signal, yield\n // to the event loop every 65,536 splats so a pending cancel message\n // can be processed at all (the worker is single-threaded), then honor\n // it. Without a signal this branch never runs - no overhead.\n if (signal && (i & 0xffff) === 0 && i > 0) {\n await new Promise((resolve) => setTimeout(resolve, 0));\n signal.throwIfAborted();\n }\n const p = i * 4; // Every attribute image shares the same pixel layout.\n\n for (let axis = 0; axis < 3; axis++) {\n const quantized =\n (((meansU.data[p + axis] as number) << 8) | (meansL.data[p + axis] as number)) / 65535;\n const lo = mins[axis] as number;\n const hi = maxs[axis] as number;\n positions[i * 3 + axis] = unlog(lo + (hi - lo) * quantized);\n }\n\n // SH0: RGB are codebook indices for the DC term; alpha is the opacity,\n // stored already activated (no sigmoid, unlike PLY).\n colors[p + 0] = toColorByte(meta.sh0.codebook[sh0.data[p + 0] as number] as number);\n colors[p + 1] = toColorByte(meta.sh0.codebook[sh0.data[p + 1] as number] as number);\n colors[p + 2] = toColorByte(meta.sh0.codebook[sh0.data[p + 2] as number] as number);\n colors[p + 3] = sh0.data[p + 3] as number;\n\n const a = quatComponent(quats.data[p + 0] as number);\n const b = quatComponent(quats.data[p + 1] as number);\n const c = quatComponent(quats.data[p + 2] as number);\n const d = Math.sqrt(Math.max(0, 1 - (a * a + b * b + c * c)));\n const mode = (quats.data[p + 3] as number) - 252;\n const [qw, qx, qy, qz] =\n mode === 0\n ? [d, a, b, c]\n : mode === 1\n ? [a, d, b, c]\n : mode === 2\n ? [a, b, d, c]\n : [a, b, c, d];\n\n writeCovariance(\n covariances,\n i,\n Math.exp(meta.scales.codebook[scales.data[p + 0] as number] as number),\n Math.exp(meta.scales.codebook[scales.data[p + 1] as number] as number),\n Math.exp(meta.scales.codebook[scales.data[p + 2] as number] as number),\n qw,\n qx,\n qy,\n qz,\n );\n }\n\n signal?.throwIfAborted();\n const sh = meta.shN ? await decodeShN(meta.shN, count, readEntry) : undefined;\n return { count, positions, colors, covariances, sh, antialias: meta.antialias === true };\n}\n\n/**\n * Validates the untrusted `meta.json` before anything is allocated or\n * fetched based on it: the splat count must be a sane non-negative integer\n * and every codebook must resolve all 256 possible byte indices to finite\n * numbers (image bytes index them unchecked in the hot loop).\n */\nfunction validateMeta(meta: SogMeta): void {\n if (meta.version !== 2) throw new Error(`Unsupported SOG version: ${meta.version}.`);\n if (!Number.isSafeInteger(meta.count) || meta.count < 0) {\n throw new Error(`SOG meta.json declares an invalid splat count: ${meta.count}.`);\n }\n if (meta.count > MAX_SPLAT_COUNT) {\n throw new Error(\n `SOG meta.json declares ${meta.count} splats, above the supported maximum of ${MAX_SPLAT_COUNT}.`,\n );\n }\n validateVector3(meta.means?.mins, 'means.mins');\n validateVector3(meta.means?.maxs, 'means.maxs');\n validateCodebook(meta.scales?.codebook, 'scales');\n validateCodebook(meta.sh0?.codebook, 'sh0');\n if (meta.shN) {\n validateCodebook(meta.shN.codebook, 'shN');\n const bands = meta.shN.bands;\n if (!Number.isInteger(bands) || bands < 1 || bands > 3) {\n throw new Error(`SOG meta.json declares an invalid shN band count: ${bands}.`);\n }\n }\n}\n\n/** Asserts a meta.json vector is three finite numbers. */\nfunction validateVector3(vector: unknown, label: string): void {\n if (\n !Array.isArray(vector) ||\n vector.length !== 3 ||\n vector.some((value) => typeof value !== 'number' || !Number.isFinite(value))\n ) {\n throw new Error(`SOG meta.json: \"${label}\" must be an array of 3 finite numbers.`);\n }\n}\n\n/** Asserts a codebook covers every byte index with a finite number. */\nfunction validateCodebook(codebook: number[] | undefined, label: string): void {\n if (!Array.isArray(codebook) || codebook.length < 256) {\n throw new Error(`SOG meta.json: the \"${label}\" codebook must contain at least 256 entries.`);\n }\n for (const value of codebook) {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n throw new Error(`SOG meta.json: the \"${label}\" codebook contains a non-finite entry.`);\n }\n }\n}\n\n/** Asserts an attribute image has at least one pixel per declared splat. */\nfunction assertImageHoldsCount(image: DecodedImage, label: string, count: number): void {\n if (count > image.width * image.height) {\n throw new Error(\n `SOG \"${label}\" image (${image.width}×${image.height}) holds fewer than the declared ${count} splats.`,\n );\n }\n}\n\n/**\n * Rejects file names from `meta.json` that would resolve outside the SOG\n * directory - absolute URLs, scheme-relative or root-relative paths, and\n * `..` traversal - so a hostile manifest cannot redirect worker fetches.\n */\nfunction assertPlainRelativeName(name: string): void {\n const escapes =\n typeof name !== 'string' ||\n name.length === 0 ||\n /^[a-z][a-z0-9+.-]*:/i.test(name) ||\n name.startsWith('/') ||\n name.includes('\\\\') ||\n name.split('/').some((segment) => segment === '..');\n if (escapes) {\n throw new Error(`SOG meta.json references a file outside its directory: \"${name}\".`);\n }\n}\n\n/**\n * Decodes the palette-compressed higher-order SH data: per-splat 16-bit\n * labels plus a centroids image whose bytes are codebook indices. The\n * centroids keep their SOG grid layout so they can be uploaded as-is.\n */\nasync function decodeShN(\n meta: NonNullable<SogMeta['shN']>,\n count: number,\n readEntry: (name: string) => Promise<Uint8Array>,\n): Promise<SplatShData> {\n const [centroids, labelsImage] = await Promise.all([\n readEntry(meta.files[0] ?? 'shN_centroids.webp').then(decodeWebp),\n readEntry(meta.files[1] ?? 'shN_labels.webp').then(decodeWebp),\n ]);\n assertImageHoldsCount(labelsImage, 'shN labels', count);\n\n // A label indexes a palette entry laid out 64 entries per row, each entry\n // `coeffs` texels wide. Bound every decoded label against the centroids\n // image so a hostile labels image cannot address texels outside it.\n const coeffs = meta.bands === 1 ? 3 : meta.bands === 2 ? 8 : 15;\n const labels = new Uint32Array(count);\n for (let i = 0; i < count; i++) {\n const label =\n (labelsImage.data[i * 4] as number) | ((labelsImage.data[i * 4 + 1] as number) << 8);\n const row = Math.floor(label / 64);\n const column0 = (label % 64) * coeffs;\n if (row >= centroids.height || column0 + coeffs > centroids.width) {\n throw new Error(\n `SOG shN label ${label} (splat ${i}) lies outside the ` +\n `${centroids.width}×${centroids.height} centroids palette.`,\n );\n }\n labels[i] = label;\n }\n\n const palette = new Float32Array(centroids.width * centroids.height * 4);\n for (let i = 0; i < centroids.width * centroids.height; i++) {\n palette[i * 4 + 0] = meta.codebook[centroids.data[i * 4 + 0] as number] as number;\n palette[i * 4 + 1] = meta.codebook[centroids.data[i * 4 + 1] as number] as number;\n palette[i * 4 + 2] = meta.codebook[centroids.data[i * 4 + 2] as number] as number;\n }\n\n return {\n bands: meta.bands,\n labels,\n palette,\n paletteWidth: centroids.width,\n paletteHeight: centroids.height,\n };\n}\n\n/**\n * Minimal ZIP reader: returns a map from entry name to a function that\n * produces the entry's bytes. Supports the two methods that occur in\n * practice - STORE (SOG bundles are stored; WebP is already compressed)\n * and DEFLATE via the browser's native DecompressionStream.\n */\nfunction readZip(buffer: ArrayBuffer): Map<string, () => Promise<Uint8Array>> {\n const view = new DataView(buffer);\n const bytes = new Uint8Array(buffer);\n\n // Find the End Of Central Directory record (its signature, scanned\n // backwards past the variable-length archive comment).\n let eocd = -1;\n const scanEnd = Math.max(0, buffer.byteLength - 65557);\n for (let offset = buffer.byteLength - 22; offset >= scanEnd; offset--) {\n if (offset < 0) break;\n if (view.getUint32(offset, true) === 0x06054b50) {\n eocd = offset;\n break;\n }\n }\n if (eocd < 0) throw new Error('Not a ZIP archive (no end-of-central-directory record).');\n\n const entryCount = view.getUint16(eocd + 10, true);\n let cursor = view.getUint32(eocd + 16, true); // Central directory offset.\n\n const entries = new Map<string, () => Promise<Uint8Array>>();\n for (let i = 0; i < entryCount; i++) {\n // Range-check the untrusted cursor before every DataView read, so a\n // hostile central-directory offset fails as the corrupt ZIP it is\n // instead of surfacing a bare RangeError.\n if (cursor < 0 || cursor + 46 > buffer.byteLength) {\n throw new Error('Corrupt ZIP central directory.');\n }\n if (view.getUint32(cursor, true) !== 0x02014b50) {\n throw new Error('Corrupt ZIP central directory.');\n }\n const method = view.getUint16(cursor + 10, true);\n const compressedSize = view.getUint32(cursor + 20, true);\n const nameLength = view.getUint16(cursor + 28, true);\n const extraLength = view.getUint16(cursor + 30, true);\n const commentLength = view.getUint16(cursor + 32, true);\n const localHeaderOffset = view.getUint32(cursor + 42, true);\n if (cursor + 46 + nameLength > buffer.byteLength) {\n throw new Error('Corrupt ZIP central directory.');\n }\n const name = new TextDecoder().decode(bytes.subarray(cursor + 46, cursor + 46 + nameLength));\n cursor += 46 + nameLength + extraLength + commentLength;\n\n entries.set(name, async () => {\n // The local header repeats name/extra with possibly different sizes.\n if (localHeaderOffset + 30 > buffer.byteLength) {\n throw new Error(`Corrupt ZIP entry \"${name}\": local header extends past the archive.`);\n }\n const localNameLength = view.getUint16(localHeaderOffset + 26, true);\n const localExtraLength = view.getUint16(localHeaderOffset + 28, true);\n const dataStart = localHeaderOffset + 30 + localNameLength + localExtraLength;\n if (dataStart + compressedSize > buffer.byteLength) {\n throw new Error(`Corrupt ZIP entry \"${name}\": data extends past the archive.`);\n }\n const compressed = bytes.subarray(dataStart, dataStart + compressedSize);\n\n if (method === 0) return compressed; // STORE\n if (method === 8) {\n // DEFLATE\n // Blob copies the viewed byte range itself; no defensive slice needed.\n const stream = new Blob([compressed])\n .stream()\n .pipeThrough(new DecompressionStream('deflate-raw'));\n return new Uint8Array(await new Response(stream).arrayBuffer());\n }\n throw new Error(`Unsupported ZIP compression method ${method} for \"${name}\".`);\n });\n }\n return entries;\n}\n\nlet webpReadbackContext: WebGL2RenderingContext | undefined;\n\n/** Returns one worker-local WebGL2 context for consistent WebP readback. */\nfunction getWebpReadbackContext(): WebGL2RenderingContext | undefined {\n if (!('OffscreenCanvas' in globalThis) || !('createImageBitmap' in globalThis)) return undefined;\n if (webpReadbackContext && !webpReadbackContext.isContextLost()) return webpReadbackContext;\n\n const canvas = new OffscreenCanvas(1, 1);\n const context = canvas.getContext('webgl2', {\n alpha: false,\n antialias: false,\n depth: false,\n premultipliedAlpha: false,\n preserveDrawingBuffer: true,\n stencil: false,\n });\n if (!context) return undefined;\n webpReadbackContext = context;\n return context;\n}\n\n/**\n * Decodes through the same path PlayCanvas uses for SOG data textures.\n * Firefox's ImageDecoder premultiplies RGB by alpha even when conversion is\n * disabled. ImageBitmap -> WebGL keeps RGB unassociated with alpha.\n */\nasync function decodeWebpViaGpu(\n bytes: Uint8Array,\n context: WebGL2RenderingContext,\n): Promise<DecodedImage> {\n const blobBytes = bytes as Uint8Array<ArrayBuffer>;\n const bitmap = await createImageBitmap(new Blob([blobBytes], { type: 'image/webp' }), {\n colorSpaceConversion: 'none',\n premultiplyAlpha: 'none',\n });\n const texture = context.createTexture();\n const framebuffer = context.createFramebuffer();\n if (!texture || !framebuffer) {\n bitmap.close();\n throw new Error('Could not allocate a WebGL texture for SOG WebP decoding.');\n }\n\n try {\n context.bindTexture(context.TEXTURE_2D, texture);\n context.pixelStorei(context.UNPACK_FLIP_Y_WEBGL, false);\n context.pixelStorei(context.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);\n context.pixelStorei(context.UNPACK_COLORSPACE_CONVERSION_WEBGL, context.NONE);\n context.texParameteri(context.TEXTURE_2D, context.TEXTURE_MIN_FILTER, context.NEAREST);\n context.texParameteri(context.TEXTURE_2D, context.TEXTURE_MAG_FILTER, context.NEAREST);\n context.texParameteri(context.TEXTURE_2D, context.TEXTURE_WRAP_S, context.CLAMP_TO_EDGE);\n context.texParameteri(context.TEXTURE_2D, context.TEXTURE_WRAP_T, context.CLAMP_TO_EDGE);\n context.texImage2D(\n context.TEXTURE_2D,\n 0,\n context.RGBA,\n context.RGBA,\n context.UNSIGNED_BYTE,\n bitmap,\n );\n\n context.bindFramebuffer(context.FRAMEBUFFER, framebuffer);\n context.framebufferTexture2D(\n context.FRAMEBUFFER,\n context.COLOR_ATTACHMENT0,\n context.TEXTURE_2D,\n texture,\n 0,\n );\n if (context.checkFramebufferStatus(context.FRAMEBUFFER) !== context.FRAMEBUFFER_COMPLETE) {\n throw new Error('Could not read the decoded SOG WebP texture.');\n }\n\n const data = new Uint8Array(bitmap.width * bitmap.height * 4);\n context.readPixels(\n 0,\n 0,\n bitmap.width,\n bitmap.height,\n context.RGBA,\n context.UNSIGNED_BYTE,\n data,\n );\n return { width: bitmap.width, height: bitmap.height, data };\n } finally {\n context.bindFramebuffer(context.FRAMEBUFFER, null);\n context.bindTexture(context.TEXTURE_2D, null);\n context.deleteFramebuffer(framebuffer);\n context.deleteTexture(texture);\n bitmap.close();\n }\n}\n\n/** Decodes a WebP image to raw RGBA bytes. */\nasync function decodeWebp(bytes: Uint8Array): Promise<DecodedImage> {\n const gpuContext = getWebpReadbackContext();\n if (gpuContext) return decodeWebpViaGpu(bytes, gpuContext);\n\n // WebCodecs fallback for environments without worker WebGL2. Chromium\n // preserves these data bytes; Firefox uses the GPU path above because its\n // VideoFrame copy currently premultiplies low-alpha RGB values.\n if ('ImageDecoder' in globalThis) {\n const decoder = new ImageDecoder({\n data: bytes,\n type: 'image/webp',\n colorSpaceConversion: 'none',\n });\n const { image } = await decoder.decode();\n const width = image.codedWidth;\n const height = image.codedHeight;\n const data = new Uint8Array(width * height * 4);\n await image.copyTo(data, { format: 'RGBA' });\n image.close();\n decoder.close();\n return { width, height, data };\n }\n\n // Fallback: canvas readback. The premultiply round-trip can cost ±1 bit\n // of RGB precision on low-alpha pixels; acceptable for a fallback path.\n // Blob copies the viewed byte range itself, so no defensive `.slice()` is\n // needed; the cast only bridges lib.dom's BlobPart rejecting the\n // ArrayBufferLike-generic view (these bytes never sit in a SharedArrayBuffer).\n const blobBytes = bytes as Uint8Array<ArrayBuffer>;\n const bitmap = await createImageBitmap(new Blob([blobBytes], { type: 'image/webp' }), {\n premultiplyAlpha: 'none',\n colorSpaceConversion: 'none',\n });\n const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);\n const context = canvas.getContext('2d');\n if (!context) throw new Error('Could not create a 2D canvas context.');\n context.drawImage(bitmap, 0, 0);\n const imageData = context.getImageData(0, 0, bitmap.width, bitmap.height);\n bitmap.close();\n return {\n width: imageData.width,\n height: imageData.height,\n data: new Uint8Array(imageData.data.buffer),\n };\n}\n"],"names":["MAX_SPLAT_COUNT","parseSog","buffer","options","archive","readZip","parseSogFromEntries","name","entry","parseSogDirectory","baseUrl","base","assertPlainRelativeName","mapped","_a","url","response","toRequestInit","error","isAbortError","toSplatLoadError","readEntry","signal","meta","validateMeta","meansL","meansU","quats","scales","sh0","decodeWebp","count","assertImageHoldsCount","positions","colors","covariances","mins","maxs","unlog","n","quatComponent","byte","toColorByte","sh","SH_C0","i","resolve","p","axis","quantized","lo","hi","a","b","c","d","mode","qw","qx","qy","qz","writeCovariance","decodeShN","validateVector3","_b","validateCodebook","_c","_d","bands","vector","label","value","codebook","image","segment","centroids","labelsImage","coeffs","labels","row","column0","palette","view","bytes","eocd","scanEnd","offset","entryCount","cursor","entries","method","compressedSize","nameLength","extraLength","commentLength","localHeaderOffset","localNameLength","localExtraLength","dataStart","compressed","stream","webpReadbackContext","getWebpReadbackContext","context","decodeWebpViaGpu","blobBytes","bitmap","texture","framebuffer","data","gpuContext","decoder","width","height","imageData"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `@voluma/vlam/formats/splat` - antimatter15 `.splat` parser.
3
+ *
4
+ * Loading through {@link loadScene} / {@link loadSceneFile} does not require
5
+ * this import; the loading worker already knows this format. Use this subpath
6
+ * for direct decode outside the default loaders.
7
+ */
8
+ export { parseSplat } from './parse-splat';
@@ -0,0 +1,10 @@
1
+ import { type SplatData } from '../../splat-data';
2
+ /**
3
+ * Parses the common 32-byte `.splat` format into flat, GPU-friendly arrays.
4
+ *
5
+ * Each record stores float32 position and linear scale, followed by RGBA and
6
+ * a byte-quantized `(w, x, y, z)` quaternion.
7
+ *
8
+ * @throws {Error} if the input length is not a whole number of records.
9
+ */
10
+ export declare function parseSplat(buffer: ArrayBuffer): SplatData;
@@ -0,0 +1,39 @@
1
+ import { w as y } from "../splat-data-BZM5dhhx.js";
2
+ const l = 32, g = 1 << 24;
3
+ function A(n) {
4
+ if (n.byteLength % l !== 0)
5
+ throw new Error(
6
+ `Invalid SPLAT file size: ${n.byteLength} bytes is not divisible by ${l}.`
7
+ );
8
+ const o = n.byteLength / l;
9
+ if (o > g)
10
+ throw new Error(
11
+ `SPLAT file declares ${o} splats, above the supported maximum of ${g}.`
12
+ );
13
+ const e = new DataView(n);
14
+ if (o > 0 && (!Number.isFinite(e.getFloat32(0, !0)) || !Number.isFinite(e.getFloat32(4, !0)) || !Number.isFinite(e.getFloat32(8, !0))))
15
+ throw new Error("Not a SPLAT file: the first record has a non-finite position.");
16
+ const r = new Uint8Array(n), a = new Float32Array(o * 3), s = new Uint8Array(o * 4), F = new Float32Array(o * 6);
17
+ for (let i = 0; i < o; i++) {
18
+ const t = i * l, w = i * 3, c = i * 4;
19
+ a[w + 0] = e.getFloat32(t + 0, !0), a[w + 1] = e.getFloat32(t + 4, !0), a[w + 2] = e.getFloat32(t + 8, !0), s[c + 0] = r[t + 24], s[c + 1] = r[t + 25], s[c + 2] = r[t + 26], s[c + 3] = r[t + 27], y(
20
+ F,
21
+ i,
22
+ e.getFloat32(t + 12, !0),
23
+ e.getFloat32(t + 16, !0),
24
+ e.getFloat32(t + 20, !0),
25
+ u(r[t + 28]),
26
+ u(r[t + 29]),
27
+ u(r[t + 30]),
28
+ u(r[t + 31])
29
+ );
30
+ }
31
+ return { count: o, positions: a, colors: s, covariances: F };
32
+ }
33
+ function u(n) {
34
+ return (n - 128) / 128;
35
+ }
36
+ export {
37
+ A as parseSplat
38
+ };
39
+ //# sourceMappingURL=splat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"splat.js","sources":["../../src/lib/formats/splat/parse-splat.ts"],"sourcesContent":["import { writeCovariance, type SplatData } from '../../splat-data';\n\nconst BYTES_PER_SPLAT = 32;\n/** Mirrors `parse-sog.ts` MAX_SPLAT_COUNT - the renderer's 2²⁴ splat ceiling. */\nconst MAX_SPLAT_COUNT = 1 << 24;\n\n/**\n * Parses the common 32-byte `.splat` format into flat, GPU-friendly arrays.\n *\n * Each record stores float32 position and linear scale, followed by RGBA and\n * a byte-quantized `(w, x, y, z)` quaternion.\n *\n * @throws {Error} if the input length is not a whole number of records.\n */\nexport function parseSplat(buffer: ArrayBuffer): SplatData {\n if (buffer.byteLength % BYTES_PER_SPLAT !== 0) {\n throw new Error(\n `Invalid SPLAT file size: ${buffer.byteLength} bytes is not divisible by ${BYTES_PER_SPLAT}.`,\n );\n }\n\n const count = buffer.byteLength / BYTES_PER_SPLAT;\n if (count > MAX_SPLAT_COUNT) {\n throw new Error(\n `SPLAT file declares ${count} splats, above the supported maximum of ${MAX_SPLAT_COUNT}.`,\n );\n }\n const view = new DataView(buffer);\n // The format has no magic - any 32-byte-divisible file \"parses\". As a cheap\n // sanity check, the first record's position must at least be finite floats.\n if (\n count > 0 &&\n (!Number.isFinite(view.getFloat32(0, true)) ||\n !Number.isFinite(view.getFloat32(4, true)) ||\n !Number.isFinite(view.getFloat32(8, true)))\n ) {\n throw new Error('Not a SPLAT file: the first record has a non-finite position.');\n }\n const bytes = new Uint8Array(buffer);\n const positions = new Float32Array(count * 3);\n const colors = new Uint8Array(count * 4);\n const covariances = new Float32Array(count * 6);\n\n for (let i = 0; i < count; i++) {\n const source = i * BYTES_PER_SPLAT;\n const position = i * 3;\n const color = i * 4;\n\n positions[position + 0] = view.getFloat32(source + 0, true);\n positions[position + 1] = view.getFloat32(source + 4, true);\n positions[position + 2] = view.getFloat32(source + 8, true);\n colors[color + 0] = bytes[source + 24] as number;\n colors[color + 1] = bytes[source + 25] as number;\n colors[color + 2] = bytes[source + 26] as number;\n colors[color + 3] = bytes[source + 27] as number;\n\n writeCovariance(\n covariances,\n i,\n view.getFloat32(source + 12, true),\n view.getFloat32(source + 16, true),\n view.getFloat32(source + 20, true),\n decodeQuaternionByte(bytes[source + 28] as number),\n decodeQuaternionByte(bytes[source + 29] as number),\n decodeQuaternionByte(bytes[source + 30] as number),\n decodeQuaternionByte(bytes[source + 31] as number),\n );\n }\n\n return { count, positions, colors, covariances };\n}\n\nfunction decodeQuaternionByte(value: number): number {\n return (value - 128) / 128;\n}\n"],"names":["BYTES_PER_SPLAT","MAX_SPLAT_COUNT","parseSplat","buffer","count","view","bytes","positions","colors","covariances","source","position","color","writeCovariance","decodeQuaternionByte","value"],"mappings":";AAEA,MAAMA,IAAkB,IAElBC,IAAkB,KAAK;AAUtB,SAASC,EAAWC,GAAgC;AACzD,MAAIA,EAAO,aAAaH,MAAoB;AAC1C,UAAM,IAAI;AAAA,MACR,4BAA4BG,EAAO,UAAU,8BAA8BH,CAAe;AAAA,IAAA;AAI9F,QAAMI,IAAQD,EAAO,aAAaH;AAClC,MAAII,IAAQH;AACV,UAAM,IAAI;AAAA,MACR,uBAAuBG,CAAK,2CAA2CH,CAAe;AAAA,IAAA;AAG1F,QAAMI,IAAO,IAAI,SAASF,CAAM;AAGhC,MACEC,IAAQ,MACP,CAAC,OAAO,SAASC,EAAK,WAAW,GAAG,EAAI,CAAC,KACxC,CAAC,OAAO,SAASA,EAAK,WAAW,GAAG,EAAI,CAAC,KACzC,CAAC,OAAO,SAASA,EAAK,WAAW,GAAG,EAAI,CAAC;AAE3C,UAAM,IAAI,MAAM,+DAA+D;AAEjF,QAAMC,IAAQ,IAAI,WAAWH,CAAM,GAC7BI,IAAY,IAAI,aAAaH,IAAQ,CAAC,GACtCI,IAAS,IAAI,WAAWJ,IAAQ,CAAC,GACjCK,IAAc,IAAI,aAAaL,IAAQ,CAAC;AAE9C,WAAS,IAAI,GAAG,IAAIA,GAAO,KAAK;AAC9B,UAAMM,IAAS,IAAIV,GACbW,IAAW,IAAI,GACfC,IAAQ,IAAI;AAElB,IAAAL,EAAUI,IAAW,CAAC,IAAIN,EAAK,WAAWK,IAAS,GAAG,EAAI,GAC1DH,EAAUI,IAAW,CAAC,IAAIN,EAAK,WAAWK,IAAS,GAAG,EAAI,GAC1DH,EAAUI,IAAW,CAAC,IAAIN,EAAK,WAAWK,IAAS,GAAG,EAAI,GAC1DF,EAAOI,IAAQ,CAAC,IAAIN,EAAMI,IAAS,EAAE,GACrCF,EAAOI,IAAQ,CAAC,IAAIN,EAAMI,IAAS,EAAE,GACrCF,EAAOI,IAAQ,CAAC,IAAIN,EAAMI,IAAS,EAAE,GACrCF,EAAOI,IAAQ,CAAC,IAAIN,EAAMI,IAAS,EAAE,GAErCG;AAAA,MACEJ;AAAA,MACA;AAAA,MACAJ,EAAK,WAAWK,IAAS,IAAI,EAAI;AAAA,MACjCL,EAAK,WAAWK,IAAS,IAAI,EAAI;AAAA,MACjCL,EAAK,WAAWK,IAAS,IAAI,EAAI;AAAA,MACjCI,EAAqBR,EAAMI,IAAS,EAAE,CAAW;AAAA,MACjDI,EAAqBR,EAAMI,IAAS,EAAE,CAAW;AAAA,MACjDI,EAAqBR,EAAMI,IAAS,EAAE,CAAW;AAAA,MACjDI,EAAqBR,EAAMI,IAAS,EAAE,CAAW;AAAA,IAAA;AAAA,EAErD;AAEA,SAAO,EAAE,OAAAN,GAAO,WAAAG,GAAW,QAAAC,GAAQ,aAAAC,EAAA;AACrC;AAEA,SAASK,EAAqBC,GAAuB;AACnD,UAAQA,IAAQ,OAAO;AACzB;"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `@voluma/vlam/formats/spz` - Niantic `.spz` parser.
3
+ *
4
+ * Loading through {@link loadScene} / {@link loadSceneFile} does not require
5
+ * this import; the loading worker already knows this format. Use this subpath
6
+ * for direct decode outside the default loaders.
7
+ */
8
+ export { parseSpz } from './parse-spz';
@@ -0,0 +1,10 @@
1
+ import { type SplatData } from '../../splat-data';
2
+ /**
3
+ * Decompresses and parses an SPZ file into flat, GPU-friendly arrays.
4
+ * Legacy gzip versions 1–3 and the current Zstandard-streamed version 4 are
5
+ * supported. Vendor extensions are skipped, except a coordinate-system
6
+ * extension is validated as Three.js-compatible RUB to prevent a wrong frame.
7
+ *
8
+ * @throws {Error} if the file is malformed, unsupported or cannot be decompressed.
9
+ */
10
+ export declare function parseSpz(buffer: ArrayBuffer): Promise<SplatData>;
@@ -0,0 +1,218 @@
1
+ import { ZSTDDecoder as z } from "three/examples/jsm/libs/zstddec.module.js";
2
+ import { h as M } from "../half-float-Cs4FDq8Q.js";
3
+ import { p as T, s as D } from "../sh-pack-D5wAe5gg.js";
4
+ import { w as B, S as $ } from "../splat-data-BZM5dhhx.js";
5
+ const Z = 1347635022, g = 16, E = 32, I = 0.15, b = Math.SQRT1_2, x = 1 << 24, C = g + 92 * x, R = 3;
6
+ async function W(t) {
7
+ const n = new Uint8Array(t), o = n[0] === 31 && n[1] === 139 ? N(await q(n)) : await O(n);
8
+ return F(o);
9
+ }
10
+ async function O(t) {
11
+ if (t.byteLength < E) throw new Error("Truncated SPZ v4 header.");
12
+ const n = new DataView(t.buffer, t.byteOffset, t.byteLength);
13
+ if (n.getUint32(0, !0) !== Z) throw new Error("Not an SPZ file.");
14
+ const o = n.getUint32(4, !0);
15
+ if (o !== 4) throw new Error(`Unsupported SPZ version ${o}.`);
16
+ const e = n.getUint32(8, !0), r = n.getUint8(12), i = n.getUint8(13), u = n.getUint8(14), s = n.getUint8(15), a = n.getUint32(16, !0);
17
+ L(e, r, i), (u & 2) !== 0 && A(n, E, a);
18
+ const c = e * 4, f = [e * 9, e, e * 3, e * 3, c];
19
+ if (r > 0 && f.push(e * y(r) * 3), s !== f.length)
20
+ throw new Error(`Invalid SPZ stream count ${s}; expected ${f.length}.`);
21
+ const w = a + s * 16;
22
+ if (a < E || w > t.byteLength)
23
+ throw new Error("Invalid SPZ table of contents.");
24
+ const l = new z();
25
+ await l.init();
26
+ const h = [];
27
+ let d = w;
28
+ for (let S = 0; S < s; S++) {
29
+ const U = a + S * 16, m = v(n, U), p = v(n, U + 8);
30
+ if (p !== f[S])
31
+ throw new Error(
32
+ `Invalid SPZ stream ${S} size ${p}; expected ${f[S]}.`
33
+ );
34
+ if (d + m > t.byteLength)
35
+ throw new Error(`Truncated SPZ stream ${S}.`);
36
+ const _ = t.subarray(d, d + m), P = l.decode(_, p);
37
+ if (P.byteLength !== p)
38
+ throw new Error(`SPZ stream ${S} decompressed to an unexpected size.`);
39
+ h.push(P), d += m;
40
+ }
41
+ if (d !== t.byteLength) throw new Error("Unexpected trailing SPZ data.");
42
+ return {
43
+ version: o,
44
+ count: e,
45
+ shDegree: r,
46
+ fractionalBits: i,
47
+ positions: h[0],
48
+ alphas: h[1],
49
+ colors: h[2],
50
+ scales: h[3],
51
+ rotations: h[4],
52
+ sh: r > 0 ? h[5] : null
53
+ };
54
+ }
55
+ function N(t) {
56
+ if (t.byteLength < g) throw new Error("Truncated SPZ header.");
57
+ const n = new DataView(t.buffer, t.byteOffset, t.byteLength);
58
+ if (n.getUint32(0, !0) !== Z) throw new Error("Not an SPZ file.");
59
+ const o = n.getUint32(4, !0);
60
+ if (o < 1 || o > 3) throw new Error(`Unsupported SPZ version ${o}.`);
61
+ const e = n.getUint32(8, !0), r = n.getUint8(12), i = n.getUint8(13), u = n.getUint8(14);
62
+ L(e, r, i);
63
+ const s = e * 3 * (o === 1 ? 2 : 3), a = e * (o >= 3 ? 4 : 3), c = e * y(r) * 3, f = g + s + e + e * 3 + e * 3 + a + c;
64
+ if (t.byteLength < f || (u & 2) === 0 && t.byteLength !== f)
65
+ throw new Error(`Invalid SPZ payload size ${t.byteLength}; expected ${f}.`);
66
+ (u & 2) !== 0 && A(n, f, t.byteLength);
67
+ let w = g;
68
+ const l = (h) => {
69
+ const d = t.subarray(w, w + h);
70
+ return w += h, d;
71
+ };
72
+ return {
73
+ version: o,
74
+ count: e,
75
+ shDegree: r,
76
+ fractionalBits: i,
77
+ positions: l(s),
78
+ alphas: l(e),
79
+ colors: l(e * 3),
80
+ scales: l(e * 3),
81
+ rotations: l(a),
82
+ sh: r > 0 ? l(c) : null
83
+ };
84
+ }
85
+ function F(t) {
86
+ const n = new Float32Array(t.count * 3), o = new Uint8Array(t.count * 4), e = new Float32Array(t.count * 6), r = new DataView(
87
+ t.positions.buffer,
88
+ t.positions.byteOffset,
89
+ t.positions.byteLength
90
+ ), i = t.version >= 3 ? 4 : 3;
91
+ for (let s = 0; s < t.count; s++) {
92
+ for (let c = 0; c < 3; c++)
93
+ n[s * 3 + c] = t.version === 1 ? M(r.getUint16((s * 3 + c) * 2, !0)) : V(t.positions, (s * 3 + c) * 3) / 2 ** t.fractionalBits;
94
+ for (let c = 0; c < 3; c++) {
95
+ const w = (t.colors[s * 3 + c] / 255 - 0.5) / I * $ + 0.5;
96
+ o[s * 4 + c] = X(Math.round(w * 255));
97
+ }
98
+ o[s * 4 + 3] = t.alphas[s];
99
+ const a = t.version >= 3 ? Y(t.rotations, s * i) : G(t.rotations, s * i);
100
+ B(
101
+ e,
102
+ s,
103
+ Math.exp(t.scales[s * 3 + 0] / 16 - 10),
104
+ Math.exp(t.scales[s * 3 + 1] / 16 - 10),
105
+ Math.exp(t.scales[s * 3 + 2] / 16 - 10),
106
+ a[3],
107
+ a[0],
108
+ a[1],
109
+ a[2]
110
+ );
111
+ }
112
+ const u = H(t);
113
+ return {
114
+ count: t.count,
115
+ positions: n,
116
+ colors: o,
117
+ covariances: e,
118
+ ...u ? { shPacked: u } : {}
119
+ };
120
+ }
121
+ function H(t) {
122
+ if (!t.sh || t.shDegree === 0) return;
123
+ const n = Math.min(t.shDegree, R), e = D(n) * 3, r = new Float32Array(t.count * e);
124
+ for (let i = 0; i < t.count; i++) {
125
+ const u = i * y(t.shDegree) * 3, s = i * e;
126
+ for (let a = 0; a < e; a++) {
127
+ const c = t.sh[u + a];
128
+ r[s + a] = (c - 128) / 128;
129
+ }
130
+ }
131
+ return T(r, t.count, n);
132
+ }
133
+ async function q(t) {
134
+ if (typeof DecompressionStream > "u")
135
+ throw new Error(
136
+ "This runtime does not provide gzip decompression required for legacy SPZ files."
137
+ );
138
+ const n = new Uint8Array(t.byteLength);
139
+ n.set(t);
140
+ const e = new Blob([n.buffer]).stream().pipeThrough(new DecompressionStream("gzip")).getReader(), r = [];
141
+ let i = 0;
142
+ for (; ; ) {
143
+ const { done: a, value: c } = await e.read();
144
+ if (a) break;
145
+ if (i += c.byteLength, i > C)
146
+ throw await e.cancel().catch(() => {
147
+ }), new Error("SPZ gzip payload expands past the supported maximum size.");
148
+ r.push(c);
149
+ }
150
+ const u = new Uint8Array(i);
151
+ let s = 0;
152
+ for (const a of r)
153
+ u.set(a, s), s += a.byteLength;
154
+ return u;
155
+ }
156
+ function L(t, n, o) {
157
+ if (t === 0 || t > x) throw new Error(`Invalid SPZ point count ${t}.`);
158
+ if (n < 0 || n > 4) throw new Error(`Unsupported SPZ SH degree ${n}.`);
159
+ if (o > 24) throw new Error(`Invalid SPZ fractional bit count ${o}.`);
160
+ }
161
+ function y(t) {
162
+ return t === 0 ? 0 : t === 1 ? 3 : t === 2 ? 8 : t === 3 ? 15 : 24;
163
+ }
164
+ function v(t, n) {
165
+ const o = t.getBigUint64(n, !0);
166
+ if (o > BigInt(Number.MAX_SAFE_INTEGER))
167
+ throw new Error("SPZ stream size exceeds JavaScript limits.");
168
+ return Number(o);
169
+ }
170
+ function A(t, n, o) {
171
+ if (n > o || o > t.byteLength) throw new Error("Invalid SPZ extension block.");
172
+ let e = n;
173
+ for (; e < o; ) {
174
+ if (e + 8 > o) throw new Error("Truncated SPZ extension header.");
175
+ const r = t.getUint32(e, !0), i = t.getUint32(e + 4, !0);
176
+ if (e += 8, e + i > o) throw new Error("Truncated SPZ extension payload.");
177
+ if (r === 2914910211) {
178
+ if (i !== 4) throw new Error("Invalid SPZ coordinate-system extension.");
179
+ const u = t.getUint32(e, !0);
180
+ if (u !== 4)
181
+ throw new Error(
182
+ `SPZ coordinate system ${u} is not supported; expected RUB (4).`
183
+ );
184
+ }
185
+ e += i;
186
+ }
187
+ }
188
+ function V(t, n) {
189
+ const o = t[n] | t[n + 1] << 8 | t[n + 2] << 16;
190
+ return o & 8388608 ? o | 4278190080 : o;
191
+ }
192
+ function G(t, n) {
193
+ const o = t[n] / 127.5 - 1, e = t[n + 1] / 127.5 - 1, r = t[n + 2] / 127.5 - 1;
194
+ return [o, e, r, Math.sqrt(Math.max(0, 1 - o * o - e * e - r * r))];
195
+ }
196
+ function Y(t, n) {
197
+ let o = (t[n] | t[n + 1] << 8 | t[n + 2] << 16 | t[n + 3] << 24) >>> 0;
198
+ const e = o >>> 30, r = [0, 0, 0, 0];
199
+ let i = 0;
200
+ for (let u = 3; u >= 0; u--) {
201
+ if (u === e) continue;
202
+ const s = o & 511, c = (o >>> 9 & 1 ? -1 : 1) * b * (s / 511);
203
+ r[u] = c, i += c * c, o >>>= 10;
204
+ }
205
+ return r[e] = Math.sqrt(Math.max(0, 1 - i)), [
206
+ r[0],
207
+ r[1],
208
+ r[2],
209
+ r[3]
210
+ ];
211
+ }
212
+ function X(t) {
213
+ return Math.max(0, Math.min(255, t));
214
+ }
215
+ export {
216
+ W as parseSpz
217
+ };
218
+ //# sourceMappingURL=spz.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spz.js","sources":["../../src/lib/formats/spz/parse-spz.ts"],"sourcesContent":["import { ZSTDDecoder } from 'three/examples/jsm/libs/zstddec.module.js';\nimport { halfToFloat } from '../../half-float';\nimport { packShCoefficients, shCoefficientCount } from '../../sh-pack';\nimport { SH_C0, writeCovariance, type SplatData } from '../../splat-data';\n\nconst SPZ_MAGIC = 0x5053474e;\nconst LEGACY_HEADER_BYTES = 16;\nconst VERSION_4_HEADER_BYTES = 32;\nconst COLOR_SCALE = 0.15;\nconst SQRT_HALF = Math.SQRT1_2;\n/**\n * Upper bound on the declared point count (mirrors `parse-sog.ts`\n * MAX_SPLAT_COUNT): the renderer cannot draw more than 2²⁴ splats, so a\n * hostile header is rejected before it sizes any allocation.\n */\nconst MAX_SAFE_POINTS = 1 << 24;\n/**\n * Expansion ceiling for the legacy gzip path: header plus the largest legal\n * payload (positions 9 + alpha 1 + color 3 + scales 3 + rotation 4 + degree-3\n * SH 72 = 92 bytes per point at the maximum count), so a gzip bomb cannot\n * expand unbounded before the size checks run.\n */\nconst MAX_LEGACY_DECOMPRESSED_BYTES = LEGACY_HEADER_BYTES + 92 * MAX_SAFE_POINTS;\nconst MAX_RENDER_SH_BANDS = 3;\n\ninterface PackedSpz {\n version: number;\n count: number;\n shDegree: number;\n fractionalBits: number;\n positions: Uint8Array;\n alphas: Uint8Array;\n colors: Uint8Array;\n scales: Uint8Array;\n rotations: Uint8Array;\n sh: Uint8Array | null;\n}\n\n/**\n * Decompresses and parses an SPZ file into flat, GPU-friendly arrays.\n * Legacy gzip versions 1–3 and the current Zstandard-streamed version 4 are\n * supported. Vendor extensions are skipped, except a coordinate-system\n * extension is validated as Three.js-compatible RUB to prevent a wrong frame.\n *\n * @throws {Error} if the file is malformed, unsupported or cannot be decompressed.\n */\nexport async function parseSpz(buffer: ArrayBuffer): Promise<SplatData> {\n const bytes = new Uint8Array(buffer);\n const packed =\n bytes[0] === 0x1f && bytes[1] === 0x8b\n ? parseLegacySpz(await decompressGzip(bytes))\n : await parseVersion4Spz(bytes);\n return unpackSpz(packed);\n}\n\nasync function parseVersion4Spz(bytes: Uint8Array): Promise<PackedSpz> {\n if (bytes.byteLength < VERSION_4_HEADER_BYTES) throw new Error('Truncated SPZ v4 header.');\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n if (view.getUint32(0, true) !== SPZ_MAGIC) throw new Error('Not an SPZ file.');\n const version = view.getUint32(4, true);\n if (version !== 4) throw new Error(`Unsupported SPZ version ${version}.`);\n const count = view.getUint32(8, true);\n const shDegree = view.getUint8(12);\n const fractionalBits = view.getUint8(13);\n const flags = view.getUint8(14);\n const streamCount = view.getUint8(15);\n const tocOffset = view.getUint32(16, true);\n validateSpzHeader(count, shDegree, fractionalBits);\n if ((flags & 0x02) !== 0) validateExtensions(view, VERSION_4_HEADER_BYTES, tocOffset);\n\n const rotationBytes = count * 4;\n const expectedSizes = [count * 9, count, count * 3, count * 3, rotationBytes];\n if (shDegree > 0) expectedSizes.push(count * shDimension(shDegree) * 3);\n if (streamCount !== expectedSizes.length) {\n throw new Error(`Invalid SPZ stream count ${streamCount}; expected ${expectedSizes.length}.`);\n }\n const tocEnd = tocOffset + streamCount * 16;\n if (tocOffset < VERSION_4_HEADER_BYTES || tocEnd > bytes.byteLength) {\n throw new Error('Invalid SPZ table of contents.');\n }\n\n const decoder = new ZSTDDecoder();\n await decoder.init();\n const streams: Uint8Array[] = [];\n let compressedOffset = tocEnd;\n for (let i = 0; i < streamCount; i++) {\n const entry = tocOffset + i * 16;\n const compressedSize = safeUint64(view, entry);\n const uncompressedSize = safeUint64(view, entry + 8);\n if (uncompressedSize !== expectedSizes[i]) {\n throw new Error(\n `Invalid SPZ stream ${i} size ${uncompressedSize}; expected ${expectedSizes[i]}.`,\n );\n }\n if (compressedOffset + compressedSize > bytes.byteLength) {\n throw new Error(`Truncated SPZ stream ${i}.`);\n }\n const compressed = bytes.subarray(compressedOffset, compressedOffset + compressedSize);\n const decoded = decoder.decode(compressed, uncompressedSize);\n if (decoded.byteLength !== uncompressedSize) {\n throw new Error(`SPZ stream ${i} decompressed to an unexpected size.`);\n }\n streams.push(decoded);\n compressedOffset += compressedSize;\n }\n if (compressedOffset !== bytes.byteLength) throw new Error('Unexpected trailing SPZ data.');\n\n return {\n version,\n count,\n shDegree,\n fractionalBits,\n positions: streams[0] as Uint8Array,\n alphas: streams[1] as Uint8Array,\n colors: streams[2] as Uint8Array,\n scales: streams[3] as Uint8Array,\n rotations: streams[4] as Uint8Array,\n sh: shDegree > 0 ? (streams[5] as Uint8Array) : null,\n };\n}\n\nfunction parseLegacySpz(bytes: Uint8Array): PackedSpz {\n if (bytes.byteLength < LEGACY_HEADER_BYTES) throw new Error('Truncated SPZ header.');\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n if (view.getUint32(0, true) !== SPZ_MAGIC) throw new Error('Not an SPZ file.');\n const version = view.getUint32(4, true);\n if (version < 1 || version > 3) throw new Error(`Unsupported SPZ version ${version}.`);\n const count = view.getUint32(8, true);\n const shDegree = view.getUint8(12);\n const fractionalBits = view.getUint8(13);\n const flags = view.getUint8(14);\n validateSpzHeader(count, shDegree, fractionalBits);\n\n const positionBytes = count * 3 * (version === 1 ? 2 : 3);\n const rotationBytes = count * (version >= 3 ? 4 : 3);\n const shBytes = count * shDimension(shDegree) * 3;\n const expectedBytes =\n LEGACY_HEADER_BYTES + positionBytes + count + count * 3 + count * 3 + rotationBytes + shBytes;\n if (\n bytes.byteLength < expectedBytes ||\n ((flags & 0x02) === 0 && bytes.byteLength !== expectedBytes)\n ) {\n throw new Error(`Invalid SPZ payload size ${bytes.byteLength}; expected ${expectedBytes}.`);\n }\n if ((flags & 0x02) !== 0) validateExtensions(view, expectedBytes, bytes.byteLength);\n\n let offset = LEGACY_HEADER_BYTES;\n const take = (length: number): Uint8Array => {\n const result = bytes.subarray(offset, offset + length);\n offset += length;\n return result;\n };\n return {\n version,\n count,\n shDegree,\n fractionalBits,\n positions: take(positionBytes),\n alphas: take(count),\n colors: take(count * 3),\n scales: take(count * 3),\n rotations: take(rotationBytes),\n sh: shDegree > 0 ? take(shBytes) : null,\n };\n}\n\nfunction unpackSpz(packed: PackedSpz): SplatData {\n const positions = new Float32Array(packed.count * 3);\n const colors = new Uint8Array(packed.count * 4);\n const covariances = new Float32Array(packed.count * 6);\n const positionView = new DataView(\n packed.positions.buffer,\n packed.positions.byteOffset,\n packed.positions.byteLength,\n );\n const rotationStride = packed.version >= 3 ? 4 : 3;\n\n for (let i = 0; i < packed.count; i++) {\n for (let axis = 0; axis < 3; axis++) {\n positions[i * 3 + axis] =\n packed.version === 1\n ? halfToFloat(positionView.getUint16((i * 3 + axis) * 2, true))\n : readSigned24(packed.positions, (i * 3 + axis) * 3) / 2 ** packed.fractionalBits;\n }\n\n for (let channel = 0; channel < 3; channel++) {\n const encoded = packed.colors[i * 3 + channel] as number;\n const linear = ((encoded / 255 - 0.5) / COLOR_SCALE) * SH_C0 + 0.5;\n colors[i * 4 + channel] = clampByte(Math.round(linear * 255));\n }\n colors[i * 4 + 3] = packed.alphas[i] as number;\n\n const quaternion =\n packed.version >= 3\n ? decodeSmallestThree(packed.rotations, i * rotationStride)\n : decodeFirstThree(packed.rotations, i * rotationStride);\n writeCovariance(\n covariances,\n i,\n Math.exp((packed.scales[i * 3 + 0] as number) / 16 - 10),\n Math.exp((packed.scales[i * 3 + 1] as number) / 16 - 10),\n Math.exp((packed.scales[i * 3 + 2] as number) / 16 - 10),\n quaternion[3],\n quaternion[0],\n quaternion[1],\n quaternion[2],\n );\n }\n\n const shPacked = decodeSpzSh(packed);\n return {\n count: packed.count,\n positions,\n colors,\n covariances,\n ...(shPacked ? { shPacked } : {}),\n };\n}\n\n/** Dequantizes SPZ SH bytes into per-splat packed form (coefficient-major RGB). */\nfunction decodeSpzSh(packed: PackedSpz): SplatData['shPacked'] {\n if (!packed.sh || packed.shDegree === 0) return undefined;\n const bands = Math.min(packed.shDegree, MAX_RENDER_SH_BANDS) as 1 | 2 | 3;\n const coeffs = shCoefficientCount(bands);\n const components = coeffs * 3;\n const coefficients = new Float32Array(packed.count * components);\n for (let i = 0; i < packed.count; i++) {\n const source = i * shDimension(packed.shDegree) * 3;\n const dest = i * components;\n for (let c = 0; c < components; c++) {\n const encoded = packed.sh[source + c] as number;\n coefficients[dest + c] = (encoded - 128) / 128;\n }\n }\n return packShCoefficients(coefficients, packed.count, bands);\n}\n\nasync function decompressGzip(bytes: Uint8Array): Promise<Uint8Array> {\n if (typeof DecompressionStream === 'undefined') {\n throw new Error(\n 'This runtime does not provide gzip decompression required for legacy SPZ files.',\n );\n }\n const copy = new Uint8Array(bytes.byteLength);\n copy.set(bytes);\n const stream = new Blob([copy.buffer]).stream().pipeThrough(new DecompressionStream('gzip'));\n // Read incrementally with an expansion cap, so a gzip bomb fails fast\n // instead of expanding unbounded before the payload size checks run.\n const reader = stream.getReader();\n const chunks: Uint8Array[] = [];\n let total = 0;\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n total += value.byteLength;\n if (total > MAX_LEGACY_DECOMPRESSED_BYTES) {\n await reader.cancel().catch(() => undefined);\n throw new Error('SPZ gzip payload expands past the supported maximum size.');\n }\n chunks.push(value);\n }\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return out;\n}\n\nfunction validateSpzHeader(count: number, shDegree: number, fractionalBits: number): void {\n if (count === 0 || count > MAX_SAFE_POINTS) throw new Error(`Invalid SPZ point count ${count}.`);\n if (shDegree < 0 || shDegree > 4) throw new Error(`Unsupported SPZ SH degree ${shDegree}.`);\n if (fractionalBits > 24) throw new Error(`Invalid SPZ fractional bit count ${fractionalBits}.`);\n}\n\nfunction shDimension(degree: number): number {\n return degree === 0 ? 0 : degree === 1 ? 3 : degree === 2 ? 8 : degree === 3 ? 15 : 24;\n}\n\nfunction safeUint64(view: DataView, offset: number): number {\n const value = view.getBigUint64(offset, true);\n if (value > BigInt(Number.MAX_SAFE_INTEGER))\n throw new Error('SPZ stream size exceeds JavaScript limits.');\n return Number(value);\n}\n\nfunction validateExtensions(view: DataView, start: number, end: number): void {\n if (start > end || end > view.byteLength) throw new Error('Invalid SPZ extension block.');\n let offset = start;\n while (offset < end) {\n if (offset + 8 > end) throw new Error('Truncated SPZ extension header.');\n const type = view.getUint32(offset, true);\n const length = view.getUint32(offset + 4, true);\n offset += 8;\n if (offset + length > end) throw new Error('Truncated SPZ extension payload.');\n if (type === 0xadbe0003) {\n if (length !== 4) throw new Error('Invalid SPZ coordinate-system extension.');\n const coordinateSystem = view.getUint32(offset, true);\n if (coordinateSystem !== 4) {\n throw new Error(\n `SPZ coordinate system ${coordinateSystem} is not supported; expected RUB (4).`,\n );\n }\n }\n offset += length;\n }\n}\n\nfunction readSigned24(bytes: Uint8Array, offset: number): number {\n const value =\n (bytes[offset] as number) |\n ((bytes[offset + 1] as number) << 8) |\n ((bytes[offset + 2] as number) << 16);\n return value & 0x800000 ? value | 0xff000000 : value;\n}\n\nfunction decodeFirstThree(\n bytes: Uint8Array,\n offset: number,\n): readonly [number, number, number, number] {\n const x = (bytes[offset] as number) / 127.5 - 1;\n const y = (bytes[offset + 1] as number) / 127.5 - 1;\n const z = (bytes[offset + 2] as number) / 127.5 - 1;\n return [x, y, z, Math.sqrt(Math.max(0, 1 - x * x - y * y - z * z))];\n}\n\nfunction decodeSmallestThree(\n bytes: Uint8Array,\n offset: number,\n): readonly [number, number, number, number] {\n let packed =\n ((bytes[offset] as number) |\n ((bytes[offset + 1] as number) << 8) |\n ((bytes[offset + 2] as number) << 16) |\n ((bytes[offset + 3] as number) << 24)) >>>\n 0;\n const largest = packed >>> 30;\n const quaternion = [0, 0, 0, 0];\n let sumSquares = 0;\n for (let component = 3; component >= 0; component--) {\n if (component === largest) continue;\n const magnitude = packed & 0x1ff;\n const sign = (packed >>> 9) & 1 ? -1 : 1;\n const value = sign * SQRT_HALF * (magnitude / 0x1ff);\n quaternion[component] = value;\n sumSquares += value * value;\n packed >>>= 10;\n }\n quaternion[largest] = Math.sqrt(Math.max(0, 1 - sumSquares));\n return [\n quaternion[0] as number,\n quaternion[1] as number,\n quaternion[2] as number,\n quaternion[3] as number,\n ];\n}\n\nfunction clampByte(value: number): number {\n return Math.max(0, Math.min(255, value));\n}\n"],"names":["SPZ_MAGIC","LEGACY_HEADER_BYTES","VERSION_4_HEADER_BYTES","COLOR_SCALE","SQRT_HALF","MAX_SAFE_POINTS","MAX_LEGACY_DECOMPRESSED_BYTES","MAX_RENDER_SH_BANDS","parseSpz","buffer","bytes","packed","parseLegacySpz","decompressGzip","parseVersion4Spz","unpackSpz","view","version","count","shDegree","fractionalBits","flags","streamCount","tocOffset","validateSpzHeader","validateExtensions","rotationBytes","expectedSizes","shDimension","tocEnd","decoder","ZSTDDecoder","streams","compressedOffset","i","entry","compressedSize","safeUint64","uncompressedSize","compressed","decoded","positionBytes","shBytes","expectedBytes","offset","take","length","result","positions","colors","covariances","positionView","rotationStride","axis","halfToFloat","readSigned24","channel","linear","SH_C0","clampByte","quaternion","decodeSmallestThree","decodeFirstThree","writeCovariance","shPacked","decodeSpzSh","bands","components","shCoefficientCount","coefficients","source","dest","c","encoded","packShCoefficients","copy","reader","chunks","total","done","value","out","chunk","degree","start","end","type","coordinateSystem","x","y","z","largest","sumSquares","component","magnitude"],"mappings":"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"}