@voluma/vlam 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/budget-governor.d.ts +4 -4
- package/dist/camera-budget-governor.d.ts +11 -11
- package/dist/chunk-cache-budget.d.ts +5 -5
- package/dist/chunk-fetch-scheduler.d.ts +4 -4
- package/dist/chunk-loader-DlNwggfG.js +1859 -0
- package/dist/chunk-loader-DlNwggfG.js.map +1 -0
- package/dist/chunk-loader.d.ts +2 -2
- package/dist/compute-sorter.d.ts +1 -1
- package/dist/effects.d.ts +11 -10
- package/dist/effects.js.map +1 -1
- package/dist/formats/ksplat/index.d.ts +1 -1
- package/dist/formats/lcc/collision-mesh.d.ts +2 -2
- package/dist/formats/lcc.js +17 -16
- package/dist/formats/lcc.js.map +1 -1
- package/dist/formats/ply/index.d.ts +1 -1
- package/dist/formats/ply/parse-splat-ply.d.ts +2 -2
- package/dist/formats/ply.js +1 -1
- package/dist/formats/ply.js.map +1 -1
- package/dist/formats/rad/index.d.ts +1 -1
- package/dist/formats/rad/parse-rad.d.ts +1 -1
- package/dist/formats/rad/rad.d.ts +2 -2
- package/dist/formats/rad.js +3 -3
- package/dist/formats/rad.js.map +1 -1
- package/dist/formats/sog/index.d.ts +1 -1
- package/dist/formats/sog/parse-sog.d.ts +4 -4
- package/dist/formats/sog.js.map +1 -1
- package/dist/formats/splat/index.d.ts +1 -1
- package/dist/formats/spz/index.d.ts +1 -1
- package/dist/index.d.ts +16 -25
- package/dist/index.js +253 -9047
- package/dist/index.js.map +1 -1
- package/dist/{load-scene.d.ts → load-splat-data.d.ts} +15 -13
- package/dist/loaders.d.ts +15 -0
- package/dist/loaders.js +26 -0
- package/dist/loaders.js.map +1 -0
- package/dist/loading-wLo8vRbA.js.map +1 -1
- package/dist/loading.d.ts +8 -8
- package/dist/{splat-mesh-types-CTN_duw2.js → lod-scheduler-B0a_uBlv.js} +133 -140
- package/dist/lod-scheduler-B0a_uBlv.js.map +1 -0
- package/dist/lod-scheduler.d.ts +1 -1
- package/dist/lod-source.d.ts +5 -5
- package/dist/{splat-scene.d.ts → merged-splat-mesh.d.ts} +14 -15
- package/dist/orientation.d.ts +7 -7
- package/dist/{ply-header-BwgExCn-.js → ply-header-B9WllbPg.js} +3 -3
- package/dist/{ply-header-BwgExCn-.js.map → ply-header-B9WllbPg.js.map} +1 -1
- package/dist/radix-sorter-R0FCSqKu.js +178 -0
- package/dist/radix-sorter-R0FCSqKu.js.map +1 -0
- package/dist/relighting-CSQF3UcL.js +969 -0
- package/dist/relighting-CSQF3UcL.js.map +1 -0
- package/dist/relighting.d.ts +1 -1
- package/dist/selection.d.ts +13 -0
- package/dist/selection.js +137 -0
- package/dist/selection.js.map +1 -0
- package/dist/source-transform.d.ts +4 -4
- package/dist/{splat-budget-DXew_86c.js → splat-budget-DuVZspPQ.js} +7 -7
- package/dist/{splat-budget-DXew_86c.js.map → splat-budget-DuVZspPQ.js.map} +1 -1
- package/dist/splat-budget.d.ts +1 -1
- package/dist/splat-data-BZM5dhhx.js.map +1 -1
- package/dist/splat-data.d.ts +4 -4
- package/dist/splat-mesh-aQdcWA6v.js +1942 -0
- package/dist/splat-mesh-aQdcWA6v.js.map +1 -0
- package/dist/splat-mesh-material.d.ts +2 -2
- package/dist/splat-mesh-types-BZIko-_9.js +18 -0
- package/dist/splat-mesh-types-BZIko-_9.js.map +1 -0
- package/dist/splat-mesh-types.d.ts +14 -8
- package/dist/splat-mesh.d.ts +4 -4
- package/dist/splat-modifier.d.ts +5 -5
- package/dist/splat-partition.d.ts +1 -1
- package/dist/static-lod-entry.d.ts +12 -0
- package/dist/static-lod-splat-mesh.d.ts +2 -2
- package/dist/{static-lod-worker-D2fyqQZO.js → static-lod-worker-DktKnJkR.js} +46 -46
- package/dist/{static-lod-worker-D2fyqQZO.js.map → static-lod-worker-DktKnJkR.js.map} +1 -1
- package/dist/static-lod.js +148 -0
- package/dist/static-lod.js.map +1 -0
- package/dist/streamed-splat-mesh.d.ts +21 -27
- package/dist/streaming.d.ts +23 -0
- package/dist/streaming.js +2877 -0
- package/dist/streaming.js.map +1 -0
- package/dist/{unified-splat-renderer.d.ts → unified-splat-mesh.d.ts} +10 -10
- package/dist/unified-work-buffer.d.ts +66 -0
- package/dist/unified.d.ts +11 -0
- package/dist/unified.js +738 -0
- package/dist/unified.js.map +1 -0
- package/dist/webgpu-limits-Y3CI_6_6.js +43 -0
- package/dist/webgpu-limits-Y3CI_6_6.js.map +1 -0
- package/dist/webgpu-limits.d.ts +0 -59
- package/dist/work-buffer-gather.d.ts +1 -1
- package/package.json +24 -4
- package/dist/splat-mesh-types-CTN_duw2.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"splat-mesh-types-BZIko-_9.js","sources":["../src/lib/splat-mesh-types.ts"],"sourcesContent":["/** Public configuration and result types for SplatMesh. */\nimport type * as THREE from 'three/webgpu';\nimport type { SplatData } from './splat-data';\nimport type { SplatOrientation } from './orientation';\nimport type { SplatModifier } from './splat-modifier';\nimport {\n detectSplatDeviceProfile,\n isFillConstrainedSplatDevice,\n type SplatDeviceProfile,\n} from './splat-budget';\nimport type { SplatPool } from './splat-mesh-pool';\nimport type { SplatShInputs, Vec3Uniform } from './splat-mesh-material';\n\n/** Construction-time projected-footprint policy selected by a streamed format. */\nexport type ProjectedFilterProfile = 'default' | 'lcc';\n\n/** Canonical `.rad` foveation modes. */\nexport type SplatFoveationMode = 'band' | 'frontier' | 'page-table';\n\n/** Resolve a caller-supplied foveation mode, defaulting when unset. */\nexport function resolveSplatFoveationMode(\n mode: SplatFoveationMode | undefined,\n fallback: SplatFoveationMode = 'band',\n): SplatFoveationMode {\n return mode ?? fallback;\n}\n\n/** True when the mode is the `.rad` page-table pager. */\nexport function isPageTableFoveation(mode: string | undefined): boolean {\n return mode === 'page-table';\n}\n\n/**\n * Opaque handle for a range of splats appended to a {@link SplatMesh},\n * used to remove the range again.\n */\nexport interface SplatRange {\n /** Number of splats in this range. */\n readonly count: number;\n}\n\n/**\n * Storage format of a per-splat channel (see {@link SplatMesh.defineChannel}).\n *\n * - `'byte'`: one `Uint8` per splat (`r8unorm`). Compact - a good fit for\n * masks and labels. `ctx.channel(name)` reads it back **normalized** to\n * `[0, 1]`, so a painted `255` reads as `1.0`.\n * - `'float'`: one `Float32` per splat (`r32float`), read back verbatim.\n */\nexport type SplatChannelType = 'byte' | 'float';\n\n/** Options for {@link SplatMesh.defineChannel}. */\nexport interface SplatChannelOptions {\n /** Storage format; default `'float'`. */\n type?: SplatChannelType;\n /**\n * Value every splat starts at before any {@link SplatMesh.writeChannel}.\n * Default `0`. For `'byte'` channels this is a raw `0..255` value.\n */\n fill?: number;\n}\n\n/** The highest SH order this renderer evaluates (3rd → 15 coefficients). */\nexport const MAX_SH_BANDS = 3;\n\n/**\n * The contribution-culling profile a mesh will use, given an optional\n * explicit override. Exported so callers that must decide something *before*\n * constructing the mesh - such as whether a streamed scene should fetch its\n * SH at all - agree with what the mesh itself will pick.\n */\nexport function resolveSplatPerformanceProfile(\n explicit?: SplatPerformanceProfile,\n profile: SplatDeviceProfile | undefined = detectSplatDeviceProfile(),\n): SplatPerformanceProfile {\n return explicit ?? (isFillConstrainedSplatDevice(profile) ? 'smooth' : 'quality');\n}\n\n/** Construction options for {@link SplatMesh}. */\nexport interface SplatMeshOptions {\n /**\n * Storage for per-splat higher-order SH in a dynamic-capacity pool, in\n * bands (1, 2 or 3 → 3, 8 or 15 coefficients per channel); 0 (default)\n * allocates nothing.\n *\n * Only formats that store SH per splat can fill this - LCC `Quality`, `.rad`, etc.\n * It costs 16 bytes per splat per band-group of four\n * coefficients (64 B/splat at 3 bands), so it is opt-in. On a static mesh\n * this is ignored: packed SH is taken from `source.shPacked` when present,\n * otherwise palette `source.sh` (SOG).\n */\n shBands?: 0 | 1 | 2 | 3;\n /**\n * Minimum interval between WebGPU sorts while the camera moves. When\n * omitted, the interval adapts to the active splat count. Use `0` to sort\n * every changed frame. WebGL worker sorting is unaffected.\n */\n sortIntervalMs?: number;\n /**\n * WebGPU sorter used for A/B validation. Defaults to the proven counting\n * sorter. `'radix'` keeps the fast 24-bit key path; `'exact'` lazy-loads a\n * stable 32-bit Float32-depth radix path that avoids scene-range\n * quantization. The first frames may skip sorting until the module resolves.\n *\n * @experimental Radix strategies may change in a minor release.\n */\n sortStrategy?: SplatSortStrategy;\n /**\n * Render-quality policy. `smooth` rejects negligible projected contributions.\n *\n * The default is device-aware: `smooth` on mobile (where rejecting splats too\n * small or too faint to see is worth far more than it costs), `quality`\n * everywhere else. Passing a value opts out of the detection.\n */\n performanceProfile?: SplatPerformanceProfile;\n /**\n * How far out, in standard deviations, each Gaussian is drawn before it is\n * cut off. Every splat is an alpha-blended quad sized to this radius, so it\n * sets how much each one costs to blend - the dominant cost in a busy view.\n * Lowering it shrinks every quad and clips the faint outer tail of each\n * Gaussian; the falloff within the remaining radius is unchanged.\n *\n * Defaults to `3`, the reference 3DGS rasterizer's radius. Below ~2 the\n * truncation shows as visible splat edges; much above ~5 the extra fill is not\n * worth it. Mobile coverage gaps are handled by `minSplatSizePx` instead of\n * growing every splat.\n */\n maxStdDev?: number;\n /**\n * Floor, in viewport pixels, on each rendered splat's projected quad radius.\n *\n * A screen-space *minimum* size, the counterpart to `maxScreenRadiusPx`'s\n * maximum. When a splat projects smaller than this - because it is distant, or\n * because the whole scene is zoomed out - its quad is grown to this radius and\n * the Gaussian is stretched to fill it (the falloff normalizes to the quad, so\n * no hard edge appears). Splats already larger are untouched, so it costs no\n * extra fill on the near-camera splats that dominate overdraw.\n *\n * This is the fix for the \"dark gaps when zoomed out\" failure mode: a capture\n * whose finest splats are spaced farther apart than their footprint leaves the\n * background showing between them, and the effect is worst at low resolution -\n * i.e. on a phone. Raising `maxStdDev` also closes the gaps but inflates\n * *every* splat's fragment count by its square, paying the coverage cost on the\n * large splats too; this floor spends it only where a gap can actually open.\n *\n * Defaults to `1.5` px on mobile and `0` (disabled) elsewhere, including\n * fill-constrained desktops. Values around 1–3 px close typical gaps; too\n * large a floor blurs distinct small features into discs, so tune it up from\n * small on the target device. An explicit `0` always disables the floor.\n */\n minSplatSizePx?: number;\n /**\n * Apply the Mip-Splatting 2D antialiasing filter - the screen-space low-pass\n * dilation plus the opacity compensation that conserves each Gaussian's\n * integral, so small/distant splats stop over-brightening. Match the\n * exporter: enable it for scenes trained/exported with antialiasing (the SOG\n * `antialias` meta flag sets this automatically). Defaults to `false` (the\n * classic 3DGS dilation without compensation).\n */\n antialias?: boolean;\n /**\n * Internal format-selected reconstruction profile. Classic LCC uses the\n * XGRIDS-compatible 0.1 px² compensated low-pass; callers should leave this\n * unset and select a format through {@link StreamedSplatMesh.load} instead.\n *\n * @internal\n */\n projectedFilterProfile?: ProjectedFilterProfile;\n /**\n * Emit splat colors in sRGB (display) space instead of decoding them to the\n * renderer's linear working space. Pair with a renderer that skips output\n * conversion (`outputColorSpace = LinearSRGBColorSpace`, `NoToneMapping`,\n * inline sRGB encode for other materials via `renderer.contextNode`): splats\n * then alpha-composite on gamma-encoded values - the math 3DGS training\n * optimizes against, and what WebGL splat viewers render. Defaults to\n * `false` (linear working-space compositing).\n */\n srgbOutput?: boolean;\n /**\n * Cull any splat whose projected on-screen radius exceeds this many pixels,\n * rendering a hole instead. A physically large splat close to the camera -\n * a coarse merged LOD node (a Spark `.rad` \"blob\"), or a giant background\n * Gaussian - projects huge while a fine surface splat stays small, so this\n * removes the near-camera blobs without touching detailed geometry. `0` or\n * unset disables it (the default). Baked into the material graph.\n */\n maxSplatScreenRadius?: number;\n /**\n * Foveation band lower bound (px): cull any splat whose projected on-screen\n * radius is *below* this. Paired with {@link maxSplatScreenRadius}, only\n * splats sized `(min, max]` on screen draw. Because a `.rad` LOD tree's node\n * sizes shrink geometrically, exactly one level per view ray lands in the\n * band - near rays on fine leaves, far rays on coarse nodes - giving a\n * camera-distance foveated cut. `0` or unset disables it (the default).\n * Baked into the material graph. See `docs/formats/rad-notes.md` M14.6.\n */\n minSplatScreenRadius?: number;\n /**\n * How a `.rad` foveated mesh picks its per-splat LOD cut:\n * - `'band'` (default): the screen-radius band above\n * ({@link minSplatScreenRadius}, {@link maxSplatScreenRadius}].\n * - `'frontier'`: Spark's exact tree cut - draw splat `i` iff its parent is\n * too big and it is small enough (`parentPixelScale > limit ≥ ownPixelScale`),\n * using per-splat `own_size`/`parent_size`. Full coverage by construction, no\n * band leapfrogging. Baked into the material graph. See `docs/formats/rad-notes.md`.\n * - `'page-table'`: the {@link StreamedSplatMesh} default for `.rad` - a worker\n * owns the tree traversal and pages only the *selected* frontier into the\n * pool (Spark's selected-index model), so the whole splat budget buys\n * on-screen detail. Requires the streamed `.rad` machinery; on a plain\n * `SplatMesh` it has no worker to drive it.\n *\n * @experimental `.rad` foveation option; may change in a minor release.\n */\n foveationMode?: SplatFoveationMode;\n /**\n * Target on-screen size (px) for the frontier / page-table cut: it keeps one\n * LOD level per view ray whose projected node size is about this. Larger =\n * coarser/fewer splats, smaller = finer/denser. Default\n * {@link DEFAULT_FOVEATION_TARGET_PX} (1, matching Spark's `lodRenderScale`),\n * so the draw budget rather than the cut size is what bounds detail. Raise it\n * to trade sharpness for fill rate on weak GPUs.\n * Acts as the *finest* bound: the adaptive limit coarsens above it to hold the\n * draw budget but never dips below it.\n *\n * @experimental `.rad` foveation option; may change in a minor release.\n */\n foveationTargetPx?: number;\n /**\n * Target upper bound on the number of splats the frontier cut *draws*\n * (Spark's `maxSplats`). Each reschedule the cut's `pixelScaleLimit`\n * self-adjusts - coarsening when the estimated drawn count exceeds this - so\n * frame cost stays bounded as detail streams in. Default\n * {@link DEFAULT_FOVEATION_DRAW_BUDGET}. Only used in `'frontier'` mode.\n *\n * @experimental `.rad` foveation option; may change in a minor release.\n */\n foveationDrawBudget?: number;\n /**\n * Cap on a rendered splat's major/minor axis ratio (`0`/unset = off). A very\n * anisotropic Gaussian (a flat 3DGS disk edge-on, or an expansion-enlarged\n * coarse LOD node) otherwise projects to a long needle; this bounds its drawn\n * length to `maxSplatAspect`× its width. Baked into the material graph.\n *\n * @experimental `.rad` foveation option; may change in a minor release.\n */\n maxSplatAspect?: number;\n /**\n * Spark's LOD alpha encoding (`.rad`): the stored opacity is `alpha/2` so the\n * shader recovers `alpha ∈ [0,2]`, and `alpha > 1` marks a merged node rendered\n * with a grown σ-cutoff + super-Gaussian falloff. Set for foveated `.rad`.\n *\n * @experimental `.rad` foveation option; may change in a minor release.\n */\n lodAlpha?: boolean;\n /**\n * How the scene is oriented into the three.js Y-up world. `'y-up'` (default)\n * normalizes every known format to Y-up - 3DGS Y-down formats\n * (PLY/`.splat`/`.ksplat`/SOG) are flipped 180° about X; SPZ/`.rad` are\n * already Y-up; LCC keeps its own Z-up→Y-up matrix. `'source'` applies no\n * cosmetic flip and renders in the data frame (raw Spark / mkkellogg parity);\n * LCC still self-orients (that is format semantics, not part of the switch).\n *\n * For a fully loaded mesh the flip is chosen from {@link SplatData.format}\n * (stamped by the loaders); a dynamic-capacity mesh carries no format, so the\n * caller applies {@link yUpTransformForFormat} itself. See {@link SplatOrientation}.\n */\n orientation?: SplatOrientation;\n /**\n * GPU storage type for the pool's continuous float textures (`centers` and\n * `covarianceA`). `'float16'` uploads them as `rgba16float` (~16 B/splat\n * saved vs the default). CPU backing stays float32 (sorter, query, writes).\n *\n * `covarianceB` is always float32: it packs integer IDs (SOG palette labels,\n * RAD frontier parents) that half floats cannot represent exactly above\n * 2048. Colors and packed SH are unchanged. Construction-time only.\n */\n poolFloatTextures?: 'float32' | 'float16';\n /**\n * An existing pool to draw from instead of allocating one.\n *\n * Several meshes sharing a pool share its memory envelope: rows go to\n * whichever mesh needs them, so a mesh the camera is near can hold far more\n * than an even split would give it, and one that is far away holds almost\n * nothing - without every mesh having reserved a private ceiling up front.\n * This is the multi-mesh analogue of a single streamed mesh's LOD budget.\n *\n * The pool is *not* owned by the mesh: {@link SplatMesh.dispose} releases the\n * mesh's rows and leaves the textures alone, so the pool's creator disposes\n * it once every tenant is gone. `capacity` on the source is then only used\n * for the mesh's own draw list, not to size storage.\n *\n * Sharing costs a whole-pool stall when the pool fragments - see\n * {@link SplatMesh.compact}.\n */\n pool?: SplatPool;\n /**\n * The device's `maxTextureDimension2D`, forwarded to a pool this mesh\n * allocates for itself so an over-tall pool fails at construction with a\n * readable error instead of at first draw. Pass `deviceMaxTextureSize(renderer)`.\n *\n * Ignored when {@link SplatMeshOptions.pool} supplies the pool - that pool\n * was already checked when its creator built it.\n */\n maxTextureSize?: number;\n}\n\n/** Available WebGPU depth-sort implementations. */\nexport type SplatSortStrategy = 'counting' | 'radix' | 'exact';\n\n/** Controls optional work during a per-frame source update. */\nexport interface SplatUpdateOptions {\n /** Leave sorting to {@link UnifiedSplatMesh}; uploads and LOD state still update. */\n sort?: boolean;\n}\n\n/**\n * Read-only GPU-facing view of a mesh's current active pool. It is consumed by\n * the M15.4 unified gather path; streamed meshes expose their current LOD cut\n * through the same view because they inherit {@link SplatMesh}.\n *\n * @experimental May change in a minor release.\n */\nexport interface UnifiedSourceView {\n /** Total addressable pool slots. */\n readonly capacity: number;\n /** Pool indices of active splats, packed from zero. */\n readonly sourceIndex: THREE.StorageBufferAttribute;\n /** Active entries at the front of {@link sourceIndex}. */\n readonly activeCount: number;\n /** Local-space centers, RGBA32F and pool-indexed. */\n readonly centersTexture: THREE.DataTexture;\n /** Source display color and opacity, RGBA8 and pool-indexed. */\n readonly colorsTexture: THREE.DataTexture;\n /** Upper covariance rows, RGBA32F and pool-indexed. */\n readonly covarianceATexture: THREE.DataTexture;\n /** Final covariance row, RGBA32F and pool-indexed. */\n readonly covarianceBTexture: THREE.DataTexture;\n /** Centers texture row width. */\n readonly dataTextureWidth: number;\n /** Current source-local → world transform. */\n readonly matrixWorld: THREE.Matrix4;\n /** Conservative world-space bound for depth quantization. */\n readonly worldBounds: THREE.Sphere;\n /** Higher-order color data resolved by the gather pass when present. */\n readonly sh: SplatShInputs | null;\n /** Effect hooks and their source-local data channels. */\n readonly modifiers: readonly SplatModifier[];\n /**\n * True when this source is itself a unified pool with per-source placement\n * (`MergedSplatMesh`). The gather path cannot resolve nested placement, so\n * `UnifiedSplatMesh` rejects these sources.\n */\n readonly hasSourcePlacement: boolean;\n readonly channels: ReadonlyMap<string, { texture: THREE.DataTexture }>;\n /** Same uniform node the source graph updates each frame. */\n readonly localCameraPosition: Vec3Uniform;\n /** Changes whenever a modifier graph must be rebuilt. */\n readonly graphRevision: number;\n /** Whether this source intentionally composites in display (sRGB) space. */\n readonly srgbOutput: boolean;\n /** Shared draw-path settings that must agree across unified sources. */\n readonly maxStdDev: number;\n /** Screen-space minimum splat radius, px (0 = off). */\n readonly minSplatSizePx: number;\n readonly antialias: boolean;\n /** Construction-time projected-footprint policy shared by one unified pass. */\n readonly projectedFilterProfile: ProjectedFilterProfile;\n /**\n * Whether this source stores Spark LOD alpha (`alpha ÷ 2`, `.rad`). The\n * gather recovers the full `alpha ∈ [0,2]`; the draw material then treats\n * `alpha > 1` as a merged node. Per source, not a compatibility field - a\n * scene may mix `.rad` and non-`.rad` sources.\n */\n readonly lodAlpha: boolean;\n /** Increments whenever pool-backed data or active residency changes. */\n readonly contentRevision: number;\n}\n\n/** Quality-compatible rendering or smoother contribution-culling rendering. */\nexport type SplatPerformanceProfile = 'quality' | 'smooth';\n\n/**\n * Options for {@link SplatMesh.pick}.\n *\n * Picking returns the selected splat's rendered center plane (depth-tested\n * Gaussian coverage), not a persistent splat identifier or a collision mesh.\n */\nexport interface SplatPickOptions {\n /**\n * Minimum Gaussian opacity (after falloff × splat alpha) for a fragment to\n * count as a hit. Default `0.1`.\n */\n alphaThreshold?: number;\n}\n\n/**\n * Result of a successful {@link SplatMesh.pick}.\n *\n * The point lies on the frontmost splat's billboard plane at the picked\n * pixel - suitable for click-to-focus and placement anchors, not physics.\n */\nexport interface SplatPickResult {\n /** Hit position in world space. */\n readonly point: THREE.Vector3;\n /** Distance from the camera position to {@link point}. */\n readonly distance: number;\n}\n\n/**\n * Result of {@link SplatMesh.queryNearest}: the resident splat center closest\n * to the query point, in world space.\n */\nexport interface SplatNearestResult {\n /** The splat's center, in world space. */\n readonly point: THREE.Vector3;\n /** World-space distance from the query point to {@link point}. */\n readonly distance: number;\n}\n\n/** Result of a successful synchronous {@link SplatMesh.queryRay}. */\nexport interface SplatRayResult {\n /** Resident splat center in world space. */\n readonly point: THREE.Vector3;\n /** Distance along the ray from its origin to the center's closest plane. */\n readonly distance: number;\n}\n\n/**\n * Result of {@link SplatMesh.queryHeight}: the supporting surface found beneath\n * the query point (the highest resident splat within the drop and horizontal\n * radius), in world space.\n */\nexport interface SplatHeightResult {\n /** The supporting splat's center, in world space. */\n readonly point: THREE.Vector3;\n /** How far below the query point the surface sits (world units, ≥ 0). */\n readonly drop: number;\n}\n"],"names":["resolveSplatFoveationMode","mode","fallback","isPageTableFoveation","MAX_SH_BANDS","resolveSplatPerformanceProfile","explicit","profile","detectSplatDeviceProfile","isFillConstrainedSplatDevice"],"mappings":";AAoBO,SAASA,EACdC,GACAC,IAA+B,QACX;AACpB,SAAOD,KAAQC;AACjB;AAGO,SAASC,EAAqBF,GAAmC;AACtE,SAAOA,MAAS;AAClB;AAiCO,MAAMG,IAAe;AAQrB,SAASC,EACdC,GACAC,IAA0CC,KACjB;AACzB,SAAOF,MAAaG,EAA6BF,CAAO,IAAI,WAAW;AACzE;"}
|
|
@@ -7,6 +7,12 @@ import type { SplatPool } from './splat-mesh-pool';
|
|
|
7
7
|
import type { SplatShInputs, Vec3Uniform } from './splat-mesh-material';
|
|
8
8
|
/** Construction-time projected-footprint policy selected by a streamed format. */
|
|
9
9
|
export type ProjectedFilterProfile = 'default' | 'lcc';
|
|
10
|
+
/** Canonical `.rad` foveation modes. */
|
|
11
|
+
export type SplatFoveationMode = 'band' | 'frontier' | 'page-table';
|
|
12
|
+
/** Resolve a caller-supplied foveation mode, defaulting when unset. */
|
|
13
|
+
export declare function resolveSplatFoveationMode(mode: SplatFoveationMode | undefined, fallback?: SplatFoveationMode): SplatFoveationMode;
|
|
14
|
+
/** True when the mode is the `.rad` page-table pager. */
|
|
15
|
+
export declare function isPageTableFoveation(mode: string | undefined): boolean;
|
|
10
16
|
/**
|
|
11
17
|
* Opaque handle for a range of splats appended to a {@link SplatMesh},
|
|
12
18
|
* used to remove the range again.
|
|
@@ -162,7 +168,7 @@ export interface SplatMeshOptions {
|
|
|
162
168
|
* too big and it is small enough (`parentPixelScale > limit ≥ ownPixelScale`),
|
|
163
169
|
* using per-splat `own_size`/`parent_size`. Full coverage by construction, no
|
|
164
170
|
* band leapfrogging. Baked into the material graph. See `docs/formats/rad-notes.md`.
|
|
165
|
-
* - `'
|
|
171
|
+
* - `'page-table'`: the {@link StreamedSplatMesh} default for `.rad` - a worker
|
|
166
172
|
* owns the tree traversal and pages only the *selected* frontier into the
|
|
167
173
|
* pool (Spark's selected-index model), so the whole splat budget buys
|
|
168
174
|
* on-screen detail. Requires the streamed `.rad` machinery; on a plain
|
|
@@ -170,7 +176,7 @@ export interface SplatMeshOptions {
|
|
|
170
176
|
*
|
|
171
177
|
* @experimental `.rad` foveation option; may change in a minor release.
|
|
172
178
|
*/
|
|
173
|
-
foveationMode?:
|
|
179
|
+
foveationMode?: SplatFoveationMode;
|
|
174
180
|
/**
|
|
175
181
|
* Target on-screen size (px) for the frontier / page-table cut: it keeps one
|
|
176
182
|
* LOD level per view ray whose projected node size is about this. Larger =
|
|
@@ -219,9 +225,9 @@ export interface SplatMeshOptions {
|
|
|
219
225
|
* cosmetic flip and renders in the data frame (raw Spark / mkkellogg parity);
|
|
220
226
|
* LCC still self-orients (that is format semantics, not part of the switch).
|
|
221
227
|
*
|
|
222
|
-
* For a
|
|
223
|
-
* (stamped by the loaders); a dynamic-capacity mesh carries no format, so
|
|
224
|
-
*
|
|
228
|
+
* For a fully loaded mesh the flip is chosen from {@link SplatData.format}
|
|
229
|
+
* (stamped by the loaders); a dynamic-capacity mesh carries no format, so the
|
|
230
|
+
* caller applies {@link yUpTransformForFormat} itself. See {@link SplatOrientation}.
|
|
225
231
|
*/
|
|
226
232
|
orientation?: SplatOrientation;
|
|
227
233
|
/**
|
|
@@ -266,7 +272,7 @@ export interface SplatMeshOptions {
|
|
|
266
272
|
export type SplatSortStrategy = 'counting' | 'radix' | 'exact';
|
|
267
273
|
/** Controls optional work during a per-frame source update. */
|
|
268
274
|
export interface SplatUpdateOptions {
|
|
269
|
-
/** Leave sorting to {@link
|
|
275
|
+
/** Leave sorting to {@link UnifiedSplatMesh}; uploads and LOD state still update. */
|
|
270
276
|
sort?: boolean;
|
|
271
277
|
}
|
|
272
278
|
/**
|
|
@@ -303,8 +309,8 @@ export interface UnifiedSourceView {
|
|
|
303
309
|
readonly modifiers: readonly SplatModifier[];
|
|
304
310
|
/**
|
|
305
311
|
* True when this source is itself a unified pool with per-source placement
|
|
306
|
-
* (`
|
|
307
|
-
* `
|
|
312
|
+
* (`MergedSplatMesh`). The gather path cannot resolve nested placement, so
|
|
313
|
+
* `UnifiedSplatMesh` rejects these sources.
|
|
308
314
|
*/
|
|
309
315
|
readonly hasSourcePlacement: boolean;
|
|
310
316
|
readonly channels: ReadonlyMap<string, {
|
package/dist/splat-mesh.d.ts
CHANGED
|
@@ -133,7 +133,7 @@ export declare class SplatMesh extends THREE.Mesh implements SplatPoolTenant {
|
|
|
133
133
|
/** Created on the first update, when the renderer backend is known. */
|
|
134
134
|
private sorter;
|
|
135
135
|
/**
|
|
136
|
-
* Set by a unified {@link
|
|
136
|
+
* Set by a unified {@link MergedSplatMesh} subclass before the first sort: makes
|
|
137
137
|
* the WebGPU sorter transform each splat's center to world space by its
|
|
138
138
|
* source's matrix before measuring depth. `null` for an ordinary mesh, whose
|
|
139
139
|
* sorter is byte-identical to before. See `source-transform.ts`.
|
|
@@ -376,7 +376,7 @@ export declare class SplatMesh extends THREE.Mesh implements SplatPoolTenant {
|
|
|
376
376
|
*/
|
|
377
377
|
getUnifiedSourceView(): UnifiedSourceView;
|
|
378
378
|
/**
|
|
379
|
-
* Marks this mesh as a source drawn by {@link
|
|
379
|
+
* Marks this mesh as a source drawn by {@link UnifiedSplatMesh}.
|
|
380
380
|
* The source stays invisible to the regular scene draw, while its own picker
|
|
381
381
|
* mirrors this resolved visibility so existing per-source hit testing works.
|
|
382
382
|
* Internal consumers should clear the state with `null` before disposing.
|
|
@@ -384,7 +384,7 @@ export declare class SplatMesh extends THREE.Mesh implements SplatPoolTenant {
|
|
|
384
384
|
setUnifiedPickVisibility(visible: boolean | null): void;
|
|
385
385
|
/**
|
|
386
386
|
* The visibility that actually decides whether this mesh's splats reach the
|
|
387
|
-
* screen: the owning {@link
|
|
387
|
+
* screen: the owning {@link UnifiedSplatMesh}'s per-source visibility
|
|
388
388
|
* while one owns the draw, else `Object3D.visible`. Consumers that gate on
|
|
389
389
|
* "is this mesh showing" - the picker, `CameraBudgetGovernor` - must read
|
|
390
390
|
* this rather than `visible`, which a unified renderer forces to `false` on
|
|
@@ -708,7 +708,7 @@ export declare class SplatMesh extends THREE.Mesh implements SplatPoolTenant {
|
|
|
708
708
|
/**
|
|
709
709
|
* Refreshes {@link boundingSphereLocal} - the sphere the sorter quantizes
|
|
710
710
|
* depth over - when {@link boundsDirty}. The base mesh uses its local splat
|
|
711
|
-
* bounds; a unified {@link
|
|
711
|
+
* bounds; a unified {@link MergedSplatMesh} overrides this to supply a world-space
|
|
712
712
|
* bound spanning all its sources (whose transforms live in the shader).
|
|
713
713
|
*/
|
|
714
714
|
protected refreshSortBounds(): void;
|
package/dist/splat-modifier.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface SplatContext {
|
|
|
9
9
|
/**
|
|
10
10
|
* Splat center in mesh-local space (pre-displacement).
|
|
11
11
|
*
|
|
12
|
-
* Inside a `
|
|
12
|
+
* Inside a `MergedSplatMesh` this is the splat's **placed** position
|
|
13
13
|
* (`sourceMatrix · poolCenter`) - the placement is applied before the stack
|
|
14
14
|
* runs, so effects stay anchored to the scene rather than travelling with a
|
|
15
15
|
* moved source. Use {@link sourceCenter} for the pre-placement position.
|
|
@@ -17,14 +17,14 @@ export interface SplatContext {
|
|
|
17
17
|
readonly localCenter: Node<'vec3'>;
|
|
18
18
|
/**
|
|
19
19
|
* The splat's position in its own source's data frame. Identical to
|
|
20
|
-
* {@link localCenter} on a plain mesh; inside a `
|
|
20
|
+
* {@link localCenter} on a plain mesh; inside a `MergedSplatMesh` it is the
|
|
21
21
|
* **pre-placement** position, for effects that should travel *with* a moved
|
|
22
22
|
* source instead of staying put in the scene.
|
|
23
23
|
*/
|
|
24
24
|
readonly sourceCenter: Node<'vec3'>;
|
|
25
25
|
/**
|
|
26
26
|
* Linear transform from the source data frame to mesh-local space. This is
|
|
27
|
-
* identity on a plain mesh; inside a {@link
|
|
27
|
+
* identity on a plain mesh; inside a {@link MergedSplatMesh} it is the linear part
|
|
28
28
|
* of the source placement. Apply it to source-frame displacement vectors.
|
|
29
29
|
* To express a source-frame rotation `R` as a mesh-local `rotation` output,
|
|
30
30
|
* conjugate it: `sourceToLocal · R · sourceToLocal.inverse()`.
|
|
@@ -109,8 +109,8 @@ export interface ModifierStackTarget {
|
|
|
109
109
|
* - An **empty slot costs nothing**: it is omitted from the compacted list
|
|
110
110
|
* entirely, never compiled as a passthrough. All slots empty ⇒ the mesh
|
|
111
111
|
* gets an empty list ⇒ the unhooked graph (and the unified renderer's
|
|
112
|
-
* zero-modifier gather fast path stays eligible - except for a `
|
|
113
|
-
* which `
|
|
112
|
+
* zero-modifier gather fast path stays eligible - except for a `MergedSplatMesh`,
|
|
113
|
+
* which `UnifiedSplatMesh` does not accept as a source at all).
|
|
114
114
|
* - {@link apply} hands the mesh a **compacted array whose reference is
|
|
115
115
|
* stable** while the occupancy is unchanged, so re-applying after
|
|
116
116
|
* uniform-only changes is a guaranteed no-op on the mesh's identity diff -
|
|
@@ -6,7 +6,7 @@ import { type SelectionVolume } from './selection-volume';
|
|
|
6
6
|
* transformed (and therefore independently animated) object.
|
|
7
7
|
*
|
|
8
8
|
* Both halves keep the source's local frame - separation does not bake any
|
|
9
|
-
* transform. Register them as `
|
|
9
|
+
* transform. Register them as `MergedSplatMesh` or `UnifiedSplatMesh` sources
|
|
10
10
|
* for one global sort while they are posed independently. Separate
|
|
11
11
|
* `SplatMesh` draw calls sort only within each mesh and can misblend where
|
|
12
12
|
* their Gaussian footprints overlap.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@voluma/vlam/static-lod` - worker-built merged hierarchy for a fully
|
|
3
|
+
* decoded capture.
|
|
4
|
+
*
|
|
5
|
+
* {@link StaticLodSplatMesh.load} fetches through `@voluma/vlam/loaders` and
|
|
6
|
+
* then builds the hierarchy off the main thread. Import this entry rather
|
|
7
|
+
* than calling a streaming alias: there is none.
|
|
8
|
+
*
|
|
9
|
+
* @module static-lod
|
|
10
|
+
*/
|
|
11
|
+
export { StaticLodSplatMesh, type StaticLodSplatMeshOptions, type StaticLodSplatMeshLoadOptions, type StaticLodLoadProgress, } from './static-lod-splat-mesh';
|
|
12
|
+
export type { StaticLodBuildProgress } from './static-lod';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @role Bridge - Camera-aware rendering for worker-built static splat hierarchies. */
|
|
2
2
|
import * as THREE from 'three/webgpu';
|
|
3
|
-
import { type
|
|
3
|
+
import { type SplatDataLoadOptions } from './load-splat-data';
|
|
4
4
|
import { SplatMesh, type SplatMeshOptions, type SplatUpdateOptions } from './splat-mesh';
|
|
5
5
|
import type { StaticLodBuildProgress } from './static-lod';
|
|
6
6
|
export interface StaticLodSplatMeshOptions extends SplatMeshOptions {
|
|
@@ -18,7 +18,7 @@ export interface StaticLodLoadProgress {
|
|
|
18
18
|
readonly completed: number;
|
|
19
19
|
readonly total: number;
|
|
20
20
|
}
|
|
21
|
-
export interface StaticLodSplatMeshLoadOptions extends StaticLodSplatMeshOptions,
|
|
21
|
+
export interface StaticLodSplatMeshLoadOptions extends StaticLodSplatMeshOptions, SplatDataLoadOptions {
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* Static splat mesh backed by a merged hierarchy and camera-aware frontier.
|
|
@@ -18,14 +18,14 @@ function rt(t, n, o = lt) {
|
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
20
|
function ht(t, n, o, s, e, i = Number.POSITIVE_INFINITY) {
|
|
21
|
-
const c = /* @__PURE__ */ new Map(),
|
|
22
|
-
const
|
|
23
|
-
return !m || !m.radTree ? null : { data: m, local: y -
|
|
24
|
-
}, D = (y,
|
|
21
|
+
const c = /* @__PURE__ */ new Map(), d = /* @__PURE__ */ new Map(), l = new ft(), p = s.origin.x, h = s.origin.y, f = s.origin.z, r = s.forward, a = (r == null ? void 0 : r.x) ?? 0, M = (r == null ? void 0 : r.y) ?? 0, b = (r == null ? void 0 : r.z) ?? 0, N = r !== void 0, { coneDot0: S, coneDot: v, coneFoveate: F, behindFoveate: A } = s, R = (y) => {
|
|
22
|
+
const u = Math.floor(y / o), m = t.get(u);
|
|
23
|
+
return !m || !m.radTree ? null : { data: m, local: y - u * o };
|
|
24
|
+
}, D = (y, u) => {
|
|
25
25
|
let m = c.get(y);
|
|
26
|
-
m || (m = [], c.set(y, m)), m.push(
|
|
27
|
-
}, V = (y,
|
|
28
|
-
const m = y.positions, P =
|
|
26
|
+
m || (m = [], c.set(y, m)), m.push(u);
|
|
27
|
+
}, V = (y, u) => {
|
|
28
|
+
const m = y.positions, P = u * 3, E = m[P] - p, C = m[P + 1] - h, O = m[P + 2] - f, x = Math.sqrt(E * E + C * C + O * O) || 1e-6, U = y.radTree.size[u] / x;
|
|
29
29
|
if (!N) return U;
|
|
30
30
|
const I = E * a + C * M + O * b;
|
|
31
31
|
if (I <= 0) return U * A;
|
|
@@ -41,19 +41,19 @@ function ht(t, n, o, s, e, i = Number.POSITIVE_INFINITY) {
|
|
|
41
41
|
const $ = /* @__PURE__ */ new Set();
|
|
42
42
|
for (const y of n) {
|
|
43
43
|
if ($.has(y)) continue;
|
|
44
|
-
const
|
|
45
|
-
|
|
44
|
+
const u = R(y);
|
|
45
|
+
u && ($.add(y), l.push(y, V(u.data, u.local)), _++);
|
|
46
46
|
}
|
|
47
47
|
let k = !1;
|
|
48
48
|
for (; l.size > 0; ) {
|
|
49
49
|
const y = l.peekPriority();
|
|
50
50
|
if (y <= e) break;
|
|
51
|
-
const
|
|
51
|
+
const u = l.peek(), m = R(u);
|
|
52
52
|
if (!m) {
|
|
53
53
|
l.pop(), _--;
|
|
54
54
|
continue;
|
|
55
55
|
}
|
|
56
|
-
const P = Math.floor(
|
|
56
|
+
const P = Math.floor(u / o), E = m.data.radTree, C = E.childCount[m.local];
|
|
57
57
|
if (C === 0) {
|
|
58
58
|
l.pop(), D(P, m.local);
|
|
59
59
|
continue;
|
|
@@ -67,7 +67,7 @@ function ht(t, n, o, s, e, i = Number.POSITIVE_INFINITY) {
|
|
|
67
67
|
const x = E.childStart[m.local], U = Math.floor(x / o), I = Math.floor((x + C - 1) / o);
|
|
68
68
|
let T = !0;
|
|
69
69
|
for (let L = U; L <= I; L++)
|
|
70
|
-
t.has(L) || (T = !1, y > (
|
|
70
|
+
t.has(L) || (T = !1, y > (d.get(L) ?? 0) && d.set(L, y));
|
|
71
71
|
if (!T) {
|
|
72
72
|
D(P, m.local);
|
|
73
73
|
continue;
|
|
@@ -80,9 +80,9 @@ function ht(t, n, o, s, e, i = Number.POSITIVE_INFINITY) {
|
|
|
80
80
|
}
|
|
81
81
|
let w = !1;
|
|
82
82
|
for (; l.size > 0; ) {
|
|
83
|
-
const y = l.pop(),
|
|
84
|
-
if (D(
|
|
85
|
-
const P = t.get(
|
|
83
|
+
const y = l.pop(), u = Math.floor(y / o), m = y - u * o;
|
|
84
|
+
if (D(u, m), !w) {
|
|
85
|
+
const P = t.get(u), E = P == null ? void 0 : P.radTree, C = E ? E.childCount[m] : 0;
|
|
86
86
|
if (C > 0) {
|
|
87
87
|
const O = E.childStart[m], x = Math.floor(O / o), U = Math.floor((O + C - 1) / o);
|
|
88
88
|
let I = !0;
|
|
@@ -94,9 +94,9 @@ function ht(t, n, o, s, e, i = Number.POSITIVE_INFINITY) {
|
|
|
94
94
|
}
|
|
95
95
|
const G = /* @__PURE__ */ new Map();
|
|
96
96
|
let Y = 0;
|
|
97
|
-
for (const [y,
|
|
98
|
-
G.set(y, Uint32Array.from(
|
|
99
|
-
return { selection: G, count: Y, touched:
|
|
97
|
+
for (const [y, u] of c)
|
|
98
|
+
G.set(y, Uint32Array.from(u)), Y += u.length;
|
|
99
|
+
return { selection: G, count: Y, touched: d, budgetClamped: k, refinable: w };
|
|
100
100
|
}
|
|
101
101
|
class ft {
|
|
102
102
|
constructor() {
|
|
@@ -129,13 +129,13 @@ class ft {
|
|
|
129
129
|
if (n.length > 0) {
|
|
130
130
|
n[0] = e, o[0] = i;
|
|
131
131
|
const c = n.length;
|
|
132
|
-
let
|
|
132
|
+
let d = 0;
|
|
133
133
|
for (; ; ) {
|
|
134
|
-
const l = 2 *
|
|
135
|
-
let h =
|
|
136
|
-
if (l < c && o[l] > o[h] && (h = l), p < c && o[p] > o[h] && (h = p), h ===
|
|
137
|
-
const f = n[
|
|
138
|
-
n[
|
|
134
|
+
const l = 2 * d + 1, p = l + 1;
|
|
135
|
+
let h = d;
|
|
136
|
+
if (l < c && o[l] > o[h] && (h = l), p < c && o[p] > o[h] && (h = p), h === d) break;
|
|
137
|
+
const f = n[d], r = o[d];
|
|
138
|
+
n[d] = n[h], o[d] = o[h], n[h] = f, o[h] = r, d = h;
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
return s;
|
|
@@ -145,8 +145,8 @@ function nt(t) {
|
|
|
145
145
|
return [0, 3, 8, 15][t] ?? 0;
|
|
146
146
|
}
|
|
147
147
|
const ot = 10, z = 1 << ot, q = 1e-18, st = (t, n) => {
|
|
148
|
-
const o = n * 6, s = t[o], e = t[o + 1], i = t[o + 2], c = t[o + 3],
|
|
149
|
-
return s * (c * l -
|
|
148
|
+
const o = n * 6, s = t[o], e = t[o + 1], i = t[o + 2], c = t[o + 3], d = t[o + 4], l = t[o + 5];
|
|
149
|
+
return s * (c * l - d * d) - e * (e * l - d * i) + i * (e * d - c * i);
|
|
150
150
|
}, pt = (t, n, o) => {
|
|
151
151
|
const s = t[o * 4 + 3] / 255, e = Math.sqrt(Math.max(q, st(n, o)));
|
|
152
152
|
return Math.max(q, s * e);
|
|
@@ -156,7 +156,7 @@ const ot = 10, z = 1 << ot, q = 1e-18, st = (t, n) => {
|
|
|
156
156
|
}, W = (t) => {
|
|
157
157
|
let n = t & 1023;
|
|
158
158
|
return n = (n | n << 16) & 50331903, n = (n | n << 8) & 50393103, n = (n | n << 4) & 51130563, n = (n | n << 2) & 153391689, n >>> 0;
|
|
159
|
-
},
|
|
159
|
+
}, dt = (t, n, o) => (W(t) | W(n) << 1 | W(o) << 2) >>> 0, ut = (t, n) => {
|
|
160
160
|
const o = [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY], s = [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY];
|
|
161
161
|
for (let p = 0; p < n; p++) {
|
|
162
162
|
const h = p * 3;
|
|
@@ -165,7 +165,7 @@ const ot = 10, z = 1 << ot, q = 1e-18, st = (t, n) => {
|
|
|
165
165
|
o[f] = Math.min(o[f], r), s[f] = Math.max(s[f], r);
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
|
-
let e = new Uint32Array(n), i = new Uint32Array(n), c = new Uint32Array(n),
|
|
168
|
+
let e = new Uint32Array(n), i = new Uint32Array(n), c = new Uint32Array(n), d = new Uint32Array(n);
|
|
169
169
|
const l = o.map(
|
|
170
170
|
(p, h) => (z - 1) / Math.max(1e-12, s[h] - p)
|
|
171
171
|
);
|
|
@@ -189,7 +189,7 @@ const ot = 10, z = 1 << ot, q = 1e-18, st = (t, n) => {
|
|
|
189
189
|
Math.floor((t[h + 2] - o[2]) * l[2])
|
|
190
190
|
)
|
|
191
191
|
);
|
|
192
|
-
e[p] = p, c[p] =
|
|
192
|
+
e[p] = p, c[p] = dt(f, r, a);
|
|
193
193
|
}
|
|
194
194
|
for (let p = 0; p < 30; p += ot) {
|
|
195
195
|
const h = new Uint32Array(z);
|
|
@@ -204,9 +204,9 @@ const ot = 10, z = 1 << ot, q = 1e-18, st = (t, n) => {
|
|
|
204
204
|
}
|
|
205
205
|
for (let r = 0; r < n; r++) {
|
|
206
206
|
const a = c[r], M = a >>> p & z - 1, b = h[M];
|
|
207
|
-
h[M] = b + 1,
|
|
207
|
+
h[M] = b + 1, d[b] = a, i[b] = e[r];
|
|
208
208
|
}
|
|
209
|
-
[c,
|
|
209
|
+
[c, d] = [d, c], [e, i] = [i, e];
|
|
210
210
|
}
|
|
211
211
|
return e;
|
|
212
212
|
};
|
|
@@ -261,11 +261,11 @@ const B = (t) => t.shPacked ? nt(t.shPacked.bands) : 0, mt = (t, n) => ({
|
|
|
261
261
|
);
|
|
262
262
|
}
|
|
263
263
|
}, ct = (t, n, o, s, e) => {
|
|
264
|
-
let i = 0, c = n,
|
|
264
|
+
let i = 0, c = n, d = -1;
|
|
265
265
|
const l = [0, 0, 0], p = [0, 0, 0];
|
|
266
266
|
for (let a = n; a < o; a++) {
|
|
267
267
|
const M = t.masses[a];
|
|
268
|
-
i += M, M >
|
|
268
|
+
i += M, M > d && (d = M, c = a);
|
|
269
269
|
for (let b = 0; b < 3; b++)
|
|
270
270
|
l[b] = l[b] + t.positions[a * 3 + b] * M, p[b] = p[b] + t.colors[a * 4 + b] * M;
|
|
271
271
|
}
|
|
@@ -305,8 +305,8 @@ const B = (t) => t.shPacked ? nt(t.shPacked.bands) : 0, mt = (t, n) => ({
|
|
|
305
305
|
}
|
|
306
306
|
const s = Math.ceil(t.count / o.count), e = mt(s, t);
|
|
307
307
|
for (let i = 0; i < o.count; i++) {
|
|
308
|
-
const c = Math.floor(i * t.count / o.count),
|
|
309
|
-
e.count =
|
|
308
|
+
const c = Math.floor(i * t.count / o.count), d = Math.max(c + 1, Math.floor((i + 1) * t.count / o.count));
|
|
309
|
+
e.count = d - c;
|
|
310
310
|
for (let l = 0; l < e.count; l++)
|
|
311
311
|
g(t, n[c + l], e, l);
|
|
312
312
|
ct(e, 0, e.count, o, i);
|
|
@@ -323,17 +323,17 @@ function Mt(t, n, o) {
|
|
|
323
323
|
return { data: t, contentSplatCount: 0, finestSplatCount: 0, roots: new Uint32Array() };
|
|
324
324
|
const s = Math.max(1, Math.min(t.count, Math.floor(n)));
|
|
325
325
|
o == null || o({ completed: 0, total: t.count });
|
|
326
|
-
const e =
|
|
326
|
+
const e = ut(t.positions, t.count), i = [s];
|
|
327
327
|
for (; (i.at(-1) ?? 0) > 1; )
|
|
328
328
|
i.push(Math.ceil(i.at(-1) / 2));
|
|
329
|
-
const c = i.reduce((k, w) => k + w, 0),
|
|
329
|
+
const c = i.reduce((k, w) => k + w, 0), d = new Float32Array(c * 3), l = new Uint8Array(c * 4), p = new Float32Array(c * 6), h = new Float64Array(c), f = new Uint32Array(c), r = new Uint16Array(c), a = new Uint32Array(c), M = new Float32Array(c), b = t.sh ? new Uint32Array(c) : void 0, N = B(t), S = t.shPacked ? new Uint32Array(c * N) : void 0, v = [];
|
|
330
330
|
let F = 0;
|
|
331
331
|
const A = i.map((k) => {
|
|
332
332
|
v.push(F);
|
|
333
333
|
const w = F;
|
|
334
334
|
return F += k, {
|
|
335
335
|
count: k,
|
|
336
|
-
positions:
|
|
336
|
+
positions: d.subarray(w * 3, F * 3),
|
|
337
337
|
colors: l.subarray(w * 4, F * 4),
|
|
338
338
|
covariances: p.subarray(w * 6, F * 6),
|
|
339
339
|
masses: h.subarray(w, F),
|
|
@@ -351,20 +351,20 @@ function Mt(t, n, o) {
|
|
|
351
351
|
yt(A[k - 1], A[k]);
|
|
352
352
|
for (let k = 1; k < A.length; k++) {
|
|
353
353
|
const w = A[k], G = A[k - 1], Y = v[k], y = v[k - 1];
|
|
354
|
-
for (let
|
|
355
|
-
const m = Math.min(2, G.count -
|
|
356
|
-
r[Y +
|
|
354
|
+
for (let u = 0; u < w.count; u++) {
|
|
355
|
+
const m = Math.min(2, G.count - u * 2);
|
|
356
|
+
r[Y + u] = m, a[Y + u] = y + u * 2;
|
|
357
357
|
}
|
|
358
358
|
}
|
|
359
359
|
const R = { childCount: r, childStart: a, size: M }, D = t.sh && b ? { ...t.sh, labels: b } : void 0, V = t.shPacked && S ? { ...t.shPacked, packed: S } : void 0, _ = {
|
|
360
360
|
count: c,
|
|
361
|
-
positions:
|
|
361
|
+
positions: d,
|
|
362
362
|
colors: l,
|
|
363
363
|
covariances: p,
|
|
364
364
|
...D ? { sh: D } : {},
|
|
365
365
|
...V ? { shPacked: V } : {},
|
|
366
366
|
...t.antialias === void 0 ? {} : { antialias: t.antialias },
|
|
367
|
-
...t.
|
|
367
|
+
...t.format === void 0 ? {} : { format: t.format },
|
|
368
368
|
radTree: R
|
|
369
369
|
};
|
|
370
370
|
return o == null || o({ completed: t.count, total: t.count }), {
|
|
@@ -383,7 +383,7 @@ const kt = (t) => (J(t), {
|
|
|
383
383
|
...t.sh ? { sh: { ...t.sh, labels: t.sh.labels.slice() } } : {},
|
|
384
384
|
...t.shPacked ? { shPacked: { ...t.shPacked, packed: t.shPacked.packed.slice() } } : {},
|
|
385
385
|
...t.antialias === void 0 ? {} : { antialias: t.antialias },
|
|
386
|
-
...t.
|
|
386
|
+
...t.format === void 0 ? {} : { format: t.format }
|
|
387
387
|
});
|
|
388
388
|
function wt(t, n) {
|
|
389
389
|
try {
|
|
@@ -445,7 +445,7 @@ self.onmessage = (t) => {
|
|
|
445
445
|
j(n);
|
|
446
446
|
});
|
|
447
447
|
};
|
|
448
|
-
//# sourceMappingURL=static-lod-worker-
|
|
448
|
+
//# sourceMappingURL=static-lod-worker-CjrXUR8x.js.map
|
|
449
449
|
`, o = typeof self < "u" && self.Blob && new Blob(["URL.revokeObjectURL(import.meta.url);", a], { type: "text/javascript;charset=utf-8" });
|
|
450
450
|
function r(n) {
|
|
451
451
|
let t;
|
|
@@ -471,4 +471,4 @@ function r(n) {
|
|
|
471
471
|
export {
|
|
472
472
|
r as default
|
|
473
473
|
};
|
|
474
|
-
//# sourceMappingURL=static-lod-worker-
|
|
474
|
+
//# sourceMappingURL=static-lod-worker-DktKnJkR.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"static-lod-worker-
|
|
1
|
+
{"version":3,"file":"static-lod-worker-DktKnJkR.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
var S = Object.defineProperty;
|
|
2
|
+
var V = (s, r, e) => r in s ? S(s, r, { enumerable: !0, configurable: !0, writable: !0, value: e }) : s[r] = e;
|
|
3
|
+
var i = (s, r, e) => V(s, typeof r != "symbol" ? r + "" : r, e);
|
|
4
|
+
import * as g from "three/webgpu";
|
|
5
|
+
import { loadSplatData as L } from "./loaders.js";
|
|
6
|
+
import { S as P } from "./splat-mesh-aQdcWA6v.js";
|
|
7
|
+
const b = 4096, A = 250, x = (s) => {
|
|
8
|
+
const r = [
|
|
9
|
+
s.positions.buffer,
|
|
10
|
+
s.colors.buffer,
|
|
11
|
+
s.covariances.buffer
|
|
12
|
+
];
|
|
13
|
+
return s.sh && r.push(s.sh.labels.buffer), s.shPacked && r.push(s.shPacked.packed.buffer), r;
|
|
14
|
+
}, w = (s, r) => {
|
|
15
|
+
if (!Number.isFinite(s) || s <= 0)
|
|
16
|
+
throw new RangeError("StaticLodSplatMesh budget must be a positive finite number.");
|
|
17
|
+
const e = Math.min(r, Math.floor(s));
|
|
18
|
+
return e === r || e < b ? Math.max(1, e) : Math.max(
|
|
19
|
+
b,
|
|
20
|
+
Math.floor(e / b) * b
|
|
21
|
+
);
|
|
22
|
+
}, E = async (s, r, e) => {
|
|
23
|
+
const n = (await import("./static-lod-worker-DktKnJkR.js")).default, o = new n();
|
|
24
|
+
return await new Promise((c, d) => {
|
|
25
|
+
const a = () => {
|
|
26
|
+
o.terminate(), d(new DOMException("Static LOD build aborted.", "AbortError"));
|
|
27
|
+
};
|
|
28
|
+
if (e != null && e.aborted) {
|
|
29
|
+
a();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
e == null || e.addEventListener("abort", a, { once: !0 }), o.onerror = (l) => {
|
|
33
|
+
e == null || e.removeEventListener("abort", a), o.terminate(), d(new Error(l.message));
|
|
34
|
+
}, o.onmessage = (l) => {
|
|
35
|
+
var f, m;
|
|
36
|
+
const u = l.data;
|
|
37
|
+
if (u.type === "progress") {
|
|
38
|
+
(f = r.onLodProgress) == null || f.call(r, u.progress), (m = r.onAutoLodProgress) == null || m.call(r, { phase: "lod-build", ...u.progress });
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (u.type === "error") {
|
|
42
|
+
e == null || e.removeEventListener("abort", a), o.terminate(), d(new Error(u.message));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
u.type === "built" && (e == null || e.removeEventListener("abort", a), c({
|
|
46
|
+
data: u.data,
|
|
47
|
+
contentSplatCount: u.contentSplatCount,
|
|
48
|
+
finestSplatCount: u.finestSplatCount,
|
|
49
|
+
worker: o
|
|
50
|
+
}));
|
|
51
|
+
};
|
|
52
|
+
const h = {
|
|
53
|
+
type: "build",
|
|
54
|
+
source: s,
|
|
55
|
+
maxBudget: r.maxBudget
|
|
56
|
+
};
|
|
57
|
+
o.postMessage(h, x(s));
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
class p extends P {
|
|
61
|
+
constructor(e, t, n, o, c) {
|
|
62
|
+
super(e, c);
|
|
63
|
+
i(this, "worker");
|
|
64
|
+
i(this, "ceilingValue");
|
|
65
|
+
i(this, "contentCountValue");
|
|
66
|
+
i(this, "budgetValue");
|
|
67
|
+
i(this, "sequence", 0);
|
|
68
|
+
i(this, "lastScheduleAt", -1 / 0);
|
|
69
|
+
i(this, "selectionPending", !1);
|
|
70
|
+
i(this, "cameraLocal", new g.Vector3());
|
|
71
|
+
i(this, "cameraForward", new g.Vector3(0, 0, -1));
|
|
72
|
+
i(this, "inverseWorld", new g.Matrix4());
|
|
73
|
+
this.worker = o, this.ceilingValue = n, this.contentCountValue = t, this.budgetValue = w(c.budget ?? this.ceilingValue, this.ceilingValue), this.replaceActivePrefix(this.ceilingValue), this.worker.onmessage = (d) => {
|
|
74
|
+
const a = d.data;
|
|
75
|
+
if (a.type === "error") {
|
|
76
|
+
this.selectionPending = !1;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
a.type !== "selection" || a.sequence !== this.sequence || (this.selectionPending = !1, this.replaceActiveIndices(a.indices));
|
|
80
|
+
}, this.budgetValue < this.ceilingValue && this.scheduleSelection(!0);
|
|
81
|
+
}
|
|
82
|
+
/** Loads, decodes and builds a merged static hierarchy without blocking UI. */
|
|
83
|
+
static async load(e, t) {
|
|
84
|
+
var d, a;
|
|
85
|
+
let n = !1;
|
|
86
|
+
const o = await L(e, {
|
|
87
|
+
...t,
|
|
88
|
+
onProgress: (h, l) => {
|
|
89
|
+
var u, f, m;
|
|
90
|
+
(u = t.onProgress) == null || u.call(t, h, l), (f = t.onAutoLodProgress) == null || f.call(t, { phase: "download", completed: h, total: l }), !n && l > 0 && h >= l && (n = !0, (m = t.onAutoLodProgress) == null || m.call(t, { phase: "decode", completed: 0, total: 1 }));
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
n || (d = t.onAutoLodProgress) == null || d.call(t, { phase: "decode", completed: 0, total: 1 }), (a = t.onAutoLodProgress) == null || a.call(t, { phase: "decode", completed: 1, total: 1 });
|
|
94
|
+
const c = await E(o, t, t.signal);
|
|
95
|
+
return new p(
|
|
96
|
+
c.data,
|
|
97
|
+
c.contentSplatCount,
|
|
98
|
+
c.finestSplatCount,
|
|
99
|
+
c.worker,
|
|
100
|
+
t
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
get budget() {
|
|
104
|
+
return this.budgetValue;
|
|
105
|
+
}
|
|
106
|
+
get budgetCeiling() {
|
|
107
|
+
return this.ceilingValue;
|
|
108
|
+
}
|
|
109
|
+
get maxBudget() {
|
|
110
|
+
return this.ceilingValue;
|
|
111
|
+
}
|
|
112
|
+
get contentSplatCount() {
|
|
113
|
+
return this.contentCountValue;
|
|
114
|
+
}
|
|
115
|
+
/** Changes the selected frontier budget within the immutable build ceiling. */
|
|
116
|
+
setBudget(e) {
|
|
117
|
+
const t = w(e, this.ceilingValue);
|
|
118
|
+
return t === this.budgetValue || (this.budgetValue = t, this.selectionPending = !1, this.scheduleSelection(!0)), t;
|
|
119
|
+
}
|
|
120
|
+
update(e, t, n = {}) {
|
|
121
|
+
super.update(e, t, n), this.updateLod(e);
|
|
122
|
+
}
|
|
123
|
+
/** Schedules a camera-aware cut when a unified renderer owns drawing/sorting. */
|
|
124
|
+
updateLod(e) {
|
|
125
|
+
e.updateMatrixWorld(), this.updateWorldMatrix(!0, !1), this.inverseWorld.copy(this.matrixWorld).invert(), this.cameraLocal.setFromMatrixPosition(e.matrixWorld).applyMatrix4(this.inverseWorld), e.getWorldDirection(this.cameraForward).transformDirection(this.inverseWorld), this.scheduleSelection(!1);
|
|
126
|
+
}
|
|
127
|
+
dispose() {
|
|
128
|
+
this.disposed || (this.worker.terminate(), super.dispose());
|
|
129
|
+
}
|
|
130
|
+
scheduleSelection(e) {
|
|
131
|
+
if (this.disposed || this.selectionPending) return;
|
|
132
|
+
const t = performance.now();
|
|
133
|
+
if (!e && t - this.lastScheduleAt < A) return;
|
|
134
|
+
this.lastScheduleAt = t, this.selectionPending = !0;
|
|
135
|
+
const n = {
|
|
136
|
+
type: "select",
|
|
137
|
+
sequence: ++this.sequence,
|
|
138
|
+
budget: this.budgetValue,
|
|
139
|
+
cameraLocal: this.cameraLocal.toArray(),
|
|
140
|
+
cameraForward: this.cameraForward.toArray()
|
|
141
|
+
};
|
|
142
|
+
this.worker.postMessage(n);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
export {
|
|
146
|
+
p as StaticLodSplatMesh
|
|
147
|
+
};
|
|
148
|
+
//# sourceMappingURL=static-lod.js.map
|