@adrianmaj/gaussian-splats-3d
Version:
Forked Mark Kellogg 3D viewer with customizable container styles
1 lines • 1.22 MB
Source Map (JSON)
{"version":3,"file":"gaussian-splats-3d.umd.cjs","sources":["../src/AbortablePromise.js","../src/Util.js","../src/loaders/UncompressedSplatArray.js","../src/Constants.js","../src/loaders/SplatBuffer.js","../src/loaders/ply/PlayCanvasCompressedPlyParser.js","../src/loaders/ply/PlyFormat.js","../src/loaders/ply/PlyParserUtils.js","../src/loaders/ply/INRIAV1PlyParser.js","../src/loaders/ply/INRIAV2PlyParser.js","../src/loaders/ply/PlyParser.js","../src/loaders/SplatPartitioner.js","../src/loaders/SplatBufferGenerator.js","../src/loaders/LoaderStatus.js","../src/loaders/DirectLoadError.js","../src/loaders/InternalLoadType.js","../src/loaders/ply/PlyLoader.js","../src/loaders/Compression.js","../src/loaders/spz/SpzLoader.js","../src/loaders/splat/SplatParser.js","../src/loaders/splat/SplatLoader.js","../src/loaders/ksplat/KSplatLoader.js","../src/loaders/SceneFormat.js","../src/loaders/Utils.js","../src/OrbitControls.js","../src/ui/Util.js","../src/ui/LoadingSpinner.js","../src/ui/LoadingProgressBar.js","../src/ui/InfoPanel.js","../src/ArrowHelper.js","../src/SceneHelper.js","../src/raycaster/Ray.js","../src/raycaster/Hit.js","../src/SplatRenderMode.js","../src/raycaster/Raycaster.js","../src/splatmesh/SplatMaterial.js","../src/splatmesh/SplatMaterial3D.js","../src/splatmesh/SplatMaterial2D.js","../src/splatmesh/SplatGeometry.js","../src/splatmesh/SplatScene.js","../src/splattree/SplatTree.js","../src/three-shim/WebGLExtensions.js","../src/three-shim/WebGLCapabilities.js","../src/SceneRevealMode.js","../src/LogLevel.js","../src/splatmesh/SplatMesh.js","../src/worker/sorter.wasm","../src/worker/sorter_no_simd.wasm","../src/worker/sorter_non_shared.wasm","../src/worker/sorter_no_simd_non_shared.wasm","../src/worker/SortWorker.js","../src/webxr/WebXRMode.js","../src/webxr/VRButton.js","../src/webxr/ARButton.js","../src/RenderMode.js","../src/ui/GSVisionLogo.js","../src/ui/Controls.js","../src/ui/Presets.js","../src/Viewer.js","../src/DropInViewer.js"],"sourcesContent":["/**\n * AbortablePromise: A quick & dirty wrapper for JavaScript's Promise class that allows the underlying\n * asynchronous operation to be cancelled. It is only meant for simple situations where no complex promise\n * chaining or merging occurs. It needs a significant amount of work to truly replicate the full\n * functionality of JavaScript's Promise class. Look at Util.fetchWithProgress() for example usage.\n *\n * This class was primarily added to allow splat scene downloads to be cancelled. It has not been tested\n * very thoroughly and the implementation is kinda janky. If you can at all help it, please avoid using it :)\n */\nexport class AbortablePromise {\n\tstatic idGen = 0;\n\n\tconstructor(promiseFunc, abortHandler) {\n\t\tlet resolver;\n\t\tlet rejecter;\n\t\tthis.promise = new Promise((resolve, reject) => {\n\t\t\tresolver = resolve;\n\t\t\trejecter = reject;\n\t\t});\n\n\t\tconst promiseResolve = resolver.bind(this);\n\t\tconst promiseReject = rejecter.bind(this);\n\n\t\tconst resolve = (...args) => {\n\t\t\tpromiseResolve(...args);\n\t\t};\n\n\t\tconst reject = (error) => {\n\t\t\tpromiseReject(error);\n\t\t};\n\n\t\tpromiseFunc(resolve.bind(this), reject.bind(this));\n\t\tthis.abortHandler = abortHandler;\n\t\tthis.id = AbortablePromise.idGen++;\n\t}\n\n\tthen(onResolve) {\n\t\treturn new AbortablePromise((resolve, reject) => {\n\t\t\tthis.promise = this.promise\n\t\t\t\t.then((...args) => {\n\t\t\t\t\tconst onResolveResult = onResolve(...args);\n\t\t\t\t\tif (onResolveResult instanceof Promise || onResolveResult instanceof AbortablePromise) {\n\t\t\t\t\t\tonResolveResult.then((...args2) => {\n\t\t\t\t\t\t\tresolve(...args2);\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresolve(onResolveResult);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch((error) => {\n\t\t\t\t\treject(error);\n\t\t\t\t});\n\t\t}, this.abortHandler);\n\t}\n\n\tcatch(onFail) {\n\t\treturn new AbortablePromise((resolve) => {\n\t\t\tthis.promise = this.promise\n\t\t\t\t.then((...args) => {\n\t\t\t\t\tresolve(...args);\n\t\t\t\t})\n\t\t\t\t.catch(onFail);\n\t\t}, this.abortHandler);\n\t}\n\n\tabort(reason) {\n\t\tif (this.abortHandler) this.abortHandler(reason);\n\t}\n}\n\nexport class AbortedPromiseError extends Error {\n\tconstructor(msg) {\n\t\tsuper(msg);\n\t}\n}\n","import { AbortablePromise, AbortedPromiseError } from './AbortablePromise.js';\n\nexport const floatToHalf = (function() {\n\tconst floatView = new Float32Array(1);\n\tconst int32View = new Int32Array(floatView.buffer);\n\n\treturn function(val) {\n\t\tfloatView[0] = val;\n\t\tconst x = int32View[0];\n\n\t\tlet bits = (x >> 16) & 0x8000;\n\t\tlet m = (x >> 12) & 0x07ff;\n\t\tconst e = (x >> 23) & 0xff;\n\n\t\tif (e < 103) return bits;\n\n\t\tif (e > 142) {\n\t\t\tbits |= 0x7c00;\n\t\t\tbits |= (e == 255 ? 0 : 1) && x & 0x007fffff;\n\t\t\treturn bits;\n\t\t}\n\n\t\tif (e < 113) {\n\t\t\tm |= 0x0800;\n\t\t\tbits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1);\n\t\t\treturn bits;\n\t\t}\n\n\t\tbits |= ((e - 112) << 10) | (m >> 1);\n\t\tbits += m & 1;\n\t\treturn bits;\n\t};\n})();\n\nexport const uintEncodedFloat = (function() {\n\tconst floatView = new Float32Array(1);\n\tconst int32View = new Int32Array(floatView.buffer);\n\n\treturn function(f) {\n\t\tfloatView[0] = f;\n\t\treturn int32View[0];\n\t};\n})();\n\nexport const rgbaToInteger = function(r, g, b, a) {\n\treturn r + (g << 8) + (b << 16) + (a << 24);\n};\n\nexport const rgbaArrayToInteger = function(arr, offset) {\n\treturn arr[offset] + (arr[offset + 1] << 8) + (arr[offset + 2] << 16) + (arr[offset + 3] << 24);\n};\n\nexport const fetchWithProgress = function(path, onProgress, saveChunks = true, headers) {\n\tconst abortController = new AbortController();\n\tconst signal = abortController.signal;\n\tlet aborted = false;\n\tconst abortHandler = (reason) => {\n\t\tabortController.abort(reason);\n\t\taborted = true;\n\t};\n\n\tlet onProgressCalledAtComplete = false;\n\tconst localOnProgress = (percent, percentLabel, chunk, fileSize) => {\n\t\tif (onProgress && !onProgressCalledAtComplete) {\n\t\t\tonProgress(percent, percentLabel, chunk, fileSize);\n\t\t\tif (percent === 100) {\n\t\t\t\tonProgressCalledAtComplete = true;\n\t\t\t}\n\t\t}\n\t};\n\n\treturn new AbortablePromise((resolve, reject) => {\n\t\tconst fetchOptions = { signal };\n\t\tif (headers) fetchOptions.headers = headers;\n\t\tfetch(path, fetchOptions)\n\t\t\t.then(async (data) => {\n\t\t\t\t// Handle error conditions where data is still returned\n\t\t\t\tif (!data.ok) {\n\t\t\t\t\tconst errorText = await data.text();\n\t\t\t\t\treject(new Error(`Fetch failed: ${data.status} ${data.statusText} ${errorText}`));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst reader = data.body.getReader();\n\t\t\t\tlet bytesDownloaded = 0;\n\t\t\t\tlet _fileSize = data.headers.get('Content-Length');\n\t\t\t\tlet fileSize = _fileSize ? parseInt(_fileSize) : undefined;\n\n\t\t\t\tconst chunks = [];\n\n\t\t\t\twhile (!aborted) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst { value: chunk, done } = await reader.read();\n\t\t\t\t\t\tif (done) {\n\t\t\t\t\t\t\tlocalOnProgress(100, '100%', chunk, fileSize);\n\t\t\t\t\t\t\tif (saveChunks) {\n\t\t\t\t\t\t\t\tconst buffer = new Blob(chunks).arrayBuffer();\n\t\t\t\t\t\t\t\tresolve(buffer);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tresolve();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbytesDownloaded += chunk.length;\n\t\t\t\t\t\tlet percent;\n\t\t\t\t\t\tlet percentLabel;\n\t\t\t\t\t\tif (fileSize !== undefined) {\n\t\t\t\t\t\t\tpercent = (bytesDownloaded / fileSize) * 100;\n\t\t\t\t\t\t\tpercentLabel = `${percent.toFixed(2)}%`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (saveChunks) {\n\t\t\t\t\t\t\tchunks.push(chunk);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlocalOnProgress(percent, percentLabel, chunk, fileSize);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\treject(error);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})\n\t\t\t.catch((error) => {\n\t\t\t\treject(new AbortedPromiseError(error));\n\t\t\t});\n\t}, abortHandler);\n};\n\nexport const clamp = function(val, min, max) {\n\treturn Math.max(Math.min(val, max), min);\n};\n\nexport const getCurrentTime = function() {\n\treturn performance.now() / 1000;\n};\n\nexport const disposeAllMeshes = (object3D) => {\n\tif (object3D.geometry) {\n\t\tobject3D.geometry.dispose();\n\t\tobject3D.geometry = null;\n\t}\n\tif (object3D.material) {\n\t\tobject3D.material.dispose();\n\t\tobject3D.material = null;\n\t}\n\tif (object3D.children) {\n\t\tfor (let child of object3D.children) {\n\t\t\tdisposeAllMeshes(child);\n\t\t}\n\t}\n};\n\nexport const delayedExecute = (func, fast) => {\n\treturn new Promise((resolve) => {\n\t\twindow.setTimeout(\n\t\t\t() => {\n\t\t\t\tresolve(func ? func() : undefined);\n\t\t\t},\n\t\t\tfast ? 1 : 50,\n\t\t);\n\t});\n};\n\nexport const getSphericalHarmonicsComponentCountForDegree = (sphericalHarmonicsDegree = 0) => {\n\tlet shCoeffPerSplat = 0;\n\tif (sphericalHarmonicsDegree === 1) {\n\t\tshCoeffPerSplat = 9;\n\t} else if (sphericalHarmonicsDegree === 2) {\n\t\tshCoeffPerSplat = 24;\n\t} else if (sphericalHarmonicsDegree === 3) {\n\t\tshCoeffPerSplat = 45;\n\t} else if (sphericalHarmonicsDegree > 3) {\n\t\tthrow new Error('getSphericalHarmonicsComponentCountForDegree() -> Invalid spherical harmonics degree');\n\t}\n\treturn shCoeffPerSplat;\n};\n\nexport const nativePromiseWithExtractedComponents = () => {\n\tlet resolver;\n\tlet rejecter;\n\tconst promise = new Promise((resolve, reject) => {\n\t\tresolver = resolve;\n\t\trejecter = reject;\n\t});\n\treturn {\n\t\tpromise: promise,\n\t\tresolve: resolver,\n\t\treject: rejecter,\n\t};\n};\n\nexport const abortablePromiseWithExtractedComponents = (abortHandler) => {\n\tlet resolver;\n\tlet rejecter;\n\tif (!abortHandler) {\n\t\tabortHandler = () => {};\n\t}\n\tconst promise = new AbortablePromise((resolve, reject) => {\n\t\tresolver = resolve;\n\t\trejecter = reject;\n\t}, abortHandler);\n\treturn {\n\t\tpromise: promise,\n\t\tresolve: resolver,\n\t\treject: rejecter,\n\t};\n};\n\nclass Semver {\n\tconstructor(major, minor, patch) {\n\t\tthis.major = major;\n\t\tthis.minor = minor;\n\t\tthis.patch = patch;\n\t}\n\n\ttoString() {\n\t\treturn `${this.major}_${this.minor}_${this.patch}`;\n\t}\n}\n\nexport function isIOS() {\n\tconst ua = navigator.userAgent;\n\treturn ua.indexOf('iPhone') > 0 || ua.indexOf('iPad') > 0;\n}\n\nexport function getIOSSemever() {\n\tif (isIOS()) {\n\t\tconst extract = navigator.userAgent.match(/OS (\\d+)_(\\d+)_?(\\d+)?/);\n\t\treturn new Semver(\n\t\t\tparseInt(extract[1] || 0, 10),\n\t\t\tparseInt(extract[2] || 0, 10),\n\t\t\tparseInt(extract[3] || 0, 10),\n\t\t);\n\t} else {\n\t\treturn null; // or [0,0,0]\n\t}\n}\n","import { getSphericalHarmonicsComponentCountForDegree } from '../Util.js';\n\nconst BASE_COMPONENT_COUNT = 14;\n\nexport class UncompressedSplatArray {\n\tstatic OFFSET = {\n\t\tX: 0,\n\t\tY: 1,\n\t\tZ: 2,\n\t\tSCALE0: 3,\n\t\tSCALE1: 4,\n\t\tSCALE2: 5,\n\t\tROTATION0: 6,\n\t\tROTATION1: 7,\n\t\tROTATION2: 8,\n\t\tROTATION3: 9,\n\t\tFDC0: 10,\n\t\tFDC1: 11,\n\t\tFDC2: 12,\n\t\tOPACITY: 13,\n\t\tFRC0: 14,\n\t\tFRC1: 15,\n\t\tFRC2: 16,\n\t\tFRC3: 17,\n\t\tFRC4: 18,\n\t\tFRC5: 19,\n\t\tFRC6: 20,\n\t\tFRC7: 21,\n\t\tFRC8: 22,\n\t\tFRC9: 23,\n\t\tFRC10: 24,\n\t\tFRC11: 25,\n\t\tFRC12: 26,\n\t\tFRC13: 27,\n\t\tFRC14: 28,\n\t\tFRC15: 29,\n\t\tFRC16: 30,\n\t\tFRC17: 31,\n\t\tFRC18: 32,\n\t\tFRC19: 33,\n\t\tFRC20: 34,\n\t\tFRC21: 35,\n\t\tFRC22: 36,\n\t\tFRC23: 37,\n\t};\n\n\tconstructor(sphericalHarmonicsDegree = 0) {\n\t\tthis.sphericalHarmonicsDegree = sphericalHarmonicsDegree;\n\t\tthis.sphericalHarmonicsCount = getSphericalHarmonicsComponentCountForDegree(\n\t\t\tthis.sphericalHarmonicsDegree,\n\t\t);\n\t\tthis.componentCount = this.sphericalHarmonicsCount + BASE_COMPONENT_COUNT;\n\t\tthis.defaultSphericalHarmonics = new Array(this.sphericalHarmonicsCount).fill(0);\n\t\tthis.splats = [];\n\t\tthis.splatCount = 0;\n\t}\n\n\tstatic createSplat(sphericalHarmonicsDegree = 0) {\n\t\tconst baseSplat = [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0];\n\t\tlet shEntries = getSphericalHarmonicsComponentCountForDegree(sphericalHarmonicsDegree);\n\t\tfor (let i = 0; i < shEntries; i++) baseSplat.push(0);\n\t\treturn baseSplat;\n\t}\n\n\taddSplat(splat) {\n\t\tthis.splats.push(splat);\n\t\tthis.splatCount++;\n\t}\n\n\tgetSplat(index) {\n\t\treturn this.splats[index];\n\t}\n\n\taddDefaultSplat() {\n\t\tconst newSplat = UncompressedSplatArray.createSplat(this.sphericalHarmonicsDegree);\n\t\tthis.addSplat(newSplat);\n\t\treturn newSplat;\n\t}\n\n\taddSplatFromComonents(x, y, z, scale0, scale1, scale2, rot0, rot1, rot2, rot3, r, g, b, opacity, ...rest) {\n\t\tconst newSplat = [\n\t\t\tx,\n\t\t\ty,\n\t\t\tz,\n\t\t\tscale0,\n\t\t\tscale1,\n\t\t\tscale2,\n\t\t\trot0,\n\t\t\trot1,\n\t\t\trot2,\n\t\t\trot3,\n\t\t\tr,\n\t\t\tg,\n\t\t\tb,\n\t\t\topacity,\n\t\t\t...this.defaultSphericalHarmonics,\n\t\t];\n\t\tfor (let i = 0; i < rest.length && i < this.sphericalHarmonicsCount; i++) {\n\t\t\tnewSplat[i] = rest[i];\n\t\t}\n\t\tthis.addSplat(newSplat);\n\t\treturn newSplat;\n\t}\n\n\taddSplatFromArray(src, srcIndex) {\n\t\tconst srcSplat = src.splats[srcIndex];\n\t\tconst newSplat = UncompressedSplatArray.createSplat(this.sphericalHarmonicsDegree);\n\t\tfor (let i = 0; i < this.componentCount && i < srcSplat.length; i++) {\n\t\t\tnewSplat[i] = srcSplat[i];\n\t\t}\n\t\tthis.addSplat(newSplat);\n\t}\n}\n","export class Constants {\n\tstatic DefaultSplatSortDistanceMapPrecision = 16;\n\tstatic MemoryPageSize = 65536;\n\tstatic BytesPerFloat = 4;\n\tstatic BytesPerInt = 4;\n\tstatic MaxScenes = 32;\n\tstatic ProgressiveLoadSectionSize = 262144;\n\tstatic ProgressiveLoadSectionDelayDuration = 15;\n\tstatic SphericalHarmonics8BitCompressionRange = 3;\n}\n","import * as THREE from 'three';\nimport { UncompressedSplatArray } from './UncompressedSplatArray.js';\nimport { clamp, getSphericalHarmonicsComponentCountForDegree } from '../Util.js';\nimport { Constants } from '../Constants.js';\n\nconst DefaultSphericalHarmonics8BitCompressionRange = Constants.SphericalHarmonics8BitCompressionRange;\nconst DefaultSphericalHarmonics8BitCompressionHalfRange = DefaultSphericalHarmonics8BitCompressionRange / 2.0;\n\nconst toHalfFloat = THREE.DataUtils.toHalfFloat.bind(THREE.DataUtils);\nconst fromHalfFloat = THREE.DataUtils.fromHalfFloat.bind(THREE.DataUtils);\n\nconst toUncompressedFloat = (f, compressionLevel, isSH = false, range8BitMin, range8BitMax) => {\n\tif (compressionLevel === 0) {\n\t\treturn f;\n\t} else if (compressionLevel === 1 || (compressionLevel === 2 && !isSH)) {\n\t\treturn THREE.DataUtils.fromHalfFloat(f);\n\t} else if (compressionLevel === 2) {\n\t\treturn fromUint8(f, range8BitMin, range8BitMax);\n\t}\n};\n\nconst toUint8 = (v, rangeMin, rangeMax) => {\n\tv = clamp(v, rangeMin, rangeMax);\n\tconst range = rangeMax - rangeMin;\n\treturn clamp(Math.floor(((v - rangeMin) / range) * 255), 0, 255);\n};\n\nconst fromUint8 = (v, rangeMin, rangeMax) => {\n\tconst range = rangeMax - rangeMin;\n\treturn (v / 255) * range + rangeMin;\n};\n\nconst fromHalfFloatToUint8 = (v, rangeMin, rangeMax) => {\n\treturn toUint8(fromHalfFloat(v, rangeMin, rangeMax));\n};\n\nconst fromUint8ToHalfFloat = (v, rangeMin, rangeMax) => {\n\treturn toHalfFloat(fromUint8(v, rangeMin, rangeMax));\n};\n\nconst dataViewFloatForCompressionLevel = (dataView, floatIndex, compressionLevel, isSH = false) => {\n\tif (compressionLevel === 0) {\n\t\treturn dataView.getFloat32(floatIndex * 4, true);\n\t} else if (compressionLevel === 1 || (compressionLevel === 2 && !isSH)) {\n\t\treturn dataView.getUint16(floatIndex * 2, true);\n\t} else {\n\t\treturn dataView.getUint8(floatIndex, true);\n\t}\n};\n\nconst convertBetweenCompressionLevels = (function() {\n\tconst noop = (v) => v;\n\n\treturn function(val, fromLevel, toLevel, isSH = false) {\n\t\tif (fromLevel === toLevel) return val;\n\t\tlet outputConversionFunc = noop;\n\n\t\tif (fromLevel === 2 && isSH) {\n\t\t\tif (toLevel === 1) outputConversionFunc = fromUint8ToHalfFloat;\n\t\t\telse if (toLevel == 0) {\n\t\t\t\toutputConversionFunc = fromUint8;\n\t\t\t}\n\t\t} else if (fromLevel === 2 || fromLevel === 1) {\n\t\t\tif (toLevel === 0) outputConversionFunc = fromHalfFloat;\n\t\t\telse if (toLevel == 2) {\n\t\t\t\tif (!isSH) outputConversionFunc = noop;\n\t\t\t\telse outputConversionFunc = fromHalfFloatToUint8;\n\t\t\t}\n\t\t} else if (fromLevel === 0) {\n\t\t\tif (toLevel === 1) outputConversionFunc = toHalfFloat;\n\t\t\telse if (toLevel == 2) {\n\t\t\t\tif (!isSH) outputConversionFunc = toHalfFloat;\n\t\t\t\telse outputConversionFunc = toUint8;\n\t\t\t}\n\t\t}\n\n\t\treturn outputConversionFunc(val);\n\t};\n})();\n\nconst copyBetweenBuffers = (srcBuffer, srcOffset, destBuffer, destOffset, byteCount = 0) => {\n\tconst src = new Uint8Array(srcBuffer, srcOffset);\n\tconst dest = new Uint8Array(destBuffer, destOffset);\n\tfor (let i = 0; i < byteCount; i++) {\n\t\tdest[i] = src[i];\n\t}\n};\n\n/**\n * SplatBuffer: Container for splat data from a single scene/file and capable of (mediocre) compression.\n */\nexport class SplatBuffer {\n\tstatic CurrentMajorVersion = 0;\n\tstatic CurrentMinorVersion = 1;\n\n\tstatic CenterComponentCount = 3;\n\tstatic ScaleComponentCount = 3;\n\tstatic RotationComponentCount = 4;\n\tstatic ColorComponentCount = 4;\n\tstatic CovarianceComponentCount = 6;\n\n\tstatic SplatScaleOffsetFloat = 3;\n\tstatic SplatRotationOffsetFloat = 6;\n\n\tstatic CompressionLevels = {\n\t\t0: {\n\t\t\tBytesPerCenter: 12,\n\t\t\tBytesPerScale: 12,\n\t\t\tBytesPerRotation: 16,\n\t\t\tBytesPerColor: 4,\n\t\t\tScaleOffsetBytes: 12,\n\t\t\tRotationffsetBytes: 24,\n\t\t\tColorOffsetBytes: 40,\n\t\t\tSphericalHarmonicsOffsetBytes: 44,\n\t\t\tScaleRange: 1,\n\t\t\tBytesPerSphericalHarmonicsComponent: 4,\n\t\t\tSphericalHarmonicsOffsetFloat: 11,\n\t\t\tSphericalHarmonicsDegrees: {\n\t\t\t\t0: { BytesPerSplat: 44 },\n\t\t\t\t1: { BytesPerSplat: 80 },\n\t\t\t\t2: { BytesPerSplat: 140 },\n\t\t\t},\n\t\t},\n\t\t1: {\n\t\t\tBytesPerCenter: 6,\n\t\t\tBytesPerScale: 6,\n\t\t\tBytesPerRotation: 8,\n\t\t\tBytesPerColor: 4,\n\t\t\tScaleOffsetBytes: 6,\n\t\t\tRotationffsetBytes: 12,\n\t\t\tColorOffsetBytes: 20,\n\t\t\tSphericalHarmonicsOffsetBytes: 24,\n\t\t\tScaleRange: 32767,\n\t\t\tBytesPerSphericalHarmonicsComponent: 2,\n\t\t\tSphericalHarmonicsOffsetFloat: 12,\n\t\t\tSphericalHarmonicsDegrees: {\n\t\t\t\t0: { BytesPerSplat: 24 },\n\t\t\t\t1: { BytesPerSplat: 42 },\n\t\t\t\t2: { BytesPerSplat: 72 },\n\t\t\t},\n\t\t},\n\t\t2: {\n\t\t\tBytesPerCenter: 6,\n\t\t\tBytesPerScale: 6,\n\t\t\tBytesPerRotation: 8,\n\t\t\tBytesPerColor: 4,\n\t\t\tScaleOffsetBytes: 6,\n\t\t\tRotationffsetBytes: 12,\n\t\t\tColorOffsetBytes: 20,\n\t\t\tSphericalHarmonicsOffsetBytes: 24,\n\t\t\tScaleRange: 32767,\n\t\t\tBytesPerSphericalHarmonicsComponent: 1,\n\t\t\tSphericalHarmonicsOffsetFloat: 12,\n\t\t\tSphericalHarmonicsDegrees: {\n\t\t\t\t0: { BytesPerSplat: 24 },\n\t\t\t\t1: { BytesPerSplat: 33 },\n\t\t\t\t2: { BytesPerSplat: 48 },\n\t\t\t},\n\t\t},\n\t};\n\n\tstatic CovarianceSizeFloats = 6;\n\n\tstatic HeaderSizeBytes = 4096;\n\tstatic SectionHeaderSizeBytes = 1024;\n\n\tstatic BucketStorageSizeBytes = 12;\n\tstatic BucketStorageSizeFloats = 3;\n\n\tstatic BucketBlockSize = 5.0;\n\tstatic BucketSize = 256;\n\n\tconstructor(bufferData, secLoadedCountsToMax = true) {\n\t\tthis.constructFromBuffer(bufferData, secLoadedCountsToMax);\n\t}\n\n\tgetSplatCount() {\n\t\treturn this.splatCount;\n\t}\n\n\tgetMaxSplatCount() {\n\t\treturn this.maxSplatCount;\n\t}\n\n\tgetMinSphericalHarmonicsDegree() {\n\t\tlet minSphericalHarmonicsDegree = 0;\n\t\tfor (let i = 0; i < this.sections.length; i++) {\n\t\t\tconst section = this.sections[i];\n\t\t\tif (i === 0 || section.sphericalHarmonicsDegree < minSphericalHarmonicsDegree) {\n\t\t\t\tminSphericalHarmonicsDegree = section.sphericalHarmonicsDegree;\n\t\t\t}\n\t\t}\n\t\treturn minSphericalHarmonicsDegree;\n\t}\n\n\tgetBucketIndex(section, localSplatIndex) {\n\t\tlet bucketIndex;\n\t\tconst maxSplatIndexInFullBuckets = section.fullBucketCount * section.bucketSize;\n\t\tif (localSplatIndex < maxSplatIndexInFullBuckets) {\n\t\t\tbucketIndex = Math.floor(localSplatIndex / section.bucketSize);\n\t\t} else {\n\t\t\tlet bucketSplatIndex = maxSplatIndexInFullBuckets;\n\t\t\tbucketIndex = section.fullBucketCount;\n\t\t\tlet partiallyFullBucketIndex = 0;\n\t\t\twhile (bucketSplatIndex < section.splatCount) {\n\t\t\t\tlet currentPartiallyFilledBucketSize = section.partiallyFilledBucketLengths[partiallyFullBucketIndex];\n\t\t\t\tif (\n\t\t\t\t\tlocalSplatIndex >= bucketSplatIndex &&\n\t\t\t\t\tlocalSplatIndex < bucketSplatIndex + currentPartiallyFilledBucketSize\n\t\t\t\t) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tbucketSplatIndex += currentPartiallyFilledBucketSize;\n\t\t\t\tbucketIndex++;\n\t\t\t\tpartiallyFullBucketIndex++;\n\t\t\t}\n\t\t}\n\t\treturn bucketIndex;\n\t}\n\n\tgetSplatCenter(globalSplatIndex, outCenter, transform) {\n\t\tconst sectionIndex = this.globalSplatIndexToSectionMap[globalSplatIndex];\n\t\tconst section = this.sections[sectionIndex];\n\t\tconst localSplatIndex = globalSplatIndex - section.splatCountOffset;\n\n\t\tconst srcSplatCentersBase = section.bytesPerSplat * localSplatIndex;\n\t\tconst dataView = new DataView(this.bufferData, section.dataBase + srcSplatCentersBase);\n\n\t\tconst x = dataViewFloatForCompressionLevel(dataView, 0, this.compressionLevel);\n\t\tconst y = dataViewFloatForCompressionLevel(dataView, 1, this.compressionLevel);\n\t\tconst z = dataViewFloatForCompressionLevel(dataView, 2, this.compressionLevel);\n\t\tif (this.compressionLevel >= 1) {\n\t\t\tconst bucketIndex = this.getBucketIndex(section, localSplatIndex);\n\t\t\tconst bucketBase = bucketIndex * SplatBuffer.BucketStorageSizeFloats;\n\t\t\tconst sf = section.compressionScaleFactor;\n\t\t\tconst sr = section.compressionScaleRange;\n\t\t\toutCenter.x = (x - sr) * sf + section.bucketArray[bucketBase];\n\t\t\toutCenter.y = (y - sr) * sf + section.bucketArray[bucketBase + 1];\n\t\t\toutCenter.z = (z - sr) * sf + section.bucketArray[bucketBase + 2];\n\t\t} else {\n\t\t\toutCenter.x = x;\n\t\t\toutCenter.y = y;\n\t\t\toutCenter.z = z;\n\t\t}\n\t\tif (transform) outCenter.applyMatrix4(transform);\n\t}\n\n\tgetSplatScaleAndRotation = (function() {\n\t\tconst scaleMatrix = new THREE.Matrix4();\n\t\tconst rotationMatrix = new THREE.Matrix4();\n\t\tconst tempMatrix = new THREE.Matrix4();\n\t\tconst tempPosition = new THREE.Vector3();\n\t\tconst scale = new THREE.Vector3();\n\t\tconst rotation = new THREE.Quaternion();\n\n\t\treturn function(index, outScale, outRotation, transform, scaleOverride) {\n\t\t\tconst sectionIndex = this.globalSplatIndexToSectionMap[index];\n\t\t\tconst section = this.sections[sectionIndex];\n\t\t\tconst localSplatIndex = index - section.splatCountOffset;\n\n\t\t\tconst srcSplatScalesBase =\n\t\t\t\tsection.bytesPerSplat * localSplatIndex +\n\t\t\t\tSplatBuffer.CompressionLevels[this.compressionLevel].ScaleOffsetBytes;\n\n\t\t\tconst dataView = new DataView(this.bufferData, section.dataBase + srcSplatScalesBase);\n\n\t\t\tscale.set(\n\t\t\t\ttoUncompressedFloat(\n\t\t\t\t\tdataViewFloatForCompressionLevel(dataView, 0, this.compressionLevel),\n\t\t\t\t\tthis.compressionLevel,\n\t\t\t\t),\n\t\t\t\ttoUncompressedFloat(\n\t\t\t\t\tdataViewFloatForCompressionLevel(dataView, 1, this.compressionLevel),\n\t\t\t\t\tthis.compressionLevel,\n\t\t\t\t),\n\t\t\t\ttoUncompressedFloat(\n\t\t\t\t\tdataViewFloatForCompressionLevel(dataView, 2, this.compressionLevel),\n\t\t\t\t\tthis.compressionLevel,\n\t\t\t\t),\n\t\t\t);\n\t\t\tif (scaleOverride) {\n\t\t\t\tif (scaleOverride.x !== undefined) scale.x = scaleOverride.x;\n\t\t\t\tif (scaleOverride.y !== undefined) scale.y = scaleOverride.y;\n\t\t\t\tif (scaleOverride.z !== undefined) scale.z = scaleOverride.z;\n\t\t\t}\n\n\t\t\trotation.set(\n\t\t\t\ttoUncompressedFloat(\n\t\t\t\t\tdataViewFloatForCompressionLevel(dataView, 4, this.compressionLevel),\n\t\t\t\t\tthis.compressionLevel,\n\t\t\t\t),\n\t\t\t\ttoUncompressedFloat(\n\t\t\t\t\tdataViewFloatForCompressionLevel(dataView, 5, this.compressionLevel),\n\t\t\t\t\tthis.compressionLevel,\n\t\t\t\t),\n\t\t\t\ttoUncompressedFloat(\n\t\t\t\t\tdataViewFloatForCompressionLevel(dataView, 6, this.compressionLevel),\n\t\t\t\t\tthis.compressionLevel,\n\t\t\t\t),\n\t\t\t\ttoUncompressedFloat(\n\t\t\t\t\tdataViewFloatForCompressionLevel(dataView, 3, this.compressionLevel),\n\t\t\t\t\tthis.compressionLevel,\n\t\t\t\t),\n\t\t\t);\n\n\t\t\tif (transform) {\n\t\t\t\tscaleMatrix.makeScale(scale.x, scale.y, scale.z);\n\t\t\t\trotationMatrix.makeRotationFromQuaternion(rotation);\n\t\t\t\ttempMatrix.copy(scaleMatrix).multiply(rotationMatrix).multiply(transform);\n\t\t\t\ttempMatrix.decompose(tempPosition, outRotation, outScale);\n\t\t\t} else {\n\t\t\t\toutScale.copy(scale);\n\t\t\t\toutRotation.copy(rotation);\n\t\t\t}\n\t\t};\n\t})();\n\n\tgetSplatColor(globalSplatIndex, outColor) {\n\t\tconst sectionIndex = this.globalSplatIndexToSectionMap[globalSplatIndex];\n\t\tconst section = this.sections[sectionIndex];\n\t\tconst localSplatIndex = globalSplatIndex - section.splatCountOffset;\n\n\t\tconst srcSplatColorsBase =\n\t\t\tsection.bytesPerSplat * localSplatIndex +\n\t\t\tSplatBuffer.CompressionLevels[this.compressionLevel].ColorOffsetBytes;\n\t\tconst splatColorsArray = new Uint8Array(this.bufferData, section.dataBase + srcSplatColorsBase, 4);\n\n\t\toutColor.set(splatColorsArray[0], splatColorsArray[1], splatColorsArray[2], splatColorsArray[3]);\n\t}\n\n\tfillSplatCenterArray(outCenterArray, transform, srcFrom, srcTo, destFrom) {\n\t\tconst splatCount = this.splatCount;\n\n\t\tsrcFrom = srcFrom || 0;\n\t\tsrcTo = srcTo || splatCount - 1;\n\t\tif (destFrom === undefined) destFrom = srcFrom;\n\n\t\tconst center = new THREE.Vector3();\n\t\tfor (let i = srcFrom; i <= srcTo; i++) {\n\t\t\tconst sectionIndex = this.globalSplatIndexToSectionMap[i];\n\t\t\tconst section = this.sections[sectionIndex];\n\t\t\tconst localSplatIndex = i - section.splatCountOffset;\n\t\t\tconst centerDestBase = (i - srcFrom + destFrom) * SplatBuffer.CenterComponentCount;\n\n\t\t\tconst srcSplatCentersBase = section.bytesPerSplat * localSplatIndex;\n\t\t\tconst dataView = new DataView(this.bufferData, section.dataBase + srcSplatCentersBase);\n\n\t\t\tconst x = dataViewFloatForCompressionLevel(dataView, 0, this.compressionLevel);\n\t\t\tconst y = dataViewFloatForCompressionLevel(dataView, 1, this.compressionLevel);\n\t\t\tconst z = dataViewFloatForCompressionLevel(dataView, 2, this.compressionLevel);\n\t\t\tif (this.compressionLevel >= 1) {\n\t\t\t\tconst bucketIndex = this.getBucketIndex(section, localSplatIndex);\n\t\t\t\tconst bucketBase = bucketIndex * SplatBuffer.BucketStorageSizeFloats;\n\t\t\t\tconst sf = section.compressionScaleFactor;\n\t\t\t\tconst sr = section.compressionScaleRange;\n\t\t\t\tcenter.x = (x - sr) * sf + section.bucketArray[bucketBase];\n\t\t\t\tcenter.y = (y - sr) * sf + section.bucketArray[bucketBase + 1];\n\t\t\t\tcenter.z = (z - sr) * sf + section.bucketArray[bucketBase + 2];\n\t\t\t} else {\n\t\t\t\tcenter.x = x;\n\t\t\t\tcenter.y = y;\n\t\t\t\tcenter.z = z;\n\t\t\t}\n\t\t\tif (transform) {\n\t\t\t\tcenter.applyMatrix4(transform);\n\t\t\t}\n\t\t\toutCenterArray[centerDestBase] = center.x;\n\t\t\toutCenterArray[centerDestBase + 1] = center.y;\n\t\t\toutCenterArray[centerDestBase + 2] = center.z;\n\t\t}\n\t}\n\n\tfillSplatScaleRotationArray = (function() {\n\t\tconst scaleMatrix = new THREE.Matrix4();\n\t\tconst rotationMatrix = new THREE.Matrix4();\n\t\tconst tempMatrix = new THREE.Matrix4();\n\t\tconst scale = new THREE.Vector3();\n\t\tconst rotation = new THREE.Quaternion();\n\t\tconst tempPosition = new THREE.Vector3();\n\n\t\tconst ensurePositiveW = (quaternion) => {\n\t\t\tconst flip = quaternion.w < 0 ? -1 : 1;\n\t\t\tquaternion.x *= flip;\n\t\t\tquaternion.y *= flip;\n\t\t\tquaternion.z *= flip;\n\t\t\tquaternion.w *= flip;\n\t\t};\n\n\t\treturn function(\n\t\t\toutScaleArray,\n\t\t\toutRotationArray,\n\t\t\ttransform,\n\t\t\tsrcFrom,\n\t\t\tsrcTo,\n\t\t\tdestFrom,\n\t\t\tdesiredOutputCompressionLevel,\n\t\t\tscaleOverride,\n\t\t) {\n\t\t\tconst splatCount = this.splatCount;\n\n\t\t\tsrcFrom = srcFrom || 0;\n\t\t\tsrcTo = srcTo || splatCount - 1;\n\t\t\tif (destFrom === undefined) destFrom = srcFrom;\n\n\t\t\tconst outputConversion = (value, srcCompressionLevel) => {\n\t\t\t\tif (srcCompressionLevel === undefined) srcCompressionLevel = this.compressionLevel;\n\t\t\t\treturn convertBetweenCompressionLevels(value, srcCompressionLevel, desiredOutputCompressionLevel);\n\t\t\t};\n\n\t\t\tfor (let i = srcFrom; i <= srcTo; i++) {\n\t\t\t\tconst sectionIndex = this.globalSplatIndexToSectionMap[i];\n\t\t\t\tconst section = this.sections[sectionIndex];\n\t\t\t\tconst localSplatIndex = i - section.splatCountOffset;\n\n\t\t\t\tconst srcSplatScalesBase =\n\t\t\t\t\tsection.bytesPerSplat * localSplatIndex +\n\t\t\t\t\tSplatBuffer.CompressionLevels[this.compressionLevel].ScaleOffsetBytes;\n\n\t\t\t\tconst scaleDestBase = (i - srcFrom + destFrom) * SplatBuffer.ScaleComponentCount;\n\t\t\t\tconst rotationDestBase = (i - srcFrom + destFrom) * SplatBuffer.RotationComponentCount;\n\t\t\t\tconst dataView = new DataView(this.bufferData, section.dataBase + srcSplatScalesBase);\n\n\t\t\t\tconst srcScaleX =\n\t\t\t\t\tscaleOverride && scaleOverride.x !== undefined ?\n\t\t\t\t\t\tscaleOverride.x :\n\t\t\t\t\t\tdataViewFloatForCompressionLevel(dataView, 0, this.compressionLevel);\n\t\t\t\tconst srcScaleY =\n\t\t\t\t\tscaleOverride && scaleOverride.y !== undefined ?\n\t\t\t\t\t\tscaleOverride.y :\n\t\t\t\t\t\tdataViewFloatForCompressionLevel(dataView, 1, this.compressionLevel);\n\t\t\t\tconst srcScaleZ =\n\t\t\t\t\tscaleOverride && scaleOverride.z !== undefined ?\n\t\t\t\t\t\tscaleOverride.z :\n\t\t\t\t\t\tdataViewFloatForCompressionLevel(dataView, 2, this.compressionLevel);\n\n\t\t\t\tconst srcRotationW = dataViewFloatForCompressionLevel(dataView, 3, this.compressionLevel);\n\t\t\t\tconst srcRotationX = dataViewFloatForCompressionLevel(dataView, 4, this.compressionLevel);\n\t\t\t\tconst srcRotationY = dataViewFloatForCompressionLevel(dataView, 5, this.compressionLevel);\n\t\t\t\tconst srcRotationZ = dataViewFloatForCompressionLevel(dataView, 6, this.compressionLevel);\n\n\t\t\t\tscale.set(\n\t\t\t\t\ttoUncompressedFloat(srcScaleX, this.compressionLevel),\n\t\t\t\t\ttoUncompressedFloat(srcScaleY, this.compressionLevel),\n\t\t\t\t\ttoUncompressedFloat(srcScaleZ, this.compressionLevel),\n\t\t\t\t);\n\n\t\t\t\trotation\n\t\t\t\t\t.set(\n\t\t\t\t\t\ttoUncompressedFloat(srcRotationX, this.compressionLevel),\n\t\t\t\t\t\ttoUncompressedFloat(srcRotationY, this.compressionLevel),\n\t\t\t\t\t\ttoUncompressedFloat(srcRotationZ, this.compressionLevel),\n\t\t\t\t\t\ttoUncompressedFloat(srcRotationW, this.compressionLevel),\n\t\t\t\t\t)\n\t\t\t\t\t.normalize();\n\n\t\t\t\tif (transform) {\n\t\t\t\t\ttempPosition.set(0, 0, 0);\n\t\t\t\t\tscaleMatrix.makeScale(scale.x, scale.y, scale.z);\n\t\t\t\t\trotationMatrix.makeRotationFromQuaternion(rotation);\n\t\t\t\t\ttempMatrix.identity().premultiply(scaleMatrix).premultiply(rotationMatrix);\n\t\t\t\t\ttempMatrix.premultiply(transform);\n\t\t\t\t\ttempMatrix.decompose(tempPosition, rotation, scale);\n\t\t\t\t\trotation.normalize();\n\t\t\t\t}\n\n\t\t\t\tensurePositiveW(rotation);\n\n\t\t\t\tif (outScaleArray) {\n\t\t\t\t\toutScaleArray[scaleDestBase] = outputConversion(scale.x, 0);\n\t\t\t\t\toutScaleArray[scaleDestBase + 1] = outputConversion(scale.y, 0);\n\t\t\t\t\toutScaleArray[scaleDestBase + 2] = outputConversion(scale.z, 0);\n\t\t\t\t}\n\n\t\t\t\tif (outRotationArray) {\n\t\t\t\t\toutRotationArray[rotationDestBase] = outputConversion(rotation.x, 0);\n\t\t\t\t\toutRotationArray[rotationDestBase + 1] = outputConversion(rotation.y, 0);\n\t\t\t\t\toutRotationArray[rotationDestBase + 2] = outputConversion(rotation.z, 0);\n\t\t\t\t\toutRotationArray[rotationDestBase + 3] = outputConversion(rotation.w, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t})();\n\n\tstatic computeCovariance = (function() {\n\t\tconst tempMatrix4 = new THREE.Matrix4();\n\t\tconst scaleMatrix = new THREE.Matrix3();\n\t\tconst rotationMatrix = new THREE.Matrix3();\n\t\tconst covarianceMatrix = new THREE.Matrix3();\n\t\tconst transformedCovariance = new THREE.Matrix3();\n\t\tconst transform3x3 = new THREE.Matrix3();\n\t\tconst transform3x3Transpose = new THREE.Matrix3();\n\n\t\treturn function(\n\t\t\tscale,\n\t\t\trotation,\n\t\t\ttransform,\n\t\t\toutCovariance,\n\t\t\toutOffset = 0,\n\t\t\tdesiredOutputCompressionLevel,\n\t\t) {\n\t\t\ttempMatrix4.makeScale(scale.x, scale.y, scale.z);\n\t\t\tscaleMatrix.setFromMatrix4(tempMatrix4);\n\n\t\t\ttempMatrix4.makeRotationFromQuaternion(rotation);\n\t\t\trotationMatrix.setFromMatrix4(tempMatrix4);\n\n\t\t\tcovarianceMatrix.copy(rotationMatrix).multiply(scaleMatrix);\n\t\t\ttransformedCovariance.copy(covarianceMatrix).transpose().premultiply(covarianceMatrix);\n\n\t\t\tif (transform) {\n\t\t\t\ttransform3x3.setFromMatrix4(transform);\n\t\t\t\ttransform3x3Transpose.copy(transform3x3).transpose();\n\t\t\t\ttransformedCovariance.multiply(transform3x3Transpose);\n\t\t\t\ttransformedCovariance.premultiply(transform3x3);\n\t\t\t}\n\n\t\t\tif (desiredOutputCompressionLevel >= 1) {\n\t\t\t\toutCovariance[outOffset] = toHalfFloat(transformedCovariance.elements[0]);\n\t\t\t\toutCovariance[outOffset + 1] = toHalfFloat(transformedCovariance.elements[3]);\n\t\t\t\toutCovariance[outOffset + 2] = toHalfFloat(transformedCovariance.elements[6]);\n\t\t\t\toutCovariance[outOffset + 3] = toHalfFloat(transformedCovariance.elements[4]);\n\t\t\t\toutCovariance[outOffset + 4] = toHalfFloat(transformedCovariance.elements[7]);\n\t\t\t\toutCovariance[outOffset + 5] = toHalfFloat(transformedCovariance.elements[8]);\n\t\t\t} else {\n\t\t\t\toutCovariance[outOffset] = transformedCovariance.elements[0];\n\t\t\t\toutCovariance[outOffset + 1] = transformedCovariance.elements[3];\n\t\t\t\toutCovariance[outOffset + 2] = transformedCovariance.elements[6];\n\t\t\t\toutCovariance[outOffset + 3] = transformedCovariance.elements[4];\n\t\t\t\toutCovariance[outOffset + 4] = transformedCovariance.elements[7];\n\t\t\t\toutCovariance[outOffset + 5] = transformedCovariance.elements[8];\n\t\t\t}\n\t\t};\n\t})();\n\n\tfillSplatCovarianceArray(\n\t\tcovarianceArray,\n\t\ttransform,\n\t\tsrcFrom,\n\t\tsrcTo,\n\t\tdestFrom,\n\t\tdesiredOutputCompressionLevel,\n\t) {\n\t\tconst splatCount = this.splatCount;\n\n\t\tconst scale = new THREE.Vector3();\n\t\tconst rotation = new THREE.Quaternion();\n\n\t\tsrcFrom = srcFrom || 0;\n\t\tsrcTo = srcTo || splatCount - 1;\n\t\tif (destFrom === undefined) destFrom = srcFrom;\n\n\t\tfor (let i = srcFrom; i <= srcTo; i++) {\n\t\t\tconst sectionIndex = this.globalSplatIndexToSectionMap[i];\n\t\t\tconst section = this.sections[sectionIndex];\n\t\t\tconst localSplatIndex = i - section.splatCountOffset;\n\n\t\t\tconst covarianceDestBase = (i - srcFrom + destFrom) * SplatBuffer.CovarianceComponentCount;\n\t\t\tconst srcSplatScalesBase =\n\t\t\t\tsection.bytesPerSplat * localSplatIndex +\n\t\t\t\tSplatBuffer.CompressionLevels[this.compressionLevel].ScaleOffsetBytes;\n\n\t\t\tconst dataView = new DataView(this.bufferData, section.dataBase + srcSplatScalesBase);\n\n\t\t\tscale.set(\n\t\t\t\ttoUncompressedFloat(\n\t\t\t\t\tdataViewFloatForCompressionLevel(dataView, 0, this.compressionLevel),\n\t\t\t\t\tthis.compressionLevel,\n\t\t\t\t),\n\t\t\t\ttoUncompressedFloat(\n\t\t\t\t\tdataViewFloatForCompressionLevel(dataView, 1, this.compressionLevel),\n\t\t\t\t\tthis.compressionLevel,\n\t\t\t\t),\n\t\t\t\ttoUncompressedFloat(\n\t\t\t\t\tdataViewFloatForCompressionLevel(dataView, 2, this.compressionLevel),\n\t\t\t\t\tthis.compressionLevel,\n\t\t\t\t),\n\t\t\t);\n\n\t\t\trotation.set(\n\t\t\t\ttoUncompressedFloat(\n\t\t\t\t\tdataViewFloatForCompressionLevel(dataView, 4, this.compressionLevel),\n\t\t\t\t\tthis.compressionLevel,\n\t\t\t\t),\n\t\t\t\ttoUncompressedFloat(\n\t\t\t\t\tdataViewFloatForCompressionLevel(dataView, 5, this.compressionLevel),\n\t\t\t\t\tthis.compressionLevel,\n\t\t\t\t),\n\t\t\t\ttoUncompressedFloat(\n\t\t\t\t\tdataViewFloatForCompressionLevel(dataView, 6, this.compressionLevel),\n\t\t\t\t\tthis.compressionLevel,\n\t\t\t\t),\n\t\t\t\ttoUncompressedFloat(\n\t\t\t\t\tdataViewFloatForCompressionLevel(dataView, 3, this.compressionLevel),\n\t\t\t\t\tthis.compressionLevel,\n\t\t\t\t),\n\t\t\t);\n\n\t\t\tSplatBuffer.computeCovariance(\n\t\t\t\tscale,\n\t\t\t\trotation,\n\t\t\t\ttransform,\n\t\t\t\tcovarianceArray,\n\t\t\t\tcovarianceDestBase,\n\t\t\t\tdesiredOutputCompressionLevel,\n\t\t\t);\n\t\t}\n\t}\n\n\tfillSplatColorArray(outColorArray, minimumAlpha, srcFrom, srcTo, destFrom) {\n\t\tconst splatCount = this.splatCount;\n\n\t\tsrcFrom = srcFrom || 0;\n\t\tsrcTo = srcTo || splatCount - 1;\n\t\tif (destFrom === undefined) destFrom = srcFrom;\n\n\t\tfor (let i = srcFrom; i <= srcTo; i++) {\n\t\t\tconst sectionIndex = this.globalSplatIndexToSectionMap[i];\n\t\t\tconst section = this.sections[sectionIndex];\n\t\t\tconst localSplatIndex = i - section.splatCountOffset;\n\n\t\t\tconst colorDestBase = (i - srcFrom + destFrom) * SplatBuffer.ColorComponentCount;\n\t\t\tconst srcSplatColorsBase =\n\t\t\t\tsection.bytesPerSplat * localSplatIndex +\n\t\t\t\tSplatBuffer.CompressionLevels[this.compressionLevel].ColorOffsetBytes;\n\n\t\t\tconst dataView = new Uint8Array(this.bufferData, section.dataBase + srcSplatColorsBase);\n\n\t\t\tlet alpha = dataView[3];\n\t\t\talpha = alpha >= minimumAlpha ? alpha : 0;\n\n\t\t\toutColorArray[colorDestBase] = dataView[0];\n\t\t\toutColorArray[colorDestBase + 1] = dataView[1];\n\t\t\toutColorArray[colorDestBase + 2] = dataView[2];\n\t\t\toutColorArray[colorDestBase + 3] = alpha;\n\t\t}\n\t}\n\n\tfillSphericalHarmonicsArray = (function() {\n\t\tconst sphericalHarmonicVectors = [];\n\t\tfor (let i = 0; i < 15; i++) {\n\t\t\tsphericalHarmonicVectors[i] = new THREE.Vector3();\n\t\t}\n\n\t\tconst tempMatrix3 = new THREE.Matrix3();\n\t\tconst tempMatrix4 = new THREE.Matrix4();\n\n\t\tconst tempTranslation = new THREE.Vector3();\n\t\tconst tempScale = new THREE.Vector3();\n\t\tconst tempRotation = new THREE.Quaternion();\n\n\t\tconst sh11 = [];\n\t\tconst sh12 = [];\n\t\tconst sh13 = [];\n\n\t\tconst sh21 = [];\n\t\tconst sh22 = [];\n\t\tconst sh23 = [];\n\t\tconst sh24 = [];\n\t\tconst sh25 = [];\n\n\t\tconst shIn1 = [];\n\t\tconst shIn2 = [];\n\t\tconst shIn3 = [];\n\t\tconst shIn4 = [];\n\t\tconst shIn5 = [];\n\n\t\tconst shOut1 = [];\n\t\tconst shOut2 = [];\n\t\tconst shOut3 = [];\n\t\tconst shOut4 = [];\n\t\tconst shOut5 = [];\n\n\t\tconst noop = (v) => v;\n\n\t\tconst set3 = (array, val1, val2, val3) => {\n\t\t\tarray[0] = val1;\n\t\t\tarray[1] = val2;\n\t\t\tarray[2] = val3;\n\t\t};\n\n\t\tconst set3FromArray = (array, srcDestView, stride, srcBase, compressionLevel) => {\n\t\t\tarray[0] = dataViewFloatForCompressionLevel(srcDestView, srcBase, compressionLevel, true);\n\t\t\tarray[1] = dataViewFloatForCompressionLevel(srcDestView, srcBase + stride, compressionLevel, true);\n\t\t\tarray[2] = dataViewFloatForCompressionLevel(\n\t\t\t\tsrcDestView,\n\t\t\t\tsrcBase + stride + stride,\n\t\t\t\tcompressionLevel,\n\t\t\t\ttrue,\n\t\t\t);\n\t\t};\n\n\t\tconst copy3 = (srcArray, destArray) => {\n\t\t\tdestArray[0] = srcArray[0];\n\t\t\tdestArray[1] = srcArray[1];\n\t\t\tdestArray[2] = srcArray[2];\n\t\t};\n\n\t\tconst setOutput3 = (srcArray, destArray, destBase, conversionFunc) => {\n\t\t\tdestArray[destBase] = conversionFunc(srcArray[0]);\n\t\t\tdestArray[destBase + 1] = conversionFunc(srcArray[1]);\n\t\t\tdestArray[destBase + 2] = conversionFunc(srcArray[2]);\n\t\t};\n\n\t\tconst toUncompressedFloatArray3 = (src, dest, compressionLevel, range8BitMin, range8BitMax) => {\n\t\t\tdest[0] = toUncompressedFloat(src[0], compressionLevel, true, range8BitMin, range8BitMax);\n\t\t\tdest[1] = toUncompressedFloat(src[1], compressionLevel, true, range8BitMin, range8BitMax);\n\t\t\tdest[2] = toUncompressedFloat(src[2], compressionLevel, true, range8BitMin, range8BitMax);\n\t\t\treturn dest;\n\t\t};\n\n\t\treturn function(\n\t\t\toutSphericalHarmonicsArray,\n\t\t\toutSphericalHarmonicsDegree,\n\t\t\ttransform,\n\t\t\tsrcFrom,\n\t\t\tsrcTo,\n\t\t\tdestFrom,\n\t\t\tdesiredOutputCompressionLevel,\n\t\t) {\n\t\t\tconst splatCount = this.splatCount;\n\n\t\t\tsrcFrom = srcFrom || 0;\n\t\t\tsrcTo = srcTo || splatCount - 1;\n\t\t\tif (destFrom === undefined) destFrom = srcFrom;\n\n\t\t\tif (transform && outSphericalHarmonicsDegree >= 1) {\n\t\t\t\ttempMatrix4.copy(transform);\n\t\t\t\ttempMatrix4.decompose(tempTranslation, tempRotation, tempScale);\n\t\t\t\ttempRotation.normalize();\n\t\t\t\ttempMatrix4.makeRotationFromQuaternion(tempRotation);\n\t\t\t\ttempMatrix3.setFromMatrix4(tempMatrix4);\n\t\t\t\tset3(sh11, tempMatrix3.elements[4], -tempMatrix3.elements[7], tempMatrix3.elements[1]);\n\t\t\t\tset3(sh12, -tempMatrix3.elements[5], tempMatrix3.elements[8], -tempMatrix3.elements[2]);\n\t\t\t\tset3(sh13, tempMatrix3.elements[3], -tempMatrix3.elements[6], tempMatrix3.elements[0]);\n\t\t\t}\n\n\t\t\tconst localFromHalfFloatToUint8 = (v) => {\n\t\t\t\treturn fromHalfFloatToUint8(v, this.minSphericalHarmonicsCoeff, this.maxSphericalHarmonicsCoeff);\n\t\t\t};\n\n\t\t\tconst localToUint8 = (v) => {\n\t\t\t\treturn toUint8(v, this.minSphericalHarmonicsCoeff, this.maxSphericalHarmonicsCoeff);\n\t\t\t};\n\n\t\t\tfor (let i = srcFrom; i <= srcTo; i++) {\n\t\t\t\tconst sectionIndex = this.globalSplatIndexToSectionMap[i];\n\t\t\t\tconst section = this.sections[sectionIndex];\n\t\t\t\toutSphericalHarmonicsDegree = Math.min(outSphericalHarmonicsDegree, section.sphericalHarmonicsDegree);\n\t\t\t\tconst outSphericalHarmonicsComponentsCount =\n\t\t\t\t\tgetSphericalHarmonicsComponentCountForDegree(outSphericalHarmonicsDegree);\n\n\t\t\t\tconst localSplatIndex = i - section.splatCountOffset;\n\n\t\t\t\tconst srcSplatSHBase =\n\t\t\t\t\tsection.bytesPerSplat * localSplatIndex +\n\t\t\t\t\tSplatBuffer.CompressionLevels[this.compressionLevel].SphericalHarmonicsOffsetBytes;\n\n\t\t\t\tconst dataView = new DataView(this.bufferData, section.dataBase + srcSplatSHBase);\n\n\t\t\t\tconst shDestBase = (i - srcFrom + destFrom) * outSphericalHarmonicsComponentsCount;\n\n\t\t\t\tlet compressionLevelForOutputConversion = transform ? 0 : this.compressionLevel;\n\t\t\t\tlet outputConversionFunc = noop;\n\t\t\t\tif (compressionLevelForOutputConversion !== desiredOutputCompressionLevel) {\n\t\t\t\t\tif (compressionLevelForOutputConversion === 1) {\n\t\t\t\t\t\tif (desiredOutputCompressionLevel === 0) outputConversionFunc = fromHalfFloat;\n\t\t\t\t\t\telse if (desiredOutputCompressionLevel == 2) outputConversionFunc = localFromHalfFloatToUint8;\n\t\t\t\t\t} else if (compressionLevelForOutputConversion === 0) {\n\t\t\t\t\t\tif (desiredOutputCompressionLevel === 1) outputConversionFunc = toHalfFloat;\n\t\t\t\t\t\telse if (desiredOutputCompressionLevel == 2) outputConversionFunc = localToUint8;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst minShCoeff = this.minSphericalHarmonicsCoeff;\n\t\t\t\tconst maxShCoeff = this.maxSphericalHarmonicsCoeff;\n\n\t\t\t\tif (outSphericalHarmonicsDegree >= 1) {\n\t\t\t\t\tset3FromArray(shIn1, dataView, 3, 0, this.compressionLevel);\n\t\t\t\t\tset3FromArray(shIn2, dataView, 3, 1, this.compressionLevel);\n\t\t\t\t\tset3FromArray(shIn3, dataView, 3, 2, this.compressionLevel);\n\n\t\t\t\t\tif (transform) {\n\t\t\t\t\t\ttoUncompressedFloatArray3(shIn1, shIn1, this.compressionLevel, minShCoeff, maxShCoeff);\n\t\t\t\t\t\ttoUncompressedFloatArray3(shIn2, shIn2, this.compressionLevel, minShCoeff, maxShCoeff);\n\t\t\t\t\t\ttoUncompressedFloatArray3(shIn3, shIn3, this.compressionLevel, minShCoeff, maxShCoeff);\n\t\t\t\t\t\tSplatBuffer.rotateSphericalHarmonics3(\n\t\t\t\t\t\t\tshIn1,\n\t\t\t\t\t\t\tshIn2,\n\t\t\t\t\t\t\tshIn3,\n\t\t\t\t\t\t\tsh11,\n\t\t\t\t\t\t\tsh12,\n\t\t\t\t\t\t\tsh13,\n\t\t\t\t\t\t\tshOut1,\n\t\t\t\t\t\t\tshOut2,\n\t\t\t\t\t\t\tshOut3,\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcopy3(shIn1, shOut1);\n\t\t\t\t\t\tcopy3(shIn2, shOut2);\n\t\t\t\t\t\tcopy3(shIn3, shOut3);\n\t\t\t\t\t}\n\n\t\t\t\t\tsetOutput3(shOut1, outSphericalHarmonicsArray, shDestBase, outputConversionFunc);\n\t\t\t\t\tsetOutput3(shOut2, outSphericalHarmonicsArray, shDestBase + 3, outputConversionFunc);\n\t\t\t\t\tsetOutput3(shOut3, outSphericalHarmonicsArray, shDestBase + 6, outputConversionFunc);\n\n\t\t\t\t\tif (outSphericalHarmonicsDegree >= 2) {\n\t\t\t\t\t\tset3FromArray(shIn1, dataView, 5, 9, this.compressionLevel);\n\t\t\t\t\t\tset3FromArray(shIn2, dataView, 5, 10, this.compressionLevel);\n\t\t\t\t\t\tset3FromArray(shIn3, dataView, 5, 11, this.compressionLevel);\n\t\t\t\t\t\tset3FromArray(shIn4, dataView, 5, 12, this.compressionLevel);\n\t\t\t\t\t\tset3FromArray(shIn5, dataView, 5, 13, this.compressionLevel);\n\n\t\t\t\t\t\tif (transform) {\n\t\t\t\t\t\t\ttoUncompressedFloatArray3(shIn1, shIn1, this.compressionLevel, minShCoeff, maxShCoeff);\n\t\t\t\t\t\t\ttoUncompressedFloatArray3(shIn2, shIn2, this.compressionLevel, minShCoeff, maxShCoeff);\n\t\t\t\t\t\t\ttoUncompressedFloatArray3(shIn3, shIn3, this.compressionLevel, minShCoeff, maxShCoeff);\n\t\t\t\t\t\t\ttoUncompressedFloatArray3(shIn4, shIn4, this.compressionLevel, minShCoeff, maxShCoeff);\n\t\t\t\t\t\t\ttoUncompressedFloatArray3(shIn5, shIn5, this.compressionLevel, minShCoeff, maxShCoeff);\n\t\t\t\t\t\t\tSplatBuffer.rotateSphericalHarmonics5(\n\t\t\t\t\t\t\t\tshIn1,\n\t\t\t\t\t\t\t\tshIn2,\n\t\t\t\t\t\t\t\tshIn3,\n\t\t\t\t\t\t\t\tshIn4,\n\t\t\t\t\t\t\t\tshIn5,\n\t\t\t\t\t\t\t\tsh11,\n\t\t\t\t\t\t\t\tsh12,\n\t\t\t\t\t\t\t\tsh13,\n\t\t\t\t\t\t\t\tsh21,\n\t\t\t\t\t\t\t\tsh22,\n\t\t\t\t\t\t\t\tsh23,\n\t\t\t\t\t\t\t\tsh24,\n\t\t\t\t\t\t\t\tsh25,\n\t\t\t\t\t\t\t\tshOut1,\n\t\t\t\t\t\t\t\tshOut2,\n\t\t\t\t\t\t\t\tshOut3,\n\t\t\t\t\t\t\t\tshOut4,\n\t\t\t\t\t\t\t\tshOut5,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tcopy3(shIn1, shOut1);\n\t\t\t\t\t\t\tcopy3(shIn2, shOut2);\n\t\t\t\t\t\t\tcopy3(shIn3, shOut3);\n\t\t\t\t\t\t\tcopy3(shIn4, shOut4);\n\t\t\t\t\t\t\tcopy3(shIn5, shOut5);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tsetOutput3(shOut1, outSphericalHarmonicsArray, shDestBase + 9, outputConversionFunc);\n\t\t\t\t\t\tsetOutput3(shOut2, outSphericalHarmonicsArray, shDestBase + 12, outputConversionFunc);\n\t\t\t\t\t\tsetOutput3(shOut3, outSphericalHarmonicsArray, shDestBase + 15, outputConversionFunc);\n\t\t\t\t\t\tsetOutput3(shOut4, outSphericalHarmonicsArray, shDestBase + 18, outputConversionFunc);\n\t\t\t\t\t\tsetOutput3(shOut5, outSphericalHarmonicsArray, shDestBase + 21, outputConversionFunc);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t})();\n\n\tstatic dot3 = (v1, v2, v3, transformRow, outArray) => {\n\t\toutArray[0] = outArray[1] = outArray[2] = 0;\n\t\tconst t0 = transformRow[0];\n\t\tconst t1 = transformRow[1];\n\t\tconst t2 = transformRow[2];\n\t\tSplatBuffer.addInto3(v1[0] * t0, v1[1] * t0, v1[2] * t0, outArray);\n\t\tSplatBuffer.addInto3(v2[0] * t1, v2[1] * t1, v2[2] * t1, outArray);\n\t\tSplatBuffer.addInto3(v3[0] * t2, v3[1] * t2, v3[2] * t2, outArray);\n\t};\n\n\tstatic addInto3 = (val1, val2, val3, destArray) => {\n\t\tdestArray[0] = destArray[0] + val1;\n\t\tdestArray[1] = destArray[1] + val2;\n\t\tdestArray[2] = destArray[2] + val3;\n\t};\n\n\tstatic dot5 = (v1, v2, v3, v4, v5, transformRow, outArray) => {\n\t\toutArray[0] = outArray[1] = outArray[2] = 0;\n\t\tconst t0 = transformRow[0];\n\t\tconst t1 = transformRow[1];\n\t\tconst t2 = transformRow[2];\n\t\tconst t3 = transformRow[3];\n\t\tconst t4 = transformRow[4];\n\t\tSplatBuffer.addInto3(v1[0] * t0, v1[1] * t0, v1[2] * t0, outArray);\n\t\tSplatBuffer.addInto3(v2[0] * t1, v2[1] * t1, v2[2] * t1, outArray);\n\t\tSplatBuffer.addInto3(v3[0] * t2, v3[1] * t2, v3[2] * t2, outArray);\n\t\tSplatBuffer.addInto3(v4[0] * t3, v4[1] * t3, v4[2] * t3, outArray);\n\t\tSplatBuffer.addInto3(v5[0] * t4, v5[1] * t4, v5[2] * t4, outArray);\n\t};\n\n\tstatic rotateSphericalHarmonics3 = (in1, in2, in3, tsh11, tsh12, tsh13, out1, out2, out3) => {\n\t\tSplatBuffer.dot3(in1, in2, in3, tsh11, out1);\n\t\tSplatBuffer.dot3(in1, in2, in3, tsh12, out2);\n\t\tSplatBuffer.dot3(in1, in2, in3, tsh13, out3);\n\t};\n\n\tstatic rotateSphericalHarmonics5 = (\n\t\tin1,\n\t\tin2,\n\t\tin3,\n\t\tin4,\n\t\tin5,\n\t\ttsh11,\n\t\ttsh12,\n\t\ttsh13,\n\t\ttsh21,\n\t\ttsh22,\n\t\ttsh23,\n\t\ttsh24,\n\t\ttsh25,\n\t\tout1,\n\t\tout2,\n\t\tout3,\n\t\tout4,\n\t\tout5,\n\t) => {\n\t\tconst kSqrt0104 = Math.sqrt(1.0 / 4.0);\n\t\tconst kSqrt0304 = Math.sqrt(3.0 / 4.0);\n\t\tconst kSqrt0103 = Math.sqrt(1.0 / 3.0);\n\t\tconst kSqrt0403 = Math.sqrt(4.0 / 3.0);\n\t\tconst kSqrt0112 = Math.sqrt(1.0 / 12.0);\n\n\t\ttsh21[0] =\n\t\t\tkSqrt0104 * (tsh13[2] * tsh11[0] + tsh13[0] * tsh11[2] + (tsh11[2] * tsh13[0] + tsh11[0] * tsh13[2]));\n\t\ttsh21[1] = tsh13[1] * tsh11[0] + tsh11[1] * tsh13[0];\n\t\ttsh21[2] = kSqrt0304 * (tsh13[1] * tsh11[1] + tsh11[1] * tsh13[1]);\n\t\ttsh21[3] = tsh13[1] * tsh11[2] + tsh11[1] * tsh13[2];\n\t\ttsh21[4] =\n\t\t\tkSqrt0104 * (tsh13[2] * tsh11[2] - tsh13[0] * tsh11[0] + (tsh11[2] * tsh13[2] - tsh11[0] * tsh13[0]));\n\t\tSplatBuffer.dot5(in1, in2, in3, in4, in5, tsh21, out1);\n\n\t\ttsh22[0] =\n\t\t\tkSqrt0104 * (tsh12[2] * tsh11[0] + tsh12[0] * tsh11[2] + (tsh11[2] * tsh12[0] + tsh11[0] * tsh12[2]));\n\t\ttsh22[1] = tsh12[1] * tsh11[0] + tsh11[1] * tsh12[0];\n\t\ttsh22[2] = kSqrt0304 * (tsh12[1] * tsh11[1] + tsh11[1] * tsh12[1]);\n\t\ttsh22[3] = tsh12[1] * tsh11[2] + tsh11[1] * tsh12[2];\n\t\ttsh22[4] =\n\t\t\tkSqrt0104 * (tsh12[2] * tsh11[2] - tsh12[0] * tsh11[0] + (tsh11[2] * tsh12[2] - tsh11[0] * tsh12[0]));\n\t\tSplatBuffer.dot5(in1, in2, in3, in4, in5, tsh22, out2);\n\n\t\ttsh23[0] =\n\t\t\tkSqrt0103 * (tsh12[2] * tsh12[0] + tsh12[0] * tsh12[2]) +\n\t\t\t-kSqrt0112 * (tsh13[2] * tsh13[0] + tsh13[0] * tsh13[2] + (tsh11[2]