@voluma/vlam 0.3.0 → 0.3.4
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 +2 -4
- package/dist/chunk-loader-DlNwggfG.js.map +1 -1
- package/dist/{index.d.ts → core/index.d.ts} +2 -2
- package/dist/{splat-budget.d.ts → core/splat-budget.d.ts} +1 -1
- package/dist/{splat-data.d.ts → core/splat-data.d.ts} +1 -1
- package/dist/{splat-mesh-types.d.ts → core/splat-mesh-types.d.ts} +5 -2
- package/dist/{effects.d.ts → effects/index.d.ts} +3 -3
- package/dist/effects.js.map +1 -1
- package/dist/formats/ksplat/parse-ksplat.d.ts +1 -1
- package/dist/formats/ksplat.js.map +1 -1
- package/dist/formats/lcc/collision-mesh.d.ts +2 -2
- package/dist/formats/lcc/collision-partition.d.ts +1 -1
- package/dist/formats/lcc/lcc.d.ts +3 -3
- package/dist/formats/lcc/lcc2.d.ts +2 -2
- package/dist/formats/lcc/parse-lcc.d.ts +1 -1
- package/dist/formats/lcc.js +276 -277
- package/dist/formats/lcc.js.map +1 -1
- package/dist/formats/ply/parse-compressed-ply.d.ts +2 -2
- package/dist/formats/ply/parse-splat-ply.d.ts +2 -2
- package/dist/formats/ply.js.map +1 -1
- package/dist/formats/rad/frontier-worker-protocol.d.ts +8 -1
- package/dist/formats/rad/index.d.ts +2 -2
- package/dist/formats/rad/parse-rad.d.ts +2 -2
- package/dist/formats/rad/rad-foveated-source.d.ts +3 -3
- package/dist/formats/rad/rad.d.ts +5 -5
- package/dist/formats/rad.js.map +1 -1
- package/dist/formats/sog/parse-sog.d.ts +2 -2
- package/dist/formats/sog.js.map +1 -1
- package/dist/formats/splat/parse-splat.d.ts +1 -1
- package/dist/formats/splat.js.map +1 -1
- package/dist/formats/spz/parse-spz.d.ts +1 -1
- package/dist/formats/spz.js.map +1 -1
- package/dist/half-float-Cs4FDq8Q.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/{chunk-loader.d.ts → loaders/chunk-loader.d.ts} +2 -2
- package/dist/{load-splat-data.d.ts → loaders/load-splat-data.d.ts} +1 -1
- package/dist/{load-worker-protocol.d.ts → loaders/load-worker-protocol.d.ts} +2 -2
- package/dist/loaders.js.map +1 -1
- package/dist/loading-wLo8vRbA.js.map +1 -1
- package/dist/lod-scheduler-ChoZyEeq.js +466 -0
- package/dist/lod-scheduler-ChoZyEeq.js.map +1 -0
- package/dist/logging-BfPdd7NJ.js.map +1 -1
- package/dist/ply-header-B9WllbPg.js.map +1 -1
- package/dist/{radix-sorter-R0FCSqKu.js → radix-sorter-BrbUg_CV.js} +2 -2
- package/dist/radix-sorter-BrbUg_CV.js.map +1 -0
- package/dist/{relighting-CSQF3UcL.js → relighting-Tiwep8yd.js} +302 -294
- package/dist/relighting-Tiwep8yd.js.map +1 -0
- package/dist/{selection-volume.d.ts → selection/selection-volume.d.ts} +1 -1
- package/dist/{splat-partition.d.ts → selection/splat-partition.d.ts} +1 -1
- package/dist/selection.js.map +1 -1
- package/dist/sh-pack-D5wAe5gg.js.map +1 -1
- package/dist/splat-budget-DuVZspPQ.js.map +1 -1
- package/dist/splat-data-BZM5dhhx.js.map +1 -1
- package/dist/{splat-mesh-aQdcWA6v.js → splat-mesh-CsLOQb08.js} +3 -3
- package/dist/splat-mesh-CsLOQb08.js.map +1 -0
- package/dist/splat-mesh-types-BZIko-_9.js.map +1 -1
- package/dist/{static-lod-splat-mesh.d.ts → static-lod/static-lod-splat-mesh.d.ts} +2 -2
- package/dist/{static-lod-worker-protocol.d.ts → static-lod/static-lod-worker-protocol.d.ts} +1 -1
- package/dist/{static-lod.d.ts → static-lod/static-lod.d.ts} +1 -1
- package/dist/static-lod.js +1 -1
- package/dist/static-lod.js.map +1 -1
- package/dist/{dataset-source.d.ts → streaming/dataset-source.d.ts} +1 -1
- package/dist/{streaming.d.ts → streaming/index.d.ts} +3 -3
- package/dist/{lod-scheduler.d.ts → streaming/lod-scheduler.d.ts} +32 -0
- package/dist/{lod-source.d.ts → streaming/lod-source.d.ts} +19 -13
- package/dist/{streamed-splat-mesh-utils.d.ts → streaming/streamed-splat-mesh-utils.d.ts} +2 -2
- package/dist/{streamed-splat-mesh.d.ts → streaming/streamed-splat-mesh.d.ts} +83 -27
- package/dist/streaming.js +711 -645
- package/dist/streaming.js.map +1 -1
- package/dist/{unified-splat-mesh.d.ts → unified/unified-splat-mesh.d.ts} +13 -6
- package/dist/{work-buffer-gather.d.ts → unified/work-buffer-gather.d.ts} +5 -5
- package/dist/{work-buffer-material.d.ts → unified/work-buffer-material.d.ts} +1 -1
- package/dist/unified.js +280 -272
- package/dist/unified.js.map +1 -1
- package/dist/webgpu-limits-Y3CI_6_6.js.map +1 -1
- package/package.json +9 -9
- package/dist/lod-scheduler-B0a_uBlv.js +0 -383
- package/dist/lod-scheduler-B0a_uBlv.js.map +0 -1
- package/dist/radix-sorter-R0FCSqKu.js.map +0 -1
- package/dist/relighting-CSQF3UcL.js.map +0 -1
- package/dist/splat-mesh-aQdcWA6v.js.map +0 -1
- /package/dist/{compute-sorter.d.ts → core/compute-sorter.d.ts} +0 -0
- /package/dist/{create-splat-renderer.d.ts → core/create-splat-renderer.d.ts} +0 -0
- /package/dist/{depth-of-field.d.ts → core/depth-of-field.d.ts} +0 -0
- /package/dist/{half-float.d.ts → core/half-float.d.ts} +0 -0
- /package/dist/{logging.d.ts → core/logging.d.ts} +0 -0
- /package/dist/{merged-splat-mesh.d.ts → core/merged-splat-mesh.d.ts} +0 -0
- /package/dist/{orientation.d.ts → core/orientation.d.ts} +0 -0
- /package/dist/{radix-sort.d.ts → core/radix-sort.d.ts} +0 -0
- /package/dist/{radix-sorter.d.ts → core/radix-sorter.d.ts} +0 -0
- /package/dist/{relighting-pass.d.ts → core/relighting-pass.d.ts} +0 -0
- /package/dist/{relighting.d.ts → core/relighting.d.ts} +0 -0
- /package/dist/{sh-pack.d.ts → core/sh-pack.d.ts} +0 -0
- /package/dist/{sort-scheduler.d.ts → core/sort-scheduler.d.ts} +0 -0
- /package/dist/{sort-worker-protocol.d.ts → core/sort-worker-protocol.d.ts} +0 -0
- /package/dist/{sorter.d.ts → core/sorter.d.ts} +0 -0
- /package/dist/{source-transform.d.ts → core/source-transform.d.ts} +0 -0
- /package/dist/{splat-depth-pack.d.ts → core/splat-depth-pack.d.ts} +0 -0
- /package/dist/{splat-mesh-material.d.ts → core/splat-mesh-material.d.ts} +0 -0
- /package/dist/{splat-mesh-picking.d.ts → core/splat-mesh-picking.d.ts} +0 -0
- /package/dist/{splat-mesh-pool.d.ts → core/splat-mesh-pool.d.ts} +0 -0
- /package/dist/{splat-mesh.d.ts → core/splat-mesh.d.ts} +0 -0
- /package/dist/{splat-modifier-stack.d.ts → core/splat-modifier-stack.d.ts} +0 -0
- /package/dist/{splat-modifier.d.ts → core/splat-modifier.d.ts} +0 -0
- /package/dist/{splat-query.d.ts → core/splat-query.d.ts} +0 -0
- /package/dist/{splat-sort-bounds.d.ts → core/splat-sort-bounds.d.ts} +0 -0
- /package/dist/{storage-attribute-mirror.d.ts → core/storage-attribute-mirror.d.ts} +0 -0
- /package/dist/{webgpu-limits.d.ts → core/webgpu-limits.d.ts} +0 -0
- /package/dist/{worker-sorter.d.ts → core/worker-sorter.d.ts} +0 -0
- /package/dist/{xr-view.d.ts → core/xr-view.d.ts} +0 -0
- /package/dist/{loaders.d.ts → loaders/index.d.ts} +0 -0
- /package/dist/{loading.d.ts → loaders/loading.d.ts} +0 -0
- /package/dist/{ply-header.d.ts → loaders/ply-header.d.ts} +0 -0
- /package/dist/{selection.d.ts → selection/index.d.ts} +0 -0
- /package/dist/{static-lod-entry.d.ts → static-lod/index.d.ts} +0 -0
- /package/dist/{budget-governor.d.ts → streaming/budget-governor.d.ts} +0 -0
- /package/dist/{camera-budget-governor.d.ts → streaming/camera-budget-governor.d.ts} +0 -0
- /package/dist/{chunk-cache-budget.d.ts → streaming/chunk-cache-budget.d.ts} +0 -0
- /package/dist/{chunk-fetch-scheduler.d.ts → streaming/chunk-fetch-scheduler.d.ts} +0 -0
- /package/dist/{lod-manifest.d.ts → streaming/lod-manifest.d.ts} +0 -0
- /package/dist/{unified.d.ts → unified/index.d.ts} +0 -0
- /package/dist/{unified-work-buffer.d.ts → unified/unified-work-buffer.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -13,8 +13,6 @@ A WebGPU Gaussian Splat viewer for three.js.
|
|
|
13
13
|
|
|
14
14
|
Local site and generated API: `npm run dev` (http://localhost:5170, viewer at `/demo/`).
|
|
15
15
|
|
|
16
|
-
> Package `@voluma/vlam`, public **0.2.0**. APIs may still move before 1.0.
|
|
17
|
-
|
|
18
16
|
## Important notice!
|
|
19
17
|
This is a pre-release not yet recommended for production. The API can still change in breaking ways before v1.0.
|
|
20
18
|
|
|
@@ -53,7 +51,7 @@ renderer.setAnimationLoop(() => {
|
|
|
53
51
|
```
|
|
54
52
|
|
|
55
53
|
|
|
56
|
-
Supported formats: `.sog`, `.ply`, `.spz`, `.splat`, `.ksplat`, `.lcc`, `.lcc2`, `.rad
|
|
54
|
+
Supported formats: `.sog`, `.ply`, `.spz`, `.splat`, `.ksplat`, `.lcc`, `.lcc2`, `.rad`.
|
|
57
55
|
|
|
58
56
|
|
|
59
57
|
## Contributing
|
|
@@ -64,4 +62,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) and [docs/architecture.md](docs/architect
|
|
|
64
62
|
|
|
65
63
|
[MIT](LICENSE). Third-party notices: [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
|
66
64
|
|
|
67
|
-
Made
|
|
65
|
+
Made by [Voluma](https://voluma.ai).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-loader-DlNwggfG.js","sources":["../src/lib/chunk-loader.ts"],"sourcesContent":["import type { SplatData } from './splat-data';\nimport type {\n LoadWorkerRequest,\n LoadWorkerResponse,\n LoadWorkerSource,\n RadChunkRangeRequest,\n} from './load-worker-protocol';\nimport type { LccChunkParams } from './formats/lcc/parse-lcc';\nimport {\n createAbortError,\n deserializeSplatLoadError,\n resolveSplatUrl,\n SplatLoadError,\n splatFormatForExtension,\n splatNameExtension,\n splatUrlExtension,\n type ChunkFileFormat,\n type SplatDataFileLoadOptions,\n type SplatFormat,\n type SplatInputOptions,\n type SplatDataFormat,\n type SplatProgressCallback,\n} from './loading';\n// Inlined worker (blob URL): survives library bundling in any consumer\n// setup, unlike an asset file referenced via `new URL(...)`.\nimport LoadWorker from './load-worker?worker&inline';\n\n/**\n * Formats served by the one-shot worker rather than the streaming one.\n *\n * Both workers are inlined, so their parsers are string literals no bundler can\n * tree-shake. Keeping these three behind a dynamic import is what moves SPZ's\n * ~39 KB ZSTD wasm blob out of the main entry and into a chunk that loads only\n * when one of these formats is actually opened.\n */\nconst ONE_SHOT_FORMATS = new Set<ChunkFileFormat>(['spz', 'splat', 'ksplat']);\n\n/**\n * One worker plus the requests in flight on it.\n *\n * `ChunkLoader` runs two of these - see {@link ChunkLoader} - so the request\n * bookkeeping, cancellation and terminal-failure handling live here once.\n */\nclass WorkerClient {\n private nextRequestId = 0;\n private readonly pending = new Map<\n number,\n {\n resolve: (data: SplatData) => void;\n reject: (error: Error) => void;\n removeAbortListener?: () => void;\n onProgress?: SplatProgressCallback;\n }\n >();\n private disposed = false;\n /** Terminal worker failure; subsequent loads reject instead of hanging. */\n private workerFailure: SplatLoadError | null = null;\n\n constructor(private readonly worker: Worker) {\n this.worker.onmessage = (event: MessageEvent<LoadWorkerResponse>) => {\n const request = this.pending.get(event.data.id);\n if (!request) return;\n if (event.data.type === 'progress') {\n // Progress does not settle the request; the result still follows.\n request.onProgress?.(event.data.loaded, event.data.total);\n return;\n }\n this.pending.delete(event.data.id);\n request.removeAbortListener?.();\n if (event.data.ok) {\n request.resolve(event.data.data);\n } else if (event.data.cancelled) {\n request.reject(createAbortError('Chunk load was cancelled.'));\n } else {\n request.reject(deserializeSplatLoadError(event.data.error));\n }\n };\n // A worker-level failure (a CSP that blocks blob: workers is the common\n // one in real deployments) must still honour the documented contract:\n // every rejection is a SplatLoadError or an AbortError, never a bare Error.\n // There is no per-request URL here - the worker itself died - so `url` is\n // empty and the failure is not retryable: retrying constructs the same\n // worker under the same policy.\n this.worker.onerror = (event?: ErrorEvent | Event) => {\n if (this.workerFailure) return;\n const detail =\n event &&\n typeof event === 'object' &&\n 'message' in event &&\n typeof event.message === 'string'\n ? event.message\n : '';\n const failure = new SplatLoadError(\n detail ? `Chunk loading worker failed. ${detail}` : 'Chunk loading worker failed.',\n { phase: 'worker', url: '', retryable: false },\n );\n this.workerFailure = failure;\n this.worker.terminate();\n this.rejectAll(failure);\n };\n }\n\n /** Posts one load to this worker and tracks its pending promise. */\n request(\n message: Omit<Extract<LoadWorkerRequest, { type: 'load' }>, 'id'>,\n signal: AbortSignal | undefined,\n onProgress?: SplatProgressCallback,\n ): Promise<SplatData> {\n if (this.disposed) return Promise.reject(createAbortError('ChunkLoader has been disposed.'));\n if (signal?.aborted) return Promise.reject(createAbortError('Chunk load was cancelled.'));\n if (this.workerFailure) return Promise.reject(this.workerFailure);\n\n const id = this.nextRequestId++;\n const promise = new Promise<SplatData>((resolve, reject) => {\n this.pending.set(id, { resolve, reject, ...(onProgress ? { onProgress } : {}) });\n });\n if (signal) {\n // Settle immediately on abort rather than waiting for the worker's\n // acknowledgement: the worker may be deep in a decode (or may already\n // have posted a success that is still in the message queue), and a\n // caller that cancelled must neither receive that stale result nor see\n // further progress callbacks. The late reply finds no pending entry and\n // is dropped in `onmessage`.\n const onAbort = () => {\n const pending = this.pending.get(id);\n if (!pending) return;\n this.pending.delete(id);\n const cancel: LoadWorkerRequest = { type: 'cancel', id };\n if (!this.disposed) this.worker.postMessage(cancel);\n pending.reject(createAbortError('Chunk load was cancelled.'));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n const pending = this.pending.get(id);\n if (pending) pending.removeAbortListener = () => signal.removeEventListener('abort', onAbort);\n }\n\n const request: LoadWorkerRequest = { ...message, id };\n this.worker.postMessage(request);\n return promise;\n }\n\n /** Terminates this worker and rejects its in-flight requests. */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n this.worker.terminate();\n this.rejectAll(createAbortError('ChunkLoader has been disposed.'));\n }\n\n private rejectAll(error: Error): void {\n for (const request of this.pending.values()) {\n request.removeAbortListener?.();\n request.reject(error);\n }\n this.pending.clear();\n }\n}\n\n/**\n * Client for the persistent scene/chunk loading workers. Multiple requests\n * may be in flight; each can be cancelled through an `AbortSignal`, which\n * aborts the worker-side fetches (useful when the camera has moved on\n * before a chunk arrived).\n *\n * There are two workers behind this one class. The streaming worker is\n * constructed eagerly and handles everything the LOD paths need; the one-shot\n * worker ({@link ONE_SHOT_FORMATS}) is constructed the first time a `.spz`,\n * `.splat` or `.ksplat` is requested. Both decode off the main thread - the\n * split exists only to keep the one-shot parsers out of the published entry.\n *\n * @example\n * const loader = new ChunkLoader();\n * const chunk = await loader.load('/scene/0_0/', { kind: 'directory' });\n */\nexport class ChunkLoader {\n private readonly streaming = new WorkerClient(new LoadWorker());\n /** The in-flight construction; `null` until a one-shot format is requested. */\n private oneShot: Promise<WorkerClient> | null = null;\n /** The constructed one-shot client, so `dispose` does not have to await. */\n private oneShotClient: WorkerClient | null = null;\n private disposed = false;\n\n /**\n * Fetches and decodes one scene file or chunk directory.\n *\n * @param url - Scene URL, absolute or relative to the page.\n * @param options.kind - 'file' (default) selects a parser by extension;\n * 'directory' decodes an unbundled SOG chunk directory.\n * @param options.format - Explicit parser; `lcc-bin` additionally needs\n * `options.lcc` to say which byte range of the file to fetch.\n * @param options.signal - Abort signal for cancellation; the returned\n * promise then rejects with a `DOMException` named `AbortError`.\n * @param options.lcc - Byte-range plumbing for `lcc-bin`. This path exists\n * for {@link StreamedSplatMesh} and is **not** part of the stable surface;\n * the type is nameable as `LccChunkParams` on `@voluma/vlam/formats/lcc`.\n * @param options.rad - Byte-range plumbing for `rad-chunk`, on the same\n * footing; the type is `RadChunkRangeRequest` on `@voluma/vlam/formats/rad`.\n * @throws Rejects only with {@link SplatLoadError} (resolve, fetch, decode or\n * worker failures - `error.phase` says which; a worker that cannot start at\n * all, e.g. under a CSP that blocks `blob:` workers, reports\n * `phase: 'worker'`) or an `Error` named `AbortError` (cancellation or\n * {@link dispose}).\n */\n // `async` so a URL or format that will not resolve rejects the returned\n // promise rather than throwing synchronously into the caller.\n async load(\n url: string,\n options: SplatInputOptions & {\n kind?: 'file' | 'directory';\n format?: SplatFormat | 'lcc-bin' | 'rad-chunk';\n lcc?: LccChunkParams;\n rad?: RadChunkRangeRequest;\n sog?: { packShBands: 1 | 2 | 3 };\n files?: Readonly<Record<string, string>>;\n } = {},\n ): Promise<SplatData> {\n const kind = options.kind ?? 'file';\n // The worker runs from an inlined blob/data URL, where relative and\n // root-relative URLs cannot be resolved - make the URL absolute here.\n const absoluteUrl = resolveSplatUrl(url, options.baseUrl).href;\n const format = resolveFileFormat(absoluteUrl, kind, options.format ?? 'auto');\n const data = await this.request(\n {\n from: 'url',\n url: absoluteUrl,\n kind,\n ...(options.request ? { request: options.request } : {}),\n },\n format,\n options.signal,\n options.lcc,\n options.rad,\n options.files,\n options.sog,\n options.onProgress,\n );\n return withSourceFormat(data, format);\n }\n\n /**\n * Decodes a local file (from a file input or a drop) in the worker - the\n * bytes are read worker-side, so a multi-million-splat decode still never\n * blocks the main thread.\n *\n * Only self-contained files are supported: `.ply`, `.spz`, `.splat`,\n * `.ksplat` and bundled `.sog`. An unbundled SOG directory needs sibling\n * fetches and so requires {@link load} with `kind: 'directory'`.\n *\n * @param file - The file; its name selects the parser unless `format` is given.\n * @throws Rejects only with {@link SplatLoadError} (unknown extension, read\n * or decode failures) or an `Error` named `AbortError` (cancellation or\n * {@link dispose}).\n */\n // `async` so an unknown extension rejects the returned promise rather than\n // throwing synchronously into the caller.\n async loadFile(file: File, options: SplatDataFileLoadOptions = {}): Promise<SplatData> {\n const requested = options.format ?? 'auto';\n const format: Exclude<SplatFormat, 'auto'> =\n requested === 'auto'\n ? splatFormatForExtension(splatNameExtension(file.name), file.name)\n : requested;\n const data = await this.request(\n { from: 'file', file },\n format,\n options.signal,\n undefined,\n undefined,\n undefined,\n undefined,\n options.onProgress,\n );\n return withSourceFormat(data, format);\n }\n\n /** Routes one load to the worker that owns its format. */\n private request(\n source: LoadWorkerSource,\n format: ChunkFileFormat,\n signal: AbortSignal | undefined,\n lcc?: LccChunkParams,\n rad?: RadChunkRangeRequest,\n files?: Readonly<Record<string, string>>,\n sog?: { packShBands: 1 | 2 | 3 },\n onProgress?: SplatProgressCallback,\n ): Promise<SplatData> {\n if (this.disposed) return Promise.reject(createAbortError('ChunkLoader has been disposed.'));\n if (signal?.aborted) return Promise.reject(createAbortError('Chunk load was cancelled.'));\n\n const message: Omit<Extract<LoadWorkerRequest, { type: 'load' }>, 'id'> = {\n type: 'load',\n source,\n format,\n ...(lcc ? { lcc } : {}),\n ...(rad ? { rad } : {}),\n ...(sog ? { sog } : {}),\n ...(files ? { files } : {}),\n ...(onProgress ? { progress: true } : {}),\n };\n\n const client = this.clientFor(format);\n // Deliberately not `async`: for a streaming format the client is already\n // there, and the request must reach the worker in the same task the caller\n // made it - a chunk scheduler that posts and then aborts within one frame\n // depends on that ordering. Only the one-shot path, which cannot avoid\n // awaiting its chunk, defers.\n if (!(client instanceof Promise)) return client.request(message, signal, onProgress);\n return client.then((resolved) => {\n // Re-checked because disposal or abort may have happened while the\n // one-shot chunk was in flight, before any listener was registered.\n if (this.disposed) throw createAbortError('ChunkLoader has been disposed.');\n return resolved.request(message, signal, onProgress);\n });\n }\n\n /**\n * The worker for `format`, constructing the one-shot worker on first need.\n *\n * The in-flight promise is cached, not just the resolved client, so two\n * concurrent `.spz` loads share one worker instead of racing to build two.\n */\n private clientFor(format: ChunkFileFormat): WorkerClient | Promise<WorkerClient> {\n if (!ONE_SHOT_FORMATS.has(format)) return this.streaming;\n if (this.oneShotClient) return this.oneShotClient;\n this.oneShot ??= import('./one-shot-worker?worker&inline').then(\n ({ default: OneShotWorker }) => {\n const client = new WorkerClient(new OneShotWorker());\n this.oneShotClient = client;\n // `dispose()` may have run while the chunk was in flight; it could not\n // terminate a worker that did not exist yet, so honour it here.\n if (this.disposed) client.dispose();\n return client;\n },\n );\n return this.oneShot;\n }\n\n /** Terminates both workers and rejects all in-flight requests. */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n this.streaming.dispose();\n // A one-shot worker still being imported is terminated in `clientFor`,\n // which sees `disposed` when it finally constructs it.\n this.oneShotClient?.dispose();\n }\n}\n\n/** Self-contained scene formats {@link SplatData.format} can hold. */\nconst SOURCE_FORMATS: readonly SplatDataFormat[] = ['ply', 'spz', 'splat', 'ksplat', 'sog', 'rad'];\n\n/**\n * Stamps the resolved format onto decoded scene data so {@link SplatMesh} can\n * pick its `orientation: 'y-up'` correction. Chunk-only formats (`lcc-bin`,\n * `rad-chunk`) carry no self-contained frame, so they are left unstamped.\n */\nfunction withSourceFormat(data: SplatData, format: ChunkFileFormat): SplatData {\n return (SOURCE_FORMATS as readonly string[]).includes(format)\n ? { ...data, format: format as SplatDataFormat }\n : data;\n}\n\nfunction resolveFileFormat(\n url: string,\n kind: 'file' | 'directory',\n format: SplatFormat | 'lcc-bin' | 'rad-chunk',\n): ChunkFileFormat {\n // An LCC or RAD chunk is a byte range of a shared file, so its extension says\n // nothing about the parser - the caller's choice is authoritative.\n if (format === 'lcc-bin' || format === 'rad-chunk') return format;\n if (kind === 'directory') return 'sog';\n if (format !== 'auto') return format;\n return splatFormatForExtension(splatUrlExtension(new URL(url)), url);\n}\n"],"names":["ONE_SHOT_FORMATS","WorkerClient","worker","__publicField","event","request","_a","_b","createAbortError","deserializeSplatLoadError","detail","failure","SplatLoadError","message","signal","onProgress","id","promise","resolve","reject","onAbort","pending","cancel","error","ChunkLoader","LoadWorker","url","options","kind","absoluteUrl","resolveSplatUrl","format","resolveFileFormat","data","withSourceFormat","file","requested","splatFormatForExtension","splatNameExtension","source","lcc","rad","files","sog","client","resolved","OneShotWorker","SOURCE_FORMATS","splatUrlExtension"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAMA,IAAmB,oBAAI,IAAqB,CAAC,OAAO,SAAS,QAAQ,CAAC;AAQ5E,MAAMC,EAAa;AAAA,EAejB,YAA6BC,GAAgB;AAdrC,IAAAC,EAAA,uBAAgB;AACP,IAAAA,EAAA,qCAAc,IAAA;AASvB,IAAAA,EAAA,kBAAW;AAEX;AAAA,IAAAA,EAAA,uBAAuC;AAElB,SAAA,SAAAD,GAC3B,KAAK,OAAO,YAAY,CAACE,MAA4C;;AACnE,YAAMC,IAAU,KAAK,QAAQ,IAAID,EAAM,KAAK,EAAE;AAC9C,UAAKC,GACL;AAAA,YAAID,EAAM,KAAK,SAAS,YAAY;AAElC,WAAAE,IAAAD,EAAQ,eAAR,QAAAC,EAAA,KAAAD,GAAqBD,EAAM,KAAK,QAAQA,EAAM,KAAK;AACnD;AAAA,QACF;AACA,aAAK,QAAQ,OAAOA,EAAM,KAAK,EAAE,IACjCG,IAAAF,EAAQ,wBAAR,QAAAE,EAAA,KAAAF,IACID,EAAM,KAAK,KACbC,EAAQ,QAAQD,EAAM,KAAK,IAAI,IACtBA,EAAM,KAAK,YACpBC,EAAQ,OAAOG,EAAiB,2BAA2B,CAAC,IAE5DH,EAAQ,OAAOI,EAA0BL,EAAM,KAAK,KAAK,CAAC;AAAA;AAAA,IAE9D,GAOA,KAAK,OAAO,UAAU,CAACA,MAA+B;AACpD,UAAI,KAAK,cAAe;AACxB,YAAMM,IACJN,KACA,OAAOA,KAAU,YACjB,aAAaA,KACb,OAAOA,EAAM,WAAY,WACrBA,EAAM,UACN,IACAO,IAAU,IAAIC;AAAA,QAClBF,IAAS,gCAAgCA,CAAM,KAAK;AAAA,QACpD,EAAE,OAAO,UAAU,KAAK,IAAI,WAAW,GAAA;AAAA,MAAM;AAE/C,WAAK,gBAAgBC,GACrB,KAAK,OAAO,UAAA,GACZ,KAAK,UAAUA,CAAO;AAAA,IACxB;AAAA,EACF;AAAA;AAAA,EAGA,QACEE,GACAC,GACAC,GACoB;AACpB,QAAI,KAAK,SAAU,QAAO,QAAQ,OAAOP,EAAiB,gCAAgC,CAAC;AAC3F,QAAIM,KAAA,QAAAA,EAAQ,QAAS,QAAO,QAAQ,OAAON,EAAiB,2BAA2B,CAAC;AACxF,QAAI,KAAK,cAAe,QAAO,QAAQ,OAAO,KAAK,aAAa;AAEhE,UAAMQ,IAAK,KAAK,iBACVC,IAAU,IAAI,QAAmB,CAACC,GAASC,MAAW;AAC1D,WAAK,QAAQ,IAAIH,GAAI,EAAE,SAAAE,GAAS,QAAAC,GAAQ,GAAIJ,IAAa,EAAE,YAAAA,EAAA,IAAe,CAAA,GAAK;AAAA,IACjF,CAAC;AACD,QAAID,GAAQ;AAOV,YAAMM,IAAU,MAAM;AACpB,cAAMC,IAAU,KAAK,QAAQ,IAAIL,CAAE;AACnC,YAAI,CAACK,EAAS;AACd,aAAK,QAAQ,OAAOL,CAAE;AACtB,cAAMM,IAA4B,EAAE,MAAM,UAAU,IAAAN,EAAA;AACpD,QAAK,KAAK,YAAU,KAAK,OAAO,YAAYM,CAAM,GAClDD,EAAQ,OAAOb,EAAiB,2BAA2B,CAAC;AAAA,MAC9D;AACA,MAAAM,EAAO,iBAAiB,SAASM,GAAS,EAAE,MAAM,IAAM;AACxD,YAAMC,IAAU,KAAK,QAAQ,IAAIL,CAAE;AACnC,MAAIK,MAASA,EAAQ,sBAAsB,MAAMP,EAAO,oBAAoB,SAASM,CAAO;AAAA,IAC9F;AAEA,UAAMf,IAA6B,EAAE,GAAGQ,GAAS,IAAAG,EAAA;AACjD,gBAAK,OAAO,YAAYX,CAAO,GACxBY;AAAA,EACT;AAAA;AAAA,EAGA,UAAgB;AACd,IAAI,KAAK,aACT,KAAK,WAAW,IAChB,KAAK,OAAO,UAAA,GACZ,KAAK,UAAUT,EAAiB,gCAAgC,CAAC;AAAA,EACnE;AAAA,EAEQ,UAAUe,GAAoB;;AACpC,eAAWlB,KAAW,KAAK,QAAQ,OAAA;AACjC,OAAAC,IAAAD,EAAQ,wBAAR,QAAAC,EAAA,KAAAD,IACAA,EAAQ,OAAOkB,CAAK;AAEtB,SAAK,QAAQ,MAAA;AAAA,EACf;AACF;AAkBO,MAAMC,EAAY;AAAA,EAAlB;AACY,IAAArB,EAAA,mBAAY,IAAIF,EAAa,IAAIwB,GAAY;AAEtD;AAAA,IAAAtB,EAAA,iBAAwC;AAExC;AAAA,IAAAA,EAAA,uBAAqC;AACrC,IAAAA,EAAA,kBAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyBnB,MAAM,KACJuB,GACAC,IAOI,IACgB;AACpB,UAAMC,IAAOD,EAAQ,QAAQ,QAGvBE,IAAcC,EAAgBJ,GAAKC,EAAQ,OAAO,EAAE,MACpDI,IAASC,EAAkBH,GAAaD,GAAMD,EAAQ,UAAU,MAAM,GACtEM,IAAO,MAAM,KAAK;AAAA,MACtB;AAAA,QACE,MAAM;AAAA,QACN,KAAKJ;AAAA,QACL,MAAAD;AAAA,QACA,GAAID,EAAQ,UAAU,EAAE,SAASA,EAAQ,QAAA,IAAY,CAAA;AAAA,MAAC;AAAA,MAExDI;AAAA,MACAJ,EAAQ;AAAA,MACRA,EAAQ;AAAA,MACRA,EAAQ;AAAA,MACRA,EAAQ;AAAA,MACRA,EAAQ;AAAA,MACRA,EAAQ;AAAA,IAAA;AAEV,WAAOO,EAAiBD,GAAMF,CAAM;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,SAASI,GAAYR,IAAoC,IAAwB;AACrF,UAAMS,IAAYT,EAAQ,UAAU,QAC9BI,IACJK,MAAc,SACVC,EAAwBC,EAAmBH,EAAK,IAAI,GAAGA,EAAK,IAAI,IAChEC,GACAH,IAAO,MAAM,KAAK;AAAA,MACtB,EAAE,MAAM,QAAQ,MAAAE,EAAA;AAAA,MAChBJ;AAAA,MACAJ,EAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACAA,EAAQ;AAAA,IAAA;AAEV,WAAOO,EAAiBD,GAAMF,CAAM;AAAA,EACtC;AAAA;AAAA,EAGQ,QACNQ,GACAR,GACAjB,GACA0B,GACAC,GACAC,GACAC,GACA5B,GACoB;AACpB,QAAI,KAAK,SAAU,QAAO,QAAQ,OAAOP,EAAiB,gCAAgC,CAAC;AAC3F,QAAIM,KAAA,QAAAA,EAAQ,QAAS,QAAO,QAAQ,OAAON,EAAiB,2BAA2B,CAAC;AAExF,UAAMK,IAAoE;AAAA,MACxE,MAAM;AAAA,MACN,QAAA0B;AAAA,MACA,QAAAR;AAAA,MACA,GAAIS,IAAM,EAAE,KAAAA,EAAA,IAAQ,CAAA;AAAA,MACpB,GAAIC,IAAM,EAAE,KAAAA,EAAA,IAAQ,CAAA;AAAA,MACpB,GAAIE,IAAM,EAAE,KAAAA,EAAA,IAAQ,CAAA;AAAA,MACpB,GAAID,IAAQ,EAAE,OAAAA,EAAA,IAAU,CAAA;AAAA,MACxB,GAAI3B,IAAa,EAAE,UAAU,OAAS,CAAA;AAAA,IAAC,GAGnC6B,IAAS,KAAK,UAAUb,CAAM;AAMpC,WAAMa,aAAkB,UACjBA,EAAO,KAAK,CAACC,MAAa;AAG/B,UAAI,KAAK,SAAU,OAAMrC,EAAiB,gCAAgC;AAC1E,aAAOqC,EAAS,QAAQhC,GAASC,GAAQC,CAAU;AAAA,IACrD,CAAC,IANwC6B,EAAO,QAAQ/B,GAASC,GAAQC,CAAU;AAAA,EAOrF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,UAAUgB,GAA+D;AAC/E,WAAK/B,EAAiB,IAAI+B,CAAM,IAC5B,KAAK,gBAAsB,KAAK,iBACpC,KAAK,YAAL,KAAK,UAAY,OAAO,+BAAiC,EAAE;AAAA,MACzD,CAAC,EAAE,SAASe,QAAoB;AAC9B,cAAMF,IAAS,IAAI3C,EAAa,IAAI6C,GAAe;AACnD,oBAAK,gBAAgBF,GAGjB,KAAK,YAAUA,EAAO,QAAA,GACnBA;AAAA,MACT;AAAA,IAAA,IAEK,KAAK,WAZ8B,KAAK;AAAA,EAajD;AAAA;AAAA,EAGA,UAAgB;;AACd,IAAI,KAAK,aACT,KAAK,WAAW,IAChB,KAAK,UAAU,QAAA,IAGftC,IAAA,KAAK,kBAAL,QAAAA,EAAoB;AAAA,EACtB;AACF;AAGA,MAAMyC,IAA6C,CAAC,OAAO,OAAO,SAAS,UAAU,OAAO,KAAK;AAOjG,SAASb,EAAiBD,GAAiBF,GAAoC;AAC7E,SAAQgB,EAAqC,SAAShB,CAAM,IACxD,EAAE,GAAGE,GAAM,QAAAF,MACXE;AACN;AAEA,SAASD,EACPN,GACAE,GACAG,GACiB;AAGjB,SAAIA,MAAW,aAAaA,MAAW,cAAoBA,IACvDH,MAAS,cAAoB,QAC7BG,MAAW,SAAeA,IACvBM,EAAwBW,EAAkB,IAAI,IAAItB,CAAG,CAAC,GAAGA,CAAG;AACrE;"}
|
|
1
|
+
{"version":3,"file":"chunk-loader-DlNwggfG.js","sources":["../src/lib/loaders/chunk-loader.ts"],"sourcesContent":["import type { SplatData } from '../core/splat-data';\nimport type {\n LoadWorkerRequest,\n LoadWorkerResponse,\n LoadWorkerSource,\n RadChunkRangeRequest,\n} from './load-worker-protocol';\nimport type { LccChunkParams } from '../formats/lcc/parse-lcc';\nimport {\n createAbortError,\n deserializeSplatLoadError,\n resolveSplatUrl,\n SplatLoadError,\n splatFormatForExtension,\n splatNameExtension,\n splatUrlExtension,\n type ChunkFileFormat,\n type SplatDataFileLoadOptions,\n type SplatFormat,\n type SplatInputOptions,\n type SplatDataFormat,\n type SplatProgressCallback,\n} from './loading';\n// Inlined worker (blob URL): survives library bundling in any consumer\n// setup, unlike an asset file referenced via `new URL(...)`.\nimport LoadWorker from './load-worker?worker&inline';\n\n/**\n * Formats served by the one-shot worker rather than the streaming one.\n *\n * Both workers are inlined, so their parsers are string literals no bundler can\n * tree-shake. Keeping these three behind a dynamic import is what moves SPZ's\n * ~39 KB ZSTD wasm blob out of the main entry and into a chunk that loads only\n * when one of these formats is actually opened.\n */\nconst ONE_SHOT_FORMATS = new Set<ChunkFileFormat>(['spz', 'splat', 'ksplat']);\n\n/**\n * One worker plus the requests in flight on it.\n *\n * `ChunkLoader` runs two of these - see {@link ChunkLoader} - so the request\n * bookkeeping, cancellation and terminal-failure handling live here once.\n */\nclass WorkerClient {\n private nextRequestId = 0;\n private readonly pending = new Map<\n number,\n {\n resolve: (data: SplatData) => void;\n reject: (error: Error) => void;\n removeAbortListener?: () => void;\n onProgress?: SplatProgressCallback;\n }\n >();\n private disposed = false;\n /** Terminal worker failure; subsequent loads reject instead of hanging. */\n private workerFailure: SplatLoadError | null = null;\n\n constructor(private readonly worker: Worker) {\n this.worker.onmessage = (event: MessageEvent<LoadWorkerResponse>) => {\n const request = this.pending.get(event.data.id);\n if (!request) return;\n if (event.data.type === 'progress') {\n // Progress does not settle the request; the result still follows.\n request.onProgress?.(event.data.loaded, event.data.total);\n return;\n }\n this.pending.delete(event.data.id);\n request.removeAbortListener?.();\n if (event.data.ok) {\n request.resolve(event.data.data);\n } else if (event.data.cancelled) {\n request.reject(createAbortError('Chunk load was cancelled.'));\n } else {\n request.reject(deserializeSplatLoadError(event.data.error));\n }\n };\n // A worker-level failure (a CSP that blocks blob: workers is the common\n // one in real deployments) must still honour the documented contract:\n // every rejection is a SplatLoadError or an AbortError, never a bare Error.\n // There is no per-request URL here - the worker itself died - so `url` is\n // empty and the failure is not retryable: retrying constructs the same\n // worker under the same policy.\n this.worker.onerror = (event?: ErrorEvent | Event) => {\n if (this.workerFailure) return;\n const detail =\n event &&\n typeof event === 'object' &&\n 'message' in event &&\n typeof event.message === 'string'\n ? event.message\n : '';\n const failure = new SplatLoadError(\n detail ? `Chunk loading worker failed. ${detail}` : 'Chunk loading worker failed.',\n { phase: 'worker', url: '', retryable: false },\n );\n this.workerFailure = failure;\n this.worker.terminate();\n this.rejectAll(failure);\n };\n }\n\n /** Posts one load to this worker and tracks its pending promise. */\n request(\n message: Omit<Extract<LoadWorkerRequest, { type: 'load' }>, 'id'>,\n signal: AbortSignal | undefined,\n onProgress?: SplatProgressCallback,\n ): Promise<SplatData> {\n if (this.disposed) return Promise.reject(createAbortError('ChunkLoader has been disposed.'));\n if (signal?.aborted) return Promise.reject(createAbortError('Chunk load was cancelled.'));\n if (this.workerFailure) return Promise.reject(this.workerFailure);\n\n const id = this.nextRequestId++;\n const promise = new Promise<SplatData>((resolve, reject) => {\n this.pending.set(id, { resolve, reject, ...(onProgress ? { onProgress } : {}) });\n });\n if (signal) {\n // Settle immediately on abort rather than waiting for the worker's\n // acknowledgement: the worker may be deep in a decode (or may already\n // have posted a success that is still in the message queue), and a\n // caller that cancelled must neither receive that stale result nor see\n // further progress callbacks. The late reply finds no pending entry and\n // is dropped in `onmessage`.\n const onAbort = () => {\n const pending = this.pending.get(id);\n if (!pending) return;\n this.pending.delete(id);\n const cancel: LoadWorkerRequest = { type: 'cancel', id };\n if (!this.disposed) this.worker.postMessage(cancel);\n pending.reject(createAbortError('Chunk load was cancelled.'));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n const pending = this.pending.get(id);\n if (pending) pending.removeAbortListener = () => signal.removeEventListener('abort', onAbort);\n }\n\n const request: LoadWorkerRequest = { ...message, id };\n this.worker.postMessage(request);\n return promise;\n }\n\n /** Terminates this worker and rejects its in-flight requests. */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n this.worker.terminate();\n this.rejectAll(createAbortError('ChunkLoader has been disposed.'));\n }\n\n private rejectAll(error: Error): void {\n for (const request of this.pending.values()) {\n request.removeAbortListener?.();\n request.reject(error);\n }\n this.pending.clear();\n }\n}\n\n/**\n * Client for the persistent scene/chunk loading workers. Multiple requests\n * may be in flight; each can be cancelled through an `AbortSignal`, which\n * aborts the worker-side fetches (useful when the camera has moved on\n * before a chunk arrived).\n *\n * There are two workers behind this one class. The streaming worker is\n * constructed eagerly and handles everything the LOD paths need; the one-shot\n * worker ({@link ONE_SHOT_FORMATS}) is constructed the first time a `.spz`,\n * `.splat` or `.ksplat` is requested. Both decode off the main thread - the\n * split exists only to keep the one-shot parsers out of the published entry.\n *\n * @example\n * const loader = new ChunkLoader();\n * const chunk = await loader.load('/scene/0_0/', { kind: 'directory' });\n */\nexport class ChunkLoader {\n private readonly streaming = new WorkerClient(new LoadWorker());\n /** The in-flight construction; `null` until a one-shot format is requested. */\n private oneShot: Promise<WorkerClient> | null = null;\n /** The constructed one-shot client, so `dispose` does not have to await. */\n private oneShotClient: WorkerClient | null = null;\n private disposed = false;\n\n /**\n * Fetches and decodes one scene file or chunk directory.\n *\n * @param url - Scene URL, absolute or relative to the page.\n * @param options.kind - 'file' (default) selects a parser by extension;\n * 'directory' decodes an unbundled SOG chunk directory.\n * @param options.format - Explicit parser; `lcc-bin` additionally needs\n * `options.lcc` to say which byte range of the file to fetch.\n * @param options.signal - Abort signal for cancellation; the returned\n * promise then rejects with a `DOMException` named `AbortError`.\n * @param options.lcc - Byte-range plumbing for `lcc-bin`. This path exists\n * for {@link StreamedSplatMesh} and is **not** part of the stable surface;\n * the type is nameable as `LccChunkParams` on `@voluma/vlam/formats/lcc`.\n * @param options.rad - Byte-range plumbing for `rad-chunk`, on the same\n * footing; the type is `RadChunkRangeRequest` on `@voluma/vlam/formats/rad`.\n * @throws Rejects only with {@link SplatLoadError} (resolve, fetch, decode or\n * worker failures - `error.phase` says which; a worker that cannot start at\n * all, e.g. under a CSP that blocks `blob:` workers, reports\n * `phase: 'worker'`) or an `Error` named `AbortError` (cancellation or\n * {@link dispose}).\n */\n // `async` so a URL or format that will not resolve rejects the returned\n // promise rather than throwing synchronously into the caller.\n async load(\n url: string,\n options: SplatInputOptions & {\n kind?: 'file' | 'directory';\n format?: SplatFormat | 'lcc-bin' | 'rad-chunk';\n lcc?: LccChunkParams;\n rad?: RadChunkRangeRequest;\n sog?: { packShBands: 1 | 2 | 3 };\n files?: Readonly<Record<string, string>>;\n } = {},\n ): Promise<SplatData> {\n const kind = options.kind ?? 'file';\n // The worker runs from an inlined blob/data URL, where relative and\n // root-relative URLs cannot be resolved - make the URL absolute here.\n const absoluteUrl = resolveSplatUrl(url, options.baseUrl).href;\n const format = resolveFileFormat(absoluteUrl, kind, options.format ?? 'auto');\n const data = await this.request(\n {\n from: 'url',\n url: absoluteUrl,\n kind,\n ...(options.request ? { request: options.request } : {}),\n },\n format,\n options.signal,\n options.lcc,\n options.rad,\n options.files,\n options.sog,\n options.onProgress,\n );\n return withSourceFormat(data, format);\n }\n\n /**\n * Decodes a local file (from a file input or a drop) in the worker - the\n * bytes are read worker-side, so a multi-million-splat decode still never\n * blocks the main thread.\n *\n * Only self-contained files are supported: `.ply`, `.spz`, `.splat`,\n * `.ksplat` and bundled `.sog`. An unbundled SOG directory needs sibling\n * fetches and so requires {@link load} with `kind: 'directory'`.\n *\n * @param file - The file; its name selects the parser unless `format` is given.\n * @throws Rejects only with {@link SplatLoadError} (unknown extension, read\n * or decode failures) or an `Error` named `AbortError` (cancellation or\n * {@link dispose}).\n */\n // `async` so an unknown extension rejects the returned promise rather than\n // throwing synchronously into the caller.\n async loadFile(file: File, options: SplatDataFileLoadOptions = {}): Promise<SplatData> {\n const requested = options.format ?? 'auto';\n const format: Exclude<SplatFormat, 'auto'> =\n requested === 'auto'\n ? splatFormatForExtension(splatNameExtension(file.name), file.name)\n : requested;\n const data = await this.request(\n { from: 'file', file },\n format,\n options.signal,\n undefined,\n undefined,\n undefined,\n undefined,\n options.onProgress,\n );\n return withSourceFormat(data, format);\n }\n\n /** Routes one load to the worker that owns its format. */\n private request(\n source: LoadWorkerSource,\n format: ChunkFileFormat,\n signal: AbortSignal | undefined,\n lcc?: LccChunkParams,\n rad?: RadChunkRangeRequest,\n files?: Readonly<Record<string, string>>,\n sog?: { packShBands: 1 | 2 | 3 },\n onProgress?: SplatProgressCallback,\n ): Promise<SplatData> {\n if (this.disposed) return Promise.reject(createAbortError('ChunkLoader has been disposed.'));\n if (signal?.aborted) return Promise.reject(createAbortError('Chunk load was cancelled.'));\n\n const message: Omit<Extract<LoadWorkerRequest, { type: 'load' }>, 'id'> = {\n type: 'load',\n source,\n format,\n ...(lcc ? { lcc } : {}),\n ...(rad ? { rad } : {}),\n ...(sog ? { sog } : {}),\n ...(files ? { files } : {}),\n ...(onProgress ? { progress: true } : {}),\n };\n\n const client = this.clientFor(format);\n // Deliberately not `async`: for a streaming format the client is already\n // there, and the request must reach the worker in the same task the caller\n // made it - a chunk scheduler that posts and then aborts within one frame\n // depends on that ordering. Only the one-shot path, which cannot avoid\n // awaiting its chunk, defers.\n if (!(client instanceof Promise)) return client.request(message, signal, onProgress);\n return client.then((resolved) => {\n // Re-checked because disposal or abort may have happened while the\n // one-shot chunk was in flight, before any listener was registered.\n if (this.disposed) throw createAbortError('ChunkLoader has been disposed.');\n return resolved.request(message, signal, onProgress);\n });\n }\n\n /**\n * The worker for `format`, constructing the one-shot worker on first need.\n *\n * The in-flight promise is cached, not just the resolved client, so two\n * concurrent `.spz` loads share one worker instead of racing to build two.\n */\n private clientFor(format: ChunkFileFormat): WorkerClient | Promise<WorkerClient> {\n if (!ONE_SHOT_FORMATS.has(format)) return this.streaming;\n if (this.oneShotClient) return this.oneShotClient;\n this.oneShot ??= import('./one-shot-worker?worker&inline').then(\n ({ default: OneShotWorker }) => {\n const client = new WorkerClient(new OneShotWorker());\n this.oneShotClient = client;\n // `dispose()` may have run while the chunk was in flight; it could not\n // terminate a worker that did not exist yet, so honour it here.\n if (this.disposed) client.dispose();\n return client;\n },\n );\n return this.oneShot;\n }\n\n /** Terminates both workers and rejects all in-flight requests. */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n this.streaming.dispose();\n // A one-shot worker still being imported is terminated in `clientFor`,\n // which sees `disposed` when it finally constructs it.\n this.oneShotClient?.dispose();\n }\n}\n\n/** Self-contained scene formats {@link SplatData.format} can hold. */\nconst SOURCE_FORMATS: readonly SplatDataFormat[] = ['ply', 'spz', 'splat', 'ksplat', 'sog', 'rad'];\n\n/**\n * Stamps the resolved format onto decoded scene data so {@link SplatMesh} can\n * pick its `orientation: 'y-up'` correction. Chunk-only formats (`lcc-bin`,\n * `rad-chunk`) carry no self-contained frame, so they are left unstamped.\n */\nfunction withSourceFormat(data: SplatData, format: ChunkFileFormat): SplatData {\n return (SOURCE_FORMATS as readonly string[]).includes(format)\n ? { ...data, format: format as SplatDataFormat }\n : data;\n}\n\nfunction resolveFileFormat(\n url: string,\n kind: 'file' | 'directory',\n format: SplatFormat | 'lcc-bin' | 'rad-chunk',\n): ChunkFileFormat {\n // An LCC or RAD chunk is a byte range of a shared file, so its extension says\n // nothing about the parser - the caller's choice is authoritative.\n if (format === 'lcc-bin' || format === 'rad-chunk') return format;\n if (kind === 'directory') return 'sog';\n if (format !== 'auto') return format;\n return splatFormatForExtension(splatUrlExtension(new URL(url)), url);\n}\n"],"names":["ONE_SHOT_FORMATS","WorkerClient","worker","__publicField","event","request","_a","_b","createAbortError","deserializeSplatLoadError","detail","failure","SplatLoadError","message","signal","onProgress","id","promise","resolve","reject","onAbort","pending","cancel","error","ChunkLoader","LoadWorker","url","options","kind","absoluteUrl","resolveSplatUrl","format","resolveFileFormat","data","withSourceFormat","file","requested","splatFormatForExtension","splatNameExtension","source","lcc","rad","files","sog","client","resolved","OneShotWorker","SOURCE_FORMATS","splatUrlExtension"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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"}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* - `@voluma/vlam/effects` - tree-shakeable modifier presets
|
|
15
15
|
* - `@voluma/vlam/formats/ply` - 3DGS `.ply`, raw and compressed
|
|
16
16
|
* - `@voluma/vlam/formats/sog` - PlayCanvas SOG, bundled and unbundled
|
|
17
|
-
* - `@voluma/vlam/formats/rad` - Spark `.rad
|
|
17
|
+
* - `@voluma/vlam/formats/rad` - Spark `.rad`
|
|
18
18
|
* - `@voluma/vlam/formats/lcc` - XGRIDS LCC / `.lcc2` + collision helpers
|
|
19
19
|
* - `@voluma/vlam/formats/spz` - Niantic `.spz`
|
|
20
20
|
* - `@voluma/vlam/formats/splat` - antimatter15 `.splat`
|
|
@@ -39,7 +39,7 @@ export { xrSessionInit } from './xr-view';
|
|
|
39
39
|
export { createSplatRenderer, type CreateSplatRendererOptions, type SplatRendererGpu, type SplatRendererGpuAdapter, type SplatRendererGpuDevice, } from './create-splat-renderer';
|
|
40
40
|
export { recommendedWebGpuRequiredLimits, supportsWebGpuPowerPreference, webGpuPowerPreferenceOptions, deviceMaxStorageBufferBindingSize, assertStorageBufferFitsDevice, WEBGPU_DEFAULT_MAX_STORAGE_BUFFER_BINDING_SIZE, type WebGpuRequiredLimits, type WebGpuPowerPreference, } from './webgpu-limits';
|
|
41
41
|
export type { SplatData, SplatShData, SplatPackedShData } from './splat-data';
|
|
42
|
-
export type { SplatDataFormat, StreamedSplatFormat } from '
|
|
42
|
+
export type { SplatDataFormat, StreamedSplatFormat } from '../loaders/loading';
|
|
43
43
|
export type { SplatModifier, SplatContext, SplatOutputs, ModifierStackTarget, } from './splat-modifier';
|
|
44
44
|
export { ModifierSlots } from './splat-modifier';
|
|
45
45
|
export { setVlamLogHandler, type VlamLogHandler, type VlamLogLevel } from './logging';
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* pool capacity. Capping the active splat count therefore caps memory
|
|
11
11
|
* directly, which is what matters on mobile GPUs.
|
|
12
12
|
*/
|
|
13
|
-
import type { StreamedSplatFormat } from '
|
|
13
|
+
import type { StreamedSplatFormat } from '../loaders/loading';
|
|
14
14
|
/**
|
|
15
15
|
* Desktop GPU capability class for budget / quality defaults.
|
|
16
16
|
*
|
|
@@ -211,8 +211,11 @@ export interface SplatMeshOptions {
|
|
|
211
211
|
maxSplatAspect?: number;
|
|
212
212
|
/**
|
|
213
213
|
* Spark's LOD alpha encoding (`.rad`): the stored opacity is `alpha/2` so the
|
|
214
|
-
* shader recovers `alpha ∈ [0,2]
|
|
215
|
-
* with a grown σ-cutoff +
|
|
214
|
+
* shader recovers `alpha ∈ [0,2]` from the original texture channel, and
|
|
215
|
+
* `alpha > 1` marks a merged node rendered with a grown σ-cutoff +
|
|
216
|
+
* super-Gaussian falloff. Visual fades and alpha modifiers scale the completed
|
|
217
|
+
* fragment after that classification; they must not change node type or shape.
|
|
218
|
+
* Set for foveated `.rad`.
|
|
216
219
|
*
|
|
217
220
|
* @experimental `.rad` foveation option; may change in a minor release.
|
|
218
221
|
*/
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
* See `docs/guide/effects-and-modifiers.md`.
|
|
39
39
|
*/
|
|
40
40
|
import * as THREE from 'three/webgpu';
|
|
41
|
-
import type { SplatModifier } from '
|
|
42
|
-
import type { CollisionMeshTile } from '
|
|
41
|
+
import type { SplatModifier } from '../core/splat-modifier';
|
|
42
|
+
import type { CollisionMeshTile } from '../formats/lcc/collision-mesh';
|
|
43
43
|
/**
|
|
44
44
|
* A signed-distance primitive kind. `cylinder` is capped and runs along the
|
|
45
45
|
* shape-local Y axis (rotate it with {@link SdfShape.rotation}).
|
|
@@ -269,7 +269,7 @@ export declare function worldWarpPreset(options?: {
|
|
|
269
269
|
intensity?: number;
|
|
270
270
|
radius?: number;
|
|
271
271
|
}): WorldWarpPreset;
|
|
272
|
-
export { renderRelightingFactorMap, type RelightingFactorRenderer } from '
|
|
272
|
+
export { renderRelightingFactorMap, type RelightingFactorRenderer } from '../core/relighting-pass';
|
|
273
273
|
/** Handle returned by {@link createRelightingProxy}. */
|
|
274
274
|
export interface RelightingProxy {
|
|
275
275
|
/** World-space group of lit proxy meshes (hide from the main splat scene). */
|