UNPKG

@threepipe/plugin-gaussian-splatting

Version:

Gaussian Splatting for Threepipe

1 lines 66 kB
{"version":3,"file":"index.mjs","sources":["../src/three-gaussian-splat/materials/util.ts","../src/three-gaussian-splat/shaders/gsplat.pars.frag.glsl","../src/three-gaussian-splat/shaders/gsplat.pars.vert.glsl","../src/three-gaussian-splat/shaders/gsplat.main.vert.glsl","../src/three-gaussian-splat/shaders/gsplat.main.frag.glsl","../src/three-gaussian-splat/shaders/index.ts","../src/three-gaussian-splat/materials/GaussianSplatMaterialExtension.ts","../src/three-gaussian-splat/materials/GaussianSplatMaterialPhysical.ts","../src/three-gaussian-splat/materials/GaussianSplatMaterialRaw.ts","../src/three-gaussian-splat/materials/GaussianSplatMaterialUnlit.ts","../node_modules/comlink/dist/esm/comlink.mjs","../src/three-gaussian-splat/geometry/GaussianSplatGeometry.ts","../src/three-gaussian-splat/mesh/GaussianSplatMesh.ts","../src/three-gaussian-splat/cpp-sorter/SortWorkerManager.ts","../src/three-gaussian-splat/loaders/SplatLoader.ts","../src/three-gaussian-splat/ThreeGaussianSplatPlugin.ts","../src/three-gaussian-splat/index.ts","../src/index.ts"],"sourcesContent":["import {MathUtils, Vector2} from 'threepipe'\n\nexport const computeFocalLengths = (width: number, height: number, fov: number, aspect: number, dpr: number) => {\n const fovRad = MathUtils.degToRad(fov)\n const fovXRad = 2 * Math.atan(Math.tan(fovRad / 2) * aspect)\n const fy = dpr * height / (2 * Math.tan(fovRad / 2))\n const fx = dpr * width / (2 * Math.tan(fovXRad / 2))\n return new Vector2(fx, fy)\n}\n","// https://github.com/vincent-lecrubier-skydio/react-three-fiber-gaussian-splat\nprecision mediump float;\n\nvarying vec4 vColor;\nvarying vec3 vConic;\nvarying vec2 vCenter;\n\nuniform vec2 viewport;\nuniform vec2 focal;\nuniform float minAlpha;\n","// https://github.com/vincent-lecrubier-skydio/react-three-fiber-gaussian-splat\nprecision mediump float;\n\n#ifndef SHADER_NAME // isRawShaderMaterial\nattribute vec3 position;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nmat3 transpose(mat3 m) { return mat3(m[0][0], m[1][0], m[2][0], m[0][1], m[1][1], m[2][1], m[0][2], m[1][2], m[2][2]); }\n#endif\n\nattribute vec4 color;\nattribute vec4 quat;\nattribute vec3 scale;\nattribute vec3 center;\n\nuniform vec2 focal;\nuniform vec2 viewport;\n//uniform vec3 sphereCenter;\n//uniform vec3 planeNormal;\n//uniform float planeDistance;\n\nvarying vec4 vColor;\nvarying vec3 vConic;\nvarying vec2 vCenter;\nvarying vec2 vPosition;\n//varying float vDistance;\n//varying float vPlaneSide;\n\nmat3 compute_cov3d(vec3 scale, vec4 rot) {\n mat3 S = mat3(\n scale.x, 0.0, 0.0,\n 0.0, scale.y, 0.0,\n 0.0, 0.0, scale.z\n );\n mat3 R = mat3(\n 1.0 - 2.0 * (rot.z * rot.z + rot.w * rot.w), 2.0 * (rot.y * rot.z - rot.x * rot.w), 2.0 * (rot.y * rot.w + rot.x * rot.z),\n 2.0 * (rot.y * rot.z + rot.x * rot.w), 1.0 - 2.0 * (rot.y * rot.y + rot.w * rot.w), 2.0 * (rot.z * rot.w - rot.x * rot.y),\n 2.0 * (rot.y * rot.w - rot.x * rot.z), 2.0 * (rot.z * rot.w + rot.x * rot.y), 1.0 - 2.0 * (rot.y * rot.y + rot.z * rot.z)\n );\n mat3 M = S * R;\n return transpose(M) * M;\n}\n\nvec3 compute_cov2d(vec3 center, vec3 scale, vec4 rot){\n mat3 Vrk = compute_cov3d(scale, rot);\n vec4 t = modelViewMatrix * vec4(center, 1.0);\n vec2 lims = 1.3 * 0.5 * viewport / focal;\n t.xy = min(lims, max(-lims, t.xy / t.z)) * t.z;\n mat3 J = mat3(\n focal.x / t.z, 0., -(focal.x * t.x) / (t.z * t.z),\n 0., focal.y / t.z, -(focal.y * t.y) / (t.z * t.z),\n 0., 0., 0.\n );\n mat3 W = transpose(mat3(modelViewMatrix));\n mat3 T = W * J;\n mat3 cov = transpose(T) * transpose(Vrk) * T;\n return vec3(cov[0][0] + 0.3, cov[0][1], cov[1][1] + 0.3);\n}\n","vec3 cov2d = compute_cov2d(center, scale, quat);\nfloat det = cov2d.x * cov2d.z - cov2d.y * cov2d.y;\nvec3 conic = vec3(cov2d.z, cov2d.y, cov2d.x) / det;\nfloat mid = 0.5 * (cov2d.x + cov2d.z);\nfloat lambda1 = mid + sqrt(max(0.1, mid * mid - det));\nfloat lambda2 = mid - sqrt(max(0.1, mid * mid - det));\nvec2 v1 = 7.0 * sqrt(lambda1) * normalize(vec2(cov2d.y, lambda1 - cov2d.x));\nvec2 v2 = 7.0 * sqrt(lambda2) * normalize(vec2(-(lambda1 - cov2d.x),cov2d.y));\n\nvColor = color;\nvConic = conic;\nvCenter = vec2(gl_Position) / gl_Position.w;\n\nvPosition = vec2(vCenter + position.x * (position.y < 0.0 ? v1 : v2) / viewport);\ngl_Position = vec4(vPosition, gl_Position.z / gl_Position.w, 1);\n","vec2 d = (vCenter - 2.0 * (gl_FragCoord.xy/viewport - vec2(0.5, 0.5))) * viewport * 0.5;\n\nfloat power = -0.5 * (vConic.x * d.x * d.x + vConic.z * d.y * d.y) + vConic.y * d.x * d.y;\n\nif (power > 0.0) discard;\nfloat alpha = min(0.99, vColor.a * exp(power));\nif(alpha < minAlpha) discard;\n\ngl_FragColor = vec4(vColor.rgb, alpha);\n//vec2 d = (vCenter - 2.0 * (gl_FragCoord.xy/viewport - vec2(0.5, 0.5))) * viewport * 0.5;\n//\n//float power = -0.5 * (vConic.x * d.x * d.x + vConic.z * d.y * d.y) + vConic.y * d.x * d.y;\n//\n//if (power > 0.0) discard;\n//float alpha = min(0.99, vColor.a * exp(power));\n//if(alpha < minAlpha) discard;\n//\n//diffuseColor *= vec4(vColor.rgb, alpha);\n","import fragmentParsShaderSource from '../shaders/gsplat.pars.frag.glsl'\nimport vertexParsShaderSource from '../shaders/gsplat.pars.vert.glsl'\nimport vertexShaderSource from '../shaders/gsplat.main.vert.glsl'\nimport fragmentShaderSource from '../shaders/gsplat.main.frag.glsl'\n\nexport const gaussianSplatShaders = {\n main_frag: '\\n' + fragmentShaderSource + '\\n',\n main_vert: '\\n' + vertexShaderSource + '\\n',\n pars_frag: fragmentParsShaderSource,\n pars_vert: vertexParsShaderSource,\n}\n","import {\n Camera,\n IMaterial,\n MaterialExtension,\n PerspectiveCamera,\n Shader,\n shaderReplaceString,\n Vector2,\n WebGLRenderer,\n} from 'threepipe'\nimport {computeFocalLengths} from './util'\nimport {gaussianSplatShaders} from '../shaders'\n\nexport class GaussianSplatMaterialExtension implements MaterialExtension {\n readonly isGaussianSplatMaterialExtension = true\n extraUniforms = {\n viewport: {value: new Vector2()},\n focal: {value: new Vector2()},\n minAlpha: {value: 0.02},\n }\n parsFragmentSnippet = gaussianSplatShaders.pars_frag\n parsVertexSnippet = gaussianSplatShaders.pars_vert\n shaderExtender = (shader: Shader, material: IMaterial) => {\n shader.vertexShader = shaderReplaceString(shader.vertexShader,\n '#include <begin_vertex>',\n 'vec3 transformed = vec3( center );')\n if (shader.vertexShader.includes('#include <beginnormal_vertex>')) {\n // todo: get correct normal by rendering to depth and then sampling the normal\n shader.vertexShader = shaderReplaceString(shader.vertexShader,\n '#include <beginnormal_vertex>',\n '\\nobjectNormal = vec3(1,0,0);\\n', {append: true})\n }\n shader.vertexShader = shaderReplaceString(shader.vertexShader,\n '#include <project_vertex>',\n gaussianSplatShaders.main_vert, {append: true})\n\n if (!material.isGBufferMaterial && !material.userData.isGBufferMaterial) {\n shader.fragmentShader = shaderReplaceString(shader.fragmentShader,\n '#include <map_fragment>',\n shaderReplaceString(\n gaussianSplatShaders.main_frag,\n /gl_FragColor\\s*=/, // because of minification\n 'diffuseColor*='\n ), {prepend: true})\n }\n // eslint-disable-next-line no-constant-condition\n if (0)\n shader.fragmentShader = shaderReplaceString(shader.fragmentShader,\n '#include <output_fragment>',\n '\\ngl_FragColor=diffuseColor;',\n // '\\ngl_FragColor=vec4(reflectedLight.directDiffuse, 1.);',\n // '\\ngl_FragColor=vec4(geometryNormal, 1.);',\n {append: true})\n return shader\n }\n isCompatible = (material: IMaterial) =>\n !!material.isPhysicalMaterial ||\n !!material.isUnlitMaterial ||\n !!material.isGBufferMaterial ||\n !!material.userData.isGBufferMaterial\n\n setDirty?: () => void\n\n private _currentCamera?: PerspectiveCamera | Camera\n private _renderer?: WebGLRenderer\n\n public set minAlpha(value: number) {\n this.extraUniforms.minAlpha.value = value\n this.setDirty && this.setDirty()\n }\n\n\n constructor() {\n window.addEventListener('resize', this._refresh)\n }\n\n dispose() {\n // todo: add again on added to mesh?\n window.removeEventListener('resize', this._refresh)\n }\n\n update(camera: PerspectiveCamera | Camera, renderer: WebGLRenderer): void {\n if (this._currentCamera === camera && this._renderer === renderer) return\n this._renderer = renderer\n this._currentCamera = camera\n this._refresh()\n }\n\n private _refresh = (): void => {\n if (!this._currentCamera) return\n const size = new Vector2()\n this._renderer?.getSize(size)\n const dpr = this._renderer?.getPixelRatio() || 1\n let fov = 75\n let aspect = size.x / size.y\n\n if (this._currentCamera instanceof PerspectiveCamera) {\n fov = this._currentCamera.fov\n aspect = this._currentCamera.aspect\n }\n\n this.extraUniforms.focal.value = computeFocalLengths(size.x, size.y, fov, aspect, dpr)\n this.extraUniforms.viewport.value = new Vector2(size.x * dpr, size.y * dpr)\n }\n}\n","import {PhysicalMaterial} from 'threepipe'\nimport {GaussianSplatMaterialExtension} from './GaussianSplatMaterialExtension'\n\nexport class GaussianSplatMaterialPhysical extends PhysicalMaterial {\n readonly isGaussianSplatMaterialPhysical = true\n\n gsplatExtension = new GaussianSplatMaterialExtension()\n\n constructor() {\n super({\n depthTest: true,\n depthWrite: false,\n transparent: true,\n vertexColors: false,\n })\n // this.userData.renderToGBuffer = true\n // this.userData.renderToDepth = true\n this.registerMaterialExtensions([this.gsplatExtension])\n }\n\n dispose() {\n this.gsplatExtension.dispose()\n return super.dispose()\n }\n}\n","import {Camera, PerspectiveCamera, ShaderMaterial2, Vector2, WebGLRenderer} from 'threepipe'\nimport {computeFocalLengths} from './util'\nimport {gaussianSplatShaders} from '../shaders'\n\nexport class GaussianSplatMaterialRaw extends ShaderMaterial2 {\n private _currentCamera?: PerspectiveCamera | Camera\n private _renderer?: WebGLRenderer\n\n readonly isGaussianSplatMaterialRaw = true\n\n public set minAlpha(value: number) {\n this.uniforms.minAlpha.value = value\n this.needsUpdate = true\n }\n\n constructor() {\n super({\n uniforms: {\n viewport: {value: new Vector2()},\n focal: {value: new Vector2()},\n minAlpha: {value: 0.02},\n },\n fragmentShader: `${gaussianSplatShaders.pars_frag}\nvoid main () {\n${gaussianSplatShaders.main_frag}\n}`,\n vertexShader: `${gaussianSplatShaders.pars_vert}\nvoid main () {\n gl_Position = projectionMatrix * modelViewMatrix * vec4(center, 1);\n ${gaussianSplatShaders.main_vert}\n}`,\n depthTest: true,\n depthWrite: false,\n transparent: true,\n }, true)\n\n window.addEventListener('resize', this._refresh)\n }\n\n private _refresh = (): void => {\n if (!this._currentCamera) return\n\n const size = new Vector2()\n this._renderer?.getSize(size)\n const width = size.x\n const height = size.y\n\n const dpr = this._renderer?.getPixelRatio() || 1\n\n let fov = 75\n let aspect = width / height\n\n if (this._currentCamera instanceof PerspectiveCamera) {\n fov = this._currentCamera.fov\n aspect = this._currentCamera.aspect\n }\n\n this.uniforms.focal.value = computeFocalLengths(width, height, fov, aspect, dpr)\n this.uniforms.viewport.value = new Vector2(width * dpr, height * dpr)\n }\n\n dispose() {\n // todo: add again on added to mesh?\n window.removeEventListener('resize', this._refresh)\n return super.dispose()\n }\n\n update(camera: PerspectiveCamera | Camera, renderer: WebGLRenderer): void {\n this._renderer = renderer\n this._currentCamera = camera\n this._refresh()\n }\n}\n","import {UnlitMaterial} from 'threepipe'\n\nimport {GaussianSplatMaterialExtension} from './GaussianSplatMaterialExtension'\n\nexport class GaussianSplatMaterialUnlit extends UnlitMaterial {\n readonly isGaussianSplatMaterialUnlit = true\n gsplatExtension = new GaussianSplatMaterialExtension()\n\n constructor() {\n super({\n depthTest: true,\n depthWrite: false,\n transparent: true,\n vertexColors: false,\n })\n // this.userData.renderToGBuffer = true\n // this.userData.renderToDepth = true\n this.registerMaterialExtensions([this.gsplatExtension])\n }\n\n dispose() {\n this.gsplatExtension.dispose()\n return super.dispose()\n }\n}\n\n","/**\n * @license\n * Copyright 2019 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\nconst proxyMarker = Symbol(\"Comlink.proxy\");\nconst createEndpoint = Symbol(\"Comlink.endpoint\");\nconst releaseProxy = Symbol(\"Comlink.releaseProxy\");\nconst finalizer = Symbol(\"Comlink.finalizer\");\nconst throwMarker = Symbol(\"Comlink.thrown\");\nconst isObject = (val) => (typeof val === \"object\" && val !== null) || typeof val === \"function\";\n/**\n * Internal transfer handle to handle objects marked to proxy.\n */\nconst proxyTransferHandler = {\n canHandle: (val) => isObject(val) && val[proxyMarker],\n serialize(obj) {\n const { port1, port2 } = new MessageChannel();\n expose(obj, port1);\n return [port2, [port2]];\n },\n deserialize(port) {\n port.start();\n return wrap(port);\n },\n};\n/**\n * Internal transfer handler to handle thrown exceptions.\n */\nconst throwTransferHandler = {\n canHandle: (value) => isObject(value) && throwMarker in value,\n serialize({ value }) {\n let serialized;\n if (value instanceof Error) {\n serialized = {\n isError: true,\n value: {\n message: value.message,\n name: value.name,\n stack: value.stack,\n },\n };\n }\n else {\n serialized = { isError: false, value };\n }\n return [serialized, []];\n },\n deserialize(serialized) {\n if (serialized.isError) {\n throw Object.assign(new Error(serialized.value.message), serialized.value);\n }\n throw serialized.value;\n },\n};\n/**\n * Allows customizing the serialization of certain values.\n */\nconst transferHandlers = new Map([\n [\"proxy\", proxyTransferHandler],\n [\"throw\", throwTransferHandler],\n]);\nfunction isAllowedOrigin(allowedOrigins, origin) {\n for (const allowedOrigin of allowedOrigins) {\n if (origin === allowedOrigin || allowedOrigin === \"*\") {\n return true;\n }\n if (allowedOrigin instanceof RegExp && allowedOrigin.test(origin)) {\n return true;\n }\n }\n return false;\n}\nfunction expose(obj, ep = globalThis, allowedOrigins = [\"*\"]) {\n ep.addEventListener(\"message\", function callback(ev) {\n if (!ev || !ev.data) {\n return;\n }\n if (!isAllowedOrigin(allowedOrigins, ev.origin)) {\n console.warn(`Invalid origin '${ev.origin}' for comlink proxy`);\n return;\n }\n const { id, type, path } = Object.assign({ path: [] }, ev.data);\n const argumentList = (ev.data.argumentList || []).map(fromWireValue);\n let returnValue;\n try {\n const parent = path.slice(0, -1).reduce((obj, prop) => obj[prop], obj);\n const rawValue = path.reduce((obj, prop) => obj[prop], obj);\n switch (type) {\n case \"GET\" /* MessageType.GET */:\n {\n returnValue = rawValue;\n }\n break;\n case \"SET\" /* MessageType.SET */:\n {\n parent[path.slice(-1)[0]] = fromWireValue(ev.data.value);\n returnValue = true;\n }\n break;\n case \"APPLY\" /* MessageType.APPLY */:\n {\n returnValue = rawValue.apply(parent, argumentList);\n }\n break;\n case \"CONSTRUCT\" /* MessageType.CONSTRUCT */:\n {\n const value = new rawValue(...argumentList);\n returnValue = proxy(value);\n }\n break;\n case \"ENDPOINT\" /* MessageType.ENDPOINT */:\n {\n const { port1, port2 } = new MessageChannel();\n expose(obj, port2);\n returnValue = transfer(port1, [port1]);\n }\n break;\n case \"RELEASE\" /* MessageType.RELEASE */:\n {\n returnValue = undefined;\n }\n break;\n default:\n return;\n }\n }\n catch (value) {\n returnValue = { value, [throwMarker]: 0 };\n }\n Promise.resolve(returnValue)\n .catch((value) => {\n return { value, [throwMarker]: 0 };\n })\n .then((returnValue) => {\n const [wireValue, transferables] = toWireValue(returnValue);\n ep.postMessage(Object.assign(Object.assign({}, wireValue), { id }), transferables);\n if (type === \"RELEASE\" /* MessageType.RELEASE */) {\n // detach and deactive after sending release response above.\n ep.removeEventListener(\"message\", callback);\n closeEndPoint(ep);\n if (finalizer in obj && typeof obj[finalizer] === \"function\") {\n obj[finalizer]();\n }\n }\n })\n .catch((error) => {\n // Send Serialization Error To Caller\n const [wireValue, transferables] = toWireValue({\n value: new TypeError(\"Unserializable return value\"),\n [throwMarker]: 0,\n });\n ep.postMessage(Object.assign(Object.assign({}, wireValue), { id }), transferables);\n });\n });\n if (ep.start) {\n ep.start();\n }\n}\nfunction isMessagePort(endpoint) {\n return endpoint.constructor.name === \"MessagePort\";\n}\nfunction closeEndPoint(endpoint) {\n if (isMessagePort(endpoint))\n endpoint.close();\n}\nfunction wrap(ep, target) {\n return createProxy(ep, [], target);\n}\nfunction throwIfProxyReleased(isReleased) {\n if (isReleased) {\n throw new Error(\"Proxy has been released and is not useable\");\n }\n}\nfunction releaseEndpoint(ep) {\n return requestResponseMessage(ep, {\n type: \"RELEASE\" /* MessageType.RELEASE */,\n }).then(() => {\n closeEndPoint(ep);\n });\n}\nconst proxyCounter = new WeakMap();\nconst proxyFinalizers = \"FinalizationRegistry\" in globalThis &&\n new FinalizationRegistry((ep) => {\n const newCount = (proxyCounter.get(ep) || 0) - 1;\n proxyCounter.set(ep, newCount);\n if (newCount === 0) {\n releaseEndpoint(ep);\n }\n });\nfunction registerProxy(proxy, ep) {\n const newCount = (proxyCounter.get(ep) || 0) + 1;\n proxyCounter.set(ep, newCount);\n if (proxyFinalizers) {\n proxyFinalizers.register(proxy, ep, proxy);\n }\n}\nfunction unregisterProxy(proxy) {\n if (proxyFinalizers) {\n proxyFinalizers.unregister(proxy);\n }\n}\nfunction createProxy(ep, path = [], target = function () { }) {\n let isProxyReleased = false;\n const proxy = new Proxy(target, {\n get(_target, prop) {\n throwIfProxyReleased(isProxyReleased);\n if (prop === releaseProxy) {\n return () => {\n unregisterProxy(proxy);\n releaseEndpoint(ep);\n isProxyReleased = true;\n };\n }\n if (prop === \"then\") {\n if (path.length === 0) {\n return { then: () => proxy };\n }\n const r = requestResponseMessage(ep, {\n type: \"GET\" /* MessageType.GET */,\n path: path.map((p) => p.toString()),\n }).then(fromWireValue);\n return r.then.bind(r);\n }\n return createProxy(ep, [...path, prop]);\n },\n set(_target, prop, rawValue) {\n throwIfProxyReleased(isProxyReleased);\n // FIXME: ES6 Proxy Handler `set` methods are supposed to return a\n // boolean. To show good will, we return true asynchronously ¯\\_(ツ)_/¯\n const [value, transferables] = toWireValue(rawValue);\n return requestResponseMessage(ep, {\n type: \"SET\" /* MessageType.SET */,\n path: [...path, prop].map((p) => p.toString()),\n value,\n }, transferables).then(fromWireValue);\n },\n apply(_target, _thisArg, rawArgumentList) {\n throwIfProxyReleased(isProxyReleased);\n const last = path[path.length - 1];\n if (last === createEndpoint) {\n return requestResponseMessage(ep, {\n type: \"ENDPOINT\" /* MessageType.ENDPOINT */,\n }).then(fromWireValue);\n }\n // We just pretend that `bind()` didn’t happen.\n if (last === \"bind\") {\n return createProxy(ep, path.slice(0, -1));\n }\n const [argumentList, transferables] = processArguments(rawArgumentList);\n return requestResponseMessage(ep, {\n type: \"APPLY\" /* MessageType.APPLY */,\n path: path.map((p) => p.toString()),\n argumentList,\n }, transferables).then(fromWireValue);\n },\n construct(_target, rawArgumentList) {\n throwIfProxyReleased(isProxyReleased);\n const [argumentList, transferables] = processArguments(rawArgumentList);\n return requestResponseMessage(ep, {\n type: \"CONSTRUCT\" /* MessageType.CONSTRUCT */,\n path: path.map((p) => p.toString()),\n argumentList,\n }, transferables).then(fromWireValue);\n },\n });\n registerProxy(proxy, ep);\n return proxy;\n}\nfunction myFlat(arr) {\n return Array.prototype.concat.apply([], arr);\n}\nfunction processArguments(argumentList) {\n const processed = argumentList.map(toWireValue);\n return [processed.map((v) => v[0]), myFlat(processed.map((v) => v[1]))];\n}\nconst transferCache = new WeakMap();\nfunction transfer(obj, transfers) {\n transferCache.set(obj, transfers);\n return obj;\n}\nfunction proxy(obj) {\n return Object.assign(obj, { [proxyMarker]: true });\n}\nfunction windowEndpoint(w, context = globalThis, targetOrigin = \"*\") {\n return {\n postMessage: (msg, transferables) => w.postMessage(msg, targetOrigin, transferables),\n addEventListener: context.addEventListener.bind(context),\n removeEventListener: context.removeEventListener.bind(context),\n };\n}\nfunction toWireValue(value) {\n for (const [name, handler] of transferHandlers) {\n if (handler.canHandle(value)) {\n const [serializedValue, transferables] = handler.serialize(value);\n return [\n {\n type: \"HANDLER\" /* WireValueType.HANDLER */,\n name,\n value: serializedValue,\n },\n transferables,\n ];\n }\n }\n return [\n {\n type: \"RAW\" /* WireValueType.RAW */,\n value,\n },\n transferCache.get(value) || [],\n ];\n}\nfunction fromWireValue(value) {\n switch (value.type) {\n case \"HANDLER\" /* WireValueType.HANDLER */:\n return transferHandlers.get(value.name).deserialize(value.value);\n case \"RAW\" /* WireValueType.RAW */:\n return value.value;\n }\n}\nfunction requestResponseMessage(ep, msg, transfers) {\n return new Promise((resolve) => {\n const id = generateUUID();\n ep.addEventListener(\"message\", function l(ev) {\n if (!ev.data || !ev.data.id || ev.data.id !== id) {\n return;\n }\n ep.removeEventListener(\"message\", l);\n resolve(ev.data);\n });\n if (ep.start) {\n ep.start();\n }\n ep.postMessage(Object.assign({ id }, msg), transfers);\n });\n}\nfunction generateUUID() {\n return new Array(4)\n .fill(0)\n .map(() => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16))\n .join(\"-\");\n}\n\nexport { createEndpoint, expose, finalizer, proxy, proxyMarker, releaseProxy, transfer, transferHandlers, windowEndpoint, wrap };\n//# sourceMappingURL=comlink.mjs.map\n","import type {WasmSorter} from '../cpp-sorter/worker'\nimport {Remote, transfer} from 'comlink'\nimport {\n Box3,\n BufferAttribute,\n Camera,\n IGeometry,\n iGeometryCommons,\n InstancedBufferAttribute,\n InstancedBufferGeometry,\n type IObject3D,\n Matrix4,\n PerspectiveCamera,\n Sphere,\n Vector3,\n} from 'threepipe'\n\nexport class GaussianSplatGeometry extends InstancedBufferGeometry implements IGeometry {\n constructor(\n private _worker: Remote<WasmSorter>,\n private _vertexCount: number,\n private _maxSplats: number,\n private _onLoad?: (geometry: GaussianSplatGeometry) => void,\n ) {\n super()\n iGeometryCommons.upgradeGeometry.call(this)\n this.initAttributes()\n }\n\n readonly isGaussianSplatGeometry = true\n\n assetType: 'geometry' // dont set the value here since its checked in upgradeGeometry\n center2 = iGeometryCommons.center2\n setDirty = iGeometryCommons.setDirty\n refreshUi = iGeometryCommons.refreshUi\n appliedMeshes = new Set<IObject3D>()\n\n private _viewProj: number[] = []\n private _sortRunning = false\n\n private _initialized = false\n\n private _centersBuffer: Float32Array = new Float32Array(0)\n\n public async update(camera: PerspectiveCamera | Camera, meshMatrixWorld: Matrix4) {\n if (this._sortRunning || !this._initialized || !this._worker) {\n return\n }\n\n camera.updateMatrixWorld(true)\n\n this._viewProj = new Matrix4().multiply(camera.projectionMatrix).multiply(camera.matrixWorldInverse).multiply(meshMatrixWorld).elements\n\n this._sortRunning = true\n const viewProj = new Float32Array(this._viewProj)\n const result = await this._worker.runSort(viewProj)\n\n const {quat, scale, center, color} = this._extractViews(result)\n\n if (this._centersBuffer.length !== center.length) this._centersBuffer = new Float32Array(center)\n else this._centersBuffer.set(center)\n\n ;(this.attributes.color as InstancedBufferAttribute).array = color;\n (this.attributes.quat as InstancedBufferAttribute).array = quat;\n (this.attributes.scale as InstancedBufferAttribute).array = scale;\n (this.attributes.center as InstancedBufferAttribute).array = this._centersBuffer\n // (this.attributes.center as InstancedBufferAttribute).array = center\n\n const pms = Promise.all([\n new Promise<void>(resolve => (this.attributes.color as InstancedBufferAttribute).onUpload(resolve)),\n new Promise<void>(resolve => (this.attributes.quat as InstancedBufferAttribute).onUpload(resolve)),\n new Promise<void>(resolve => (this.attributes.scale as InstancedBufferAttribute).onUpload(resolve)),\n // new Promise<void>(resolve => (this.attributes.center as InstancedBufferAttribute).onUpload(resolve)),\n ])\n\n this.attributes.color.needsUpdate = true\n this.attributes.quat.needsUpdate = true\n this.attributes.scale.needsUpdate = true\n this.attributes.center.needsUpdate = true\n\n this.setDirty()\n\n await pms\n\n await this._worker.returnBuffer(transfer(result, [result]))\n\n this._sortRunning = false\n }\n\n async initAttributes() {\n const viewProj = new Float32Array(this._viewProj)\n const result = await this._worker.runSort(viewProj)\n const {quat, scale, center, color} = this._extractViews(result)\n\n this.setAttribute('color', new InstancedBufferAttribute(color, 4, true))\n this.setAttribute('quat', new InstancedBufferAttribute(quat, 4, true))\n this.setAttribute('scale', new InstancedBufferAttribute(scale, 3, true))\n this.setAttribute('center', new InstancedBufferAttribute(center, 3, true))\n this.setAttribute('position', new BufferAttribute(new Float32Array([1, -1, 0, 1, 1, 0, -1, -1, 0, -1, 1, 0]), 3, true))\n\n this.attributes.position.needsUpdate = true\n this.setIndex(new BufferAttribute(new Uint16Array([0, 1, 2, 2, 3, 0]), 1, true))\n this.instanceCount = Math.min(quat.length / 4, this._maxSplats)\n this.computeBoundingBox()\n this.computeBoundingSphere()\n\n this._initialized = true\n this.setDirty()\n this._onLoad && this._onLoad(this)\n }\n\n private _extractViews(receivedBuffer: ArrayBuffer): {quat: Float32Array; scale: Float32Array; center: Float32Array; color: Float32Array} {\n const combined = new Float32Array(receivedBuffer)\n\n const quatLength = 4 * this._vertexCount\n const scaleLength = 3 * this._vertexCount\n const centerLength = 3 * this._vertexCount\n const colorLength = 4 * this._vertexCount\n\n const quatOffset = 0\n const scaleOffset = quatOffset + quatLength\n const centerOffset = scaleOffset + scaleLength\n const colorOffset = centerOffset + centerLength\n\n const quat = combined.subarray(quatOffset, quatOffset + quatLength)\n const scale = combined.subarray(scaleOffset, scaleOffset + scaleLength)\n const center = combined.subarray(centerOffset, centerOffset + centerLength)\n const color = combined.subarray(colorOffset, colorOffset + colorLength)\n\n return {quat, scale, center, color}\n }\n\n computeBoundingBox() {\n if (!this.getAttribute('center')) return super.computeBoundingBox()\n\n const box = this.boundingBox ?? (this.boundingBox = new Box3())\n box.setFromBufferAttribute(this.getAttribute('center') as InstancedBufferAttribute)\n\n if (isNaN(this.boundingBox!.min.x) || isNaN(this.boundingBox!.min.y) || isNaN(this.boundingBox!.min.z)) {\n console.error('GaussianSplatGeometry.computeBoundingBox(): Computed min/max have NaN values. The \"position\" attribute is likely to have NaN values.', this)\n }\n }\n\n computeBoundingSphere() {\n\n if (this.boundingSphere === null) this.boundingSphere = new Sphere()\n\n const position = this.attributes.center\n\n if (position && (position as any).isGLBufferAttribute) {\n console.error('THREE.BufferGeometry.computeBoundingSphere(): GLBufferAttribute requires a manual bounding sphere. Alternatively set \"mesh.frustumCulled\" to \"false\".', this)\n this.boundingSphere.set(new Vector3(), Infinity)\n return\n }\n\n if (position) {\n // first, find the center of the bounding sphere\n const center = this.boundingSphere.center\n if (!this.boundingBox) this.computeBoundingBox()\n this.boundingBox!.getCenter(center)\n\n // second, try to find a boundingSphere with a radius smaller than the\n // boundingSphere of the boundingBox: sqrt(3) smaller in the best case\n\n let maxRadiusSq = 0\n const vector = new Vector3()\n\n for (let i = 0, il = position.count; i < il; i++) {\n vector.fromBufferAttribute(position, i)\n maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(vector))\n }\n\n this.boundingSphere.radius = Math.sqrt(maxRadiusSq)\n\n if (isNaN(this.boundingSphere.radius)) {\n console.error('THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The \"position\" attribute is likely to have NaN values.', this)\n }\n\n }\n\n }\n\n}\n","import {GaussianSplatGeometry} from '../geometry/GaussianSplatGeometry'\nimport {Camera, IMaterial, Mesh2, PerspectiveCamera, WebGLRenderer} from 'threepipe'\nimport type {GaussianSplatMaterialRaw} from '../materials/GaussianSplatMaterialRaw'\nimport {GaussianSplatMaterialUnlit} from '../materials/GaussianSplatMaterialUnlit'\nimport {Matrix4, Ray, Raycaster, Sphere, Vector3} from 'three'\nimport {GaussianSplatMaterialExtension} from '../materials/GaussianSplatMaterialExtension'\n\nexport class GaussianSplatMesh extends Mesh2<GaussianSplatGeometry, IMaterial> {\n readonly isGaussianSplatMesh = true\n\n constructor(geometry: GaussianSplatGeometry, material: IMaterial) {\n super(geometry, material)\n this.frustumCulled = false\n }\n\n public async update(camera: PerspectiveCamera | Camera, renderer: WebGLRenderer) {\n if ((this.material as any as GaussianSplatMaterialRaw)?.isGaussianSplatMaterialRaw) {\n (this.material as any as GaussianSplatMaterialRaw).update(camera, renderer)\n } else if (this.material) {\n const ext =\n (this.material as GaussianSplatMaterialUnlit).gsplatExtension ??\n this.material.materialExtensions.find(e=>\n (e as GaussianSplatMaterialExtension).isGaussianSplatMaterialExtension) as GaussianSplatMaterialExtension\n ext && ext.update(camera, renderer)\n }\n this.updateMatrixWorld(true)\n return this.geometry.update(camera, this.matrixWorld)\n }\n\n raycast(raycaster: Raycaster, intersects: any[]) {\n\n const geometry = this.geometry\n const matrixWorld = this.matrixWorld\n // const threshold = raycaster.params.Points?.threshold ?? .01\n const threshold = .02\n\n // Checking boundingSphere distance to ray\n\n if (geometry.boundingSphere === null) geometry.computeBoundingSphere()\n\n const sphere = new Sphere()\n sphere.copy(geometry.boundingSphere!)\n sphere.applyMatrix4(matrixWorld)\n sphere.radius += threshold\n\n if (!raycaster.ray.intersectsSphere(sphere)) return\n\n //\n\n const inverseMatrix = new Matrix4()\n const ray = new Ray()\n\n inverseMatrix.copy(matrixWorld).invert()\n ray.copy(raycaster.ray).applyMatrix4(inverseMatrix)\n\n const localThreshold = threshold / ((this.scale.x + this.scale.y + this.scale.z) / 3)\n const localThresholdSq = localThreshold * localThreshold\n\n const attributes = geometry.attributes\n const positionAttribute = attributes.center\n\n const position = new Vector3()\n for (let i = 0, l = positionAttribute.count; i < l; i++) {\n\n position.fromBufferAttribute(positionAttribute, i)\n\n const rayPointDistanceSq = ray.distanceSqToPoint(position)\n\n if (rayPointDistanceSq < localThresholdSq) {\n\n const intersectPoint = new Vector3()\n\n ray.closestPointToPoint(position, intersectPoint)\n intersectPoint.applyMatrix4(matrixWorld)\n\n const distance = raycaster.ray.origin.distanceTo(intersectPoint)\n\n if (distance < raycaster.near || distance > raycaster.far) return\n\n intersects.push({\n\n distance: distance,\n distanceToRay: Math.sqrt(rayPointDistanceSq),\n point: intersectPoint,\n face: null,\n object: this,\n\n })\n\n\n }\n }\n\n }\n\n}\n","import {Remote, transfer, wrap} from 'comlink'\n// @ts-expect-error query param\nimport WasmSorterWorker from './worker?worker&inline'\nimport type {WasmSorter} from './worker'\n\nexport const SPLAT_ROW_LENGTH = 3 * 4 + 3 * 4 + 4 + 4\n\nfunction trimBuffer(_buffer: Uint8Array, _maxSplats: number, _vertexCount: number): {buffer: Uint8Array; vertexCount: number} {\n const actualVertexCount = Math.min(_vertexCount, _maxSplats)\n const actualBufferSize = SPLAT_ROW_LENGTH * actualVertexCount\n const buffer = _buffer.slice(0, actualBufferSize)\n return {buffer, vertexCount: actualVertexCount}\n}\n\nexport class SortWorkerManager {\n private _workers: Remote<WasmSorter>[] = []\n private _maxWorkers = 8\n\n private _workerCtor: new (vertexCount: number, globalBuffer: Uint8Array) => Remote<WasmSorter>\n\n onError = (e: any) => {\n console.error(e)\n console.error(\n 'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message\n )\n }\n\n constructor() {\n const worker = new WasmSorterWorker()\n worker.addEventListener('error', this.onError, false)\n this._workerCtor = wrap(worker) as any\n }\n\n async createWorker(data: Uint8Array, maxSplats = 1000000) {\n if (this._workers.length < this._maxWorkers) {\n const vertexCount = Math.floor(data.length / SPLAT_ROW_LENGTH)\n const bufferInfo = trimBuffer(data, maxSplats, vertexCount)\n const globalBuffer = transfer(bufferInfo.buffer, [bufferInfo.buffer.buffer])\n const worker = await new this._workerCtor(vertexCount, globalBuffer)\n await worker.load()\n this._workers.push(worker)\n return worker\n }\n console.error('Max workers reached')\n throw new Error('Max workers reached')\n }\n\n async disposeWorker(worker: Remote<WasmSorter>) {\n const index = this._workers.indexOf(worker)\n if (index !== -1) {\n this._workers.splice(index, 1)\n await worker.dispose()\n }\n }\n}\n","import {FileLoader, ILoader, IMaterial, Loader, LoadingManager} from 'threepipe'\nimport {SortWorkerManager, SPLAT_ROW_LENGTH} from '../cpp-sorter/SortWorkerManager'\nimport {GaussianSplatGeometry} from '../geometry/GaussianSplatGeometry'\nimport {GaussianSplatMesh} from '../mesh/GaussianSplatMesh'\nimport {GaussianSplatMaterialUnlit} from '../materials/GaussianSplatMaterialUnlit'\n\nexport class SplatLoader extends Loader implements ILoader {\n sortWorkerManager: SortWorkerManager\n\n materialConstructor = (_: GaussianSplatGeometry): IMaterial|undefined => new GaussianSplatMaterialUnlit()\n\n constructor(manager?: LoadingManager) {\n super(manager)\n }\n\n onGeometryLoad = (_: GaussianSplatGeometry)=>{\n return\n }\n\n public load(url: string, onLoad?: (data: GaussianSplatMesh) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): void {\n // const path = ( scope.path === '' ) ? LoaderUtils.extractUrlBase( url ) : scope.path;\n const loader = new FileLoader(this.manager)\n loader.setPath(this.path)\n loader.setResponseType('arraybuffer')\n loader.setRequestHeader(this.requestHeader)\n loader.setWithCredentials(this.withCredentials)\n loader.load(url, async(buffer) => {\n try {\n const data = new Uint8Array(buffer as ArrayBuffer)\n const maxSplats = 1000000\n const vertexCount = Math.floor(data.length / SPLAT_ROW_LENGTH)\n const worker = await this.sortWorkerManager.createWorker(data, maxSplats)\n const geometry = new GaussianSplatGeometry(worker, vertexCount, maxSplats, this.onGeometryLoad)\n const mesh = new GaussianSplatMesh(geometry, this.materialConstructor(geometry) as any)\n // const mesh = new GaussianSplatMesh(geometry, new UnlitMaterial() as any)\n mesh.rotation.x = Math.PI\n if (!url.startsWith('blob:') && !url.startsWith('data:'))\n mesh.name = url.split('/').pop()!.split('?')[0]\n onLoad && onLoad(mesh)\n } catch (e) {\n if (onError) onError(e)\n else console.error(e)\n this.manager.itemError(url)\n }\n }, onProgress, onError)\n }\n\n public async loadAsync(url: string, onProgress?: (event: ProgressEvent) => void): Promise<GaussianSplatMesh> {\n return new Promise((resolve, reject) => this.load(url, resolve, onProgress, reject))\n }\n}\n","import {AViewerPluginSync, createStyles, IGeometryEvent, ILoader, Importer, ThreeViewer} from 'threepipe'\nimport styles from './ThreeGaussianSplatPlugin.css?inline'\nimport {GaussianSplatMesh} from './index'\nimport {AnyOptions} from 'ts-browser-helpers'\nimport {SplatLoader} from './loaders/SplatLoader'\nimport {SortWorkerManager} from './cpp-sorter/SortWorkerManager'\nimport {GaussianSplatGeometry} from './geometry/GaussianSplatGeometry'\n\nexport class ThreeGaussianSplatPlugin extends AViewerPluginSync<string> {\n public static readonly PluginType: string = 'ThreeGaussianSplatPlugin'\n enabled = true\n dependencies = []\n toJSON: any = null\n\n constructor() {\n super()\n }\n\n splats: GaussianSplatMesh[] = []\n private _ready = false\n onAdded(viewer: ThreeViewer) {\n super.onAdded(viewer)\n createStyles(styles, viewer.container)\n viewer.assetManager.importer.addImporter(this._importer)\n viewer.scene.addEventListener('mainCameraUpdate', this._activeCameraUpdate)\n viewer.scene.addEventListener('geometryUpdate', this._geometryUpdate)\n this._ready = true\n }\n\n onRemove(viewer: ThreeViewer) {\n viewer.assetManager.importer.removeImporter(this._importer)\n }\n\n private _activeCameraUpdate = () => {\n if (!this._ready || this.isDisabled()) return\n this.splats.forEach(async splat=>splat.update(this._viewer!.scene.mainCamera, this._viewer!.renderManager.webglRenderer))\n }\n\n private _geometryUpdate = (event: IGeometryEvent) => {\n if (!this._ready || this.isDisabled() || !(event.geometry as GaussianSplatGeometry)?.isGaussianSplatGeometry) return\n event.geometry!.appliedMeshes.forEach(async(splat: GaussianSplatMesh)=>splat.update ? splat.update(this._viewer!.scene.mainCamera, this._viewer!.renderManager.webglRenderer) : undefined)\n }\n\n private _sortWorkerManager = new SortWorkerManager() // todo: dispose?\n protected _importer = new Importer(class extends SplatLoader implements ILoader {\n onDispose: (mesh: GaussianSplatMesh)=>void = ()=>{return}\n onCreate: (mesh: GaussianSplatMesh)=>void = ()=>{return}\n transform(res: GaussianSplatMesh, _: AnyOptions): any {\n res.addEventListener('dispose', ()=>this.onDispose(res))\n this.onCreate(res)\n return res\n }\n }, ['splat'], [], true, (l)=>{\n if (!l) return l\n l.sortWorkerManager = this._sortWorkerManager\n l.onDispose = (mesh: GaussianSplatMesh)=>{ // todo: dispose should only remove from GPU?\n this.splats = this.splats.filter(splat=>splat !== mesh)\n }\n l.onCreate = (mesh: GaussianSplatMesh)=>{\n this.splats.push(mesh)\n }\n l.onGeometryLoad = (_)=>{\n // console.log('geometry loaded')\n // console.log(geometry.boundingBox)\n this._viewer?.setDirty()\n }\n return l\n })\n\n // protected _viewerListeners = {\n // preRender: (_: IViewerEvent) => {\n // },\n // }\n}\n","/**\n * ThreeGaussianSplatPlugin: Threejs utilities and threepipe plugin and material extension for Gaussian Splatting\n *\n * @license\n * A port of -\n * https://github.com/zappar-xr/three-gaussian-splat\n * MIT License (c) 2023 Zappar Limited\n * Which is based on -\n * gsplat.js, MIT License (c) 2023 Dylan Ebert\n * antimatter15/splat, MIT License (c) 2023 Kevin Kwok\n */\n\nexport {GaussianSplatMaterialExtension} from './materials/GaussianSplatMaterialExtension'\nexport {GaussianSplatMaterialPhysical} from './materials/GaussianSplatMaterialPhysical'\nexport {GaussianSplatMaterialRaw} from './materials/GaussianSplatMaterialRaw'\nexport {GaussianSplatMaterialUnlit} from './materials/GaussianSplatMaterialUnlit'\nexport {computeFocalLengths} from './materials/util'\nexport {GaussianSplatGeometry} from './geometry/GaussianSplatGeometry'\nexport {GaussianSplatMesh} from './mesh/GaussianSplatMesh'\nexport {ThreeGaussianSplatPlugin} from './ThreeGaussianSplatPlugin'\nexport {SplatLoader} from './loaders/SplatLoader'\nexport {gaussianSplatShaders} from './shaders'\n","import {ThreeGaussianSplatPlugin} from './three-gaussian-splat'\n\nexport class GaussianSplattingPlugin extends ThreeGaussianSplatPlugin {}\n\nexport * as threeGaussianSplat from './three-gaussian-splat'\n"],"names":["obj","returnValue","proxy","Sphere","Matrix4","Vector3","WasmSorterWorker","index"],"mappings":"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