three-openvdb 0.5.0 → 0.6.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 CHANGED
@@ -1,8 +1,8 @@
1
1
  # three-openvdb
2
2
 
3
- three.js rendering of OpenVDB volumes, built on `openvdb`: `VDBLoader`, `FrameVolume`, `VDBAnimationLoader`, `FogVolume`, `LevelSet`, `Points`, `Bbox`, volume materials and `MeshVolume`.
3
+ three.js rendering of OpenVDB volumes, built on `openvdb`: `VDBLoader`, `VDBAnimationLoader` and `FrameVolume`.
4
4
 
5
- Streamed `.wvdb` files (made from `.vdb` files with the `wvdb` CLI):
5
+ Streamed `.wvdb` files, made from `.vdb` files with the `wvdb` CLI (npm package `webvdb`):
6
6
 
7
7
  ```js
8
8
  import { FrameVolume, WVDBLoader, WVDBAnimationLoader } from 'three-openvdb';
package/index.cjs.js CHANGED
@@ -31,13 +31,13 @@ class VDBLoader extends Three__namespace.Loader{constructor(e){super(e);}load(e,
31
31
 
32
32
  const Bbox=Three__namespace.Box3;
33
33
 
34
- const sampleColors=[16711680,65280,255];class VolumeToBbox extends Three__namespace.Group{processes=[];constructor(e,{color:o,opacity:t}={}){let r;super(),r=e instanceof Array?e:void 0!==e.grids?Object.values(e.grids):[e];const s=new Three__namespace.Object3D,a=new Bbox;Object.values(r).forEach(((e,r)=>{const c=new Three__namespace.InstancedMesh(new Three__namespace.BoxGeometry(1,1,1),new Three__namespace.MeshBasicMaterial({wireframe:!0,color:o||sampleColors[r],transparent:!0,opacity:t}),e.leavesCount);let n=0;const i=o=>{o.isLeaf()&&(a.set(...e.getWorldBbox(o)),a.getCenter(s.position),a.getSize(s.scale),s.updateMatrix(),c.setMatrixAt(n++,s.matrix)),o.childMask?.countOn()>0&&o.childMask.forEachOn((({offset:e})=>{i(o.table[e]);}));};e.root.table.forEach((e=>i(e))),this.add(c);}));}}
34
+ const sampleColors=[16711680,65280,255];class VolumeToBbox extends Three__namespace.Group{processes=[];meshes=[];constructor(e,{color:s,opacity:o}={}){let t;super(),t=e instanceof Array?e:void 0!==e.grids?Object.values(e.grids):[e];const r=new Three__namespace.Object3D,a=new Bbox;Object.values(t).forEach(((e,t)=>{const i=new Three__namespace.InstancedMesh(new Three__namespace.BoxGeometry(1,1,1),new Three__namespace.MeshBasicMaterial({wireframe:!0,color:s||sampleColors[t],transparent:!0,opacity:o}),e.leavesCount);let c=0;const h=s=>{s.isLeaf()&&(a.set(...e.getWorldBbox(s)),a.getCenter(r.position),a.getSize(r.scale),r.updateMatrix(),i.setMatrixAt(c++,r.matrix)),s.childMask?.countOn()>0&&s.childMask.forEachOn((({offset:e})=>{h(s.table[e]);}));};e.root.table.forEach((e=>h(e))),this.meshes.push(i),this.add(i);}));}dispose(){this.meshes.forEach((e=>{e.geometry.dispose(),e.material.dispose(),e.dispose();})),this.meshes=[];}}
35
35
 
36
36
  const lights={useAmbientLights:32,usePointLights:16,useDirectionalLights:8,useSpotLights:4,useHemisphereLights:2,useEnvironment:1};
37
37
 
38
- const vector2=new Three__namespace.Vector2,clearColor$1=new Three__namespace.Color;let depthRenderTarget,depthMaterial,depthBuffer,depthView,depthPacking=0,cachedFrame=-1,cachedCamera=null;class MeshDistanceMaterial extends Three__namespace.MeshBasicMaterial{name="MeshDistanceMaterial";customProgramCacheKey=()=>"MeshDistanceMaterial";_uniforms={};constructor(e){super(e),this._uniforms=e._uniforms||{},this.onBeforeCompile=e=>{Object.entries(this._uniforms).forEach((([r,a])=>{e.uniforms[r]=a;}));const r="\n #define DISTANCE\n\n varying mat4 mModelMatrix;\n varying mat4 mViewMatrix;\n varying mat4 mProjectionMatrix;\n varying mat4 mModelViewMatrix;\n ";e.vertexShader=e.vertexShader.replace("#include <common>",`\n ${r}\n\n out vec3 vPosition;\n\n #include <common>\n `).replace("#include <worldpos_vertex>","\n #include <worldpos_vertex>\n\n vPosition = worldPosition.xyz;\n\n mModelMatrix = modelMatrix;\n mViewMatrix = viewMatrix;\n mProjectionMatrix = projectionMatrix;\n mModelViewMatrix = modelViewMatrix;\n "),e.fragmentShader=e.fragmentShader.replace("#include <common>",`\n precision highp float;\n\n in vec3 vPosition;\n\n uniform float cameraNear;\n uniform float cameraFar;\n\n ${r}\n\n #include <packing>\n #include <common>\n `).replace("#include <opaque_fragment>","\n float dist = length(vPosition - cameraPosition);\n dist = (dist - cameraNear) / (cameraFar - cameraNear);\n dist = saturate(dist);\n \n gl_FragColor = packDepthToRGBA(dist);\n ").replace("#include <tonemapping_fragment>","").replace("#include <colorspace_fragment>","").replace("#include <fog_fragment>","").replace("#include <premultiplied_alpha_fragment>","").replace("#include <dithering_fragment>","");};}}const getDepthUniforms=()=>(depthRenderTarget||(depthRenderTarget=new Three__namespace.WebGLRenderTarget,depthRenderTarget.texture.minFilter=Three__namespace.NearestFilter,depthRenderTarget.texture.magFilter=Three__namespace.NearestFilter,depthRenderTarget.texture.generateMipmaps=!1,depthRenderTarget.depthTexture=new Three__namespace.DepthTexture,depthBuffer=depthRenderTarget.texture,depthPacking=1,depthMaterial=new MeshDistanceMaterial({_uniforms:{cameraPosition:{value:new Three__namespace.Vector3(0,0,0)},cameraNear:{value:1},cameraFar:{value:1}}}),depthView=new Three__namespace.Vector4(.1,1,1,1)),{depthScreenMap:{value:depthBuffer},depthPacking:{value:depthPacking},depthView:{value:depthView}});const updateFrameDepth=(e,r,a,t,n)=>{if(!depthBuffer)return;n._uniforms.cameraPosition.value.setFromMatrixPosition(a.matrixWorld),n._uniforms.cameraNear.value=a.near,n._uniforms.cameraFar.value=a.far;if((e?.info?.render?.frame||0)===cachedFrame&&a===cachedCamera)return;depthMaterial._uniforms.cameraPosition.value.setFromMatrixPosition(a.matrixWorld),depthMaterial._uniforms.cameraNear.value=a.near,depthMaterial._uniforms.cameraFar.value=a.far,e.getSize(vector2),depthRenderTarget.setSize(vector2.x,vector2.y),depthView.x=a.near,depthView.y=a.far,depthView.z=vector2.x,depthView.w=vector2.y;const i=[];r.traverseVisible((e=>{(e instanceof VolumeToFog||e.isFogVolume)&&(e.visible=!1,i.push(e));}));const o=r.background,l=e.getClearAlpha();e.getClearColor(clearColor$1),r.background=null,r.overrideMaterial=depthMaterial,e.setClearColor(16777215,1),e.setRenderTarget(depthRenderTarget),e.render(r,a),e.setRenderTarget(null),e.setClearColor(clearColor$1,l),r.overrideMaterial=null,r.background=o,i.forEach((e=>{e.visible=!0;})),cachedCamera=a,cachedFrame=e?.info?.render?.frame||0;};
38
+ const vector2=new Three__namespace.Vector2,clearColor$1=new Three__namespace.Color;let depthRenderTarget,depthMaterial,depthBuffer,depthView,depthPacking=0,cachedFrame=-1,cachedCamera=null;class MeshDistanceMaterial extends Three__namespace.MeshBasicMaterial{name="MeshDistanceMaterial";customProgramCacheKey=()=>"MeshDistanceMaterial";_uniforms={};constructor(e){super(e),this._uniforms=e._uniforms||{},this.onBeforeCompile=e=>{Object.entries(this._uniforms).forEach((([r,a])=>{e.uniforms[r]=a;}));const r="\n #define DISTANCE\n ";e.vertexShader=e.vertexShader.replace("#include <common>",`\n ${r}\n\n out vec3 vPosition;\n\n #include <common>\n `).replace("#include <worldpos_vertex>","\n #include <worldpos_vertex>\n\n vPosition = worldPosition.xyz;\n "),e.fragmentShader=e.fragmentShader.replace("#include <common>",`\n precision highp float;\n\n in vec3 vPosition;\n\n uniform float cameraNear;\n uniform float cameraFar;\n\n ${r}\n\n #include <packing>\n #include <common>\n `).replace("#include <opaque_fragment>","\n float dist = length(vPosition - cameraPosition);\n dist = (dist - cameraNear) / (cameraFar - cameraNear);\n dist = saturate(dist);\n \n gl_FragColor = packDepthToRGBA(dist);\n ").replace("#include <tonemapping_fragment>","").replace("#include <colorspace_fragment>","").replace("#include <fog_fragment>","").replace("#include <premultiplied_alpha_fragment>","").replace("#include <dithering_fragment>","");};}}const getDepthUniforms=()=>(depthRenderTarget||(depthRenderTarget=new Three__namespace.WebGLRenderTarget,depthRenderTarget.texture.minFilter=Three__namespace.NearestFilter,depthRenderTarget.texture.magFilter=Three__namespace.NearestFilter,depthRenderTarget.texture.generateMipmaps=!1,depthRenderTarget.depthTexture=new Three__namespace.DepthTexture,depthBuffer=depthRenderTarget.texture,depthPacking=1,depthMaterial=new MeshDistanceMaterial({_uniforms:{cameraPosition:{value:new Three__namespace.Vector3(0,0,0)},cameraNear:{value:1},cameraFar:{value:1}}}),depthView=new Three__namespace.Vector4(.1,1,1,1)),{depthScreenMap:{value:depthBuffer},depthPacking:{value:depthPacking},depthView:{value:depthView}});const updateFrameDepth=(e,r,a,t,n)=>{if(!depthBuffer)return;n._uniforms.cameraPosition.value.setFromMatrixPosition(a.matrixWorld),n._uniforms.cameraNear.value=a.near,n._uniforms.cameraFar.value=a.far;if((e?.info?.render?.frame||0)===cachedFrame&&a===cachedCamera)return;depthMaterial._uniforms.cameraPosition.value.setFromMatrixPosition(a.matrixWorld),depthMaterial._uniforms.cameraNear.value=a.near,depthMaterial._uniforms.cameraFar.value=a.far,e.getDrawingBufferSize(vector2),depthRenderTarget.setSize(vector2.x,vector2.y),depthView.x=a.near,depthView.y=a.far,depthView.z=vector2.x,depthView.w=vector2.y;const i=[];r.traverseVisible((e=>{(e instanceof VolumeToFog||e.isFogVolume)&&(e.visible=!1,i.push(e));}));const o=r.background,c=e.getClearAlpha();e.getClearColor(clearColor$1),r.background=null,r.overrideMaterial=depthMaterial,e.setClearColor(16777215,1),e.setRenderTarget(depthRenderTarget),e.render(r,a),e.setRenderTarget(null),e.setClearColor(clearColor$1,c),r.overrideMaterial=null,r.background=o,i.forEach((e=>{e.visible=!0;})),cachedCamera=a,cachedFrame=e?.info?.render?.frame||0;};
39
39
 
40
- class VolumeBasicMaterial extends Three__namespace.MeshStandardMaterial{name="VolumeBasicMaterial";isVolumetricFogMaterial=!0;customProgramCacheKey=()=>openvdb.getUuid(this);_uniforms={baseColor:{value:new Three__namespace.Color(16777215)},scatterColor:{value:new Three__namespace.Color(0)},densityMap3D:{value:null},emissiveMap3D:{value:null},baseColorMap3D:{value:null},maskMap3D:{value:null},steps:{value:100},absorbance:{value:1},roughness:{value:.5},densityScale:{value:1},densityCutoff:{value:0},resolution:{value:100},offset3D:{value:new Three__namespace.Vector3(0,0,0)},wrap3D:{value:Three__namespace.ClampToEdgeWrapping},lights:{value:lights.useDirectionalLights|lights.usePointLights|lights.useSpotLights|lights.useHemisphereLights|lights.useEnvironment},cameraPosition:{value:new Three__namespace.Vector3(0,0,0)},cameraNear:{value:1},cameraFar:{value:1}};set baseColor(e){this._uniforms.baseColor.value.set(e);}get baseColor(){return this._uniforms.baseColor.value}set scatterColor(e){this._uniforms.scatterColor.value.set(e);}get scatterColor(){return this._uniforms.scatterColor.value}set densityMap3D(e){this._uniforms.densityMap3D.value=e,e.offset3D=this._uniforms.offset3D.value;}get densityMap3D(){return this._uniforms.densityMap3D.value}set emissiveMap3D(e){this._uniforms.emissiveMap3D.value=e,e.offset3D=this._uniforms.offset3D.value,this.needsUpdate=!0;}get emissiveMap3D(){return this._uniforms.emissiveMap3D.value}set maskMap3D(e){this._uniforms.maskMap3D.value=e,this.needsUpdate=!0;}get maskMap3D(){return this._uniforms.maskMap3D.value}set baseColorMap3D(e){this._uniforms.baseColorMap3D.value=e,e.offset3D=this._uniforms.offset3D.value,this.needsUpdate=!0;}get baseColorMap3D(){return this._uniforms.baseColorMap3D.value}set steps(e){this._uniforms.steps.value=e;}get steps(){return this._uniforms.steps.value}set absorbance(e){this._uniforms.absorbance.value=e;}get absorbance(){return this._uniforms.absorbance.value}set roughness(e){this._uniforms&&(this._uniforms.roughness.value=e);}get roughness(){return this._uniforms?this._uniforms.roughness.value:.5}set resolution(e){this._uniforms.resolution.value=e;}get resolution(){return this._uniforms.resolution.value}set offset3D(e){this._uniforms.offset3D.value.copy(e);}get offset3D(){return this._uniforms.offset3D.value}set wrap3D(e){this._uniforms.wrap3D.value=e,this.needsUpdate=!0;}get wrap3D(){return this._uniforms.wrap3D.value}set densityScale(e){this._uniforms.densityScale.value=e;}get densityScale(){return this._uniforms.densityScale.value}set densityCutoff(e){this._uniforms.densityCutoff.value=e;}get densityCutoff(){return this._uniforms.densityCutoff.value}set lights(e){this._uniforms.lights.value=e,this.needsUpdate=!0;}get lights(){return this._uniforms.lights.value}set useDirectionalLights(e=!0){e?this._uniforms.lights.value|=lights.useDirectionalLights:this._uniforms.lights.value&=~lights.useDirectionalLights,this.needsUpdate=!0;}get useDirectionalLights(){return 0!=(this._uniforms.lights.value&lights.useDirectionalLights)}set usePointLights(e=!0){e?this._uniforms.lights.value|=lights.usePointLights:this._uniforms.lights.value&=~lights.usePointLights,this.needsUpdate=!0;}get usePointLights(){return 0!=(this._uniforms.lights.value&lights.usePointLights)}set useHemisphereLights(e=!0){e?this._uniforms.lights.value|=lights.useHemisphereLights:this._uniforms.lights.value&=~lights.useHemisphereLights,this.needsUpdate=!0;}get useHemisphereLights(){return 0!=(this._uniforms.lights.value&lights.useHemisphereLights)}set useSpotLights(e=!0){e?this._uniforms.lights.value|=lights.useSpotLights:this._uniforms.lights.value&=~lights.useSpotLights,this.needsUpdate=!0;}get useSpotLights(){return 0!=(this._uniforms.lights.value&lights.useSpotLights)}set useEnvironment(e=!0){e?this._uniforms.lights.value|=lights.useEnvironment:this._uniforms.lights.value&=~lights.useEnvironment,this.needsUpdate=!0;}get useEnvironment(){return 0!=(this._uniforms.lights.value&lights.useEnvironment)}constructor(e={}){super(),this.side=Three__namespace.BackSide,this.depthWrite=!1,this.depthTest=!1,this.transparent=!0,Object.keys(this._uniforms).forEach((n=>{void 0!==e[n]&&null!==e[n]&&(this[n]=e[n],e[n]instanceof Three__namespace.Texture&&(e[n].offset3D=this._uniforms.offset3D.value));})),this.onBeforeCompile=(n,t)=>{const i=getDepthUniforms();Object.entries({...this._uniforms,...i}).forEach((([e,t])=>{n.uniforms[e]=t;}));const o=`\n #define VOLUME_BBOX_SPAN 0.5\n ${e.emissiveMap3D?"#define USE_EMISSIVE_GRID 1":""}\n ${e.baseColorMap3D?"#define USE_BASE_COLOR_GRID 1":""}\n ${e.maskMap3D?"#define USE_MASK_GRID 1":""}\n\n #define VOLUME_USE_ENVIRONMENT ${Number(this.useEnvironment)} != 0\n #define VOLUME_USE_HEMI_LIGHTS ${Number(this.useHemisphereLights)} != 0\n #define VOLUME_USE_POINT_LIGHTS ${Number(this.usePointLights)} != 0\n #define VOLUME_USE_DIR_LIGHTS ${Number(this.useDirectionalLights)} != 0\n #define VOLUME_USE_SPOT_LIGHTS ${Number(this.useSpotLights)} != 0\n `,a="\n varying mat4 mModelMatrix;\n varying mat4 mViewMatrix;\n varying mat4 mProjectionMatrix;\n varying mat4 mModelViewMatrix;\n varying mat4 mInverseModelViewMatrix;\n varying mat3 mInverseNormalMatrix;\n ";n.vertexShader=n.vertexShader.replace("#include <common>",`\n ${a}\n\n out vec3 vOrigin;\n out vec3 vDirection;\n out vec3 vPosition;\n\n #include <common>\n `).replace("#include <worldpos_vertex>","\n #include <worldpos_vertex>\n\n vOrigin = vec3(inverse(modelMatrix) * vec4(cameraPosition, 1.0)).xyz;\n vDirection = position - vOrigin;\n vPosition = position;\n\n mModelMatrix = modelMatrix;\n mViewMatrix = viewMatrix;\n mProjectionMatrix = projectionMatrix;\n mModelViewMatrix = modelViewMatrix;\n mInverseModelViewMatrix = inverse(modelViewMatrix);\n mInverseNormalMatrix = inverse(normalMatrix);\n ");const r=`\n vec2 getVolumeBbox(vec3 vPointOfReference) {\n const vec3 vBoxMin = vec3(-0.5);\n const vec3 vBoxMax = vec3(0.5);\n vec3 vInvPointOfReference = 1.0 / vPointOfReference;\n \n vec3 vMinRange = (-vOrigin - VOLUME_BBOX_SPAN) * vInvPointOfReference;\n vec3 vMaxRange = (-vOrigin + VOLUME_BBOX_SPAN) * vInvPointOfReference;\n \n vec3 vMin = min(vMinRange, vMaxRange);\n vec3 vMax = max(vMinRange, vMaxRange);\n\n return vec2(\n max(vMin.x, max(vMin.y, vMin.z)),\n min(vMax.x, min(vMax.y, vMax.z))\n );\n }\n\n vec3 radiation = vec3(0.);\n vec3 getBlackBodyRadiation(float temperature) {\n if (temperature == 0.) {\n return vec3(0.);\n }\n\n #ifndef USE_EMISSIVE_GRID\n return vec3(0.);\n #endif\n\n // NOTE Blackbody radiation source - https://www.shadertoy.com/view/4tdGWM\n float temperatureScaled = temperature * 1000.;\n\n radiation.r += 1. / (exp(19E3 * 1. / temperatureScaled) - 1.);\n radiation.g += 3.375 / (exp(19E3 * 1.5 / temperatureScaled) - 1.);\n radiation.b += 8. / (exp(19E3 * 2. / temperatureScaled) - 1.);\n\n return (radiation / max(radiation.r,max(radiation.g,radiation.b)));\n }\n\n float loopUV(float x) {\n if (abs(mod(floor(x), 2.)) == 1.) {\n return ceil(x) - x;\n } else {\n return x - floor(x);\n }\n }\n\n vec3 mapTextureSample0(vec3 position){\n vec3 uv = position + VOLUME_BBOX_SPAN;\n\n ${this.wrap3D===Three__namespace.RepeatWrapping?"return mod(uv, 1.);":""}\n ${0===this.wrap3D||this.wrap3D===Three__namespace.ClampToEdgeWrapping?"// NOTE Return UV":""}\n ${this.wrap3D===Three__namespace.MirroredRepeatWrapping?"\n uv.x = loopUV(uv.x);\n uv.y = loopUV(uv.y);\n uv.z = loopUV(uv.z);\n ":""}\n\n return uv;\n }\n\n vec3 mapTextureSample(vec3 position, vec3 offset){\n vec3 uv = position + VOLUME_BBOX_SPAN + offset;\n\n ${this.wrap3D===Three__namespace.RepeatWrapping?"return mod(uv, 1.);":""}\n ${0===this.wrap3D||this.wrap3D===Three__namespace.ClampToEdgeWrapping?"// NOTE Return UV":""}\n ${this.wrap3D===Three__namespace.MirroredRepeatWrapping?"\n uv.x = loopUV(uv.x);\n uv.y = loopUV(uv.y);\n uv.z = loopUV(uv.z);\n ":""}\n\n return uv;\n }\n\n float blendSample(float value) {\n return value * densityScale;\n \n // float fSmoothingThreshold = 0.1;\n // float fSmoothingBlend = 0.1;\n\n // return smoothstep(\n // fSmoothingThreshold - fSmoothingBlend,\n // fSmoothingThreshold + fSmoothingBlend,\n // value\n // ) * densityScale;\n }\n\n float clampedTexture(sampler3D source, vec3 position) {\n ${0===this.wrap3D||this.wrap3D===Three__namespace.ClampToEdgeWrapping?"\n vec3 vAbs = abs(position);\n\n if (max(vAbs.x, max(vAbs.y, vAbs.z)) > .5) {\n return 0.;\n }\n ":""}\n\n return texture(source, mapTextureSample(position, offset3D)).r;\n }\n\n float clampedMaskTexture(sampler3D source, vec3 position) {\n ${0===this.wrap3D||this.wrap3D===Three__namespace.ClampToEdgeWrapping?"\n vec3 vAbs = abs(position);\n\n if (max(vAbs.x, max(vAbs.y, vAbs.z)) > .5) {\n return 0.;\n }\n ":""}\n\n return texture(source, mapTextureSample0(position)).r;\n }\n\n vec3 clampedTextureRGB(sampler3D source, vec3 position) {\n return texture(source, mapTextureSample(position, offset3D)).rgb;\n }\n `;n.fragmentShader=n.fragmentShader.replace("#include <uv_pars_fragment>","// NOTE Override UV calculations").replace("#include <packing>","").replace("#include <common>",`\n precision highp float;\n precision highp sampler2D;\n precision highp sampler3D;\n\n in vec3 vOrigin;\n in vec3 vDirection;\n in vec3 vPosition;\n\n uniform sampler3D densityMap3D;\n uniform sampler3D emissiveMap3D;\n uniform sampler3D baseColorMap3D;\n uniform sampler3D maskMap3D;\n uniform vec3 offset3D;\n uniform int wrap3D;\n\n uniform float steps;\n uniform float absorbance;\n uniform float densityScale;\n uniform float densityCutoff;\n uniform vec3 baseColor;\n uniform vec3 scatterColor;\n uniform float resolution;\n\n uniform float cameraNear;\n uniform float cameraFar;\n uniform sampler2D depthScreenMap;\n uniform vec4 depthView;\n\n ${o}\n ${a}\n\n #include <common>\n #include <packing>\n #include <uv_pars_fragment>\n\n ${r}\n `).replace("#include <lights_fragment_begin>","// NOTE Override light calculations").replace("#include <lights_fragment_maps>","// NOTE Override light calculations").replace("#include <lights_fragment_end>","// NOTE Override light calculations").replace("#include <opaque_fragment>","\n vec3 vWorld = -vViewPosition;\n\n vec3 vRayDirection = normalize(vDirection);\n vec2 vBounds = getVolumeBbox(vRayDirection);\n vec2 vMaxBounds = getVolumeBbox(vOrigin);\n \n if (vBounds.x > vBounds.y) {\n discard;\n }\n\n vBounds.x = max(vBounds.x, 0.0);\n vMaxBounds.x = max(vMaxBounds.x, 0.0);\n\n // Volume movement\n\n vec3 vPoint = vOrigin + vBounds.x * vRayDirection;\n vec3 vPointStep = 1.0 / abs(vRayDirection);\n float delta = min(vPointStep.x, min(vPointStep.y, vPointStep.z)) / steps;\n vec3 vDirectionDeltaStep = vRayDirection * delta;\n\n // Density calculations\n\n float density = 0.0;\n vec3 albedo = vec3(0.);\n vec3 emissive = vec3(0.);\n float volumeSample;\n float emissiveSample;\n float maskSample = 1.;\n float noiseSample;\n float noiseFactor;\n vec3 fTest;\n\n // Light calculations\n \n vec3 lightAlbedo = vec3(0.);\n vec3 vLightProbe;\n vec3 lightDirection;\n vec3 vLightStep;\n vec3 vEnvMapScatter = vec3(0.0);\n float lightSample;\n float lightAbsorbance;\n float lightDistance;\n float stepAccumulation;\n\n // Utils\n\n float absorbanceDensityRatio = min(absorbance, densityScale); // NOTE When reducing densityScale, automatically reduce absorbance\n float inverseAbsorbance = 1.0 / absorbanceDensityRatio;\n float inverseDensityScale = 1.0 / densityScale;\n float eInverseAbsorbance = exp(-1. - inverseAbsorbance); // NOTE Wrong, but looks kinda better than exp(-inverseAbsorbance)\n float eDensityAbsorbance = exp(-absorbanceDensityRatio * delta);\n float eInverseDensityScale = exp(-1. - inverseDensityScale);\n\n // Depth Testing\n\n vec2 screenUV = gl_FragCoord.xy / depthView.zw;\n float worldDepth = unpackRGBAToDepth(texture2D(depthScreenMap, screenUV));\n float fogDepth = 0.;\n vec3 vProjPoint;\n\n \n float lightMarchLimit = resolution + 2.;\n int iLightMarchLimit = int(lightMarchLimit);\n\n vec3 vUnit = (mModelMatrix * vec4(1., 0., 0., 0.)).xyz;\n float vUnitLength = length(vUnit);\n\n vec3 iblIrradiance = vec3(0.0);\n \n\n vec3 lastNonSolidPoint = vec3(vPoint);\n\n for (float i = vBounds.x; i < vBounds.y; i += delta) {\n volumeSample = clampedTexture(densityMap3D, vPoint);\n \n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vPoint);\n #endif\n\n density += blendSample(volumeSample) * eDensityAbsorbance * maskSample;\n\n #ifdef USE_EMISSIVE_GRID\n emissiveSample = clampedTexture(emissiveMap3D, vPoint);\n emissive = max(emissive, density * vec3(emissiveSample));\n #endif\n\n if (density < 1.) {\n lastNonSolidPoint = vPoint;\n }\n\n if (density >= 1.) {\n break;\n }\n\n vProjPoint = (mModelMatrix * vec4(vPoint, 1.0)).xyz;\n fogDepth = length(vProjPoint - cameraPosition);\n fogDepth = (fogDepth - cameraNear) / (cameraFar - cameraNear);\n fogDepth = saturate(fogDepth);\n \n if (worldDepth < fogDepth) {\n break;\n }\n\n vPoint += vDirectionDeltaStep;\n }\n\n density = clamp(density, 0.0, 1.0);\n\n vPoint = lastNonSolidPoint;\n\n if (density <= densityCutoff) {\n discard;\n }\n\n if (density > 0.) {\n\n \n vec3 baseColorSample = baseColor.rgb;\n\n #ifdef USE_BASE_COLOR_GRID\n baseColorSample *= clampedTextureRGB(baseColorMap3D, vPoint);\n #endif\n\n albedo += ambientLightColor;\n \n\n #if VOLUME_USE_ENVIRONMENT\n \n vec3 viewDir = isOrthographic ? vec3(0.0, 0.0, 1.0) : normalize(vViewPosition);\n vec3 vUVCoords = normalize(lastNonSolidPoint * mInverseNormalMatrix).xyz;\n\n #if defined( USE_ENVMAP )\n vEnvMapScatter = getIBLIrradiance(vUVCoords) * RECIPROCAL_PI;\n\n albedo += density * getIBLRadiance(viewDir, vUVCoords, roughness) * RECIPROCAL_PI;\n #endif\n \n #endif\n\n #if NUM_HEMI_LIGHTS > 0 && VOLUME_USE_HEMI_LIGHTS\n \n lightAlbedo = vec3(0.);\n vec3 textureProbe;\n\n float absorbanceUp;\n float absorbanceDown;\n float stepAccumulationUp;\n float stepAccumulationDown;\n\n for (int lightIndex = 0; lightIndex < NUM_HEMI_LIGHTS; lightIndex++) {\n\n HemisphereLight hemiLight = hemisphereLights[lightIndex];\n vLightProbe = vec3(vPoint);\n\n absorbanceUp = 0.0;\n absorbanceDown = 0.0;\n\n stepAccumulationUp = 0.;\n stepAccumulationDown = 0.;\n\n lightDirection = -normalize((vec4(hemiLight.direction, 1.) * viewMatrix).xyz);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 1; lightMarch < int(lightMarchLimit / 2.); lightMarch++) {\n if (absorbanceUp < 1.) {\n textureProbe = vLightProbe - float(lightMarch) * vLightStep;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, textureProbe);\n #endif\n\n lightSample = clampedTexture(densityMap3D, textureProbe);\n\n absorbanceUp += lightSample * eInverseAbsorbance;\n stepAccumulationUp += 1.;\n }\n \n if (absorbanceDown < 1.) {\n textureProbe = vLightProbe + float(lightMarch) * vLightStep;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, textureProbe);\n #endif\n\n lightSample = clampedTexture(densityMap3D, textureProbe);\n\n absorbanceDown += blendSample(lightSample) * eInverseAbsorbance;\n stepAccumulationDown += 1.;\n }\n }\n\n absorbanceUp = min(1., absorbanceUp);\n absorbanceDown = min(1., absorbanceDown);\n\n lightAlbedo += mix(\n (1. - absorbanceUp) * hemiLight.skyColor,\n (1. - absorbanceDown) * hemiLight.groundColor,\n 0.5\n );\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_POINT_LIGHTS > 0 && VOLUME_USE_POINT_LIGHTS\n \n PointLight pointLight;\n lightAlbedo = vec3(0.);\n \n for (int lightIndex = 0; lightIndex < NUM_POINT_LIGHTS; lightIndex++) {\n pointLight = pointLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.;\n stepAccumulation = 1.;\n\n lightDirection = vPoint - (mInverseModelViewMatrix * vec4(pointLight.position, 1.)).xyz;\n lightDistance = length(lightDirection) * vUnitLength;\n lightDirection = normalize(lightDirection);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vLightProbe);\n #endif\n \n lightSample = clampedTexture(densityMap3D, vLightProbe) * sqrt(lightDistance);\n \n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * pointLight.color;\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_DIR_LIGHTS > 0 && VOLUME_USE_DIR_LIGHTS\n \n DirectionalLight directionalLight;\n lightAlbedo = vec3(0.);\n\n for (int lightIndex = 0; lightIndex < NUM_DIR_LIGHTS; lightIndex++) {\n directionalLight = directionalLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.0;\n stepAccumulation = 1.;\n\n lightDirection = -normalize((vec4(directionalLight.direction, 1.) * viewMatrix).xyz);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vLightProbe);\n #endif\n \n float lightSample = clampedTexture(densityMap3D, vLightProbe);\n\n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * directionalLight.color;\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_SPOT_LIGHTS > 0 && VOLUME_USE_SPOT_LIGHTS\n \n SpotLight spotLight;\n lightAlbedo = vec3(0.);\n\n float angleCos;\n float spotAttenuation;\n \n for (int lightIndex = 0; lightIndex < NUM_SPOT_LIGHTS; lightIndex++) {\n spotLight = spotLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.0;\n stepAccumulation = 1.;\n\n lightDirection = vPoint - (mInverseModelViewMatrix * vec4(spotLight.position, 1.)).xyz;\n lightDistance = length(lightDirection) * vUnitLength;\n lightDirection = normalize(lightDirection);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n angleCos = dot(normalize(-lightDirection), normalize(mInverseNormalMatrix * spotLight.direction));\n spotAttenuation = smoothstep(spotLight.coneCos, spotLight.penumbraCos, angleCos);\n\n if (spotAttenuation > 0.) {\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vLightProbe);\n #endif\n \n lightSample = clampedTexture(densityMap3D, vLightProbe);\n \n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * spotAttenuation * spotLight.color;\n }\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n albedo *= baseColorSample;\n }\n \n emissive = getBlackBodyRadiation(emissive.r);\n albedo += emissive;\n\n outgoingLight.rgb = max(max(scatterColor, vEnvMapScatter), albedo);\n diffuseColor.a = saturate(density) * opacity;\n\n #include <opaque_fragment>\n ");};}}
40
+ class VolumeBasicMaterial extends Three__namespace.MeshStandardMaterial{name="VolumeBasicMaterial";isVolumetricFogMaterial=!0;customProgramCacheKey=()=>openvdb.getUuid(this);_uniforms={baseColor:{value:new Three__namespace.Color(16777215)},scatterColor:{value:new Three__namespace.Color(0)},densityMap3D:{value:null},emissiveMap3D:{value:null},baseColorMap3D:{value:null},maskMap3D:{value:null},steps:{value:100},absorbance:{value:1},roughness:{value:.5},densityScale:{value:1},densityCutoff:{value:0},resolution:{value:100},offset3D:{value:new Three__namespace.Vector3(0,0,0)},wrap3D:{value:Three__namespace.ClampToEdgeWrapping},lights:{value:lights.useDirectionalLights|lights.usePointLights|lights.useSpotLights|lights.useHemisphereLights|lights.useEnvironment},cameraPosition:{value:new Three__namespace.Vector3(0,0,0)},cameraNear:{value:1},cameraFar:{value:1}};set baseColor(e){this._uniforms.baseColor.value.set(e);}get baseColor(){return this._uniforms.baseColor.value}set scatterColor(e){this._uniforms.scatterColor.value.set(e);}get scatterColor(){return this._uniforms.scatterColor.value}set densityMap3D(e){this._uniforms.densityMap3D.value=e,e.offset3D=this._uniforms.offset3D.value;}get densityMap3D(){return this._uniforms.densityMap3D.value}set emissiveMap3D(e){this._uniforms.emissiveMap3D.value=e,e.offset3D=this._uniforms.offset3D.value,this.needsUpdate=!0;}get emissiveMap3D(){return this._uniforms.emissiveMap3D.value}set maskMap3D(e){this._uniforms.maskMap3D.value=e,this.needsUpdate=!0;}get maskMap3D(){return this._uniforms.maskMap3D.value}set baseColorMap3D(e){this._uniforms.baseColorMap3D.value=e,e.offset3D=this._uniforms.offset3D.value,this.needsUpdate=!0;}get baseColorMap3D(){return this._uniforms.baseColorMap3D.value}set steps(e){this._uniforms.steps.value=e;}get steps(){return this._uniforms.steps.value}set absorbance(e){this._uniforms.absorbance.value=e;}get absorbance(){return this._uniforms.absorbance.value}set roughness(e){this._uniforms&&(this._uniforms.roughness.value=e);}get roughness(){return this._uniforms?this._uniforms.roughness.value:.5}set resolution(e){this._uniforms.resolution.value=e;}get resolution(){return this._uniforms.resolution.value}set offset3D(e){this._uniforms.offset3D.value.copy(e);}get offset3D(){return this._uniforms.offset3D.value}set wrap3D(e){this._uniforms.wrap3D.value=e,this.needsUpdate=!0;}get wrap3D(){return this._uniforms.wrap3D.value}set densityScale(e){this._uniforms.densityScale.value=e;}get densityScale(){return this._uniforms.densityScale.value}set densityCutoff(e){this._uniforms.densityCutoff.value=e;}get densityCutoff(){return this._uniforms.densityCutoff.value}set lights(e){this._uniforms.lights.value=e,this.needsUpdate=!0;}get lights(){return this._uniforms.lights.value}set useDirectionalLights(e=!0){e?this._uniforms.lights.value|=lights.useDirectionalLights:this._uniforms.lights.value&=~lights.useDirectionalLights,this.needsUpdate=!0;}get useDirectionalLights(){return 0!=(this._uniforms.lights.value&lights.useDirectionalLights)}set usePointLights(e=!0){e?this._uniforms.lights.value|=lights.usePointLights:this._uniforms.lights.value&=~lights.usePointLights,this.needsUpdate=!0;}get usePointLights(){return 0!=(this._uniforms.lights.value&lights.usePointLights)}set useHemisphereLights(e=!0){e?this._uniforms.lights.value|=lights.useHemisphereLights:this._uniforms.lights.value&=~lights.useHemisphereLights,this.needsUpdate=!0;}get useHemisphereLights(){return 0!=(this._uniforms.lights.value&lights.useHemisphereLights)}set useSpotLights(e=!0){e?this._uniforms.lights.value|=lights.useSpotLights:this._uniforms.lights.value&=~lights.useSpotLights,this.needsUpdate=!0;}get useSpotLights(){return 0!=(this._uniforms.lights.value&lights.useSpotLights)}set useEnvironment(e=!0){e?this._uniforms.lights.value|=lights.useEnvironment:this._uniforms.lights.value&=~lights.useEnvironment,this.needsUpdate=!0;}get useEnvironment(){return 0!=(this._uniforms.lights.value&lights.useEnvironment)}constructor(e={}){super(),this.side=Three__namespace.BackSide,this.depthWrite=!1,this.depthTest=!1,this.transparent=!0,Object.keys(this._uniforms).forEach((n=>{void 0!==e[n]&&null!==e[n]&&(this[n]=e[n],e[n]instanceof Three__namespace.Texture&&(e[n].offset3D=this._uniforms.offset3D.value));})),this.onBeforeCompile=(n,t)=>{const i=getDepthUniforms();Object.entries({...this._uniforms,...i}).forEach((([e,t])=>{n.uniforms[e]=t;}));const o=`\n #define VOLUME_BBOX_SPAN 0.5\n ${e.emissiveMap3D?"#define USE_EMISSIVE_GRID 1":""}\n ${e.baseColorMap3D?"#define USE_BASE_COLOR_GRID 1":""}\n ${e.maskMap3D?"#define USE_MASK_GRID 1":""}\n\n #define VOLUME_USE_ENVIRONMENT ${Number(this.useEnvironment)} != 0\n #define VOLUME_USE_HEMI_LIGHTS ${Number(this.useHemisphereLights)} != 0\n #define VOLUME_USE_POINT_LIGHTS ${Number(this.usePointLights)} != 0\n #define VOLUME_USE_DIR_LIGHTS ${Number(this.useDirectionalLights)} != 0\n #define VOLUME_USE_SPOT_LIGHTS ${Number(this.useSpotLights)} != 0\n `;n.vertexShader=n.vertexShader.replace("#include <common>","\n out vec3 vOrigin;\n out vec3 vDirection;\n out vec3 vPosition;\n\n #include <common>\n ").replace("#include <worldpos_vertex>","\n #include <worldpos_vertex>\n\n vOrigin = vec3(inverse(modelMatrix) * vec4(cameraPosition, 1.0)).xyz;\n vDirection = position - vOrigin;\n vPosition = position;\n ");const a=`\n vec2 getVolumeBbox(vec3 vPointOfReference) {\n const vec3 vBoxMin = vec3(-0.5);\n const vec3 vBoxMax = vec3(0.5);\n vec3 vInvPointOfReference = 1.0 / vPointOfReference;\n \n vec3 vMinRange = (-vOrigin - VOLUME_BBOX_SPAN) * vInvPointOfReference;\n vec3 vMaxRange = (-vOrigin + VOLUME_BBOX_SPAN) * vInvPointOfReference;\n \n vec3 vMin = min(vMinRange, vMaxRange);\n vec3 vMax = max(vMinRange, vMaxRange);\n\n return vec2(\n max(vMin.x, max(vMin.y, vMin.z)),\n min(vMax.x, min(vMax.y, vMax.z))\n );\n }\n\n vec3 radiation = vec3(0.);\n vec3 getBlackBodyRadiation(float temperature) {\n if (temperature == 0.) {\n return vec3(0.);\n }\n\n #ifndef USE_EMISSIVE_GRID\n return vec3(0.);\n #endif\n\n // NOTE Blackbody radiation source - https://www.shadertoy.com/view/4tdGWM\n float temperatureScaled = temperature * 1000.;\n\n radiation.r += 1. / (exp(19E3 * 1. / temperatureScaled) - 1.);\n radiation.g += 3.375 / (exp(19E3 * 1.5 / temperatureScaled) - 1.);\n radiation.b += 8. / (exp(19E3 * 2. / temperatureScaled) - 1.);\n\n return (radiation / max(radiation.r,max(radiation.g,radiation.b)));\n }\n\n float loopUV(float x) {\n if (abs(mod(floor(x), 2.)) == 1.) {\n return ceil(x) - x;\n } else {\n return x - floor(x);\n }\n }\n\n vec3 mapTextureSample0(vec3 position){\n vec3 uv = position + VOLUME_BBOX_SPAN;\n\n ${this.wrap3D===Three__namespace.RepeatWrapping?"return mod(uv, 1.);":""}\n ${0===this.wrap3D||this.wrap3D===Three__namespace.ClampToEdgeWrapping?"// NOTE Return UV":""}\n ${this.wrap3D===Three__namespace.MirroredRepeatWrapping?"\n uv.x = loopUV(uv.x);\n uv.y = loopUV(uv.y);\n uv.z = loopUV(uv.z);\n ":""}\n\n return uv;\n }\n\n vec3 mapTextureSample(vec3 position, vec3 offset){\n vec3 uv = position + VOLUME_BBOX_SPAN + offset;\n\n ${this.wrap3D===Three__namespace.RepeatWrapping?"return mod(uv, 1.);":""}\n ${0===this.wrap3D||this.wrap3D===Three__namespace.ClampToEdgeWrapping?"// NOTE Return UV":""}\n ${this.wrap3D===Three__namespace.MirroredRepeatWrapping?"\n uv.x = loopUV(uv.x);\n uv.y = loopUV(uv.y);\n uv.z = loopUV(uv.z);\n ":""}\n\n return uv;\n }\n\n float blendSample(float value) {\n return value * densityScale;\n \n // float fSmoothingThreshold = 0.1;\n // float fSmoothingBlend = 0.1;\n\n // return smoothstep(\n // fSmoothingThreshold - fSmoothingBlend,\n // fSmoothingThreshold + fSmoothingBlend,\n // value\n // ) * densityScale;\n }\n\n float clampedTexture(sampler3D source, vec3 position) {\n ${0===this.wrap3D||this.wrap3D===Three__namespace.ClampToEdgeWrapping?"\n vec3 vAbs = abs(position);\n\n if (max(vAbs.x, max(vAbs.y, vAbs.z)) > .5) {\n return 0.;\n }\n ":""}\n\n return texture(source, mapTextureSample(position, offset3D)).r;\n }\n\n float clampedMaskTexture(sampler3D source, vec3 position) {\n ${0===this.wrap3D||this.wrap3D===Three__namespace.ClampToEdgeWrapping?"\n vec3 vAbs = abs(position);\n\n if (max(vAbs.x, max(vAbs.y, vAbs.z)) > .5) {\n return 0.;\n }\n ":""}\n\n return texture(source, mapTextureSample0(position)).r;\n }\n\n vec3 clampedTextureRGB(sampler3D source, vec3 position) {\n return texture(source, mapTextureSample(position, offset3D)).rgb;\n }\n `;n.fragmentShader=n.fragmentShader.replace("#include <uv_pars_fragment>","// NOTE Override UV calculations").replace("#include <packing>","").replace("#include <common>",`\n \n uniform mat4 modelMatrix;\n uniform mat4 modelViewMatrix;\n \n\n precision highp float;\n precision highp sampler2D;\n precision highp sampler3D;\n\n in vec3 vOrigin;\n in vec3 vDirection;\n in vec3 vPosition;\n\n uniform sampler3D densityMap3D;\n uniform sampler3D emissiveMap3D;\n uniform sampler3D baseColorMap3D;\n uniform sampler3D maskMap3D;\n uniform vec3 offset3D;\n uniform int wrap3D;\n\n uniform float steps;\n uniform float absorbance;\n uniform float densityScale;\n uniform float densityCutoff;\n uniform vec3 baseColor;\n uniform vec3 scatterColor;\n uniform float resolution;\n\n uniform float cameraNear;\n uniform float cameraFar;\n uniform sampler2D depthScreenMap;\n uniform vec4 depthView;\n\n ${o}\n\n #include <common>\n #include <packing>\n #include <uv_pars_fragment>\n\n ${a}\n `).replace("#include <lights_fragment_begin>","// NOTE Override light calculations").replace("#include <lights_fragment_maps>","// NOTE Override light calculations").replace("#include <lights_fragment_end>","// NOTE Override light calculations").replace("#include <opaque_fragment>","\n vec3 vWorld = -vViewPosition;\n\n vec3 vRayDirection = normalize(vDirection);\n vec2 vBounds = getVolumeBbox(vRayDirection);\n vec2 vMaxBounds = getVolumeBbox(vOrigin);\n \n if (vBounds.x > vBounds.y) {\n discard;\n }\n\n vBounds.x = max(vBounds.x, 0.0);\n vMaxBounds.x = max(vMaxBounds.x, 0.0);\n\n // Volume movement\n\n vec3 vPoint = vOrigin + vBounds.x * vRayDirection;\n vec3 vPointStep = 1.0 / abs(vRayDirection);\n float delta = min(vPointStep.x, min(vPointStep.y, vPointStep.z)) / steps;\n vec3 vDirectionDeltaStep = vRayDirection * delta;\n\n // Density calculations\n\n float density = 0.0;\n vec3 albedo = vec3(0.);\n vec3 emissive = vec3(0.);\n float volumeSample;\n float emissiveSample;\n float maskSample = 1.;\n float noiseSample;\n float noiseFactor;\n vec3 fTest;\n\n // Light calculations\n \n vec3 lightAlbedo = vec3(0.);\n vec3 vLightProbe;\n vec3 lightDirection;\n vec3 vLightStep;\n vec3 vEnvMapScatter = vec3(0.0);\n float lightSample;\n float lightAbsorbance;\n float lightDistance;\n float stepAccumulation;\n\n // Utils\n\n float absorbanceDensityRatio = min(absorbance, densityScale); // NOTE When reducing densityScale, automatically reduce absorbance\n float inverseAbsorbance = 1.0 / absorbanceDensityRatio;\n float inverseDensityScale = 1.0 / densityScale;\n float eInverseAbsorbance = exp(-1. - inverseAbsorbance); // NOTE Wrong, but looks kinda better than exp(-inverseAbsorbance)\n float eDensityAbsorbance = exp(-absorbanceDensityRatio * delta);\n float eInverseDensityScale = exp(-1. - inverseDensityScale);\n\n // Depth Testing\n\n vec2 screenUV = gl_FragCoord.xy / depthView.zw;\n float worldDepth = unpackRGBAToDepth(texture2D(depthScreenMap, screenUV));\n float fogDepth = 0.;\n vec3 vProjPoint;\n\n \n float lightMarchLimit = resolution + 2.;\n int iLightMarchLimit = int(lightMarchLimit);\n\n vec3 vUnit = (modelMatrix * vec4(1., 0., 0., 0.)).xyz;\n float vUnitLength = length(vUnit);\n\n vec3 iblIrradiance = vec3(0.0);\n \n\n vec3 lastNonSolidPoint = vec3(vPoint);\n\n for (float i = vBounds.x; i < vBounds.y; i += delta) {\n volumeSample = clampedTexture(densityMap3D, vPoint);\n \n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vPoint);\n #endif\n\n density += blendSample(volumeSample) * eDensityAbsorbance * maskSample;\n\n #ifdef USE_EMISSIVE_GRID\n emissiveSample = clampedTexture(emissiveMap3D, vPoint);\n emissive = max(emissive, density * vec3(emissiveSample));\n #endif\n\n if (density < 1.) {\n lastNonSolidPoint = vPoint;\n }\n\n if (density >= 1.) {\n break;\n }\n\n vProjPoint = (modelMatrix * vec4(vPoint, 1.0)).xyz;\n fogDepth = length(vProjPoint - cameraPosition);\n fogDepth = (fogDepth - cameraNear) / (cameraFar - cameraNear);\n fogDepth = saturate(fogDepth);\n \n if (worldDepth < fogDepth) {\n break;\n }\n\n vPoint += vDirectionDeltaStep;\n }\n\n density = clamp(density, 0.0, 1.0);\n\n vPoint = lastNonSolidPoint;\n\n if (density <= densityCutoff) {\n discard;\n }\n\n mat4 mInverseModelViewMatrix = inverse(modelViewMatrix);\n mat3 mInverseNormalMatrix = transpose(mat3(modelViewMatrix)); // NOTE Same as inverse(normalMatrix)\n\n if (density > 0.) {\n\n \n vec3 baseColorSample = baseColor.rgb;\n\n #ifdef USE_BASE_COLOR_GRID\n baseColorSample *= clampedTextureRGB(baseColorMap3D, vPoint);\n #endif\n\n albedo += ambientLightColor;\n \n\n #if VOLUME_USE_ENVIRONMENT\n \n vec3 viewDir = isOrthographic ? vec3(0.0, 0.0, 1.0) : normalize(vViewPosition);\n vec3 vUVCoords = normalize(lastNonSolidPoint * mInverseNormalMatrix).xyz;\n\n #if defined( USE_ENVMAP )\n vEnvMapScatter = getIBLIrradiance(vUVCoords) * RECIPROCAL_PI;\n\n albedo += density * getIBLRadiance(viewDir, vUVCoords, roughness) * RECIPROCAL_PI;\n #endif\n \n #endif\n\n #if NUM_HEMI_LIGHTS > 0 && VOLUME_USE_HEMI_LIGHTS\n \n lightAlbedo = vec3(0.);\n vec3 textureProbe;\n\n float absorbanceUp;\n float absorbanceDown;\n float stepAccumulationUp;\n float stepAccumulationDown;\n\n for (int lightIndex = 0; lightIndex < NUM_HEMI_LIGHTS; lightIndex++) {\n\n HemisphereLight hemiLight = hemisphereLights[lightIndex];\n vLightProbe = vec3(vPoint);\n\n absorbanceUp = 0.0;\n absorbanceDown = 0.0;\n\n stepAccumulationUp = 0.;\n stepAccumulationDown = 0.;\n\n lightDirection = -normalize((vec4(hemiLight.direction, 1.) * viewMatrix).xyz);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 1; lightMarch < int(lightMarchLimit / 2.); lightMarch++) {\n if (absorbanceUp < 1.) {\n textureProbe = vLightProbe - float(lightMarch) * vLightStep;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, textureProbe);\n #endif\n\n lightSample = clampedTexture(densityMap3D, textureProbe);\n\n absorbanceUp += lightSample * eInverseAbsorbance;\n stepAccumulationUp += 1.;\n }\n \n if (absorbanceDown < 1.) {\n textureProbe = vLightProbe + float(lightMarch) * vLightStep;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, textureProbe);\n #endif\n\n lightSample = clampedTexture(densityMap3D, textureProbe);\n\n absorbanceDown += blendSample(lightSample) * eInverseAbsorbance;\n stepAccumulationDown += 1.;\n }\n }\n\n absorbanceUp = min(1., absorbanceUp);\n absorbanceDown = min(1., absorbanceDown);\n\n lightAlbedo += mix(\n (1. - absorbanceUp) * hemiLight.skyColor,\n (1. - absorbanceDown) * hemiLight.groundColor,\n 0.5\n );\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_POINT_LIGHTS > 0 && VOLUME_USE_POINT_LIGHTS\n \n PointLight pointLight;\n lightAlbedo = vec3(0.);\n \n for (int lightIndex = 0; lightIndex < NUM_POINT_LIGHTS; lightIndex++) {\n pointLight = pointLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.;\n stepAccumulation = 1.;\n\n lightDirection = vPoint - (mInverseModelViewMatrix * vec4(pointLight.position, 1.)).xyz;\n lightDistance = length(lightDirection) * vUnitLength;\n lightDirection = normalize(lightDirection);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vLightProbe);\n #endif\n \n lightSample = clampedTexture(densityMap3D, vLightProbe) * sqrt(lightDistance);\n \n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * pointLight.color;\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_DIR_LIGHTS > 0 && VOLUME_USE_DIR_LIGHTS\n \n DirectionalLight directionalLight;\n lightAlbedo = vec3(0.);\n\n for (int lightIndex = 0; lightIndex < NUM_DIR_LIGHTS; lightIndex++) {\n directionalLight = directionalLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.0;\n stepAccumulation = 1.;\n\n lightDirection = -normalize((vec4(directionalLight.direction, 1.) * viewMatrix).xyz);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vLightProbe);\n #endif\n \n float lightSample = clampedTexture(densityMap3D, vLightProbe);\n\n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * directionalLight.color;\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_SPOT_LIGHTS > 0 && VOLUME_USE_SPOT_LIGHTS\n \n SpotLight spotLight;\n lightAlbedo = vec3(0.);\n\n float angleCos;\n float spotAttenuation;\n \n for (int lightIndex = 0; lightIndex < NUM_SPOT_LIGHTS; lightIndex++) {\n spotLight = spotLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.0;\n stepAccumulation = 1.;\n\n lightDirection = vPoint - (mInverseModelViewMatrix * vec4(spotLight.position, 1.)).xyz;\n lightDistance = length(lightDirection) * vUnitLength;\n lightDirection = normalize(lightDirection);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n angleCos = dot(normalize(-lightDirection), normalize(mInverseNormalMatrix * spotLight.direction));\n spotAttenuation = smoothstep(spotLight.coneCos, spotLight.penumbraCos, angleCos);\n\n if (spotAttenuation > 0.) {\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vLightProbe);\n #endif\n \n lightSample = clampedTexture(densityMap3D, vLightProbe);\n \n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * spotAttenuation * spotLight.color;\n }\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n albedo *= baseColorSample;\n }\n \n emissive = getBlackBodyRadiation(emissive.r);\n albedo += emissive;\n\n outgoingLight.rgb = max(max(scatterColor, vEnvMapScatter), albedo);\n diffuseColor.a = saturate(density) * opacity;\n\n #include <opaque_fragment>\n ");};}}
41
41
 
42
42
  const getDensity$1=(t,e)=>t.isLevelSet?.()?t.isActive(e)?1:0:Three.MathUtils.clamp(t.getValue(e),0,1);
43
43
 
@@ -45,15 +45,15 @@ const textureTypes={uint8:"uint8",half:"half",float:"float"};const textureTypeMa
45
45
 
46
46
  const textureBuilds={sample:"sample",leaves:"leaves"};const textureFilters={nearest:"nearest",trilinear:"trilinear",max:"max"};function leafTextureKernel(){const e=new Float32Array(1),t=new Uint32Array(e.buffer),n=n=>{e[0]=n;const r=t[0],o=r>>>16&32768;let l=(r>>>23&255)-127+15,s=8388607&r;return l>=31?31744|o:l<=0?l<-10?o:(s=(8388608|s)>>1-l,o|s+4096>>13):(s+=4096,8388608&s&&(s=0,l++),l>=31?31744|o:o|l<<10|s>>13)};return {create:({resolution:e,start:t,step:r,filter:o,background:l,leafLog2Dim:s})=>{const a=e*e,i=a*e,u="trilinear"===o,c="max"===o,f=1<<s,d=1<<3*s,h=Math.ceil(d/32);let p=null,g=null,x=0;const m=e=>p?p[x*h+(e>>5)]>>>(31&e)&1:Math.min(Math.max(g[x*d+e],0),1),y=new Float32Array(i),k=u?new Float32Array(i):null;u||0===l||y.fill(l);const w=[0,1,2].map((()=>({texels:new Int32Array(2*e+8),voxels:new Int32Array(2*e+8),weights:new Float32Array(2*e+8),count:0,scatter:new Int32Array(f)}))),M=(n,o,l)=>{const s=w[n],a=t[n],i=r[n],c=o+l;let f=(o-1-a)/i,d=(c+1-a)/i;f>d&&([f,d]=[d,f]);const h=Math.max(0,Math.floor(f)),p=Math.min(e-1,Math.ceil(d));let g=0;for(let e=h;e<=p;e++){const t=a+e*i;if(u){const n=Math.floor(t),r=t-n;n>=o&&n<c&&(s.texels[g]=e,s.voxels[g]=n-o,s.weights[g]=1-r,g++),n+1>=o&&n+1<c&&(s.texels[g]=e,s.voxels[g]=n+1-o,s.weights[g]=r,g++);}else {const n=Math.round(t);n>=o&&n<c&&(s.texels[g]=e,s.voxels[g]=n-o,s.weights[g]=1,g++);}}s.count=g;},v=(n,o)=>{const l=Math.round((o-t[n])/r[n]);return l>=0&&l<e?l:-1},b=(e,t,n)=>{u?(y[e]+=t*n,k[e]+=n):y[e]=c?Math.max(y[e],t):t;},C=(n,o,s,i,u)=>{if(u===l)return;M(0,n,i),M(1,o,i),M(2,s,i);const[f,d,h]=w;for(let t=0;t<h.count;t++)for(let n=0;n<d.count;n++){const r=d.texels[n]*e+h.texels[t]*a,o=d.weights[n]*h.weights[t];for(let e=0;e<f.count;e++)b(f.texels[e]+r,u,f.weights[e]*o);}if(!c)return;const p=[n,o,s].map(((n,o)=>{const l=Math.round((n-t[o])/r[o]),s=Math.round((n+i-1-t[o])/r[o]);return [Math.max(0,Math.min(l,s)),Math.min(e-1,Math.max(l,s))]}));for(let t=p[2][0];t<=p[2][1];t++)for(let n=p[1][0];n<=p[1][1];n++)for(let r=p[0][0];r<=p[0][1];r++){const o=r+n*e+t*a;y[o]=Math.max(y[o],u);}},L=(t,n,r)=>{M(0,t,f),M(1,n,f),M(2,r,f);const[o,i,u]=w,h=2*s;for(let t=0;t<u.count;t++)for(let n=0;n<i.count;n++){const r=i.texels[n]*e+u.texels[t]*a,c=i.voxels[n]<<s|u.voxels[t],f=i.weights[n]*u.weights[t];for(let e=0;e<o.count;e++){const t=m(o.voxels[e]<<h|c);t!==l&&b(o.texels[e]+r,t,o.weights[e]*f);}}if(c){[t,n,r].forEach(((e,t)=>{for(let n=0;n<f;n++)w[t].scatter[n]=v(t,e+n);}));for(let t=0;t<d;t++){const n=m(t),r=o.scatter[t>>h],c=i.scatter[t>>s&f-1],d=u.scatter[t&f-1];if(n!==l&&-1!==r&&-1!==c&&-1!==d){const t=r+c*e+d*a;y[t]=Math.max(y[t],n);}}}};return {addChunk:e=>{const{leafCount:t,origins:n,tileCount:r,tiles:o}=e;for(p=e.leafWords,g=e.leafValues,x=0;x<t;x++)L(n[3*x],n[3*x+1],n[3*x+2]);for(let e=0;e<r;e++){const t=5*e;C(o[t],o[t+1],o[t+2],o[t+3],o[t+4]);}p=null,g=null;},finish:({textureType:t,radius:r})=>{let o=y;if(u)for(let e=0;e<i;e++)o[e]+=Math.max(0,1-k[e])*l;if(r&&(o=((t,n)=>{const r=new Float32Array(i);for(let o=0;o<e;o++)for(let l=0;l<e;l++)for(let s=0;s<e;s++){const i=s+l*e+o*a,u=t[i];if(0!==u)for(let t=-n;t<n;t++)for(let c=-n;c<n;c++)for(let f=-n;f<n;f++){if(s+t<0||s+t>=e||l+c<0||l+c>=e||o+f<0||o+f>=e)continue;const d=i+t+c*e+f*a,h=Math.max(0,Math.min(1,1-Math.hypot(t,c,f)/(n/2)));r[d]=Math.min(r[d]+h*u,1);}}return r})(o,r)),"float"===t)return o;if("half"===t){const e=new Uint16Array(i);for(let t=0;t<i;t++)e[t]=n(o[t]);return e}const s=new Uint8Array(i);for(let e=0;e<i;e++)s[e]=Math.min(255*o[e],255);return s}}}}}const workerSource$1=`\nconst kernel = (${leafTextureKernel.toString()})();\nlet build = null;\n\nself.onmessage = ({ data }) => {\n if (data.type === 'create') {\n build = kernel.create(data.config);\n } else if (data.type === 'chunk') {\n build.addChunk(data.chunk);\n self.postMessage({ type: 'chunk' });\n } else if (data.type === 'finish') {\n const result = build.finish(data.options);\n build = null;\n self.postMessage({ type: 'result', result }, [result.buffer]);\n }\n};\n`;const canBuildFromLeaves=e=>e instanceof openvdb.GridDescriptor&&!!e.root&&"number"==typeof e.root.background;const getDensity=(e,t,n)=>n?t?1:0:Math.min(Math.max(e,0),1);function*packChunks(e,t,n){const r=e.isLevelSet(),o=1<<3*t,l=Math.ceil(o/32),s=getDensity(e.root.background,!1,r);let a;const i=()=>{a={leafCount:0,origins:new Int32Array(3072),leafValues:r?null:new Float32Array(1024*o),leafWords:r?new Uint32Array(1024*l):null,tileCount:0,tiles:new Float64Array(5120)};},u=()=>1024===a.leafCount||1024===a.tileCount,c=(e,t,s,i)=>{const u=a.leafCount++;a.origins[3*u]=t,a.origins[3*u+1]=s,a.origins[3*u+2]=i,r?a.leafWords.set(e.valueMask.words,u*l):e.values.length===o?a.leafValues.set(e.values,u*o):a.leafValues.fill(n,u*o,(u+1)*o);},f=(e,t,n,o,l,i)=>{const u=getDensity(l,i,r);if(u===s)return;const c=5*a.tileCount++;a.tiles[c]=e,a.tiles[c+1]=t,a.tiles[c+2]=n,a.tiles[c+3]=o,a.tiles[c+4]=u;};function*d(t,n,o,l){const s=t.log2dim,h=t.total-t.log2dim,p=1<<h,g=(1<<s)-1,x=t.childMask.words,m=t.valueMask.words;for(let y=0;y<x.length;y++){const k=x[y];let w=k|(r?m[y]&~k:~k);for(;0!==w;){const r=31-Math.clz32(w&-w),x=y<<5|r;if(w&=w-1,k>>>r&1){const e=t.table[x],r=n+e.origin.x,s=o+e.origin.y,a=l+e.origin.z;e.isLeaf()?c(e,r,s,a):yield*d(e,r,s,a);}else f(n+((x>>2*s&g)<<h),o+((x>>s&g)<<h),l+((x&g)<<h),p,t.values[x]??e.root.background,1==(m[y]>>>r&1));u()&&(yield a,i());}}}i();for(const t of e.root.table){const{x:e,y:n,z:r}=t.origin;t.isInternalNode()?yield*d(t,e,n,r):(t.isLeaf()?c(t,e,n,r):f(e,n,r,t.dim,t.value,t.active),u()&&(yield a,i()));}(a.leafCount||a.tileCount)&&(yield a);}const getChunkTransfer=e=>[e.origins,e.leafValues,e.leafWords,e.tiles].filter(Boolean).map((e=>e.buffer)),createWorkerBackend$1=()=>{if("undefined"==typeof Worker||"undefined"==typeof Blob||"undefined"==typeof URL)return null;const e=URL.createObjectURL(new Blob([workerSource$1],{type:"text/javascript"})),t=new Worker(e),n=[],r=(e,r=[])=>new Promise(((o,l)=>{n.push({resolve:o,reject:l}),t.postMessage(e,r);}));return t.onmessage=({data:e})=>{n.shift()?.resolve(e.result);},t.onerror=e=>{e.preventDefault(),n.splice(0).forEach((({reject:t})=>t(e.message||"Worker error")));},{create:e=>t.postMessage({type:"create",config:e}),addChunk:e=>r({type:"chunk",chunk:e},getChunkTransfer(e)),finish:e=>r({type:"finish",options:e}),dispose:()=>{t.terminate(),URL.revokeObjectURL(e),n.splice(0).forEach((({resolve:e})=>e(null)));}}},createLocalBackend$1=()=>{const e=leafTextureKernel();let t=null,n=performance.now();const r=e=>performance.now()-n<12?Promise.resolve().then(e):new Promise((t=>setTimeout((()=>{n=performance.now(),t(e());}),0)));return {create:n=>{t=e.create(n);},addChunk:e=>r((()=>t?.addChunk(e))),finish:e=>r((()=>t?.finish(e)??null)),dispose:()=>{t=null;}}};const buildLeafTexture=({grid:e,resolution:t,start:n,step:r,filter:o=textureFilters.nearest,textureType:l,radius:s,onProgress:a})=>{const{leafLog2Dim:i}=e.treeLayout,u=e.isLevelSet(),c=e.leavesCount*(1<<3*i);let f=!1,d=null;const h=async h=>{d=h(),d.create({resolution:t,start:[n.x,n.y,n.z],step:[r.x,r.y,r.z],filter:o,background:getDensity(e.root.background,!1,u),leafLog2Dim:i});const p=[];let g=0;for(const t of packChunks(e,i,e.root.background)){if(f)return null;g+=t.leafCount<<3*i,p.push(d.addChunk(t)),p.length>=2&&await p.shift(),a&&a({convertedVoxels:g,totalVoxels:c});}return await Promise.all(p),f?null:d.finish({textureType:l,radius:s})};return {promise:h((()=>createWorkerBackend$1()||createLocalBackend$1())).catch((e=>f?null:(console.warn("buildLeafTexture","Worker failed, building on the main thread.",{error:e}),d?.dispose(),h(createLocalBackend$1)))).finally((()=>{d?.dispose();})),cancel:()=>{f=!0,d?.dispose();}}};
47
47
 
48
- const getTextureMapping=(e,t)=>{const r=new Three.Vector3,s=new Three.Vector3,a=new Three.Vector3,o=new Three.Vector3,i=new Bbox;return i.set(...e.getPreciseWorldBbox()),i.getCenter(r),i.getSize(s),a.copy(s).multiplyScalar(1/t),e.transform.applyInverseJacobian(a),o.copy(r).sub(s.clone().multiplyScalar(.5)),e.transform.applyInverseTransformMap(o),o.add(a.clone().multiplyScalar(.5)),{center:r,size:s,start:o,step:a}};class VolumeToFog extends Three__namespace.Group{processes=[];leafBuilds=[];materials=[];isFogVolume=!0;constructor(e,t,r,s){super();const{resolution:a,progressive:o,emissiveGrid:i,baseColorGrid:n,maskGrid:l,radius:c,textureBuild:u=textureBuilds.sample,textureFilter:d=textureFilters.nearest,textureType:p=textureTypes.uint8}=t,m=getTextureScale(p);let h,x,f,T,y,w,g;if(this.frustumCulled=!1,h=e instanceof Array?e:void 0!==e.grids?Object.values(e.grids):[e],i)if(h=h.filter((e=>e!==i)),i instanceof Uint8Array)x=createScalarTexture3D(i,a,textureTypes.uint8);else {const e=createTextureData(p,Math.pow(a,3));x=createScalarTexture3D(e,a,p),f=(t,r,s)=>{const a=void 0!==s?s:getDensity$1(i,r)*m;return writeTexel(e,t,a),a};}if(n){if(n instanceof Uint8Array)T=new Three__namespace.Data3DTexture(n,a,a,a);else {const e=new Uint8Array(4*Math.pow(a,3));T=new Three__namespace.Data3DTexture(e,a,a,a);const t=new Three__namespace.Color;y=(r,s,a)=>{if(a)return e[4*r+0]=255*a.r,e[4*r+1]=255*a.g,e[4*r+2]=255*a.b,void(e[4*r+3]=255);const o=n.getValue(s);return o instanceof Three__namespace.Color?t.set(gridValue):o instanceof openvdb.Vector3||o instanceof Three__namespace.Vector3?t.setRGB(o.x,o.y,o.z):t.setRGB(1,0,1),e[4*r+0]=255*t.r,e[4*r+1]=255*t.g,e[4*r+2]=255*t.b,e[4*r+3]=255,{r:t.r,g:t.g,b:t.b,a:t.a}};}T.format=Three__namespace.RGBAFormat,T.minFilter=Three__namespace.LinearFilter,T.magFilter=Three__namespace.LinearFilter,T.unpackAlignment=1,T.needsUpdate=!0;}if(l)if(l instanceof Uint8Array)w=createScalarTexture3D(l,a,textureTypes.uint8);else {const e=createTextureData(p,Math.pow(a,3));w=createScalarTexture3D(e,a,p),g=(t,r,s)=>{const a=void 0!==s?s:getDensity$1(l,r)*m;return writeTexel(e,t,a),a};}let D=0,v=0;const M=h.length,V=M*Math.pow(a,3),B=e=>!e||e instanceof Uint8Array||canBuildFromLeaves(e),F=u===textureBuilds.leaves&&h.every((e=>!(e instanceof openvdb.GridDescriptor)||canBuildFromLeaves(e)))&&B(i)&&B(l)&&(!n||n instanceof Uint8Array);u!==textureBuilds.leaves||F||console.warn("VolumeToFog","textureBuild","Primitive or vector grids need the sample build. Using the sample build.");const b=[],U=[];let G=!1;const A=()=>{s&&s({convertedVoxels:U.reduce(((e,t)=>e+t.convertedVoxels),0),totalVoxels:U.reduce(((e,t)=>e+t.totalVoxels),0),convertedGrids:D,totalGrids:M});},S=(e,t,r,s)=>{const o={convertedVoxels:0,totalVoxels:0};U.push(o);const i=buildLeafTexture({grid:e,resolution:a,start:t.start,step:t.step,filter:d,textureType:p,radius:s,onProgress:({convertedVoxels:e,totalVoxels:t})=>{o.convertedVoxels=e,o.totalVoxels=t,A();}});return this.leafBuilds.push(i),i.promise.then((e=>!!e&&(r.image.data=e,r.needsUpdate=!0,!0))).catch((e=>(console.error("VolumeToFog","buildLeafTexture",{error:e}),!1)))};h.reverse().forEach((async(e,n)=>{if(e instanceof Uint8Array){const r=new Three__namespace.Data3DTexture(e,a,a,a);r.format=Three__namespace.RedFormat,r.minFilter=Three__namespace.LinearFilter,r.magFilter=Three__namespace.LinearFilter,r.unpackAlignment=1,r.needsUpdate=!0;const s=new Three__namespace.SphereGeometry(1),o=new VolumeBasicMaterial({...t,emissiveMap3D:x,densityMap3D:r,baseColorMap3D:T,maskMap3D:w}),i=new Three__namespace.Mesh(s,o);return i.frustumCulled=!1,i.onBeforeRender=updateFrameDepth,this.materials.push(o),void this.add(i)}if(!(e instanceof openvdb.GridDescriptor))return;const u=createTextureData(p,Math.pow(a,3)),d=createScalarTexture3D(u,a,p),h=new Three__namespace.SphereGeometry(1),B=new VolumeBasicMaterial({...t,emissiveMap3D:x,densityMap3D:d,baseColorMap3D:T,maskMap3D:w}),U=new Three__namespace.Mesh(h,B);if(U.frustumCulled=!1,U.onBeforeRender=updateFrameDepth,this.materials.push(B),F){const t=getTextureMapping(e,a);return U.position.copy(t.center),U.scale.copy(t.size),o&&this.add(U),b.push(S(e,t,d,c).then((e=>{e&&(D++,o||this.add(U),A());}))),void(G||(G=!0,!i||i instanceof Uint8Array||b.push(S(i,t,x,0)),!l||l instanceof Uint8Array||b.push(S(l,t,w,0))))}const L=a,C=Math.pow(L,2);o&&this.add(U),await(t=>new Promise((a=>{const i=Math.pow(t,2),l=Math.pow(t,3),{center:p,size:h,start:B,step:F}=getTextureMapping(e,t);U.position.copy(p),U.scale.copy(h);let b=0,G=0,A=0;let S=function*(){for(let r=0;r<l;r++){const r=b+G*t+A*i,s=getDensity$1(e,B),a=f&&f(r,B),n=y&&y(r,B),l=g&&g(r,B),p=c;if(p)for(let e=-p;e<p;e++)for(let o=-p;o<p;o++)for(let i=-p;i<p;i++){if(b+e<0||b+e>=t||G+o<0||G+o>=t||A+i<0||A+i>=t)continue;const d=r+e+o*L+i*C,p=Math.max(0,Math.min(1,1-Math.hypot(e,o,i)/(c/2))),h=p*s*m;writeTexel(u,d,readTexel(u,d)+h),writeTexel(u,d,Math.min(readTexel(u,d),m)),f&&f(d,null,a*p*m),y&&y(d,null,n*p),g&&g(d,null,l*p*m);}else {const e=s*m;writeTexel(u,r,readTexel(u,r)+e),writeTexel(u,r,Math.min(readTexel(u,r),m));}if(v++,o&&(d.needsUpdate=!0,x&&(x.needsUpdate=!0),T&&(T.needsUpdate=!0),w&&(w.needsUpdate=!0)),A>=t){x.needsUpdate=!0,T.needsUpdate=!0,w.needsUpdate=!0;break}b++,B.x+=F.x,b>=t&&(b=0,B.x-=F.x*t,G++,B.y+=F.y),G>=t&&(G=0,B.y-=F.y*t,A++,B.z+=F.z),yield;}}();const z=()=>{let e=window.performance.now();for(;window.performance.now()-e<16&&S;){const{done:e}=S.next();s&&s({convertedVoxels:v,totalVoxels:V,convertedGrids:D,totalGrids:M}),e&&(D++,S=null,delete this.processes[n],a(),D===M&&(o||this.add(U),r&&r()));}S&&this.processes&&(this.processes[n]=setTimeout(z,0));};z();})))(a);})),F&&Promise.all(b).then((()=>{this.processes&&r&&r();}));}dispose(){this.leafBuilds.forEach((e=>e.cancel())),this.leafBuilds=[],this.processes&&(this.processes.forEach(((e,t)=>{cancelAnimationFrame(e),delete this.processes[t];})),this.processes=null);}}
48
+ const getTextureMapping=(e,t)=>{const r=new Three.Vector3,s=new Three.Vector3,o=new Three.Vector3,i=new Three.Vector3,a=new Bbox;return a.set(...e.getPreciseWorldBbox()),a.getCenter(r),a.getSize(s),o.copy(s).multiplyScalar(1/t),e.transform.applyInverseJacobian(o),i.copy(r).sub(s.clone().multiplyScalar(.5)),e.transform.applyInverseTransformMap(i),i.add(o.clone().multiplyScalar(.5)),{center:r,size:s,start:i,step:o}};class VolumeToFog extends Three__namespace.Group{processes=[];leafBuilds=[];materials=[];textures=[];geometries=[];isFogVolume=!0;constructor(e,t,r,s){super();const{resolution:o,progressive:i,emissiveGrid:a,baseColorGrid:n,maskGrid:l,radius:u,textureBuild:c=textureBuilds.sample,textureFilter:p=textureFilters.nearest,textureType:d=textureTypes.uint8}=t,h=getTextureScale(d);let m,x,f,T,y,g,w;if(this.frustumCulled=!1,m=e instanceof Array?e:void 0!==e.grids?Object.values(e.grids):[e],a){if(m=m.filter((e=>e!==a)),a instanceof Uint8Array)x=createScalarTexture3D(a,o,textureTypes.uint8);else {const e=createTextureData(d,Math.pow(o,3));x=createScalarTexture3D(e,o,d),f=(t,r,s)=>{const o=void 0!==s?s:getDensity$1(a,r)*h;return writeTexel(e,t,o),o};}this.textures.push(x);}if(n){if(n instanceof Uint8Array)T=new Three__namespace.Data3DTexture(n,o,o,o);else {const e=new Uint8Array(4*Math.pow(o,3));T=new Three__namespace.Data3DTexture(e,o,o,o);const t=new Three__namespace.Color;y=(r,s,o)=>{if(o)return e[4*r+0]=255*o.r,e[4*r+1]=255*o.g,e[4*r+2]=255*o.b,void(e[4*r+3]=255);const i=n.getValue(s);return i instanceof Three__namespace.Color?t.set(gridValue):i instanceof openvdb.Vector3||i instanceof Three__namespace.Vector3?t.setRGB(i.x,i.y,i.z):t.setRGB(1,0,1),e[4*r+0]=255*t.r,e[4*r+1]=255*t.g,e[4*r+2]=255*t.b,e[4*r+3]=255,{r:t.r,g:t.g,b:t.b,a:t.a}};}this.textures.push(T),T.format=Three__namespace.RGBAFormat,T.minFilter=Three__namespace.LinearFilter,T.magFilter=Three__namespace.LinearFilter,T.unpackAlignment=1,T.needsUpdate=!0;}if(l){if(l instanceof Uint8Array)g=createScalarTexture3D(l,o,textureTypes.uint8);else {const e=createTextureData(d,Math.pow(o,3));g=createScalarTexture3D(e,o,d),w=(t,r,s)=>{const o=void 0!==s?s:getDensity$1(l,r)*h;return writeTexel(e,t,o),o};}this.textures.push(g);}let D=0,v=0;const M=m.length,V=M*Math.pow(o,3),B=e=>!e||e instanceof Uint8Array||canBuildFromLeaves(e),F=c===textureBuilds.leaves&&m.every((e=>!(e instanceof openvdb.GridDescriptor)||canBuildFromLeaves(e)))&&B(a)&&B(l)&&(!n||n instanceof Uint8Array);c!==textureBuilds.leaves||F||console.warn("VolumeToFog","textureBuild","Primitive or vector grids need the sample build. Using the sample build.");const b=[],U=[];let G=!1;const S=()=>{s&&s({convertedVoxels:U.reduce(((e,t)=>e+t.convertedVoxels),0),totalVoxels:U.reduce(((e,t)=>e+t.totalVoxels),0),convertedGrids:D,totalGrids:M});},A=(e,t,r,s)=>{const i={convertedVoxels:0,totalVoxels:0};U.push(i);const a=buildLeafTexture({grid:e,resolution:o,start:t.start,step:t.step,filter:p,textureType:d,radius:s,onProgress:({convertedVoxels:e,totalVoxels:t})=>{i.convertedVoxels=e,i.totalVoxels=t,S();}});return this.leafBuilds.push(a),a.promise.then((e=>!!e&&(r.image.data=e,r.needsUpdate=!0,!0))).catch((e=>(console.error("VolumeToFog","buildLeafTexture",{error:e}),!1)))};m.reverse().forEach((async(e,n)=>{if(e instanceof Uint8Array){const r=new Three__namespace.Data3DTexture(e,o,o,o);r.format=Three__namespace.RedFormat,r.minFilter=Three__namespace.LinearFilter,r.magFilter=Three__namespace.LinearFilter,r.unpackAlignment=1,r.needsUpdate=!0;const s=new Three__namespace.SphereGeometry(1),i=new VolumeBasicMaterial({...t,emissiveMap3D:x,densityMap3D:r,baseColorMap3D:T,maskMap3D:g}),a=new Three__namespace.Mesh(s,i);return a.frustumCulled=!1,a.onBeforeRender=updateFrameDepth,this.materials.push(i),this.textures.push(r),this.geometries.push(s),void this.add(a)}if(!(e instanceof openvdb.GridDescriptor))return;const c=createTextureData(d,Math.pow(o,3)),p=createScalarTexture3D(c,o,d),m=new Three__namespace.SphereGeometry(1),B=new VolumeBasicMaterial({...t,emissiveMap3D:x,densityMap3D:p,baseColorMap3D:T,maskMap3D:g}),U=new Three__namespace.Mesh(m,B);if(U.frustumCulled=!1,U.onBeforeRender=updateFrameDepth,this.materials.push(B),this.textures.push(p),this.geometries.push(m),F){const t=getTextureMapping(e,o);return U.position.copy(t.center),U.scale.copy(t.size),i&&this.add(U),b.push(A(e,t,p,u).then((e=>{e&&(D++,i||this.add(U),S());}))),void(G||(G=!0,!a||a instanceof Uint8Array||b.push(A(a,t,x,0)),!l||l instanceof Uint8Array||b.push(A(l,t,g,0))))}const L=o,C=Math.pow(L,2);i&&this.add(U),await(t=>new Promise((o=>{const a=Math.pow(t,2),l=Math.pow(t,3),{center:d,size:m,start:B,step:F}=getTextureMapping(e,t);U.position.copy(d),U.scale.copy(m);let b=0,G=0,S=0;let A=function*(){for(let r=0;r<l;r++){const r=b+G*t+S*a,s=getDensity$1(e,B),o=f&&f(r,B),n=y&&y(r,B),l=w&&w(r,B),d=u;if(d)for(let e=-d;e<d;e++)for(let i=-d;i<d;i++)for(let a=-d;a<d;a++){if(b+e<0||b+e>=t||G+i<0||G+i>=t||S+a<0||S+a>=t)continue;const p=r+e+i*L+a*C,d=Math.max(0,Math.min(1,1-Math.hypot(e,i,a)/(u/2))),m=d*s*h;writeTexel(c,p,readTexel(c,p)+m),writeTexel(c,p,Math.min(readTexel(c,p),h)),f&&f(p,null,o*d*h),y&&y(p,null,n*d),w&&w(p,null,l*d*h);}else {const e=s*h;writeTexel(c,r,readTexel(c,r)+e),writeTexel(c,r,Math.min(readTexel(c,r),h));}if(v++,i&&(p.needsUpdate=!0,x&&(x.needsUpdate=!0),T&&(T.needsUpdate=!0),g&&(g.needsUpdate=!0)),S>=t){x.needsUpdate=!0,T.needsUpdate=!0,g.needsUpdate=!0;break}b++,B.x+=F.x,b>=t&&(b=0,B.x-=F.x*t,G++,B.y+=F.y),G>=t&&(G=0,B.y-=F.y*t,S++,B.z+=F.z),yield;}}();const z=()=>{let e=window.performance.now();for(;window.performance.now()-e<16&&A;){const{done:e}=A.next();s&&s({convertedVoxels:v,totalVoxels:V,convertedGrids:D,totalGrids:M}),e&&(D++,A=null,delete this.processes[n],o(),D===M&&(i||this.add(U),r&&r()));}A&&this.processes&&(this.processes[n]=setTimeout(z,0));};z();})))(o);})),F&&Promise.all(b).then((()=>{this.processes&&r&&r();}));}dispose(){this.leafBuilds.forEach((e=>e.cancel())),this.leafBuilds=[],this.processes&&(this.processes.forEach((e=>clearTimeout(e))),this.processes=null),this.materials.forEach((e=>e.dispose())),this.textures.forEach((e=>e.dispose())),this.geometries.forEach((e=>e.dispose())),this.materials=[],this.textures=[],this.geometries=[];}}
49
49
 
50
- const triangleLUT=[[],[0,8,3],[0,1,9],[1,8,3,9,8,1],[1,2,10],[0,8,3,1,2,10],[9,2,10,0,2,9],[2,8,3,2,10,8,10,9,8],[3,11,2],[0,11,2,8,11,0],[1,9,0,2,3,11],[1,11,2,1,9,11,9,8,11],[3,10,1,11,10,3],[0,10,1,0,8,10,8,11,10],[3,9,0,3,11,9,11,10,9],[9,8,10,10,8,11],[4,7,8],[4,3,0,7,3,4],[0,1,9,8,4,7],[4,1,9,4,7,1,7,3,1],[1,2,10,8,4,7],[3,4,7,3,0,4,1,2,10],[9,2,10,9,0,2,8,4,7],[2,10,9,2,9,7,2,7,3,7,9,4],[8,4,7,3,11,2],[11,4,7,11,2,4,2,0,4],[9,0,1,8,4,7,2,3,11],[4,7,11,9,4,11,9,11,2,9,2,1],[3,10,1,3,11,10,7,8,4],[1,11,10,1,4,11,1,0,4,7,11,4],[4,7,8,9,0,11,9,11,10,11,0,3],[4,7,11,4,11,9,9,11,10],[9,5,4],[9,5,4,0,8,3],[0,5,4,1,5,0],[8,5,4,8,3,5,3,1,5],[1,2,10,9,5,4],[3,0,8,1,2,10,4,9,5],[5,2,10,5,4,2,4,0,2],[2,10,5,3,2,5,3,5,4,3,4,8],[9,5,4,2,3,11],[0,11,2,0,8,11,4,9,5],[0,5,4,0,1,5,2,3,11],[2,1,5,2,5,8,2,8,11,4,8,5],[10,3,11,10,1,3,9,5,4],[4,9,5,0,8,1,8,10,1,8,11,10],[5,4,0,5,0,11,5,11,10,11,0,3],[5,4,8,5,8,10,10,8,11],[9,7,8,5,7,9],[9,3,0,9,5,3,5,7,3],[0,7,8,0,1,7,1,5,7],[1,5,3,3,5,7],[9,7,8,9,5,7,10,1,2],[10,1,2,9,5,0,5,3,0,5,7,3],[8,0,2,8,2,5,8,5,7,10,5,2],[2,10,5,2,5,3,3,5,7],[7,9,5,7,8,9,3,11,2],[9,5,7,9,7,2,9,2,0,2,7,11],[2,3,11,0,1,8,1,7,8,1,5,7],[11,2,1,11,1,7,7,1,5],[9,5,8,8,5,7,10,1,3,10,3,11],[5,7,0,5,0,9,7,11,0,1,0,10,11,10,0],[11,10,0,11,0,3,10,5,0,8,0,7,5,7,0],[11,10,5,7,11,5],[10,6,5],[0,8,3,5,10,6],[9,0,1,5,10,6],[1,8,3,1,9,8,5,10,6],[1,6,5,2,6,1],[1,6,5,1,2,6,3,0,8],[9,6,5,9,0,6,0,2,6],[5,9,8,5,8,2,5,2,6,3,2,8],[2,3,11,10,6,5],[11,0,8,11,2,0,10,6,5],[0,1,9,2,3,11,5,10,6],[5,10,6,1,9,2,9,11,2,9,8,11],[6,3,11,6,5,3,5,1,3],[0,8,11,0,11,5,0,5,1,5,11,6],[3,11,6,0,3,6,0,6,5,0,5,9],[6,5,9,6,9,11,11,9,8],[5,10,6,4,7,8],[4,3,0,4,7,3,6,5,10],[1,9,0,5,10,6,8,4,7],[10,6,5,1,9,7,1,7,3,7,9,4],[6,1,2,6,5,1,4,7,8],[1,2,5,5,2,6,3,0,4,3,4,7],[8,4,7,9,0,5,0,6,5,0,2,6],[7,3,9,7,9,4,3,2,9,5,9,6,2,6,9],[3,11,2,7,8,4,10,6,5],[5,10,6,4,7,2,4,2,0,2,7,11],[0,1,9,4,7,8,2,3,11,5,10,6],[9,2,1,9,11,2,9,4,11,7,11,4,5,10,6],[8,4,7,3,11,5,3,5,1,5,11,6],[5,1,11,5,11,6,1,0,11,7,11,4,0,4,11],[0,5,9,0,6,5,0,3,6,11,6,3,8,4,7],[6,5,9,6,9,11,4,7,9,7,11,9],[10,4,9,6,4,10],[4,10,6,4,9,10,0,8,3],[10,0,1,10,6,0,6,4,0],[8,3,1,8,1,6,8,6,4,6,1,10],[1,4,9,1,2,4,2,6,4],[3,0,8,1,2,9,2,4,9,2,6,4],[0,2,4,4,2,6],[8,3,2,8,2,4,4,2,6],[10,4,9,10,6,4,11,2,3],[0,8,2,2,8,11,4,9,10,4,10,6],[3,11,2,0,1,6,0,6,4,6,1,10],[6,4,1,6,1,10,4,8,1,2,1,11,8,11,1],[9,6,4,9,3,6,9,1,3,11,6,3],[8,11,1,8,1,0,11,6,1,9,1,4,6,4,1],[3,11,6,3,6,0,0,6,4],[6,4,8,11,6,8],[7,10,6,7,8,10,8,9,10],[0,7,3,0,10,7,0,9,10,6,7,10],[10,6,7,1,10,7,1,7,8,1,8,0],[10,6,7,10,7,1,1,7,3],[1,2,6,1,6,8,1,8,9,8,6,7],[2,6,9,2,9,1,6,7,9,0,9,3,7,3,9],[7,8,0,7,0,6,6,0,2],[7,3,2,6,7,2],[2,3,11,10,6,8,10,8,9,8,6,7],[2,0,7,2,7,11,0,9,7,6,7,10,9,10,7],[1,8,0,1,7,8,1,10,7,6,7,10,2,3,11],[11,2,1,11,1,7,10,6,1,6,7,1],[8,9,6,8,6,7,9,1,6,11,6,3,1,3,6],[0,9,1,11,6,7],[7,8,0,7,0,6,3,11,0,11,6,0],[7,11,6],[7,6,11],[3,0,8,11,7,6],[0,1,9,11,7,6],[8,1,9,8,3,1,11,7,6],[10,1,2,6,11,7],[1,2,10,3,0,8,6,11,7],[2,9,0,2,10,9,6,11,7],[6,11,7,2,10,3,10,8,3,10,9,8],[7,2,3,6,2,7],[7,0,8,7,6,0,6,2,0],[2,7,6,2,3,7,0,1,9],[1,6,2,1,8,6,1,9,8,8,7,6],[10,7,6,10,1,7,1,3,7],[10,7,6,1,7,10,1,8,7,1,0,8],[0,3,7,0,7,10,0,10,9,6,10,7],[7,6,10,7,10,8,8,10,9],[6,8,4,11,8,6],[3,6,11,3,0,6,0,4,6],[8,6,11,8,4,6,9,0,1],[9,4,6,9,6,3,9,3,1,11,3,6],[6,8,4,6,11,8,2,10,1],[1,2,10,3,0,11,0,6,11,0,4,6],[4,11,8,4,6,11,0,2,9,2,10,9],[10,9,3,10,3,2,9,4,3,11,3,6,4,6,3],[8,2,3,8,4,2,4,6,2],[0,4,2,4,6,2],[1,9,0,2,3,4,2,4,6,4,3,8],[1,9,4,1,4,2,2,4,6],[8,1,3,8,6,1,8,4,6,6,10,1],[10,1,0,10,0,6,6,0,4],[4,6,3,4,3,8,6,10,3,0,3,9,10,9,3],[10,9,4,6,10,4],[4,9,5,7,6,11],[0,8,3,4,9,5,11,7,6],[5,0,1,5,4,0,7,6,11],[11,7,6,8,3,4,3,5,4,3,1,5],[9,5,4,10,1,2,7,6,11],[6,11,7,1,2,10,0,8,3,4,9,5],[7,6,11,5,4,10,4,2,10,4,0,2],[3,4,8,3,5,4,3,2,5,10,5,2,11,7,6],[7,2,3,7,6,2,5,4,9],[9,5,4,0,8,6,0,6,2,6,8,7],[3,6,2,3,7,6,1,5,0,5,4,0],[6,2,8,6,8,7,2,1,8,4,8,5,1,5,8],[9,5,4,10,1,6,1,7,6,1,3,7],[1,6,10,1,7,6,1,0,7,8,7,0,9,5,4],[4,0,10,4,10,5,0,3,10,6,10,7,3,7,10],[7,6,10,7,10,8,5,4,10,4,8,10],[6,9,5,6,11,9,11,8,9],[3,6,11,0,6,3,0,5,6,0,9,5],[0,11,8,0,5,11,0,1,5,5,6,11],[6,11,3,6,3,5,5,3,1],[1,2,10,9,5,11,9,11,8,11,5,6],[0,11,3,0,6,11,0,9,6,5,6,9,1,2,10],[11,8,5,11,5,6,8,0,5,10,5,2,0,2,5],[6,11,3,6,3,5,2,10,3,10,5,3],[5,8,9,5,2,8,5,6,2,3,8,2],[9,5,6,9,6,0,0,6,2],[1,5,8,1,8,0,5,6,8,3,8,2,6,2,8],[1,5,6,2,1,6],[1,3,6,1,6,10,3,8,6,5,6,9,8,9,6],[10,1,0,10,0,6,9,5,0,5,6,0],[0,3,8,5,6,10],[10,5,6],[11,5,10,7,5,11],[11,5,10,11,7,5,8,3,0],[5,11,7,5,10,11,1,9,0],[10,7,5,10,11,7,9,8,1,8,3,1],[11,1,2,11,7,1,7,5,1],[0,8,3,1,2,7,1,7,5,7,2,11],[9,7,5,9,2,7,9,0,2,2,11,7],[7,5,2,7,2,11,5,9,2,3,2,8,9,8,2],[2,5,10,2,3,5,3,7,5],[8,2,0,8,5,2,8,7,5,10,2,5],[9,0,1,5,10,3,5,3,7,3,10,2],[9,8,2,9,2,1,8,7,2,10,2,5,7,5,2],[1,3,5,3,7,5],[0,8,7,0,7,1,1,7,5],[9,0,3,9,3,5,5,3,7],[9,8,7,5,9,7],[5,8,4,5,10,8,10,11,8],[5,0,4,5,11,0,5,10,11,11,3,0],[0,1,9,8,4,10,8,10,11,10,4,5],[10,11,4,10,4,5,11,3,4,9,4,1,3,1,4],[2,5,1,2,8,5,2,11,8,4,5,8],[0,4,11,0,11,3,4,5,11,2,11,1,5,1,11],[0,2,5,0,5,9,2,11,5,4,5,8,11,8,5],[9,4,5,2,11,3],[2,5,10,3,5,2,3,4,5,3,8,4],[5,10,2,5,2,4,4,2,0],[3,10,2,3,5,10,3,8,5,4,5,8,0,1,9],[5,10,2,5,2,4,1,9,2,9,4,2],[8,4,5,8,5,3,3,5,1],[0,4,5,1,0,5],[8,4,5,8,5,3,9,0,5,0,3,5],[9,4,5],[4,11,7,4,9,11,9,10,11],[0,8,3,4,9,7,9,11,7,9,10,11],[1,10,11,1,11,4,1,4,0,7,4,11],[3,1,4,3,4,8,1,10,4,7,4,11,10,11,4],[4,11,7,9,11,4,9,2,11,9,1,2],[9,7,4,9,11,7,9,1,11,2,11,1,0,8,3],[11,7,4,11,4,2,2,4,0],[11,7,4,11,4,2,8,3,4,3,2,4],[2,9,10,2,7,9,2,3,7,7,4,9],[9,10,7,9,7,4,10,2,7,8,7,0,2,0,7],[3,7,10,3,10,2,7,4,10,1,10,0,4,0,10],[1,10,2,8,7,4],[4,9,1,4,1,7,7,1,3],[4,9,1,4,1,7,0,8,1,8,7,1],[4,0,3,7,4,3],[4,8,7],[9,10,8,10,11,8],[3,0,9,3,9,11,11,9,10],[0,1,10,0,10,8,8,10,11],[3,1,10,11,3,10],[1,2,11,1,11,9,9,11,8],[3,0,9,3,9,11,1,2,9,2,11,9],[0,2,11,8,0,11],[3,2,11],[2,3,8,2,8,10,10,8,9],[9,10,2,0,9,2],[2,3,8,2,8,10,0,1,8,1,10,8],[1,10,2],[1,3,8,9,1,8],[0,9,1],[0,3,8],[]];class VolumeToLevelSet extends Three__namespace.Group{processes=[];constructor(e,t,{resolution:r,progressive:o},s,n){let a;super(),a=e instanceof Array?e:void 0!==e.grids?Object.values(e.grids):[e];let i=0;const c=a.length,l=c*Math.pow(r,3);a.forEach(((e,a)=>{const y=new Three__namespace.BufferGeometry,p=[],h=[],m=[],g=new Three__namespace.Mesh(y,t),u=1/r,d=(Math.pow(r,3)),f=new Three.Vector3(0,0,0),w=new Three.Vector3(0,0,0),x=new Three.Vector3(0,0,0),b=new Bbox,T=new Three.Vector3;b.set(...e.getPreciseWorldBbox()),b.getCenter(g.position),b.getSize(g.scale),g.scale.multiplyScalar(1/r),b.getCenter(f),b.getSize(w),x.copy(w).multiplyScalar(u),e.transform.applyInverseJacobian(x),f.sub(w.clone().multiplyScalar(.5)),e.transform.applyInverseTransformMap(f),f.add(x.clone().multiplyScalar(.5));let D=0,z=0,A=0;let M=function*(){for(let t=0;t<d;t++){if(A>=r)return;D++,f.x+=x.x,D>=r&&(D=0,f.x-=x.x*r,z++,f.y+=x.y),z>=r&&(z=0,f.y-=x.y*r,A++,f.z+=x.z);const t=[];T.copy(f),t[0]=getDensity$1(e,T),T.copy(f),T.x+=x.x,t[1]=getDensity$1(e,T),T.copy(f),T.x+=x.x,T.z+=x.z,t[2]=getDensity$1(e,T),T.copy(f),T.z+=x.z,t[3]=getDensity$1(e,T),T.copy(f),T.y+=x.y,t[4]=getDensity$1(e,T),T.copy(f),T.x+=x.x,T.y+=x.y,t[5]=getDensity$1(e,T),T.copy(f),T.x+=x.x,T.y+=x.y,T.z+=x.z,t[6]=getDensity$1(e,T),T.copy(f),T.y+=x.y,T.z+=x.z,t[7]=getDensity$1(e,T);let o=0;t[0]&&(o|=1),t[1]&&(o|=2),t[2]&&(o|=4),t[3]&&(o|=8),t[4]&&(o|=16),t[5]&&(o|=32),t[6]&&(o|=64),t[7]&&(o|=128);const s=triangleLUT[o]||s[1],n=[[.5,0,0],[1,0,.5],[.5,0,1],[0,0,.5],[.5,1,0],[1,1,.5],[.5,1,1],[0,1,.5],[0,.5,0],[1,.5,0],[1,.5,1],[0,.5,1]];if(t.filter((e=>0!==e)).length){const e={r:Math.random(),g:Math.random(),b:Math.random()};s.forEach((t=>{const o=n[t];p.push(D-r/2+o[0],z-r/2+o[1],A-r/2+o[2]),m.push(e.r,e.g,e.b);const s=new Three__namespace.Vector3(Math.random(),Math.random(),Math.random()).normalize();h.push(s.x,s.y,s.z);}));}yield;}}();const U=()=>{M&&(this.processes[a]=requestAnimationFrame(U));let e=window.performance.now();for(;window.performance.now()-e<16&&M;){const{done:e}=M.next();if(n&&n({convertedVoxels:0,totalVoxels:l,convertedGrids:i,totalGrids:c}),e&&(i++,M=null,delete this.processes[a],i===c)){if(!o){const e=new Three__namespace.BufferAttribute(new Float32Array(p),3);e.setUsage(Three__namespace.DynamicDrawUsage),g.geometry.setAttribute("position",e);const t=new Three__namespace.BufferAttribute(new Float32Array(h),3);t.setUsage(Three__namespace.DynamicDrawUsage),g.geometry.setAttribute("normal",t);const r=new Three__namespace.BufferAttribute(new Float32Array(m),3);r.setUsage(Three__namespace.DynamicDrawUsage),g.geometry.setAttribute("color",r),this.add(g);}s&&s();}}};U(),o&&this.add(g);}));}dispose(){this.processes&&(this.processes.forEach(((e,t)=>{cancelAnimationFrame(e),delete this.processes[t];})),this.processes=null);}}
50
+ const triangleLUT=[[],[0,8,3],[0,1,9],[1,8,3,9,8,1],[1,2,10],[0,8,3,1,2,10],[9,2,10,0,2,9],[2,8,3,2,10,8,10,9,8],[3,11,2],[0,11,2,8,11,0],[1,9,0,2,3,11],[1,11,2,1,9,11,9,8,11],[3,10,1,11,10,3],[0,10,1,0,8,10,8,11,10],[3,9,0,3,11,9,11,10,9],[9,8,10,10,8,11],[4,7,8],[4,3,0,7,3,4],[0,1,9,8,4,7],[4,1,9,4,7,1,7,3,1],[1,2,10,8,4,7],[3,4,7,3,0,4,1,2,10],[9,2,10,9,0,2,8,4,7],[2,10,9,2,9,7,2,7,3,7,9,4],[8,4,7,3,11,2],[11,4,7,11,2,4,2,0,4],[9,0,1,8,4,7,2,3,11],[4,7,11,9,4,11,9,11,2,9,2,1],[3,10,1,3,11,10,7,8,4],[1,11,10,1,4,11,1,0,4,7,11,4],[4,7,8,9,0,11,9,11,10,11,0,3],[4,7,11,4,11,9,9,11,10],[9,5,4],[9,5,4,0,8,3],[0,5,4,1,5,0],[8,5,4,8,3,5,3,1,5],[1,2,10,9,5,4],[3,0,8,1,2,10,4,9,5],[5,2,10,5,4,2,4,0,2],[2,10,5,3,2,5,3,5,4,3,4,8],[9,5,4,2,3,11],[0,11,2,0,8,11,4,9,5],[0,5,4,0,1,5,2,3,11],[2,1,5,2,5,8,2,8,11,4,8,5],[10,3,11,10,1,3,9,5,4],[4,9,5,0,8,1,8,10,1,8,11,10],[5,4,0,5,0,11,5,11,10,11,0,3],[5,4,8,5,8,10,10,8,11],[9,7,8,5,7,9],[9,3,0,9,5,3,5,7,3],[0,7,8,0,1,7,1,5,7],[1,5,3,3,5,7],[9,7,8,9,5,7,10,1,2],[10,1,2,9,5,0,5,3,0,5,7,3],[8,0,2,8,2,5,8,5,7,10,5,2],[2,10,5,2,5,3,3,5,7],[7,9,5,7,8,9,3,11,2],[9,5,7,9,7,2,9,2,0,2,7,11],[2,3,11,0,1,8,1,7,8,1,5,7],[11,2,1,11,1,7,7,1,5],[9,5,8,8,5,7,10,1,3,10,3,11],[5,7,0,5,0,9,7,11,0,1,0,10,11,10,0],[11,10,0,11,0,3,10,5,0,8,0,7,5,7,0],[11,10,5,7,11,5],[10,6,5],[0,8,3,5,10,6],[9,0,1,5,10,6],[1,8,3,1,9,8,5,10,6],[1,6,5,2,6,1],[1,6,5,1,2,6,3,0,8],[9,6,5,9,0,6,0,2,6],[5,9,8,5,8,2,5,2,6,3,2,8],[2,3,11,10,6,5],[11,0,8,11,2,0,10,6,5],[0,1,9,2,3,11,5,10,6],[5,10,6,1,9,2,9,11,2,9,8,11],[6,3,11,6,5,3,5,1,3],[0,8,11,0,11,5,0,5,1,5,11,6],[3,11,6,0,3,6,0,6,5,0,5,9],[6,5,9,6,9,11,11,9,8],[5,10,6,4,7,8],[4,3,0,4,7,3,6,5,10],[1,9,0,5,10,6,8,4,7],[10,6,5,1,9,7,1,7,3,7,9,4],[6,1,2,6,5,1,4,7,8],[1,2,5,5,2,6,3,0,4,3,4,7],[8,4,7,9,0,5,0,6,5,0,2,6],[7,3,9,7,9,4,3,2,9,5,9,6,2,6,9],[3,11,2,7,8,4,10,6,5],[5,10,6,4,7,2,4,2,0,2,7,11],[0,1,9,4,7,8,2,3,11,5,10,6],[9,2,1,9,11,2,9,4,11,7,11,4,5,10,6],[8,4,7,3,11,5,3,5,1,5,11,6],[5,1,11,5,11,6,1,0,11,7,11,4,0,4,11],[0,5,9,0,6,5,0,3,6,11,6,3,8,4,7],[6,5,9,6,9,11,4,7,9,7,11,9],[10,4,9,6,4,10],[4,10,6,4,9,10,0,8,3],[10,0,1,10,6,0,6,4,0],[8,3,1,8,1,6,8,6,4,6,1,10],[1,4,9,1,2,4,2,6,4],[3,0,8,1,2,9,2,4,9,2,6,4],[0,2,4,4,2,6],[8,3,2,8,2,4,4,2,6],[10,4,9,10,6,4,11,2,3],[0,8,2,2,8,11,4,9,10,4,10,6],[3,11,2,0,1,6,0,6,4,6,1,10],[6,4,1,6,1,10,4,8,1,2,1,11,8,11,1],[9,6,4,9,3,6,9,1,3,11,6,3],[8,11,1,8,1,0,11,6,1,9,1,4,6,4,1],[3,11,6,3,6,0,0,6,4],[6,4,8,11,6,8],[7,10,6,7,8,10,8,9,10],[0,7,3,0,10,7,0,9,10,6,7,10],[10,6,7,1,10,7,1,7,8,1,8,0],[10,6,7,10,7,1,1,7,3],[1,2,6,1,6,8,1,8,9,8,6,7],[2,6,9,2,9,1,6,7,9,0,9,3,7,3,9],[7,8,0,7,0,6,6,0,2],[7,3,2,6,7,2],[2,3,11,10,6,8,10,8,9,8,6,7],[2,0,7,2,7,11,0,9,7,6,7,10,9,10,7],[1,8,0,1,7,8,1,10,7,6,7,10,2,3,11],[11,2,1,11,1,7,10,6,1,6,7,1],[8,9,6,8,6,7,9,1,6,11,6,3,1,3,6],[0,9,1,11,6,7],[7,8,0,7,0,6,3,11,0,11,6,0],[7,11,6],[7,6,11],[3,0,8,11,7,6],[0,1,9,11,7,6],[8,1,9,8,3,1,11,7,6],[10,1,2,6,11,7],[1,2,10,3,0,8,6,11,7],[2,9,0,2,10,9,6,11,7],[6,11,7,2,10,3,10,8,3,10,9,8],[7,2,3,6,2,7],[7,0,8,7,6,0,6,2,0],[2,7,6,2,3,7,0,1,9],[1,6,2,1,8,6,1,9,8,8,7,6],[10,7,6,10,1,7,1,3,7],[10,7,6,1,7,10,1,8,7,1,0,8],[0,3,7,0,7,10,0,10,9,6,10,7],[7,6,10,7,10,8,8,10,9],[6,8,4,11,8,6],[3,6,11,3,0,6,0,4,6],[8,6,11,8,4,6,9,0,1],[9,4,6,9,6,3,9,3,1,11,3,6],[6,8,4,6,11,8,2,10,1],[1,2,10,3,0,11,0,6,11,0,4,6],[4,11,8,4,6,11,0,2,9,2,10,9],[10,9,3,10,3,2,9,4,3,11,3,6,4,6,3],[8,2,3,8,4,2,4,6,2],[0,4,2,4,6,2],[1,9,0,2,3,4,2,4,6,4,3,8],[1,9,4,1,4,2,2,4,6],[8,1,3,8,6,1,8,4,6,6,10,1],[10,1,0,10,0,6,6,0,4],[4,6,3,4,3,8,6,10,3,0,3,9,10,9,3],[10,9,4,6,10,4],[4,9,5,7,6,11],[0,8,3,4,9,5,11,7,6],[5,0,1,5,4,0,7,6,11],[11,7,6,8,3,4,3,5,4,3,1,5],[9,5,4,10,1,2,7,6,11],[6,11,7,1,2,10,0,8,3,4,9,5],[7,6,11,5,4,10,4,2,10,4,0,2],[3,4,8,3,5,4,3,2,5,10,5,2,11,7,6],[7,2,3,7,6,2,5,4,9],[9,5,4,0,8,6,0,6,2,6,8,7],[3,6,2,3,7,6,1,5,0,5,4,0],[6,2,8,6,8,7,2,1,8,4,8,5,1,5,8],[9,5,4,10,1,6,1,7,6,1,3,7],[1,6,10,1,7,6,1,0,7,8,7,0,9,5,4],[4,0,10,4,10,5,0,3,10,6,10,7,3,7,10],[7,6,10,7,10,8,5,4,10,4,8,10],[6,9,5,6,11,9,11,8,9],[3,6,11,0,6,3,0,5,6,0,9,5],[0,11,8,0,5,11,0,1,5,5,6,11],[6,11,3,6,3,5,5,3,1],[1,2,10,9,5,11,9,11,8,11,5,6],[0,11,3,0,6,11,0,9,6,5,6,9,1,2,10],[11,8,5,11,5,6,8,0,5,10,5,2,0,2,5],[6,11,3,6,3,5,2,10,3,10,5,3],[5,8,9,5,2,8,5,6,2,3,8,2],[9,5,6,9,6,0,0,6,2],[1,5,8,1,8,0,5,6,8,3,8,2,6,2,8],[1,5,6,2,1,6],[1,3,6,1,6,10,3,8,6,5,6,9,8,9,6],[10,1,0,10,0,6,9,5,0,5,6,0],[0,3,8,5,6,10],[10,5,6],[11,5,10,7,5,11],[11,5,10,11,7,5,8,3,0],[5,11,7,5,10,11,1,9,0],[10,7,5,10,11,7,9,8,1,8,3,1],[11,1,2,11,7,1,7,5,1],[0,8,3,1,2,7,1,7,5,7,2,11],[9,7,5,9,2,7,9,0,2,2,11,7],[7,5,2,7,2,11,5,9,2,3,2,8,9,8,2],[2,5,10,2,3,5,3,7,5],[8,2,0,8,5,2,8,7,5,10,2,5],[9,0,1,5,10,3,5,3,7,3,10,2],[9,8,2,9,2,1,8,7,2,10,2,5,7,5,2],[1,3,5,3,7,5],[0,8,7,0,7,1,1,7,5],[9,0,3,9,3,5,5,3,7],[9,8,7,5,9,7],[5,8,4,5,10,8,10,11,8],[5,0,4,5,11,0,5,10,11,11,3,0],[0,1,9,8,4,10,8,10,11,10,4,5],[10,11,4,10,4,5,11,3,4,9,4,1,3,1,4],[2,5,1,2,8,5,2,11,8,4,5,8],[0,4,11,0,11,3,4,5,11,2,11,1,5,1,11],[0,2,5,0,5,9,2,11,5,4,5,8,11,8,5],[9,4,5,2,11,3],[2,5,10,3,5,2,3,4,5,3,8,4],[5,10,2,5,2,4,4,2,0],[3,10,2,3,5,10,3,8,5,4,5,8,0,1,9],[5,10,2,5,2,4,1,9,2,9,4,2],[8,4,5,8,5,3,3,5,1],[0,4,5,1,0,5],[8,4,5,8,5,3,9,0,5,0,3,5],[9,4,5],[4,11,7,4,9,11,9,10,11],[0,8,3,4,9,7,9,11,7,9,10,11],[1,10,11,1,11,4,1,4,0,7,4,11],[3,1,4,3,4,8,1,10,4,7,4,11,10,11,4],[4,11,7,9,11,4,9,2,11,9,1,2],[9,7,4,9,11,7,9,1,11,2,11,1,0,8,3],[11,7,4,11,4,2,2,4,0],[11,7,4,11,4,2,8,3,4,3,2,4],[2,9,10,2,7,9,2,3,7,7,4,9],[9,10,7,9,7,4,10,2,7,8,7,0,2,0,7],[3,7,10,3,10,2,7,4,10,1,10,0,4,0,10],[1,10,2,8,7,4],[4,9,1,4,1,7,7,1,3],[4,9,1,4,1,7,0,8,1,8,7,1],[4,0,3,7,4,3],[4,8,7],[9,10,8,10,11,8],[3,0,9,3,9,11,11,9,10],[0,1,10,0,10,8,8,10,11],[3,1,10,11,3,10],[1,2,11,1,11,9,9,11,8],[3,0,9,3,9,11,1,2,9,2,11,9],[0,2,11,8,0,11],[3,2,11],[2,3,8,2,8,10,10,8,9],[9,10,2,0,9,2],[2,3,8,2,8,10,0,1,8,1,10,8],[1,10,2],[1,3,8,9,1,8],[0,9,1],[0,3,8],[]];class VolumeToLevelSet extends Three__namespace.Group{processes=[];geometries=[];constructor(e,t,{resolution:r,progressive:o},s,n){let i;super(),i=e instanceof Array?e:void 0!==e.grids?Object.values(e.grids):[e];let a=0;const c=i.length,l=c*Math.pow(r,3);i.forEach(((e,i)=>{const y=new Three__namespace.BufferGeometry,p=[],h=[],m=[],g=new Three__namespace.Mesh(y,t);this.geometries.push(y);const u=1/r,f=(Math.pow(r,3)),d=new Three.Vector3(0,0,0),w=new Three.Vector3(0,0,0),x=new Three.Vector3(0,0,0),b=new Bbox,T=new Three.Vector3;b.set(...e.getPreciseWorldBbox()),b.getCenter(g.position),b.getSize(g.scale),g.scale.multiplyScalar(1/r),b.getCenter(d),b.getSize(w),x.copy(w).multiplyScalar(u),e.transform.applyInverseJacobian(x),d.sub(w.clone().multiplyScalar(.5)),e.transform.applyInverseTransformMap(d),d.add(x.clone().multiplyScalar(.5));let D=0,z=0,A=0;let M=function*(){for(let t=0;t<f;t++){if(A>=r)return;D++,d.x+=x.x,D>=r&&(D=0,d.x-=x.x*r,z++,d.y+=x.y),z>=r&&(z=0,d.y-=x.y*r,A++,d.z+=x.z);const t=[];T.copy(d),t[0]=getDensity$1(e,T),T.copy(d),T.x+=x.x,t[1]=getDensity$1(e,T),T.copy(d),T.x+=x.x,T.z+=x.z,t[2]=getDensity$1(e,T),T.copy(d),T.z+=x.z,t[3]=getDensity$1(e,T),T.copy(d),T.y+=x.y,t[4]=getDensity$1(e,T),T.copy(d),T.x+=x.x,T.y+=x.y,t[5]=getDensity$1(e,T),T.copy(d),T.x+=x.x,T.y+=x.y,T.z+=x.z,t[6]=getDensity$1(e,T),T.copy(d),T.y+=x.y,T.z+=x.z,t[7]=getDensity$1(e,T);let o=0;t[0]&&(o|=1),t[1]&&(o|=2),t[2]&&(o|=4),t[3]&&(o|=8),t[4]&&(o|=16),t[5]&&(o|=32),t[6]&&(o|=64),t[7]&&(o|=128);const s=triangleLUT[o]||s[1],n=[[.5,0,0],[1,0,.5],[.5,0,1],[0,0,.5],[.5,1,0],[1,1,.5],[.5,1,1],[0,1,.5],[0,.5,0],[1,.5,0],[1,.5,1],[0,.5,1]];if(t.filter((e=>0!==e)).length){const e={r:Math.random(),g:Math.random(),b:Math.random()};s.forEach((t=>{const o=n[t];p.push(D-r/2+o[0],z-r/2+o[1],A-r/2+o[2]),m.push(e.r,e.g,e.b);const s=new Three__namespace.Vector3(Math.random(),Math.random(),Math.random()).normalize();h.push(s.x,s.y,s.z);}));}yield;}}();const U=()=>{M&&(this.processes[i]=requestAnimationFrame(U));let e=window.performance.now();for(;window.performance.now()-e<16&&M;){const{done:e}=M.next();if(n&&n({convertedVoxels:0,totalVoxels:l,convertedGrids:a,totalGrids:c}),e&&(a++,M=null,delete this.processes[i],a===c)){if(!o){const e=new Three__namespace.BufferAttribute(new Float32Array(p),3);e.setUsage(Three__namespace.DynamicDrawUsage),g.geometry.setAttribute("position",e);const t=new Three__namespace.BufferAttribute(new Float32Array(h),3);t.setUsage(Three__namespace.DynamicDrawUsage),g.geometry.setAttribute("normal",t);const r=new Three__namespace.BufferAttribute(new Float32Array(m),3);r.setUsage(Three__namespace.DynamicDrawUsage),g.geometry.setAttribute("color",r),this.add(g);}s&&s();}}};U(),o&&this.add(g);}));}dispose(){this.processes&&(this.processes.forEach((e=>cancelAnimationFrame(e))),this.processes=null),this.geometries.forEach((e=>e.dispose())),this.geometries=[];}}
51
51
 
52
- class VolumeToPoints extends Three__namespace.Group{processes=[];constructor(e,t,{resolution:r,progressive:o},s,n){let i;super(),i=e instanceof Array?e:void 0!==e.grids?Object.values(e.grids):[e];let l=0;const a=i.length,c=a*Math.pow(r,3);i.forEach(((e,i)=>{const p=new Three__namespace.BufferGeometry,h=[],m=new Three__namespace.Points(p,new Three__namespace.PointsMaterial({color:16711935,size:1,...t||{}})),d=1/r,u=(Math.pow(r,3)),f=new Three.Vector3(0,0,0),w=new Three.Vector3(0,0,0),y=new Three.Vector3(0,0,0),x=new Bbox;x.set(...e.getPreciseWorldBbox()),x.getCenter(m.position),x.getSize(m.scale),m.scale.multiplyScalar(1/r),x.getCenter(f),x.getSize(w),y.copy(w).multiplyScalar(d),e.transform.applyInverseJacobian(y),f.sub(w.clone().multiplyScalar(.5)),e.transform.applyInverseTransformMap(f),f.add(y.clone().multiplyScalar(.5));let g=0,b=0,T=0;let v=function*(){for(let t=0;t<u;t++){if(T>=r)return;g++,f.x+=y.x,g>=r&&(g=0,f.x-=y.x*r,b++,f.y+=y.y),b>=r&&(b=0,f.y-=y.y*r,T++,f.z+=y.z);getDensity$1(e,f)&&(h.push(g-r/2,b-r/2,T-r/2),p.setAttribute("position",new Three__namespace.BufferAttribute(new Float32Array(h),3))),yield;}}();const V=()=>{v&&(this.processes[i]=requestAnimationFrame(V));let e=window.performance.now();for(;window.performance.now()-e<16&&v;){const{done:e}=v.next();n&&n({convertedVoxels:0,totalVoxels:c,convertedGrids:l,totalGrids:a}),e&&(l++,v=null,delete this.processes[i],l===a&&(o||this.add(m),s&&s()));}};V(),o&&this.add(m);}));}dispose(){this.processes&&(this.processes.forEach(((e,t)=>{cancelAnimationFrame(e),delete this.processes[t];})),this.processes=null);}}
52
+ class VolumeToPoints extends Three__namespace.Group{processes=[];materials=[];geometries=[];constructor(e,t,{resolution:s,progressive:r},o,i){let n;super(),n=e instanceof Array?e:void 0!==e.grids?Object.values(e.grids):[e];let a=0;const l=n.length,c=l*Math.pow(s,3);n.forEach(((e,n)=>{const p=new Three__namespace.BufferGeometry,h=[],m=new Three__namespace.PointsMaterial({color:16711935,size:1,...t||{}}),u=new Three__namespace.Points(p,m);this.materials.push(m),this.geometries.push(p);const d=1/s,f=(Math.pow(s,3)),w=new Three.Vector3(0,0,0),y=new Three.Vector3(0,0,0),g=new Three.Vector3(0,0,0),x=new Bbox;x.set(...e.getPreciseWorldBbox()),x.getCenter(u.position),x.getSize(u.scale),u.scale.multiplyScalar(1/s),x.getCenter(w),x.getSize(y),g.copy(y).multiplyScalar(d),e.transform.applyInverseJacobian(g),w.sub(y.clone().multiplyScalar(.5)),e.transform.applyInverseTransformMap(w),w.add(g.clone().multiplyScalar(.5));let b=0,T=0,v=0;let V=function*(){for(let t=0;t<f;t++){if(v>=s)return;b++,w.x+=g.x,b>=s&&(b=0,w.x-=g.x*s,T++,w.y+=g.y),T>=s&&(T=0,w.y-=g.y*s,v++,w.z+=g.z);getDensity$1(e,w)&&(h.push(b-s/2,T-s/2,v-s/2),p.setAttribute("position",new Three__namespace.BufferAttribute(new Float32Array(h),3))),yield;}}();const A=()=>{V&&(this.processes[n]=requestAnimationFrame(A));let e=window.performance.now();for(;window.performance.now()-e<16&&V;){const{done:e}=V.next();i&&i({convertedVoxels:0,totalVoxels:c,convertedGrids:a,totalGrids:l}),e&&(a++,V=null,delete this.processes[n],a===l&&(r||this.add(u),o&&o()));}};A(),r&&this.add(u);}));}dispose(){this.processes&&(this.processes.forEach((e=>cancelAnimationFrame(e))),this.processes=null),this.materials.forEach((e=>e.dispose())),this.geometries.forEach((e=>e.dispose())),this.materials=[],this.geometries=[];}}
53
53
 
54
54
  class MeshVolume extends openvdb.GridDescriptor{static halfFloatGridPrefix=openvdb.GridDescriptor.halfFloatGridPrefix;saveAsHalfFloat=!1;leavesCount=0;uniqueName;gridName;gridType;mesh=new Three__namespace.Object3D;constructor(e){super(),this.readGrid();const t=e.clone(),r=[],i=new Three__namespace.Vector3,s=new Three__namespace.Quaternion;t.position.setScalar(0,0,0),t.scale.setScalar(1),t.quaternion.identity(),t.updateMatrix(),t.updateMatrixWorld(),t.updateWorldMatrix(),t.traverse((e=>{if(e.geometry){const t=e.geometry.clone();e.getWorldQuaternion(s),t.applyQuaternion(s),e.getWorldScale(i),t.scale(i.x,i.y,i.z),e.getWorldPosition(i),t.translate(i.x,i.y,i.z),t.attributes={position:t.attributes.position,normal:t.attributes.normal},r.push(t);}}));const a=BufferGeometryUtils__namespace.mergeVertices(BufferGeometryUtils__namespace.mergeGeometries(r,!1));this.mesh=new Three__namespace.Mesh(a,new Three__namespace.MeshBasicMaterial({side:Three__namespace.DoubleSide})),this.cacheMeshGeometry();}readGrid(){this.readGridHeader(),this.readGridTransform();}readGridHeader(){this.uniqueName=`MeshVolume#${openvdb.getUuid()}`,this.gridName=this.uniqueName.split("#")[0],this.gridType="fog volume";}readCompression(){}readMetadata(){}getGridValueType(){return openvdb.floatType}getGridClass(){return "fog volume"}readGridTransform(){this.transform=new openvdb.GridTransform;}readTopology(){}getWorldBbox(){return this.getLocalBbox()}getLocalBbox(){return [new openvdb.Vector3(-.5,-.5,-.5),new openvdb.Vector3(.5,.5,.5)]}getPreciseWorldBbox(){return this.getWorldBbox()}bbox=null;center=null;size=null;target=new Three__namespace.Vector3(0,0,0);raycaster=new Three__namespace.Raycaster;rayDirection=new Three__namespace.Vector3(1,0,0);cacheMeshGeometry(){this.bbox=new Three__namespace.Box3,this.bbox.expandByObject(this.mesh),this.center=new Three__namespace.Vector3,this.size=new Three__namespace.Vector3,this.bbox.getCenter(this.center),this.bbox.getSize(this.size);const e=Math.max(this.size.x,this.size.y,this.size.z),t=(new Three__namespace.Vector3).copy(this.size).divideScalar(e);this.size.divide(t),this.size.addScalar(.1),this.raycaster.near=0,this.raycaster.far=1e3;}getValue(e){this.target.copy(e).multiply(this.size).add(this.center),this.rayDirection.copy(e).normalize(),this.raycaster.set(this.target,this.rayDirection);const t=this.raycaster.intersectObjects([this.mesh],!0);return 0===t.length||t.length%2==0?0:1}getLeafAt(e){return null}}
55
55
 
56
- class VolumeNormalMaterial extends Three__namespace.MeshStandardMaterial{name="VolumeNormalMaterial";isVolumetricFogMaterial=!0;customProgramCacheKey=()=>openvdb.getUuid(this);_uniforms={baseColor:{value:new Three__namespace.Color(16777215)},scatterColor:{value:new Three__namespace.Color(0)},densityMap3D:{value:null},emissiveMap3D:{value:null},baseColorMap3D:{value:null},maskMap3D:{value:null},steps:{value:100},absorbance:{value:1},densityScale:{value:1},resolution:{value:100},offset3D:{value:new Three__namespace.Vector3(0,0,0)},wrap3D:{value:Three__namespace.ClampToEdgeWrapping},lights:{value:lights.useDirectionalLights|lights.usePointLights|lights.useSpotLights|lights.useHemisphereLights|lights.useEnvironment},cameraPosition:{value:new Three__namespace.Vector3(0,0,0)},cameraNear:{value:1},cameraFar:{value:1}};set baseColor(e){this._uniforms.baseColor.value.set(e);}get baseColor(){return this._uniforms.baseColor.value}set scatterColor(e){this._uniforms.scatterColor.value.set(e);}get scatterColor(){return this._uniforms.scatterColor.value}set densityMap3D(e){this._uniforms.densityMap3D.value=e,e.offset3D=this._uniforms.offset3D.value;}get densityMap3D(){return this._uniforms.densityMap3D.value}set emissiveMap3D(e){this._uniforms.emissiveMap3D.value=e,e.offset3D=this._uniforms.offset3D.value,this.needsUpdate=!0;}get emissiveMap3D(){return this._uniforms.emissiveMap3D.value}set maskMap3D(e){this._uniforms.maskMap3D.value=e,this.needsUpdate=!0;}get maskMap3D(){return this._uniforms.maskMap3D.value}set baseColorMap3D(e){this._uniforms.baseColorMap3D.value=e,e.offset3D=this._uniforms.offset3D.value,this.needsUpdate=!0;}get baseColorMap3D(){return this._uniforms.baseColorMap3D.value}set steps(e){this._uniforms.steps.value=e;}get steps(){return this._uniforms.steps.value}set absorbance(e){this._uniforms.absorbance.value=e;}get absorbance(){return this._uniforms.absorbance.value}set resolution(e){this._uniforms.resolution.value=e;}get resolution(){return this._uniforms.resolution.value}set offset3D(e){this._uniforms.offset3D.value.copy(e);}get offset3D(){return this._uniforms.offset3D.value}set wrap3D(e){this._uniforms.wrap3D.value=e,this.needsUpdate=!0;}get wrap3D(){return this._uniforms.wrap3D.value}set densityScale(e){this._uniforms.densityScale.value=e;}get densityScale(){return this._uniforms.densityScale.value}set lights(e){this._uniforms.lights.value=e,this.needsUpdate=!0;}get lights(){return this._uniforms.lights.value}set useDirectionalLights(e=!0){e?this._uniforms.lights.value|=lights.useDirectionalLights:this._uniforms.lights.value&=~lights.useDirectionalLights,this.needsUpdate=!0;}get useDirectionalLights(){return 0!=(this._uniforms.lights.value&lights.useDirectionalLights)}set usePointLights(e=!0){e?this._uniforms.lights.value|=lights.usePointLights:this._uniforms.lights.value&=~lights.usePointLights,this.needsUpdate=!0;}get usePointLights(){return 0!=(this._uniforms.lights.value&lights.usePointLights)}set useHemisphereLights(e=!0){e?this._uniforms.lights.value|=lights.useHemisphereLights:this._uniforms.lights.value&=~lights.useHemisphereLights,this.needsUpdate=!0;}get useHemisphereLights(){return 0!=(this._uniforms.lights.value&lights.useHemisphereLights)}set useSpotLights(e=!0){e?this._uniforms.lights.value|=lights.useSpotLights:this._uniforms.lights.value&=~lights.useSpotLights,this.needsUpdate=!0;}get useSpotLights(){return 0!=(this._uniforms.lights.value&lights.useSpotLights)}set useEnvironment(e=!0){e?this._uniforms.lights.value|=lights.useEnvironment:this._uniforms.lights.value&=~lights.useEnvironment,this.needsUpdate=!0;}get useEnvironment(){return 0!=(this._uniforms.lights.value&lights.useEnvironment)}constructor(e={}){super(),this.side=Three__namespace.BackSide,this.depthWrite=!1,this.depthTest=!1,this.transparent=!0,Object.keys(this._uniforms).forEach((n=>{void 0!==e[n]&&null!==e[n]&&(this[n]=e[n],e[n]instanceof Three__namespace.Texture&&(e[n].offset3D=this._uniforms.offset3D.value));})),this.onBeforeCompile=(n,i)=>{const t=getDepthUniforms();Object.entries({...this._uniforms,...t}).forEach((([e,i])=>{n.uniforms[e]=i;}));const o=`\n #define VOLUME_BBOX_SPAN 0.5\n ${e.emissiveMap3D?"#define USE_EMISSIVE_GRID 1":""}\n ${e.baseColorMap3D?"#define USE_BASE_COLOR_GRID 1":""}\n ${e.maskMap3D?"#define USE_MASK_GRID 1":""}\n\n #define VOLUME_USE_ENVIRONMENT ${Number(this.useEnvironment)} != 0\n #define VOLUME_USE_HEMI_LIGHTS ${Number(this.useHemisphereLights)} != 0\n #define VOLUME_USE_POINT_LIGHTS ${Number(this.usePointLights)} != 0\n #define VOLUME_USE_DIR_LIGHTS ${Number(this.useDirectionalLights)} != 0\n #define VOLUME_USE_SPOT_LIGHTS ${Number(this.useSpotLights)} != 0\n `,a="\n varying mat4 mModelMatrix;\n varying mat4 mViewMatrix;\n varying mat4 mProjectionMatrix;\n varying mat4 mModelViewMatrix;\n varying mat4 mInverseModelViewMatrix;\n varying mat3 mInverseNormalMatrix;\n ";n.vertexShader=n.vertexShader.replace("#include <common>",`\n ${a}\n\n out vec3 vOrigin;\n out vec3 vDirection;\n out vec3 vPosition;\n\n #include <common>\n `).replace("#include <worldpos_vertex>","\n #include <worldpos_vertex>\n\n vOrigin = vec3(inverse(modelMatrix) * vec4(cameraPosition, 1.0)).xyz;\n vDirection = position - vOrigin;\n vPosition = position;\n\n mModelMatrix = modelMatrix;\n mViewMatrix = viewMatrix;\n mProjectionMatrix = projectionMatrix;\n mModelViewMatrix = modelViewMatrix;\n mInverseModelViewMatrix = inverse(modelViewMatrix);\n mInverseNormalMatrix = inverse(normalMatrix);\n ");const r=`\n vec2 getVolumeBbox(vec3 vPointOfReference) {\n const vec3 vBoxMin = vec3(-0.5);\n const vec3 vBoxMax = vec3(0.5);\n vec3 vInvPointOfReference = 1.0 / vPointOfReference;\n \n vec3 vMinRange = (-vOrigin - VOLUME_BBOX_SPAN) * vInvPointOfReference;\n vec3 vMaxRange = (-vOrigin + VOLUME_BBOX_SPAN) * vInvPointOfReference;\n \n vec3 vMin = min(vMinRange, vMaxRange);\n vec3 vMax = max(vMinRange, vMaxRange);\n\n return vec2(\n max(vMin.x, max(vMin.y, vMin.z)),\n min(vMax.x, min(vMax.y, vMax.z))\n );\n }\n\n vec3 radiation = vec3(0.);\n vec3 getBlackBodyRadiation(float temperature) {\n if (temperature == 0.) {\n return vec3(0.);\n }\n\n #ifndef USE_EMISSIVE_GRID\n return vec3(0.);\n #endif\n\n // NOTE Blackbody radiation source - https://www.shadertoy.com/view/4tdGWM\n float temperatureScaled = temperature * 1000.;\n\n radiation.r += 1. / (exp(19E3 * 1. / temperatureScaled) - 1.);\n radiation.g += 3.375 / (exp(19E3 * 1.5 / temperatureScaled) - 1.);\n radiation.b += 8. / (exp(19E3 * 2. / temperatureScaled) - 1.);\n\n return (radiation / max(radiation.r,max(radiation.g,radiation.b)));\n }\n\n float loopUV(float x) {\n if (abs(mod(floor(x), 2.)) == 1.) {\n return ceil(x) - x;\n } else {\n return x - floor(x);\n }\n }\n\n vec3 mapTextureSample0(vec3 position){\n vec3 uv = position + VOLUME_BBOX_SPAN;\n\n ${this.wrap3D===Three__namespace.RepeatWrapping?"return mod(uv, 1.);":""}\n ${0===this.wrap3D||this.wrap3D===Three__namespace.ClampToEdgeWrapping?"// NOTE Return UV":""}\n ${this.wrap3D===Three__namespace.MirroredRepeatWrapping?"\n uv.x = loopUV(uv.x);\n uv.y = loopUV(uv.y);\n uv.z = loopUV(uv.z);\n ":""}\n\n return uv;\n }\n\n vec3 mapTextureSample(vec3 position, vec3 offset){\n vec3 uv = position + VOLUME_BBOX_SPAN + offset;\n\n ${this.wrap3D===Three__namespace.RepeatWrapping?"return mod(uv, 1.);":""}\n ${0===this.wrap3D||this.wrap3D===Three__namespace.ClampToEdgeWrapping?"// NOTE Return UV":""}\n ${this.wrap3D===Three__namespace.MirroredRepeatWrapping?"\n uv.x = loopUV(uv.x);\n uv.y = loopUV(uv.y);\n uv.z = loopUV(uv.z);\n ":""}\n\n return uv;\n }\n\n float blendSample(float value) {\n float fSmoothingThreshold = 0.25;\n float fSmoothingBlend = 0.25;\n\n return smoothstep(\n fSmoothingThreshold - fSmoothingBlend,\n fSmoothingThreshold + fSmoothingBlend,\n value\n ) * densityScale;\n }\n\n float clampedTexture(sampler3D source, vec3 position) {\n ${0===this.wrap3D||this.wrap3D===Three__namespace.ClampToEdgeWrapping?"\n vec3 vAbs = abs(position);\n\n if (max(vAbs.x, max(vAbs.y, vAbs.z)) > .5) {\n return 0.;\n }\n ":""}\n\n return texture(source, mapTextureSample(position, offset3D)).r;\n }\n\n float clampedMaskTexture(sampler3D source, vec3 position) {\n ${0===this.wrap3D||this.wrap3D===Three__namespace.ClampToEdgeWrapping?"\n vec3 vAbs = abs(position);\n\n if (max(vAbs.x, max(vAbs.y, vAbs.z)) > .5) {\n return 0.;\n }\n ":""}\n\n return texture(source, mapTextureSample0(position)).r;\n }\n\n vec3 clampedTextureRGB(sampler3D source, vec3 position) {\n return texture(source, mapTextureSample(position, offset3D)).rgb;\n }\n `;n.fragmentShader=n.fragmentShader.replace("#include <uv_pars_fragment>","// NOTE Override UV calculations").replace("#include <packing>","").replace("#include <common>",`\n precision highp float;\n precision highp sampler2D;\n precision highp sampler3D;\n\n in vec3 vOrigin;\n in vec3 vDirection;\n in vec3 vPosition;\n\n uniform sampler3D densityMap3D;\n uniform sampler3D emissiveMap3D;\n uniform sampler3D baseColorMap3D;\n uniform sampler3D maskMap3D;\n uniform vec3 offset3D;\n uniform int wrap3D;\n\n uniform float steps;\n uniform float absorbance;\n uniform float densityScale;\n uniform vec3 baseColor;\n uniform vec3 scatterColor;\n uniform float resolution;\n\n uniform float cameraNear;\n uniform float cameraFar;\n uniform sampler2D depthScreenMap;\n uniform vec4 depthView;\n\n ${o}\n ${a}\n\n #include <common>\n #include <packing>\n #include <uv_pars_fragment>\n\n ${r}\n `).replace("#include <lights_fragment_begin>","// NOTE Override light calculations").replace("#include <lights_fragment_maps>","// NOTE Override light calculations").replace("#include <lights_fragment_end>","// NOTE Override light calculations").replace("#include <opaque_fragment>","\n vec3 vWorld = -vViewPosition;\n\n vec3 vRayDirection = normalize(vDirection);\n vec2 vBounds = getVolumeBbox(vRayDirection);\n vec2 vMaxBounds = getVolumeBbox(vOrigin);\n \n if (vBounds.x > vBounds.y) {\n discard;\n }\n\n vBounds.x = max(vBounds.x, 0.0);\n vMaxBounds.x = max(vMaxBounds.x, 0.0);\n\n // Volume movement\n\n vec3 vPoint = vOrigin + vBounds.x * vRayDirection;\n vec3 vPointStep = 1.0 / abs(vRayDirection);\n float delta = min(vPointStep.x, min(vPointStep.y, vPointStep.z)) / steps;\n vec3 vDirectionDeltaStep = vRayDirection * delta;\n\n // Density calculations\n\n float density = 0.0;\n vec3 albedo = vec3(0.);\n vec3 emissive = vec3(0.);\n float volumeSample;\n float emissiveSample;\n float maskSample = 1.;\n float noiseSample;\n float noiseFactor;\n vec3 fTest;\n\n // Light calculations\n \n vec3 lightAlbedo = vec3(0.);\n vec3 vLightProbe;\n vec3 lightDirection;\n vec3 vLightStep;\n vec3 vEnvMapScatter = vec3(0.0);\n float lightSample;\n float lightAbsorbance;\n float lightDistance;\n float stepAccumulation;\n\n // Utils\n\n float absorbanceDensityRatio = min(absorbance, densityScale); // NOTE When reducing densityScale, automatically reduce absorbance\n float inverseAbsorbance = 1.0 / absorbanceDensityRatio;\n float inverseDensityScale = 1.0 / densityScale;\n float eInverseAbsorbance = exp(-1. - inverseAbsorbance); // NOTE Wrong, but looks kinda better than exp(-inverseAbsorbance)\n float eDensityAbsorbance = exp(-absorbanceDensityRatio * delta);\n float eInverseDensityScale = exp(-1. - inverseDensityScale);\n\n // Depth Testing\n\n vec2 screenUV = gl_FragCoord.xy / depthView.zw;\n float worldDepth = unpackRGBAToDepth(texture2D(depthScreenMap, screenUV));\n float fogDepth = 0.;\n vec3 vProjPoint;\n\n \n float lightMarchLimit = resolution + 2.;\n int iLightMarchLimit = int(lightMarchLimit);\n\n vec3 vUnit = (mModelMatrix * vec4(1., 0., 0., 0.)).xyz;\n float vUnitLength = length(vUnit);\n\n vec3 iblIrradiance = vec3(0.0);\n \n\n vec3 lastNonSolidPoint = vec3(vPoint);\n\n for (float i = vBounds.x; i < vBounds.y; i += delta) {\n volumeSample = clampedTexture(densityMap3D, vPoint);\n \n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vPoint);\n #endif\n\n density += blendSample(volumeSample) * eDensityAbsorbance * maskSample;\n\n #ifdef USE_EMISSIVE_GRID\n emissiveSample = clampedTexture(emissiveMap3D, vPoint);\n emissive = max(emissive, density * vec3(emissiveSample));\n #endif\n\n if (density < 1.) {\n lastNonSolidPoint = vPoint;\n }\n\n if (density >= 1.) {\n break;\n }\n\n vProjPoint = (mModelMatrix * vec4(vPoint, 1.0)).xyz;\n fogDepth = length(vProjPoint - cameraPosition);\n fogDepth = (fogDepth - cameraNear) / (cameraFar - cameraNear);\n fogDepth = saturate(fogDepth);\n \n if (worldDepth < fogDepth) {\n break;\n }\n\n vPoint += vDirectionDeltaStep;\n }\n\n density = clamp(density, 0.0, 1.0);\n\n vPoint = lastNonSolidPoint;\n\n if (density > 0.) {\n\n \n vec3 baseColorSample = baseColor.rgb;\n\n #ifdef USE_BASE_COLOR_GRID\n baseColorSample *= clampedTextureRGB(baseColorMap3D, vPoint);\n #endif\n\n albedo += ambientLightColor;\n \n\n #if VOLUME_USE_ENVIRONMENT\n \n vec3 viewDir = isOrthographic ? vec3(0.0, 0.0, 1.0) : normalize(vViewPosition);\n vec3 vUVCoords = normalize(lastNonSolidPoint * mInverseNormalMatrix).xyz;\n\n #if defined( USE_ENVMAP )\n vEnvMapScatter = getIBLIrradiance(vUVCoords) * RECIPROCAL_PI;\n\n albedo += density * getIBLRadiance(viewDir, vUVCoords, .5) * RECIPROCAL_PI;\n #endif\n \n #endif\n\n #if NUM_HEMI_LIGHTS > 0 && VOLUME_USE_HEMI_LIGHTS\n \n lightAlbedo = vec3(0.);\n vec3 textureProbe;\n\n float absorbanceUp;\n float absorbanceDown;\n float stepAccumulationUp;\n float stepAccumulationDown;\n\n for (int lightIndex = 0; lightIndex < NUM_HEMI_LIGHTS; lightIndex++) {\n\n HemisphereLight hemiLight = hemisphereLights[lightIndex];\n vLightProbe = vec3(vPoint);\n\n absorbanceUp = 0.0;\n absorbanceDown = 0.0;\n\n stepAccumulationUp = 0.;\n stepAccumulationDown = 0.;\n\n lightDirection = -normalize((vec4(hemiLight.direction, 1.) * viewMatrix).xyz);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 1; lightMarch < int(lightMarchLimit / 2.); lightMarch++) {\n if (absorbanceUp < 1.) {\n textureProbe = vLightProbe - float(lightMarch) * vLightStep;\n lightSample = clampedTexture(densityMap3D, textureProbe);\n\n absorbanceUp += lightSample * eInverseAbsorbance;\n stepAccumulationUp += 1.;\n }\n \n if (absorbanceDown < 1.) {\n textureProbe = vLightProbe + float(lightMarch) * vLightStep;\n lightSample = clampedTexture(densityMap3D, textureProbe);\n\n absorbanceDown += blendSample(lightSample) * eInverseAbsorbance;\n stepAccumulationDown += 1.;\n }\n }\n\n absorbanceUp = min(1., absorbanceUp);\n absorbanceDown = min(1., absorbanceDown);\n\n lightAlbedo += mix(\n (1. - absorbanceUp) * hemiLight.skyColor,\n (1. - absorbanceDown) * hemiLight.groundColor,\n 0.5\n );\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_POINT_LIGHTS > 0 && VOLUME_USE_POINT_LIGHTS\n \n PointLight pointLight;\n lightAlbedo = vec3(0.);\n \n for (int lightIndex = 0; lightIndex < NUM_POINT_LIGHTS; lightIndex++) {\n pointLight = pointLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.;\n stepAccumulation = 1.;\n\n lightDirection = vPoint - (mInverseModelViewMatrix * vec4(pointLight.position, 1.)).xyz;\n lightDistance = length(lightDirection) * vUnitLength;\n lightDirection = normalize(lightDirection);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n \n lightSample = clampedTexture(densityMap3D, vLightProbe);\n \n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * pointLight.color;\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_DIR_LIGHTS > 0 && VOLUME_USE_DIR_LIGHTS\n \n DirectionalLight directionalLight;\n lightAlbedo = vec3(0.);\n\n for (int lightIndex = 0; lightIndex < NUM_DIR_LIGHTS; lightIndex++) {\n directionalLight = directionalLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.0;\n stepAccumulation = 1.;\n\n lightDirection = -normalize((vec4(directionalLight.direction, 1.) * viewMatrix).xyz);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n \n float lightSample = clampedTexture(densityMap3D, vLightProbe);\n\n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * directionalLight.color;\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_SPOT_LIGHTS > 0 && VOLUME_USE_SPOT_LIGHTS\n \n SpotLight spotLight;\n lightAlbedo = vec3(0.);\n\n float angleCos;\n float spotAttenuation;\n \n for (int lightIndex = 0; lightIndex < NUM_SPOT_LIGHTS; lightIndex++) {\n spotLight = spotLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.0;\n stepAccumulation = 1.;\n\n lightDirection = vPoint - (mInverseModelViewMatrix * vec4(spotLight.position, 1.)).xyz;\n lightDistance = length(lightDirection) * vUnitLength;\n lightDirection = normalize(lightDirection);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n angleCos = dot(normalize(-lightDirection), normalize(mInverseNormalMatrix * spotLight.direction));\n spotAttenuation = smoothstep(spotLight.coneCos, spotLight.penumbraCos, angleCos);\n\n if (spotAttenuation > 0.) {\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n \n lightSample = clampedTexture(densityMap3D, vLightProbe);\n \n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * spotAttenuation * spotLight.color;\n }\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n albedo *= baseColorSample;\n }\n \n emissive = getBlackBodyRadiation(emissive.r);\n albedo += emissive;\n\n outgoingLight.rgb = vPoint.xyz;\n diffuseColor.a = saturate(density * opacity);\n\n if (density <= 0.) {\n discard;\n }\n\n #include <opaque_fragment>\n ");};}}
56
+ class VolumeNormalMaterial extends Three__namespace.MeshStandardMaterial{name="VolumeNormalMaterial";isVolumetricFogMaterial=!0;customProgramCacheKey=()=>openvdb.getUuid(this);_uniforms={baseColor:{value:new Three__namespace.Color(16777215)},scatterColor:{value:new Three__namespace.Color(0)},densityMap3D:{value:null},emissiveMap3D:{value:null},baseColorMap3D:{value:null},maskMap3D:{value:null},steps:{value:100},absorbance:{value:1},densityScale:{value:1},resolution:{value:100},offset3D:{value:new Three__namespace.Vector3(0,0,0)},wrap3D:{value:Three__namespace.ClampToEdgeWrapping},lights:{value:lights.useDirectionalLights|lights.usePointLights|lights.useSpotLights|lights.useHemisphereLights|lights.useEnvironment},cameraPosition:{value:new Three__namespace.Vector3(0,0,0)},cameraNear:{value:1},cameraFar:{value:1}};set baseColor(e){this._uniforms.baseColor.value.set(e);}get baseColor(){return this._uniforms.baseColor.value}set scatterColor(e){this._uniforms.scatterColor.value.set(e);}get scatterColor(){return this._uniforms.scatterColor.value}set densityMap3D(e){this._uniforms.densityMap3D.value=e,e.offset3D=this._uniforms.offset3D.value;}get densityMap3D(){return this._uniforms.densityMap3D.value}set emissiveMap3D(e){this._uniforms.emissiveMap3D.value=e,e.offset3D=this._uniforms.offset3D.value,this.needsUpdate=!0;}get emissiveMap3D(){return this._uniforms.emissiveMap3D.value}set maskMap3D(e){this._uniforms.maskMap3D.value=e,this.needsUpdate=!0;}get maskMap3D(){return this._uniforms.maskMap3D.value}set baseColorMap3D(e){this._uniforms.baseColorMap3D.value=e,e.offset3D=this._uniforms.offset3D.value,this.needsUpdate=!0;}get baseColorMap3D(){return this._uniforms.baseColorMap3D.value}set steps(e){this._uniforms.steps.value=e;}get steps(){return this._uniforms.steps.value}set absorbance(e){this._uniforms.absorbance.value=e;}get absorbance(){return this._uniforms.absorbance.value}set resolution(e){this._uniforms.resolution.value=e;}get resolution(){return this._uniforms.resolution.value}set offset3D(e){this._uniforms.offset3D.value.copy(e);}get offset3D(){return this._uniforms.offset3D.value}set wrap3D(e){this._uniforms.wrap3D.value=e,this.needsUpdate=!0;}get wrap3D(){return this._uniforms.wrap3D.value}set densityScale(e){this._uniforms.densityScale.value=e;}get densityScale(){return this._uniforms.densityScale.value}set lights(e){this._uniforms.lights.value=e,this.needsUpdate=!0;}get lights(){return this._uniforms.lights.value}set useDirectionalLights(e=!0){e?this._uniforms.lights.value|=lights.useDirectionalLights:this._uniforms.lights.value&=~lights.useDirectionalLights,this.needsUpdate=!0;}get useDirectionalLights(){return 0!=(this._uniforms.lights.value&lights.useDirectionalLights)}set usePointLights(e=!0){e?this._uniforms.lights.value|=lights.usePointLights:this._uniforms.lights.value&=~lights.usePointLights,this.needsUpdate=!0;}get usePointLights(){return 0!=(this._uniforms.lights.value&lights.usePointLights)}set useHemisphereLights(e=!0){e?this._uniforms.lights.value|=lights.useHemisphereLights:this._uniforms.lights.value&=~lights.useHemisphereLights,this.needsUpdate=!0;}get useHemisphereLights(){return 0!=(this._uniforms.lights.value&lights.useHemisphereLights)}set useSpotLights(e=!0){e?this._uniforms.lights.value|=lights.useSpotLights:this._uniforms.lights.value&=~lights.useSpotLights,this.needsUpdate=!0;}get useSpotLights(){return 0!=(this._uniforms.lights.value&lights.useSpotLights)}set useEnvironment(e=!0){e?this._uniforms.lights.value|=lights.useEnvironment:this._uniforms.lights.value&=~lights.useEnvironment,this.needsUpdate=!0;}get useEnvironment(){return 0!=(this._uniforms.lights.value&lights.useEnvironment)}constructor(e={}){super(),this.side=Three__namespace.BackSide,this.depthWrite=!1,this.depthTest=!1,this.transparent=!0,Object.keys(this._uniforms).forEach((n=>{void 0!==e[n]&&null!==e[n]&&(this[n]=e[n],e[n]instanceof Three__namespace.Texture&&(e[n].offset3D=this._uniforms.offset3D.value));})),this.onBeforeCompile=(n,i)=>{const t=getDepthUniforms();Object.entries({...this._uniforms,...t}).forEach((([e,i])=>{n.uniforms[e]=i;}));const o=`\n #define VOLUME_BBOX_SPAN 0.5\n ${e.emissiveMap3D?"#define USE_EMISSIVE_GRID 1":""}\n ${e.baseColorMap3D?"#define USE_BASE_COLOR_GRID 1":""}\n ${e.maskMap3D?"#define USE_MASK_GRID 1":""}\n\n #define VOLUME_USE_ENVIRONMENT ${Number(this.useEnvironment)} != 0\n #define VOLUME_USE_HEMI_LIGHTS ${Number(this.useHemisphereLights)} != 0\n #define VOLUME_USE_POINT_LIGHTS ${Number(this.usePointLights)} != 0\n #define VOLUME_USE_DIR_LIGHTS ${Number(this.useDirectionalLights)} != 0\n #define VOLUME_USE_SPOT_LIGHTS ${Number(this.useSpotLights)} != 0\n `;n.vertexShader=n.vertexShader.replace("#include <common>","\n out vec3 vOrigin;\n out vec3 vDirection;\n out vec3 vPosition;\n\n #include <common>\n ").replace("#include <worldpos_vertex>","\n #include <worldpos_vertex>\n\n vOrigin = vec3(inverse(modelMatrix) * vec4(cameraPosition, 1.0)).xyz;\n vDirection = position - vOrigin;\n vPosition = position;\n ");const a=`\n vec2 getVolumeBbox(vec3 vPointOfReference) {\n const vec3 vBoxMin = vec3(-0.5);\n const vec3 vBoxMax = vec3(0.5);\n vec3 vInvPointOfReference = 1.0 / vPointOfReference;\n \n vec3 vMinRange = (-vOrigin - VOLUME_BBOX_SPAN) * vInvPointOfReference;\n vec3 vMaxRange = (-vOrigin + VOLUME_BBOX_SPAN) * vInvPointOfReference;\n \n vec3 vMin = min(vMinRange, vMaxRange);\n vec3 vMax = max(vMinRange, vMaxRange);\n\n return vec2(\n max(vMin.x, max(vMin.y, vMin.z)),\n min(vMax.x, min(vMax.y, vMax.z))\n );\n }\n\n vec3 radiation = vec3(0.);\n vec3 getBlackBodyRadiation(float temperature) {\n if (temperature == 0.) {\n return vec3(0.);\n }\n\n #ifndef USE_EMISSIVE_GRID\n return vec3(0.);\n #endif\n\n // NOTE Blackbody radiation source - https://www.shadertoy.com/view/4tdGWM\n float temperatureScaled = temperature * 1000.;\n\n radiation.r += 1. / (exp(19E3 * 1. / temperatureScaled) - 1.);\n radiation.g += 3.375 / (exp(19E3 * 1.5 / temperatureScaled) - 1.);\n radiation.b += 8. / (exp(19E3 * 2. / temperatureScaled) - 1.);\n\n return (radiation / max(radiation.r,max(radiation.g,radiation.b)));\n }\n\n float loopUV(float x) {\n if (abs(mod(floor(x), 2.)) == 1.) {\n return ceil(x) - x;\n } else {\n return x - floor(x);\n }\n }\n\n vec3 mapTextureSample0(vec3 position){\n vec3 uv = position + VOLUME_BBOX_SPAN;\n\n ${this.wrap3D===Three__namespace.RepeatWrapping?"return mod(uv, 1.);":""}\n ${0===this.wrap3D||this.wrap3D===Three__namespace.ClampToEdgeWrapping?"// NOTE Return UV":""}\n ${this.wrap3D===Three__namespace.MirroredRepeatWrapping?"\n uv.x = loopUV(uv.x);\n uv.y = loopUV(uv.y);\n uv.z = loopUV(uv.z);\n ":""}\n\n return uv;\n }\n\n vec3 mapTextureSample(vec3 position, vec3 offset){\n vec3 uv = position + VOLUME_BBOX_SPAN + offset;\n\n ${this.wrap3D===Three__namespace.RepeatWrapping?"return mod(uv, 1.);":""}\n ${0===this.wrap3D||this.wrap3D===Three__namespace.ClampToEdgeWrapping?"// NOTE Return UV":""}\n ${this.wrap3D===Three__namespace.MirroredRepeatWrapping?"\n uv.x = loopUV(uv.x);\n uv.y = loopUV(uv.y);\n uv.z = loopUV(uv.z);\n ":""}\n\n return uv;\n }\n\n float blendSample(float value) {\n float fSmoothingThreshold = 0.25;\n float fSmoothingBlend = 0.25;\n\n return smoothstep(\n fSmoothingThreshold - fSmoothingBlend,\n fSmoothingThreshold + fSmoothingBlend,\n value\n ) * densityScale;\n }\n\n float clampedTexture(sampler3D source, vec3 position) {\n ${0===this.wrap3D||this.wrap3D===Three__namespace.ClampToEdgeWrapping?"\n vec3 vAbs = abs(position);\n\n if (max(vAbs.x, max(vAbs.y, vAbs.z)) > .5) {\n return 0.;\n }\n ":""}\n\n return texture(source, mapTextureSample(position, offset3D)).r;\n }\n\n float clampedMaskTexture(sampler3D source, vec3 position) {\n ${0===this.wrap3D||this.wrap3D===Three__namespace.ClampToEdgeWrapping?"\n vec3 vAbs = abs(position);\n\n if (max(vAbs.x, max(vAbs.y, vAbs.z)) > .5) {\n return 0.;\n }\n ":""}\n\n return texture(source, mapTextureSample0(position)).r;\n }\n\n vec3 clampedTextureRGB(sampler3D source, vec3 position) {\n return texture(source, mapTextureSample(position, offset3D)).rgb;\n }\n `;n.fragmentShader=n.fragmentShader.replace("#include <uv_pars_fragment>","// NOTE Override UV calculations").replace("#include <packing>","").replace("#include <common>",`\n \n uniform mat4 modelMatrix;\n uniform mat4 modelViewMatrix;\n \n\n precision highp float;\n precision highp sampler2D;\n precision highp sampler3D;\n\n in vec3 vOrigin;\n in vec3 vDirection;\n in vec3 vPosition;\n\n uniform sampler3D densityMap3D;\n uniform sampler3D emissiveMap3D;\n uniform sampler3D baseColorMap3D;\n uniform sampler3D maskMap3D;\n uniform vec3 offset3D;\n uniform int wrap3D;\n\n uniform float steps;\n uniform float absorbance;\n uniform float densityScale;\n uniform vec3 baseColor;\n uniform vec3 scatterColor;\n uniform float resolution;\n\n uniform float cameraNear;\n uniform float cameraFar;\n uniform sampler2D depthScreenMap;\n uniform vec4 depthView;\n\n ${o}\n\n #include <common>\n #include <packing>\n #include <uv_pars_fragment>\n\n ${a}\n `).replace("#include <lights_fragment_begin>","// NOTE Override light calculations").replace("#include <lights_fragment_maps>","// NOTE Override light calculations").replace("#include <lights_fragment_end>","// NOTE Override light calculations").replace("#include <opaque_fragment>","\n vec3 vWorld = -vViewPosition;\n\n vec3 vRayDirection = normalize(vDirection);\n vec2 vBounds = getVolumeBbox(vRayDirection);\n vec2 vMaxBounds = getVolumeBbox(vOrigin);\n \n if (vBounds.x > vBounds.y) {\n discard;\n }\n\n vBounds.x = max(vBounds.x, 0.0);\n vMaxBounds.x = max(vMaxBounds.x, 0.0);\n\n // Volume movement\n\n vec3 vPoint = vOrigin + vBounds.x * vRayDirection;\n vec3 vPointStep = 1.0 / abs(vRayDirection);\n float delta = min(vPointStep.x, min(vPointStep.y, vPointStep.z)) / steps;\n vec3 vDirectionDeltaStep = vRayDirection * delta;\n\n // Density calculations\n\n float density = 0.0;\n vec3 albedo = vec3(0.);\n vec3 emissive = vec3(0.);\n float volumeSample;\n float emissiveSample;\n float maskSample = 1.;\n float noiseSample;\n float noiseFactor;\n vec3 fTest;\n\n // Light calculations\n \n vec3 lightAlbedo = vec3(0.);\n vec3 vLightProbe;\n vec3 lightDirection;\n vec3 vLightStep;\n vec3 vEnvMapScatter = vec3(0.0);\n float lightSample;\n float lightAbsorbance;\n float lightDistance;\n float stepAccumulation;\n\n // Utils\n\n float absorbanceDensityRatio = min(absorbance, densityScale); // NOTE When reducing densityScale, automatically reduce absorbance\n float inverseAbsorbance = 1.0 / absorbanceDensityRatio;\n float inverseDensityScale = 1.0 / densityScale;\n float eInverseAbsorbance = exp(-1. - inverseAbsorbance); // NOTE Wrong, but looks kinda better than exp(-inverseAbsorbance)\n float eDensityAbsorbance = exp(-absorbanceDensityRatio * delta);\n float eInverseDensityScale = exp(-1. - inverseDensityScale);\n\n // Depth Testing\n\n vec2 screenUV = gl_FragCoord.xy / depthView.zw;\n float worldDepth = unpackRGBAToDepth(texture2D(depthScreenMap, screenUV));\n float fogDepth = 0.;\n vec3 vProjPoint;\n\n \n float lightMarchLimit = resolution + 2.;\n int iLightMarchLimit = int(lightMarchLimit);\n\n vec3 vUnit = (modelMatrix * vec4(1., 0., 0., 0.)).xyz;\n float vUnitLength = length(vUnit);\n\n vec3 iblIrradiance = vec3(0.0);\n \n\n vec3 lastNonSolidPoint = vec3(vPoint);\n\n for (float i = vBounds.x; i < vBounds.y; i += delta) {\n volumeSample = clampedTexture(densityMap3D, vPoint);\n \n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vPoint);\n #endif\n\n density += blendSample(volumeSample) * eDensityAbsorbance * maskSample;\n\n #ifdef USE_EMISSIVE_GRID\n emissiveSample = clampedTexture(emissiveMap3D, vPoint);\n emissive = max(emissive, density * vec3(emissiveSample));\n #endif\n\n if (density < 1.) {\n lastNonSolidPoint = vPoint;\n }\n\n if (density >= 1.) {\n break;\n }\n\n vProjPoint = (modelMatrix * vec4(vPoint, 1.0)).xyz;\n fogDepth = length(vProjPoint - cameraPosition);\n fogDepth = (fogDepth - cameraNear) / (cameraFar - cameraNear);\n fogDepth = saturate(fogDepth);\n \n if (worldDepth < fogDepth) {\n break;\n }\n\n vPoint += vDirectionDeltaStep;\n }\n\n density = clamp(density, 0.0, 1.0);\n\n vPoint = lastNonSolidPoint;\n\n mat4 mInverseModelViewMatrix = inverse(modelViewMatrix);\n mat3 mInverseNormalMatrix = transpose(mat3(modelViewMatrix)); // NOTE Same as inverse(normalMatrix)\n\n if (density > 0.) {\n\n \n vec3 baseColorSample = baseColor.rgb;\n\n #ifdef USE_BASE_COLOR_GRID\n baseColorSample *= clampedTextureRGB(baseColorMap3D, vPoint);\n #endif\n\n albedo += ambientLightColor;\n \n\n #if VOLUME_USE_ENVIRONMENT\n \n vec3 viewDir = isOrthographic ? vec3(0.0, 0.0, 1.0) : normalize(vViewPosition);\n vec3 vUVCoords = normalize(lastNonSolidPoint * mInverseNormalMatrix).xyz;\n\n #if defined( USE_ENVMAP )\n vEnvMapScatter = getIBLIrradiance(vUVCoords) * RECIPROCAL_PI;\n\n albedo += density * getIBLRadiance(viewDir, vUVCoords, .5) * RECIPROCAL_PI;\n #endif\n \n #endif\n\n #if NUM_HEMI_LIGHTS > 0 && VOLUME_USE_HEMI_LIGHTS\n \n lightAlbedo = vec3(0.);\n vec3 textureProbe;\n\n float absorbanceUp;\n float absorbanceDown;\n float stepAccumulationUp;\n float stepAccumulationDown;\n\n for (int lightIndex = 0; lightIndex < NUM_HEMI_LIGHTS; lightIndex++) {\n\n HemisphereLight hemiLight = hemisphereLights[lightIndex];\n vLightProbe = vec3(vPoint);\n\n absorbanceUp = 0.0;\n absorbanceDown = 0.0;\n\n stepAccumulationUp = 0.;\n stepAccumulationDown = 0.;\n\n lightDirection = -normalize((vec4(hemiLight.direction, 1.) * viewMatrix).xyz);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 1; lightMarch < int(lightMarchLimit / 2.); lightMarch++) {\n if (absorbanceUp < 1.) {\n textureProbe = vLightProbe - float(lightMarch) * vLightStep;\n lightSample = clampedTexture(densityMap3D, textureProbe);\n\n absorbanceUp += lightSample * eInverseAbsorbance;\n stepAccumulationUp += 1.;\n }\n \n if (absorbanceDown < 1.) {\n textureProbe = vLightProbe + float(lightMarch) * vLightStep;\n lightSample = clampedTexture(densityMap3D, textureProbe);\n\n absorbanceDown += blendSample(lightSample) * eInverseAbsorbance;\n stepAccumulationDown += 1.;\n }\n }\n\n absorbanceUp = min(1., absorbanceUp);\n absorbanceDown = min(1., absorbanceDown);\n\n lightAlbedo += mix(\n (1. - absorbanceUp) * hemiLight.skyColor,\n (1. - absorbanceDown) * hemiLight.groundColor,\n 0.5\n );\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_POINT_LIGHTS > 0 && VOLUME_USE_POINT_LIGHTS\n \n PointLight pointLight;\n lightAlbedo = vec3(0.);\n \n for (int lightIndex = 0; lightIndex < NUM_POINT_LIGHTS; lightIndex++) {\n pointLight = pointLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.;\n stepAccumulation = 1.;\n\n lightDirection = vPoint - (mInverseModelViewMatrix * vec4(pointLight.position, 1.)).xyz;\n lightDistance = length(lightDirection) * vUnitLength;\n lightDirection = normalize(lightDirection);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n \n lightSample = clampedTexture(densityMap3D, vLightProbe);\n \n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * pointLight.color;\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_DIR_LIGHTS > 0 && VOLUME_USE_DIR_LIGHTS\n \n DirectionalLight directionalLight;\n lightAlbedo = vec3(0.);\n\n for (int lightIndex = 0; lightIndex < NUM_DIR_LIGHTS; lightIndex++) {\n directionalLight = directionalLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.0;\n stepAccumulation = 1.;\n\n lightDirection = -normalize((vec4(directionalLight.direction, 1.) * viewMatrix).xyz);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n \n float lightSample = clampedTexture(densityMap3D, vLightProbe);\n\n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * directionalLight.color;\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_SPOT_LIGHTS > 0 && VOLUME_USE_SPOT_LIGHTS\n \n SpotLight spotLight;\n lightAlbedo = vec3(0.);\n\n float angleCos;\n float spotAttenuation;\n \n for (int lightIndex = 0; lightIndex < NUM_SPOT_LIGHTS; lightIndex++) {\n spotLight = spotLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.0;\n stepAccumulation = 1.;\n\n lightDirection = vPoint - (mInverseModelViewMatrix * vec4(spotLight.position, 1.)).xyz;\n lightDistance = length(lightDirection) * vUnitLength;\n lightDirection = normalize(lightDirection);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n angleCos = dot(normalize(-lightDirection), normalize(mInverseNormalMatrix * spotLight.direction));\n spotAttenuation = smoothstep(spotLight.coneCos, spotLight.penumbraCos, angleCos);\n\n if (spotAttenuation > 0.) {\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n \n lightSample = clampedTexture(densityMap3D, vLightProbe);\n \n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * spotAttenuation * spotLight.color;\n }\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n albedo *= baseColorSample;\n }\n \n emissive = getBlackBodyRadiation(emissive.r);\n albedo += emissive;\n\n outgoingLight.rgb = vPoint.xyz;\n diffuseColor.a = saturate(density * opacity);\n\n if (density <= 0.) {\n discard;\n }\n\n #include <opaque_fragment>\n ");};}}
57
57
 
58
58
  const froxelPars="\n uniform vec3 froxelGrid;\n uniform float froxelColumns;\n uniform vec2 froxelRange;\n uniform mat4 froxelProjectionInverse;\n uniform int froxelSubSteps;\n\n float frameSliceDepth(float t) {\n return froxelRange.x * pow(froxelRange.y / froxelRange.x, t);\n }\n\n vec3 frameGetViewPosition(vec2 uv, float viewDepth) {\n vec4 nearPoint = froxelProjectionInverse * vec4(uv * 2.0 - 1.0, -1.0, 1.0);\n vec4 farPoint = froxelProjectionInverse * vec4(uv * 2.0 - 1.0, 1.0, 1.0);\n nearPoint /= nearPoint.w;\n farPoint /= farPoint.w;\n\n float t = (viewDepth + nearPoint.z) / (nearPoint.z - farPoint.z);\n\n return mix(nearPoint.xyz, farPoint.xyz, t);\n }\n";const noisePars="\n uniform float frameJitter;\n\n // NOTE Returns 0.5 when jitter is off (frameJitter is negative)\n float frameNoise(vec2 pixel) {\n if (frameJitter < 0.0) {\n return 0.5;\n }\n\n float noise = fract(52.9829189 * fract(dot(pixel, vec2(0.06711056, 0.00583715))));\n\n return fract(noise + frameJitter);\n }\n";const volumePars=`\n #define FRAME_WRAP_REPEAT ${Three__namespace.RepeatWrapping}\n #define FRAME_WRAP_MIRROR ${Three__namespace.MirroredRepeatWrapping}\n\n uniform mat4 frameViewToLocal;\n uniform mat4 frameLocalToView;\n uniform mat4 frameWorldToLocal;\n uniform float frameWorldScale;\n\n uniform highp sampler3D frameDensityMap;\n uniform highp sampler3D frameEmissiveMap;\n uniform highp sampler3D frameBaseColorMap;\n uniform highp sampler3D frameMaskMap;\n uniform bool frameUseEmissiveMap;\n uniform bool frameUseBaseColorMap;\n uniform bool frameUseMaskMap;\n\n uniform vec3 frameBaseColor;\n uniform vec3 frameScatterColor;\n uniform float frameExtinction;\n uniform float frameDensityScale;\n uniform float frameDensityCutoff;\n uniform float frameAbsorbance;\n uniform float frameOpacity;\n uniform float frameEmissiveIntensity;\n uniform vec2 frameTemperatureRange;\n uniform vec3 frameOffset3D;\n uniform int frameWrap3D;\n\n float frameMirrorAxis(float value) {\n float period = mod(value, 2.0);\n\n return period > 1.0 ? 2.0 - period : period;\n }\n\n vec3 frameWrapUv(vec3 uv) {\n if (frameWrap3D == FRAME_WRAP_REPEAT) {\n return fract(uv);\n }\n\n if (frameWrap3D == FRAME_WRAP_MIRROR) {\n return vec3(frameMirrorAxis(uv.x), frameMirrorAxis(uv.y), frameMirrorAxis(uv.z));\n }\n\n return uv;\n }\n\n // NOTE Mip level for a footprint of the given size in local units of the longest box side\n float frameLod(float localSize) {\n vec3 texels = vec3(textureSize(frameDensityMap, 0));\n\n return max(0.0, log2(localSize * max(texels.x, max(texels.y, texels.z))));\n }\n\n // NOTE Returns extinction for each world unit\n float frameSampleExtinction(vec3 localPosition, float lod) {\n vec3 uv = frameWrapUv(localPosition + 0.5 + frameOffset3D);\n float value = textureLod(frameDensityMap, uv, lod).r;\n\n if (frameUseMaskMap) {\n value *= textureLod(frameMaskMap, localPosition + 0.5, lod).r;\n }\n\n value = value > frameDensityCutoff ? value : 0.0;\n\n return value * frameDensityScale * frameExtinction / frameWorldScale;\n }\n\n bool frameInsideBox(vec3 localPosition) {\n vec3 localAbs = abs(localPosition);\n\n return max(localAbs.x, max(localAbs.y, localAbs.z)) <= 0.5;\n }\n\n float frameBoxExit(vec3 origin, vec3 direction) {\n vec3 inverseDirection = 1.0 / direction;\n vec3 tMin = (vec3(-0.5) - origin) * inverseDirection;\n vec3 tMax = (vec3(0.5) - origin) * inverseDirection;\n vec3 tFar = max(tMin, tMax);\n\n return max(0.0, min(tFar.x, min(tFar.y, tFar.z)));\n }\n\n // NOTE Returns the part of the segment from start to end (0.0 to 1.0) that is in the box\n vec2 frameBoxSegment(vec3 start, vec3 end) {\n vec3 inverseDirection = 1.0 / (end - start);\n vec3 tMin = (vec3(-0.5) - start) * inverseDirection;\n vec3 tMax = (vec3(0.5) - start) * inverseDirection;\n vec3 tNear = min(tMin, tMax);\n vec3 tFar = max(tMin, tMax);\n\n return vec2(\n max(0.0, max(tNear.x, max(tNear.y, tNear.z))),\n min(1.0, min(tFar.x, min(tFar.y, tFar.z)))\n );\n }\n\n vec3 frameSampleAlbedo(vec3 localPosition, float lod) {\n vec3 albedo = frameBaseColor;\n\n if (frameUseBaseColorMap) {\n albedo *= textureLod(frameBaseColorMap, frameWrapUv(localPosition + 0.5 + frameOffset3D), lod).rgb;\n }\n\n return albedo;\n }\n\n // NOTE Blackbody color, temperature in Kelvin. Source - https://www.shadertoy.com/view/4tdGWM\n vec3 frameBlackBody(float temperature) {\n if (temperature <= 0.0) {\n return vec3(0.0);\n }\n\n vec3 radiation = vec3(\n 1.0 / (exp(19E3 * 1.0 / temperature) - 1.0),\n 3.375 / (exp(19E3 * 1.5 / temperature) - 1.0),\n 8.0 / (exp(19E3 * 2.0 / temperature) - 1.0)\n );\n\n float peak = max(radiation.r, max(radiation.g, radiation.b));\n\n return peak > 0.0 ? radiation / peak : vec3(0.0);\n }\n\n vec3 frameSampleEmission(vec3 localPosition, float lod) {\n if (!frameUseEmissiveMap) {\n return vec3(0.0);\n }\n\n float value = textureLod(frameEmissiveMap, frameWrapUv(localPosition + 0.5 + frameOffset3D), lod).r;\n float temperature = mix(frameTemperatureRange.x, frameTemperatureRange.y, value);\n\n // NOTE Emitted power increases with temperature^4 (Stefan-Boltzmann). Range top gives 1.0.\n float power = pow(temperature / max(frameTemperatureRange.y, 1.0), 4.0);\n\n return frameBlackBody(temperature) * power * frameEmissiveIntensity;\n }\n`;const frameMaxShadowCascades=4;const gridPars="\n #define FRAME_MAX_CASCADES 4\n\n uniform vec3 frameGridMin[FRAME_MAX_CASCADES];\n uniform vec3 frameGridSize[FRAME_MAX_CASCADES];\n uniform vec3 frameGridVoxel[FRAME_MAX_CASCADES];\n uniform int frameGridCount;\n uniform float frameGridResolution;\n uniform float frameGridColumns;\n uniform vec2 frameGridAtlasSize;\n\n vec3 frameGridUvw(int cascade, vec3 worldPosition) {\n return (worldPosition - frameGridMin[cascade]) / frameGridSize[cascade];\n }\n\n // NOTE Half a voxel inside the cascade. Then the linear filter does not read the clamped edge.\n bool frameInsideGrid(vec3 gridUvw) {\n float inset = 0.5 / frameGridResolution;\n\n return all(greaterThanEqual(gridUvw, vec3(inset))) && all(lessThanEqual(gridUvw, vec3(1.0 - inset)));\n }\n\n // NOTE Finest cascade from the first cascade that contains the position, or -1\n int frameFindCascade(vec3 worldPosition, int firstCascade) {\n for (int i = 0; i < FRAME_MAX_CASCADES; i++) {\n if (i >= frameGridCount) {\n break;\n }\n\n if (i >= firstCascade && frameInsideGrid(frameGridUvw(i, worldPosition))) {\n return i;\n }\n }\n\n return -1;\n }\n\n // NOTE Hardware bilinear filter in the slice, manual linear filter between slices\n vec4 frameSampleGrid(sampler2D atlas, int cascade, vec3 gridUvw) {\n float resolution = frameGridResolution;\n float z = gridUvw.z * resolution - 0.5;\n float z0 = clamp(floor(z), 0.0, resolution - 1.0);\n float z1 = min(z0 + 1.0, resolution - 1.0);\n float blend = clamp(z - z0, 0.0, 1.0);\n vec2 xy = clamp(gridUvw.xy * resolution, vec2(0.5), vec2(resolution - 0.5));\n\n float tileOffset = float(cascade) * resolution;\n float tile0Index = tileOffset + z0;\n float tile1Index = tileOffset + z1;\n vec2 tile0 = vec2(mod(tile0Index, frameGridColumns), floor(tile0Index / frameGridColumns));\n vec2 tile1 = vec2(mod(tile1Index, frameGridColumns), floor(tile1Index / frameGridColumns));\n\n vec4 sample0 = texture(atlas, (tile0 * resolution + xy) / frameGridAtlasSize);\n vec4 sample1 = texture(atlas, (tile1 * resolution + xy) / frameGridAtlasSize);\n\n return mix(sample0, sample1, blend);\n }\n\n // NOTE Transmittance of the shadow slots at a world position. 1.0 outside the grid.\n void frameSampleShadows(\n sampler2D map0,\n sampler2D map1,\n vec3 worldPosition,\n out vec4 shadow0,\n out vec4 shadow1\n ) {\n shadow0 = vec4(1.0);\n shadow1 = vec4(1.0);\n\n int cascade = frameFindCascade(worldPosition, 0);\n\n if (cascade < 0) {\n return;\n }\n\n vec3 gridUvw = frameGridUvw(cascade, worldPosition);\n\n shadow0 = frameSampleGrid(map0, cascade, gridUvw);\n shadow1 = frameSampleGrid(map1, cascade, gridUvw);\n\n // NOTE Blend to the next cascade near the sides of this cascade. Then cascade borders are not visible.\n vec3 sideDistance = min(gridUvw, 1.0 - gridUvw);\n float blend = 1.0 - smoothstep(0.0, 0.1, min(sideDistance.x, min(sideDistance.y, sideDistance.z)));\n int nextCascade = blend > 0.0 ? frameFindCascade(worldPosition, cascade + 1) : -1;\n\n if (nextCascade >= 0) {\n vec3 nextUvw = frameGridUvw(nextCascade, worldPosition);\n\n shadow0 = mix(shadow0, frameSampleGrid(map0, nextCascade, nextUvw), blend);\n shadow1 = mix(shadow1, frameSampleGrid(map1, nextCascade, nextUvw), blend);\n }\n }\n\n // NOTE Cascade and grid position of an atlas pixel\n int frameGetGridCell(vec2 fragCoord, out vec3 gridUvw) {\n float resolution = frameGridResolution;\n vec2 tile = floor(fragCoord / resolution);\n float tileIndex = tile.y * frameGridColumns + tile.x;\n float cascade = floor(tileIndex / resolution);\n float z = tileIndex - cascade * resolution;\n\n gridUvw = vec3(fragCoord - tile * resolution, z + 0.5) / resolution;\n\n return int(cascade);\n }\n";const tileQuadVertexShader="\n uniform vec4 frameRect;\n uniform float frameSliceStart;\n uniform vec2 frameTileSize;\n uniform float frameTileColumns;\n uniform vec2 frameTargetSize;\n\n void main() {\n float slice = frameSliceStart + float(gl_InstanceID);\n vec2 tile = vec2(mod(slice, frameTileColumns), floor(slice / frameTileColumns));\n vec2 cell = mix(frameRect.xy, frameRect.zw, position.xy * 0.5 + 0.5);\n\n gl_Position = vec4((tile * frameTileSize + cell) / frameTargetSize * 2.0 - 1.0, 0.0, 1.0);\n }\n";const createVolumeUniforms=()=>({frameViewToLocal:{value:new Three__namespace.Matrix4},frameLocalToView:{value:new Three__namespace.Matrix4},frameWorldToLocal:{value:new Three__namespace.Matrix4},frameWorldScale:{value:1},frameDensityMap:{value:null},frameEmissiveMap:{value:null},frameBaseColorMap:{value:null},frameMaskMap:{value:null},frameUseEmissiveMap:{value:!1},frameUseBaseColorMap:{value:!1},frameUseMaskMap:{value:!1},frameBaseColor:{value:new Three__namespace.Color(16777215)},frameScatterColor:{value:new Three__namespace.Color(0)},frameExtinction:{value:100},frameDensityScale:{value:1},frameDensityCutoff:{value:0},frameAbsorbance:{value:1},frameOpacity:{value:1},frameEmissiveIntensity:{value:1},frameTemperatureRange:{value:new Three__namespace.Vector2(0,6500)},frameOffset3D:{value:new Three__namespace.Vector3},frameWrap3D:{value:Three__namespace.ClampToEdgeWrapping}});const setVolumeUniforms=(e,a,n)=>{e.frameViewToLocal.value.copy(n.viewToLocal),e.frameLocalToView.value.copy(n.localToView),e.frameWorldToLocal.value.copy(n.worldToLocal),e.frameWorldScale.value=n.matrixWorld.getMaxScaleOnAxis(),e.frameDensityMap.value=n.densityMap3D,e.frameEmissiveMap.value=n.emissiveMap3D,e.frameBaseColorMap.value=n.baseColorMap3D,e.frameMaskMap.value=n.maskMap3D,e.frameUseEmissiveMap.value=!!n.emissiveMap3D,e.frameUseBaseColorMap.value=!!n.baseColorMap3D,e.frameUseMaskMap.value=!!n.maskMap3D,e.frameBaseColor.value.copy(a.baseColor),e.frameScatterColor.value.copy(a.scatterColor),e.frameExtinction.value=a.extinction,e.frameDensityScale.value=a.densityScale,e.frameDensityCutoff.value=a.densityCutoff,e.frameAbsorbance.value=a.absorbance,e.frameOpacity.value=a.opacity,e.frameEmissiveIntensity.value=a.emissiveIntensity,e.frameTemperatureRange.value.copy(a.temperatureRange),e.frameOffset3D.value.copy(a.offset3D),e.frameWrap3D.value=a.wrap3D;};
59
59
 
@@ -81,11 +81,11 @@ const gridOptions$1=["emissiveGrid","baseColorGrid","maskGrid"],findGrid=(e,o)=>
81
81
 
82
82
  function wvdbKernel(e,t){const n=512,r={},a={},s={},l=e=>255*Math.min(Math.max(e,0),1)|0,o=(e,t)=>{if(1===t.length)return e.fill(t[0]);for(let n=0;n<e.length;n+=t.length)e.set(t,n);return e},i=(e,[t,r,a],s,l,o=1)=>{const i=Math.ceil(t/8),c=Math.ceil(r/8);for(let d=0;d<s.length;d++){const f=s[d],h=f%i*8,g=Math.floor(f/i)%c*8,u=8*Math.floor(f/(i*c));let p=d*n;for(let n=0;n<8;n++)for(let s=0;s<8;s++){const i=(g+s)*t+(u+n)*t*r,c=g+s<r&&u+n<a;for(let n=0;n<8;n++,p++)if(c&&h+n<t){const t=(h+n+i)*o,r=p*o;for(let n=0;n<o;n++)e[t+n]=l[r+n];}}}},c=e=>e.backgroundTexel.length;return {fillTexels:o,create:({readerId:e,config:t})=>{r[e]=t;},release:({readerId:e})=>{delete r[e],[a,s].forEach((t=>Object.keys(t).filter((t=>t.startsWith(`${e}:`))).forEach((e=>delete t[e]))));},chunk:async({readerId:d,gridIndex:f,lod:h,kind:g,firstIndex:u,bytes:p})=>{const m=r[d];if(!m)return null;const w=m.grids[f],y=w.lods[h],M=`${d}:${f}`,k=`${M}:${h}`,x=c(w),[b,I,A]=y.dimensions,T=await(async e=>{const t=new Blob([e]).stream().pipeThrough(new DecompressionStream("deflate"));return new Uint8Array(await new Response(t).arrayBuffer())})(p);a[k]||(a[k]={data:o(new Uint8Array(b*I*A*x),w.backgroundTexel),pagesLeft:y.pages,mask:null});const U=a[k];if("mask"===g)U.mask=t(T);else {const t=s[M];t&&U.pagesLeft===y.pages&&(((e,[t,n,r],a,[s,l,o],i)=>{const c=t*n,d=(e,t)=>{const n=new Int32Array(e),r=new Int32Array(e),a=new Float32Array(e);for(let s=0;s<e;s++){const l=Math.min(Math.max((s+.5)/e*t-.5,0),t-1);n[s]=Math.floor(l),r[s]=Math.min(n[s]+1,t-1),a[s]=l-n[s];}return {lower:n,upper:r,weight:a}},f=d(s,t),h=d(l,n),g=d(o,r);let u=0;for(let n=0;n<o;n++){const r=g.lower[n]*c,o=g.upper[n]*c,d=g.weight[n];for(let n=0;n<l;n++){const l=h.lower[n]*t,c=h.upper[n]*t,g=h.weight[n];for(let t=0;t<s;t++){const n=f.lower[t],s=f.upper[t],h=f.weight[t];for(let t=0;t<i;t++){const f=n=>e[n*i+t],p=f(n+l+r)+(f(s+l+r)-f(n+l+r))*h,m=f(n+c+r)+(f(s+c+r)-f(n+c+r))*h,w=f(n+l+o)+(f(s+l+o)-f(n+l+o))*h,y=p+(m-p)*g,M=w+(f(n+c+o)+(f(s+c+o)-f(n+c+o))*h-w)*g;a[u++]=y+(M-y)*d+.5;}}}}})(t.data,t.dimensions,U.data,y.dimensions,x),delete s[M],U.mask&&((e,t,r)=>{const a=c(t),s=o(new Uint8Array(n*a),t.backgroundTexel),l=Math.ceil(r[0]/8)*Math.ceil(r[1]/8)*Math.ceil(r[2]/8);for(let t=0;t<l;t++)e.mask[t>>>5]&1<<(31&t)||i(e.data,r,[t],s,a);})(U,w,y.dimensions));const{indices:r,values:a}=e(T,{layout:y.layout,bits:m.bits,firstIndex:u,channels:w.channels});i(U.data,y.dimensions,r,((e,t,{class:r,background:a,channels:s,scale:o})=>{if(3===s){const r=new Uint8Array(t*n*4);for(let a=0;a<t;a++){const t=a*n*3,s=a*n*4;for(let a=0;a<n;a++){for(let o=0;o<3;o++)r[s+4*a+o]=l(e[t+o*n+a]);r[s+4*a+3]=255;}}return r}const i=new Uint8Array(e.length),c="level set"===r;for(let t=0;t<e.length;t++)i[t]=c?Math.abs(e[t])<a?255:0:l(e[t]/o);return i})(a,r.length,w),x),U.pagesLeft--;}const $=0===U.pagesLeft;if(!$&&("mask"===g||1===y.pages))return null;if($){delete a[k];const e=w.lods[h+1];e&&e.pages>1&&(s[M]={data:U.data.slice(),dimensions:y.dimensions});}return {type:"texture",gridIndex:f,lod:h,complete:$,data:$?U.data:U.data.slice()}}}}
83
83
 
84
- const{fillTexels:fillTexels}=wvdbKernel(openvdb.decodeWVDBPage,openvdb.decodeWVDBMask),workerSource=`\nconst kernel = (${wvdbKernel.toString()})(\n (${openvdb.decodeWVDBPage.toString()}),\n (${openvdb.decodeWVDBMask.toString()})\n);\nlet queue = Promise.resolve();\n\nself.onmessage = ({ data }) => {\n queue = queue.then(async () => {\n try {\n const result = await kernel[data.type](data.payload);\n\n self.postMessage({ result }, result && result.data ? [result.data.buffer] : []);\n } catch (error) {\n self.postMessage({ error: String((error && error.message) || error) });\n }\n });\n};\n`,createWorkerBackend=e=>{const t=new Worker(e),a=[];return t.onmessage=({data:e})=>{const t=a.shift();e.error?t?.reject(e.error):t?.resolve(e.result);},t.onerror=e=>{e.preventDefault(),a.splice(0).forEach((({reject:t})=>t(e.message||"Worker error")));},{request:(e,s,r=[])=>new Promise(((n,d)=>{a.push({resolve:n,reject:d}),t.postMessage({type:e,payload:s},r);}))}},createLocalBackend=()=>{const e=wvdbKernel(openvdb.decodeWVDBPage,openvdb.decodeWVDBMask);let t=Promise.resolve();return {request:(a,s)=>{const r=t.then((()=>e[a](s)));return t=r.catch((()=>null)),r}}};let backends=null,nextReaderId=0;const getBackend=e=>{if(!backends)if("undefined"!=typeof Worker&&"undefined"!=typeof Blob&&"undefined"!=typeof URL){const e=URL.createObjectURL(new Blob([workerSource],{type:"text/javascript"})),t="undefined"!=typeof navigator&&navigator.hardwareConcurrency||2;backends=Array.from({length:Math.min(Math.max(t-1,1),4)},(()=>createWorkerBackend(e)));}else backends=[createLocalBackend()];return backends[e%backends.length]},toByte=e=>255*Math.min(Math.max(e,0),1)|0,getBackgroundTexel=({class:e,background:t,channels:a=1},s)=>3===a?[...t.map(toByte),255]:["level set"===e?0:toByte(t/s)];class WVDBReader{url=null;header=null;grids={};finalLod=0;loadedLod=-1;bytesLoaded=0;bytesTotal=0;done=!1;cancelled=!1;dataStart=0;streamReader=null;constructor({maxLod:e=1/0,scales:t={}}={}){this.maxLod=e,this.scales=t,this.readerId=nextReaderId++,this.backend=getBackend(this.readerId);}async load(e,{onHeader:t,onUpdate:a,untilLod:s=1/0}={}){return this.url=e,await this.read(s,{onHeader:t,onUpdate:a}),this}async loadMore(e,{onUpdate:t}={}){if(!this.header)throw new Error("Call load() first.");return await this.read(e,{onUpdate:t}),this}async read(e,{onHeader:t,onUpdate:a}){if(this.cancelled||this.header&&this.loadedLod>=Math.min(e,this.finalLod))return;let s=null,r=0,n={};if(this.header){s=this.getChunks(e),r=this.dataStart+s[0].offset;const t=this.dataStart+Math.max(...s.map((({offset:e,length:t})=>e+t)));n={Range:`bytes=${r}-${t-1}`};}const d=await fetch(this.url,{headers:n}).catch((e=>{if(n.Range)return fetch(this.url);throw e}));if(!d.ok)throw new Error(`Could not load ${this.url} (${d.status}).`);if(!d.body)throw new Error("Streams are not supported.");const o=206===d.status?r:0;this.streamReader=d.body.getReader();let l=new Uint8Array(0),i=0,h=0;const c=[],u=e=>{if(i+e.length>l.length){const t=new Uint8Array(Math.max(2*l.length,i+e.length));t.set(l.subarray(0,i)),l=t;}l.set(e,i),i+=e.length;},g=()=>{for(;h<s.length;){const e=s[h],t=this.dataStart+e.offset-o;if(t+e.length>i)break;h++;const r=l.slice(t,t+e.length),n=this.backend.request("chunk",{readerId:this.readerId,gridIndex:e.gridIndex,lod:e.lod,kind:e.kind,firstIndex:e.firstIndex,bytes:r},[r.buffer]);c.push(n.then((t=>{this.cancelled||this.apply(e,t,a);})));}};for(;!this.cancelled;){const{done:r,value:n}=await this.streamReader.read();if(r||this.cancelled)break;if(u(n),this.bytesLoaded+=n.length,!this.header){const r=openvdb.readWVDBHeader(l.subarray(0,i));if(!r)continue;this.header=r.header,this.dataStart=r.dataStart,this.prepare(),s=this.getChunks(e),t&&t(this),s.length||(this.done=!0,this.loadedLod=this.finalLod,a&&a({reader:this,grid:null,lod:-1,lodComplete:!0,done:!0}));}if(g(),h===s.length){this.streamReader.cancel().catch((()=>{}));break}}if(!this.cancelled&&(!s||h<s.length))throw new Error("The WVDB file is incomplete.");await Promise.all(c),s.length&&(this.loadedLod=Math.max(...s.map((({lod:e})=>e)))),this.done=this.loadedLod>=this.finalLod,this.done&&this.release();}getChunks(e){return openvdb.getWVDBChunks(this.header,Math.min(e,this.finalLod)).filter((({lod:e})=>e>this.loadedLod))}prepare(){const{grids:e,bake:t}=this.header,a=({name:e})=>this.scales[e]||1;this.finalLod=Math.min(Math.max(Math.floor(this.maxLod),0),t.lods-1),this.bytesTotal=this.dataStart+openvdb.getWVDBChunks(this.header,this.finalLod).reduce(((e,{offset:t,length:a})=>Math.max(e,t+a)),0),e.forEach((e=>{const t=getBackgroundTexel(e,a(e));this.grids[e.name]={name:e.name,class:e.class,channels:e.channels||1,bbox:e.bbox,texelSize:t.length,dimensions:[1,1,1],data:fillTexels(new Uint8Array(t.length),t),lod:-1,chunksLeft:e.lods.map((({mask:e,pages:t})=>t.length+(e?1:0)))};})),this.backend.request("create",{readerId:this.readerId,config:{bits:t.bits,grids:e.map((e=>({class:e.class,background:e.background,channels:e.channels||1,scale:a(e),backgroundTexel:getBackgroundTexel(e,a(e)),lods:e.lods.map((({dimensions:e,mask:t,pages:a})=>({dimensions:e,layout:t?openvdb.wvdbLayouts.sparse:openvdb.wvdbLayouts.dense,pages:a.length})))})))}});}apply(e,t,a){const s=this.grids[this.header.grids[e.gridIndex].name],{lod:r}=e;s.chunksLeft[r]--,t&&(s.data=t.data,s.dimensions=this.header.grids[e.gridIndex].lods[r].dimensions,s.lod=r);const n=0===s.chunksLeft[r];a&&(t||n)&&a({reader:this,grid:s,lod:r,lodComplete:n,done:this.isComplete()});}isComplete(){return Object.values(this.grids).every((({chunksLeft:e})=>e.slice(0,this.finalLod+1).every((e=>0===e))))}release(){this.backend.request("release",{readerId:this.readerId}).catch((()=>{}));}cancel(){this.cancelled||(this.cancelled=!0,this.streamReader?.cancel().catch((()=>{})),this.release());}}
84
+ const{fillTexels:fillTexels}=wvdbKernel(openvdb.decodeWVDBPage,openvdb.decodeWVDBMask),workerSource=`\nconst kernel = (${wvdbKernel.toString()})(\n (${openvdb.decodeWVDBPage.toString()}),\n (${openvdb.decodeWVDBMask.toString()})\n);\nlet queue = Promise.resolve();\n\nself.onmessage = ({ data }) => {\n queue = queue.then(async () => {\n try {\n const result = await kernel[data.type](data.payload);\n\n self.postMessage({ result }, result && result.data ? [result.data.buffer] : []);\n } catch (error) {\n self.postMessage({ error: String((error && error.message) || error) });\n }\n });\n};\n`,createWorkerBackend=e=>{const t=new Worker(e),s=[];return t.onmessage=({data:e})=>{const t=s.shift();e.error?t?.reject(e.error):t?.resolve(e.result);},t.onerror=e=>{e.preventDefault(),s.splice(0).forEach((({reject:t})=>t(e.message||"Worker error")));},{request:(e,r,a=[])=>new Promise(((i,d)=>{s.push({resolve:i,reject:d}),t.postMessage({type:e,payload:r},a);}))}},createLocalBackend=()=>{const e=wvdbKernel(openvdb.decodeWVDBPage,openvdb.decodeWVDBMask);let t=Promise.resolve();return {request:(s,r)=>{const a=t.then((()=>e[s](r)));return t=a.catch((()=>null)),a}}};let backends=null,nextReaderId=0;const getBackend=e=>{if(!backends)if("undefined"!=typeof Worker&&"undefined"!=typeof Blob&&"undefined"!=typeof URL){const e=URL.createObjectURL(new Blob([workerSource],{type:"text/javascript"})),t="undefined"!=typeof navigator&&navigator.hardwareConcurrency||2;backends=Array.from({length:Math.min(Math.max(t-1,1),4)},(()=>createWorkerBackend(e)));}else backends=[createLocalBackend()];return backends[e%backends.length]},toByte=e=>255*Math.min(Math.max(e,0),1)|0,getBackgroundTexel=({class:e,background:t,channels:s=1},r)=>3===s?[...t.map(toByte),255]:["level set"===e?0:toByte(t/r)];class WVDBReader{url=null;header=null;grids={};finalLod=0;loadedLod=-1;bytesLoaded=0;bytesTotal=0;done=!1;cancelled=!1;dataStart=0;chunks=[];streamReader=null;buffer=new Uint8Array(0);received=0;waiters=[];streamError=null;streamEnded=!1;constructor({maxLod:e=1/0,scales:t={}}={}){this.maxLod=e,this.scales=t,this.readerId=nextReaderId++,this.backend=getBackend(this.readerId),this.headerReady=new Promise(((e,t)=>{this.resolveHeader=e,this.rejectHeader=t;})),this.headerReady.catch((()=>{}));}async load(e,{onHeader:t,onUpdate:s,untilLod:r=1/0}={}){return this.start(e,{onHeader:t}),await this.decode(r,{onUpdate:s}),this}async loadMore(e,{onUpdate:t}={}){return await this.decode(e,{onUpdate:t}),this}start(e,{onHeader:t}={}){return this.streaming||(this.url=e,this.streaming=this.stream(t).catch((e=>{this.streamError=e,this.rejectHeader(e),this.wakeWaiters();}))),this.streaming}async stream(e){const t=await fetch(this.url);if(!t.ok)throw new Error(`Could not load ${this.url} (${t.status}).`);if(!t.body)throw new Error("Streams are not supported.");for(this.streamReader=t.body.getReader();!this.cancelled;){const{done:t,value:s}=await this.streamReader.read();if(t||this.cancelled)break;if(this.receive(s,e),this.received>=this.bytesTotal){this.streamReader.cancel().catch((()=>{}));break}}if(this.streamEnded=!0,!this.header&&!this.cancelled)throw new Error("Not a complete WVDB file.");this.wakeWaiters();}receive(e,t){if(this.append(e),!this.header){const e=openvdb.readWVDBHeader(this.buffer.subarray(0,this.received));e&&(this.header=e.header,this.dataStart=e.dataStart,this.prepare(),this.chunks=openvdb.getWVDBChunks(this.header,this.finalLod),t&&t(this),this.resolveHeader());}this.wakeWaiters();}async loadLods(e,t,{onHeader:s,onUpdate:r}={}){this.url=e;const a=this.fetchPrefix(t,s).catch((e=>{this.streamError=e,this.rejectHeader(e),this.wakeWaiters();}));return await this.decode(t,{onUpdate:r}),await a,this}getLodEnd(e){return this.chunks.filter((t=>t.lod<=e)).reduce(((e,{offset:t,length:s})=>Math.max(e,this.dataStart+t+s)),this.dataStart)}async fetchPrefix(e,t){const s=()=>this.header?this.getLodEnd(Math.min(e,this.finalLod)):16384;for(;!this.cancelled&&this.received<s();){const e=this.received;if(await this.fetchRange(s,t),this.received===e&&!this.cancelled)throw new Error("The WVDB file is incomplete.")}}async fetchRange(e,t){const s=e(),r=s+Math.ceil(s/1e3)+1024-1,a=await fetch(this.url,{headers:{Range:`bytes=0-${r}`}});if(!a.ok)throw new Error(`Could not load ${this.url} (${a.status}).`);this.streamReader=a.body.getReader();let i=0;try{for(;!this.cancelled;){const{done:s,value:r}=await this.streamReader.read();if(s)break;const a=Math.min(Math.max(this.received-i,0),r.length);if(i+=r.length,this.bytesLoaded+=a,a<r.length&&this.receive(r.subarray(a),t),this.header&&this.received>=e()){this.streamReader.cancel().catch((()=>{}));break}}}catch(t){if(!this.header||this.received<e())throw t}}append(e){if(!this.buffer)return;const t=this.header?this.bytesTotal:1/0,s=Math.max(0,Math.min(e.length,t-this.received));if(this.received+s>this.buffer.length){const e=this.header?this.bytesTotal:Math.max(2*this.buffer.length,this.received+s),t=new Uint8Array(e);t.set(this.buffer.subarray(0,this.received)),this.buffer=t;}this.buffer.set(e.subarray(0,s),this.received),this.received+=s,this.bytesLoaded+=e.length;}wakeWaiters(){this.waiters=this.waiters.filter((({end:e,resolve:t,reject:s})=>{if(this.received>=e)t();else {if(!(this.streamError||this.cancelled||this.streamEnded))return !0;s(this.streamError||new Error("The WVDB file is incomplete."));}return !1}));}waitForBytes(e){return new Promise(((t,s)=>{this.waiters.push({end:e,resolve:t,reject:s}),this.wakeWaiters();}))}async decode(e,{onUpdate:t}={}){await this.headerReady;const s=Math.min(e,this.finalLod);if(this.cancelled||s<=this.loadedLod)return;const r=this.chunks.filter((e=>e.lod>this.loadedLod&&e.lod<=s)),a=[];this.loadedLod=s,!this.chunks.length&&t&&t({reader:this,grid:null,lod:-1,lodComplete:!0,done:!0});for(const e of r){const s=this.dataStart+e.offset;if(await this.waitForBytes(s+e.length),this.cancelled)return;const r=this.buffer.slice(s,s+e.length),i=this.backend.request("chunk",{readerId:this.readerId,gridIndex:e.gridIndex,lod:e.lod,kind:e.kind,firstIndex:e.firstIndex,bytes:r},[r.buffer]);a.push(i.then((s=>{this.cancelled||this.apply(e,s,t);})));}await Promise.all(a),this.done=this.loadedLod>=this.finalLod,this.done&&(this.buffer=null,this.release());}prepare(){const{grids:e,bake:t}=this.header,s=({name:e})=>this.scales[e]||1;this.finalLod=Math.min(Math.max(Math.floor(this.maxLod),0),t.lods-1),this.bytesTotal=this.dataStart+openvdb.getWVDBChunks(this.header,this.finalLod).reduce(((e,{offset:t,length:s})=>Math.max(e,t+s)),0),e.forEach((e=>{const t=getBackgroundTexel(e,s(e));this.grids[e.name]={name:e.name,class:e.class,channels:e.channels||1,bbox:e.bbox,texelSize:t.length,dimensions:[1,1,1],data:fillTexels(new Uint8Array(t.length),t),lod:-1,chunksLeft:e.lods.map((({mask:e,pages:t})=>t.length+(e?1:0)))};})),this.backend.request("create",{readerId:this.readerId,config:{bits:t.bits,grids:e.map((e=>({class:e.class,background:e.background,channels:e.channels||1,scale:s(e),backgroundTexel:getBackgroundTexel(e,s(e)),lods:e.lods.map((({dimensions:e,mask:t,pages:s})=>({dimensions:e,layout:t?openvdb.wvdbLayouts.sparse:openvdb.wvdbLayouts.dense,pages:s.length})))})))}});}apply(e,t,s){const r=this.grids[this.header.grids[e.gridIndex].name],{lod:a}=e;r.chunksLeft[a]--,t&&(r.data=t.data,r.dimensions=this.header.grids[e.gridIndex].lods[a].dimensions,r.lod=a);const i=0===r.chunksLeft[a];s&&(t||i)&&s({reader:this,grid:r,lod:a,lodComplete:i,done:this.isComplete()});}isComplete(){return Object.values(this.grids).every((({chunksLeft:e})=>e.slice(0,this.finalLod+1).every((e=>0===e))))}release(){this.backend.request("release",{readerId:this.readerId}).catch((()=>{}));}cancel(){this.cancelled||(this.cancelled=!0,this.streamReader?.cancel().catch((()=>{})),this.rejectHeader(new Error("Cancelled.")),this.wakeWaiters(),this.release());}}
85
85
 
86
86
  const textureKeys=["densityMap3D","emissiveMap3D","baseColorMap3D","maskMap3D"],updateTexture=(e,{data:t,dimensions:[s,r,a]})=>{const{image:d}=e,o=d.width!==s||d.height!==r||d.depth!==a;e.image={data:t,width:s,height:r,depth:a},o&&e.dispose(),e.needsUpdate=!0;};class WVDBVolumeData{grids={};bindings=[];constructor(e){this.reader=e;}readGrids(){Object.values(this.reader.grids).forEach((e=>{const{name:t,class:s,channels:r,bbox:a}=e;this.grids[t]={name:t,isLevelSet:"level set"===s,isColor:3===r,bbox:a?new Three__namespace.Box3(new Three__namespace.Vector3(...a[0]),new Three__namespace.Vector3(...a[1])):new Three__namespace.Box3,get data(){return e.data},get dimensions(){return e.dimensions},get resolution(){return e.dimensions[0]},get lod(){return e.lod},source:e};}));}get lod(){return Math.min(...Object.values(this.reader.grids).map((({lod:e})=>e)))}get lods(){return this.reader.finalLod+1}get done(){return this.reader.done}attach(e){const t=Object.values(this.grids);return [...e.volumes||[],...e.materials||[]].forEach((s=>{textureKeys.forEach((r=>{const a=s[r],d=a&&t.find((({data:e})=>e===a.image?.data));d&&this.bindings.push({object:e,texture:a,grid:d.source});}));})),e}detach(e){this.bindings=this.bindings.filter((t=>t.object!==e));}updateTextures(){this.bindings.forEach((({texture:e,grid:t})=>{e.image.data!==t.data&&updateTexture(e,t);}));}dispose(){this.reader.cancel(),this.bindings=[];}}class WVDBLoader extends Three__namespace.Loader{maxLod=1/0;scales={};constructor(e){super(e);}setMaxLod(e){return this.maxLod=e,this}setGridScales(e){return this.scales={...e},this}load(e,t,s,r){const a=new WVDBReader({maxLod:this.maxLod,scales:this.scales}),d=new WVDBVolumeData(a);let o=!1;return a.load(e,{onHeader:()=>d.readGrids(),onUpdate:e=>{d.updateTextures(),!o&&d.lod>=0&&(o=!0,t&&t(d)),o&&s&&s({lod:e.lod,lods:d.lods,lodComplete:e.lodComplete,done:e.done,bytesLoaded:a.bytesLoaded,bytesTotal:a.bytesTotal});}}).catch((e=>{a.cancelled||(r?r(e):console.error(e));})),d}}
87
87
 
88
- const gridOptions=["emissiveGrid","baseColorGrid","maskGrid"];class WVDBAnimationLoader extends Three__namespace.Loader{options={};maxLod=1/0;concurrency=6;constructor(e){super(e);}setOptions(e){return this.options={...e},this}setMaxLod(e){return this.maxLod=e,this}setConcurrency(e){return this.concurrency=Math.max(1,e),this}load(e,o,a,t){const r=new FrameVolumeAnimation;return r.totalFrames=e.length,this.loadFrames(r,e,o,a).catch((e=>{r.disposed||(t?t(e):console.error(e));})),r}async loadFrames(e,o,a,t){const{emissiveGrid:r,emissiveScale:n=1}=this.options,i="string"==typeof r?{[r]:n}:{},s=o.map((()=>new WVDBReader({maxLod:this.maxLod,scales:i}))),m=s.map((e=>new WVDBVolumeData(e))),d=new Array(o.length);let l=0;const c=[],u=()=>(e.disposed&&s.forEach((e=>e.cancel())),e.disposed),h=e=>{c[e]=(c[e]||0)+1,t&&t({lod:e,loadedFrames:c[e],totalFrames:o.length});},p=async e=>{let a=0;await Promise.all(Array.from({length:Math.min(this.concurrency,o.length)},(async()=>{for(;a<o.length&&!u();)await e(a++);})));},f=e=>()=>{u()||m[e].updateTextures();};if(await p((async a=>{await s[a].load(o[a],{untilLod:0,onHeader:()=>m[a].readGrids(),onUpdate:f(a)}),u()||(d[a]=this.createFrame(m[a],e),(()=>{for(;l<d.length&&d[l];)e.addFrame(d[l]),l++,e.loadedFrames=l;})(),h(0));})),u())return;a&&a(e);const y=Math.max(0,...s.map((({finalLod:e})=>e)));for(let e=1;e<=y;e++)if(await p((async o=>{s[o].finalLod<e||(await s[o].loadMore(e,{onUpdate:f(o)}),u()||h(e));})),u())return}createFrame(e,o){const{grids:a}=e,t={...this.options};gridOptions.forEach((e=>{const o="string"==typeof t[e]?a[t[e]]:null;delete t[e],o&&(t[e]=o);}));const r=gridOptions.map((e=>t[e])).filter(Boolean),n=a.density&&!r.includes(a.density)&&a.density||Object.values(a).find((e=>!e.isColor&&!r.includes(e)));return n?(gridOptions.forEach((e=>{t[e]&&(t[e]=t[e].data);})),n.bbox.isEmpty()||o.bbox.union(n.bbox),e.attach(new FrameVolume(n.data,{...t,resolution:n.resolution,bbox:n.bbox}))):new FrameVolume(new Uint8Array(1),{resolution:1})}}
88
+ const gridOptions=["emissiveGrid","baseColorGrid","maskGrid"];class WVDBAnimationLoader extends Three__namespace.Loader{options={};maxLod=1/0;concurrency=1/0;constructor(e){super(e);}setOptions(e){return this.options={...e},this}setMaxLod(e){return this.maxLod=e,this}setConcurrency(e){return this.concurrency=Math.max(1,e),this}load(e,o,a,t){const r=new FrameVolumeAnimation;r.totalFrames=e.length;const n=e.map((()=>new WVDBReader({maxLod:this.maxLod,scales:this.getScales()})));return this.loadFrames(r,e,n,o,a).catch((e=>{n.forEach((e=>e.cancel())),r.disposed||(t?t(e):console.error(e));})),r}getScales(){const{emissiveGrid:e,emissiveScale:o=1}=this.options;return "string"==typeof e?{[e]:o}:{}}async loadFrames(e,o,a,t,r){const n=a.map((e=>new WVDBVolumeData(e))),s=new Array(o.length);let i=0;const m=[],d=()=>(e.disposed&&a.forEach((e=>e.cancel())),e.disposed),l=e=>{m[e]=(m[e]||0)+1,r&&r({lod:e,loadedFrames:m[e],totalFrames:o.length});},c=async e=>{let a=0;await Promise.all(Array.from({length:Math.min(this.concurrency,o.length)},(async()=>{for(;a<o.length&&!d();)await e(a++);})));},u=e=>()=>{d()||n[e].updateTextures();};if(await c((async t=>{await a[t].loadLods(o[t],0,{onHeader:()=>n[t].readGrids(),onUpdate:u(t)}),d()||(s[t]=this.createFrame(n[t],e),(()=>{for(;i<s.length&&s[i];)e.addFrame(s[i]),i++,e.loadedFrames=i;})(),l(0));})),d())return;t&&t(e);const h=Math.max(0,...a.map((({finalLod:e})=>e)));for(let e=1;e<=h;e++)if(await c((async t=>{a[t].finalLod<e||(await a[t].loadLods(o[t],e,{onUpdate:u(t)}),d()||l(e));})),d())return}createFrame(e,o){const{grids:a}=e,t={...this.options};gridOptions.forEach((e=>{const o="string"==typeof t[e]?a[t[e]]:null;delete t[e],o&&(t[e]=o);}));const r=gridOptions.map((e=>t[e])).filter(Boolean),n=a.density&&!r.includes(a.density)&&a.density||Object.values(a).find((e=>!e.isColor&&!r.includes(e)));return n?(gridOptions.forEach((e=>{t[e]&&(t[e]=t[e].data);})),n.bbox.isEmpty()||o.bbox.union(n.bbox),e.attach(new FrameVolume(n.data,{...t,resolution:n.resolution,bbox:n.bbox}))):new FrameVolume(new Uint8Array(1),{resolution:1})}}
89
89
 
90
90
  Object.defineProperty(exports, 'CloudVolume', {
91
91
  enumerable: true,
package/index.js CHANGED
@@ -8,13 +8,13 @@ class VDBLoader extends Three.Loader{constructor(e){super(e);}load(e,o,r,a){load
8
8
 
9
9
  const Bbox=Three.Box3;
10
10
 
11
- const sampleColors=[16711680,65280,255];class VolumeToBbox extends Three.Group{processes=[];constructor(e,{color:o,opacity:t}={}){let r;super(),r=e instanceof Array?e:void 0!==e.grids?Object.values(e.grids):[e];const s=new Three.Object3D,a=new Bbox;Object.values(r).forEach(((e,r)=>{const c=new Three.InstancedMesh(new Three.BoxGeometry(1,1,1),new Three.MeshBasicMaterial({wireframe:!0,color:o||sampleColors[r],transparent:!0,opacity:t}),e.leavesCount);let n=0;const i=o=>{o.isLeaf()&&(a.set(...e.getWorldBbox(o)),a.getCenter(s.position),a.getSize(s.scale),s.updateMatrix(),c.setMatrixAt(n++,s.matrix)),o.childMask?.countOn()>0&&o.childMask.forEachOn((({offset:e})=>{i(o.table[e]);}));};e.root.table.forEach((e=>i(e))),this.add(c);}));}}
11
+ const sampleColors=[16711680,65280,255];class VolumeToBbox extends Three.Group{processes=[];meshes=[];constructor(e,{color:s,opacity:o}={}){let t;super(),t=e instanceof Array?e:void 0!==e.grids?Object.values(e.grids):[e];const r=new Three.Object3D,a=new Bbox;Object.values(t).forEach(((e,t)=>{const i=new Three.InstancedMesh(new Three.BoxGeometry(1,1,1),new Three.MeshBasicMaterial({wireframe:!0,color:s||sampleColors[t],transparent:!0,opacity:o}),e.leavesCount);let c=0;const h=s=>{s.isLeaf()&&(a.set(...e.getWorldBbox(s)),a.getCenter(r.position),a.getSize(r.scale),r.updateMatrix(),i.setMatrixAt(c++,r.matrix)),s.childMask?.countOn()>0&&s.childMask.forEachOn((({offset:e})=>{h(s.table[e]);}));};e.root.table.forEach((e=>h(e))),this.meshes.push(i),this.add(i);}));}dispose(){this.meshes.forEach((e=>{e.geometry.dispose(),e.material.dispose(),e.dispose();})),this.meshes=[];}}
12
12
 
13
13
  const lights={useAmbientLights:32,usePointLights:16,useDirectionalLights:8,useSpotLights:4,useHemisphereLights:2,useEnvironment:1};
14
14
 
15
- const vector2=new Three.Vector2,clearColor$1=new Three.Color;let depthRenderTarget,depthMaterial,depthBuffer,depthView,depthPacking=0,cachedFrame=-1,cachedCamera=null;class MeshDistanceMaterial extends Three.MeshBasicMaterial{name="MeshDistanceMaterial";customProgramCacheKey=()=>"MeshDistanceMaterial";_uniforms={};constructor(e){super(e),this._uniforms=e._uniforms||{},this.onBeforeCompile=e=>{Object.entries(this._uniforms).forEach((([r,a])=>{e.uniforms[r]=a;}));const r="\n #define DISTANCE\n\n varying mat4 mModelMatrix;\n varying mat4 mViewMatrix;\n varying mat4 mProjectionMatrix;\n varying mat4 mModelViewMatrix;\n ";e.vertexShader=e.vertexShader.replace("#include <common>",`\n ${r}\n\n out vec3 vPosition;\n\n #include <common>\n `).replace("#include <worldpos_vertex>","\n #include <worldpos_vertex>\n\n vPosition = worldPosition.xyz;\n\n mModelMatrix = modelMatrix;\n mViewMatrix = viewMatrix;\n mProjectionMatrix = projectionMatrix;\n mModelViewMatrix = modelViewMatrix;\n "),e.fragmentShader=e.fragmentShader.replace("#include <common>",`\n precision highp float;\n\n in vec3 vPosition;\n\n uniform float cameraNear;\n uniform float cameraFar;\n\n ${r}\n\n #include <packing>\n #include <common>\n `).replace("#include <opaque_fragment>","\n float dist = length(vPosition - cameraPosition);\n dist = (dist - cameraNear) / (cameraFar - cameraNear);\n dist = saturate(dist);\n \n gl_FragColor = packDepthToRGBA(dist);\n ").replace("#include <tonemapping_fragment>","").replace("#include <colorspace_fragment>","").replace("#include <fog_fragment>","").replace("#include <premultiplied_alpha_fragment>","").replace("#include <dithering_fragment>","");};}}const getDepthUniforms=()=>(depthRenderTarget||(depthRenderTarget=new Three.WebGLRenderTarget,depthRenderTarget.texture.minFilter=Three.NearestFilter,depthRenderTarget.texture.magFilter=Three.NearestFilter,depthRenderTarget.texture.generateMipmaps=!1,depthRenderTarget.depthTexture=new Three.DepthTexture,depthBuffer=depthRenderTarget.texture,depthPacking=1,depthMaterial=new MeshDistanceMaterial({_uniforms:{cameraPosition:{value:new Three.Vector3(0,0,0)},cameraNear:{value:1},cameraFar:{value:1}}}),depthView=new Three.Vector4(.1,1,1,1)),{depthScreenMap:{value:depthBuffer},depthPacking:{value:depthPacking},depthView:{value:depthView}});const updateFrameDepth=(e,r,a,t,n)=>{if(!depthBuffer)return;n._uniforms.cameraPosition.value.setFromMatrixPosition(a.matrixWorld),n._uniforms.cameraNear.value=a.near,n._uniforms.cameraFar.value=a.far;if((e?.info?.render?.frame||0)===cachedFrame&&a===cachedCamera)return;depthMaterial._uniforms.cameraPosition.value.setFromMatrixPosition(a.matrixWorld),depthMaterial._uniforms.cameraNear.value=a.near,depthMaterial._uniforms.cameraFar.value=a.far,e.getSize(vector2),depthRenderTarget.setSize(vector2.x,vector2.y),depthView.x=a.near,depthView.y=a.far,depthView.z=vector2.x,depthView.w=vector2.y;const i=[];r.traverseVisible((e=>{(e instanceof VolumeToFog||e.isFogVolume)&&(e.visible=!1,i.push(e));}));const o=r.background,l=e.getClearAlpha();e.getClearColor(clearColor$1),r.background=null,r.overrideMaterial=depthMaterial,e.setClearColor(16777215,1),e.setRenderTarget(depthRenderTarget),e.render(r,a),e.setRenderTarget(null),e.setClearColor(clearColor$1,l),r.overrideMaterial=null,r.background=o,i.forEach((e=>{e.visible=!0;})),cachedCamera=a,cachedFrame=e?.info?.render?.frame||0;};
15
+ const vector2=new Three.Vector2,clearColor$1=new Three.Color;let depthRenderTarget,depthMaterial,depthBuffer,depthView,depthPacking=0,cachedFrame=-1,cachedCamera=null;class MeshDistanceMaterial extends Three.MeshBasicMaterial{name="MeshDistanceMaterial";customProgramCacheKey=()=>"MeshDistanceMaterial";_uniforms={};constructor(e){super(e),this._uniforms=e._uniforms||{},this.onBeforeCompile=e=>{Object.entries(this._uniforms).forEach((([r,a])=>{e.uniforms[r]=a;}));const r="\n #define DISTANCE\n ";e.vertexShader=e.vertexShader.replace("#include <common>",`\n ${r}\n\n out vec3 vPosition;\n\n #include <common>\n `).replace("#include <worldpos_vertex>","\n #include <worldpos_vertex>\n\n vPosition = worldPosition.xyz;\n "),e.fragmentShader=e.fragmentShader.replace("#include <common>",`\n precision highp float;\n\n in vec3 vPosition;\n\n uniform float cameraNear;\n uniform float cameraFar;\n\n ${r}\n\n #include <packing>\n #include <common>\n `).replace("#include <opaque_fragment>","\n float dist = length(vPosition - cameraPosition);\n dist = (dist - cameraNear) / (cameraFar - cameraNear);\n dist = saturate(dist);\n \n gl_FragColor = packDepthToRGBA(dist);\n ").replace("#include <tonemapping_fragment>","").replace("#include <colorspace_fragment>","").replace("#include <fog_fragment>","").replace("#include <premultiplied_alpha_fragment>","").replace("#include <dithering_fragment>","");};}}const getDepthUniforms=()=>(depthRenderTarget||(depthRenderTarget=new Three.WebGLRenderTarget,depthRenderTarget.texture.minFilter=Three.NearestFilter,depthRenderTarget.texture.magFilter=Three.NearestFilter,depthRenderTarget.texture.generateMipmaps=!1,depthRenderTarget.depthTexture=new Three.DepthTexture,depthBuffer=depthRenderTarget.texture,depthPacking=1,depthMaterial=new MeshDistanceMaterial({_uniforms:{cameraPosition:{value:new Three.Vector3(0,0,0)},cameraNear:{value:1},cameraFar:{value:1}}}),depthView=new Three.Vector4(.1,1,1,1)),{depthScreenMap:{value:depthBuffer},depthPacking:{value:depthPacking},depthView:{value:depthView}});const updateFrameDepth=(e,r,a,t,n)=>{if(!depthBuffer)return;n._uniforms.cameraPosition.value.setFromMatrixPosition(a.matrixWorld),n._uniforms.cameraNear.value=a.near,n._uniforms.cameraFar.value=a.far;if((e?.info?.render?.frame||0)===cachedFrame&&a===cachedCamera)return;depthMaterial._uniforms.cameraPosition.value.setFromMatrixPosition(a.matrixWorld),depthMaterial._uniforms.cameraNear.value=a.near,depthMaterial._uniforms.cameraFar.value=a.far,e.getDrawingBufferSize(vector2),depthRenderTarget.setSize(vector2.x,vector2.y),depthView.x=a.near,depthView.y=a.far,depthView.z=vector2.x,depthView.w=vector2.y;const i=[];r.traverseVisible((e=>{(e instanceof VolumeToFog||e.isFogVolume)&&(e.visible=!1,i.push(e));}));const o=r.background,c=e.getClearAlpha();e.getClearColor(clearColor$1),r.background=null,r.overrideMaterial=depthMaterial,e.setClearColor(16777215,1),e.setRenderTarget(depthRenderTarget),e.render(r,a),e.setRenderTarget(null),e.setClearColor(clearColor$1,c),r.overrideMaterial=null,r.background=o,i.forEach((e=>{e.visible=!0;})),cachedCamera=a,cachedFrame=e?.info?.render?.frame||0;};
16
16
 
17
- class VolumeBasicMaterial extends Three.MeshStandardMaterial{name="VolumeBasicMaterial";isVolumetricFogMaterial=!0;customProgramCacheKey=()=>getUuid(this);_uniforms={baseColor:{value:new Three.Color(16777215)},scatterColor:{value:new Three.Color(0)},densityMap3D:{value:null},emissiveMap3D:{value:null},baseColorMap3D:{value:null},maskMap3D:{value:null},steps:{value:100},absorbance:{value:1},roughness:{value:.5},densityScale:{value:1},densityCutoff:{value:0},resolution:{value:100},offset3D:{value:new Three.Vector3(0,0,0)},wrap3D:{value:Three.ClampToEdgeWrapping},lights:{value:lights.useDirectionalLights|lights.usePointLights|lights.useSpotLights|lights.useHemisphereLights|lights.useEnvironment},cameraPosition:{value:new Three.Vector3(0,0,0)},cameraNear:{value:1},cameraFar:{value:1}};set baseColor(e){this._uniforms.baseColor.value.set(e);}get baseColor(){return this._uniforms.baseColor.value}set scatterColor(e){this._uniforms.scatterColor.value.set(e);}get scatterColor(){return this._uniforms.scatterColor.value}set densityMap3D(e){this._uniforms.densityMap3D.value=e,e.offset3D=this._uniforms.offset3D.value;}get densityMap3D(){return this._uniforms.densityMap3D.value}set emissiveMap3D(e){this._uniforms.emissiveMap3D.value=e,e.offset3D=this._uniforms.offset3D.value,this.needsUpdate=!0;}get emissiveMap3D(){return this._uniforms.emissiveMap3D.value}set maskMap3D(e){this._uniforms.maskMap3D.value=e,this.needsUpdate=!0;}get maskMap3D(){return this._uniforms.maskMap3D.value}set baseColorMap3D(e){this._uniforms.baseColorMap3D.value=e,e.offset3D=this._uniforms.offset3D.value,this.needsUpdate=!0;}get baseColorMap3D(){return this._uniforms.baseColorMap3D.value}set steps(e){this._uniforms.steps.value=e;}get steps(){return this._uniforms.steps.value}set absorbance(e){this._uniforms.absorbance.value=e;}get absorbance(){return this._uniforms.absorbance.value}set roughness(e){this._uniforms&&(this._uniforms.roughness.value=e);}get roughness(){return this._uniforms?this._uniforms.roughness.value:.5}set resolution(e){this._uniforms.resolution.value=e;}get resolution(){return this._uniforms.resolution.value}set offset3D(e){this._uniforms.offset3D.value.copy(e);}get offset3D(){return this._uniforms.offset3D.value}set wrap3D(e){this._uniforms.wrap3D.value=e,this.needsUpdate=!0;}get wrap3D(){return this._uniforms.wrap3D.value}set densityScale(e){this._uniforms.densityScale.value=e;}get densityScale(){return this._uniforms.densityScale.value}set densityCutoff(e){this._uniforms.densityCutoff.value=e;}get densityCutoff(){return this._uniforms.densityCutoff.value}set lights(e){this._uniforms.lights.value=e,this.needsUpdate=!0;}get lights(){return this._uniforms.lights.value}set useDirectionalLights(e=!0){e?this._uniforms.lights.value|=lights.useDirectionalLights:this._uniforms.lights.value&=~lights.useDirectionalLights,this.needsUpdate=!0;}get useDirectionalLights(){return 0!=(this._uniforms.lights.value&lights.useDirectionalLights)}set usePointLights(e=!0){e?this._uniforms.lights.value|=lights.usePointLights:this._uniforms.lights.value&=~lights.usePointLights,this.needsUpdate=!0;}get usePointLights(){return 0!=(this._uniforms.lights.value&lights.usePointLights)}set useHemisphereLights(e=!0){e?this._uniforms.lights.value|=lights.useHemisphereLights:this._uniforms.lights.value&=~lights.useHemisphereLights,this.needsUpdate=!0;}get useHemisphereLights(){return 0!=(this._uniforms.lights.value&lights.useHemisphereLights)}set useSpotLights(e=!0){e?this._uniforms.lights.value|=lights.useSpotLights:this._uniforms.lights.value&=~lights.useSpotLights,this.needsUpdate=!0;}get useSpotLights(){return 0!=(this._uniforms.lights.value&lights.useSpotLights)}set useEnvironment(e=!0){e?this._uniforms.lights.value|=lights.useEnvironment:this._uniforms.lights.value&=~lights.useEnvironment,this.needsUpdate=!0;}get useEnvironment(){return 0!=(this._uniforms.lights.value&lights.useEnvironment)}constructor(e={}){super(),this.side=Three.BackSide,this.depthWrite=!1,this.depthTest=!1,this.transparent=!0,Object.keys(this._uniforms).forEach((n=>{void 0!==e[n]&&null!==e[n]&&(this[n]=e[n],e[n]instanceof Three.Texture&&(e[n].offset3D=this._uniforms.offset3D.value));})),this.onBeforeCompile=(n,t)=>{const i=getDepthUniforms();Object.entries({...this._uniforms,...i}).forEach((([e,t])=>{n.uniforms[e]=t;}));const o=`\n #define VOLUME_BBOX_SPAN 0.5\n ${e.emissiveMap3D?"#define USE_EMISSIVE_GRID 1":""}\n ${e.baseColorMap3D?"#define USE_BASE_COLOR_GRID 1":""}\n ${e.maskMap3D?"#define USE_MASK_GRID 1":""}\n\n #define VOLUME_USE_ENVIRONMENT ${Number(this.useEnvironment)} != 0\n #define VOLUME_USE_HEMI_LIGHTS ${Number(this.useHemisphereLights)} != 0\n #define VOLUME_USE_POINT_LIGHTS ${Number(this.usePointLights)} != 0\n #define VOLUME_USE_DIR_LIGHTS ${Number(this.useDirectionalLights)} != 0\n #define VOLUME_USE_SPOT_LIGHTS ${Number(this.useSpotLights)} != 0\n `,a="\n varying mat4 mModelMatrix;\n varying mat4 mViewMatrix;\n varying mat4 mProjectionMatrix;\n varying mat4 mModelViewMatrix;\n varying mat4 mInverseModelViewMatrix;\n varying mat3 mInverseNormalMatrix;\n ";n.vertexShader=n.vertexShader.replace("#include <common>",`\n ${a}\n\n out vec3 vOrigin;\n out vec3 vDirection;\n out vec3 vPosition;\n\n #include <common>\n `).replace("#include <worldpos_vertex>","\n #include <worldpos_vertex>\n\n vOrigin = vec3(inverse(modelMatrix) * vec4(cameraPosition, 1.0)).xyz;\n vDirection = position - vOrigin;\n vPosition = position;\n\n mModelMatrix = modelMatrix;\n mViewMatrix = viewMatrix;\n mProjectionMatrix = projectionMatrix;\n mModelViewMatrix = modelViewMatrix;\n mInverseModelViewMatrix = inverse(modelViewMatrix);\n mInverseNormalMatrix = inverse(normalMatrix);\n ");const r=`\n vec2 getVolumeBbox(vec3 vPointOfReference) {\n const vec3 vBoxMin = vec3(-0.5);\n const vec3 vBoxMax = vec3(0.5);\n vec3 vInvPointOfReference = 1.0 / vPointOfReference;\n \n vec3 vMinRange = (-vOrigin - VOLUME_BBOX_SPAN) * vInvPointOfReference;\n vec3 vMaxRange = (-vOrigin + VOLUME_BBOX_SPAN) * vInvPointOfReference;\n \n vec3 vMin = min(vMinRange, vMaxRange);\n vec3 vMax = max(vMinRange, vMaxRange);\n\n return vec2(\n max(vMin.x, max(vMin.y, vMin.z)),\n min(vMax.x, min(vMax.y, vMax.z))\n );\n }\n\n vec3 radiation = vec3(0.);\n vec3 getBlackBodyRadiation(float temperature) {\n if (temperature == 0.) {\n return vec3(0.);\n }\n\n #ifndef USE_EMISSIVE_GRID\n return vec3(0.);\n #endif\n\n // NOTE Blackbody radiation source - https://www.shadertoy.com/view/4tdGWM\n float temperatureScaled = temperature * 1000.;\n\n radiation.r += 1. / (exp(19E3 * 1. / temperatureScaled) - 1.);\n radiation.g += 3.375 / (exp(19E3 * 1.5 / temperatureScaled) - 1.);\n radiation.b += 8. / (exp(19E3 * 2. / temperatureScaled) - 1.);\n\n return (radiation / max(radiation.r,max(radiation.g,radiation.b)));\n }\n\n float loopUV(float x) {\n if (abs(mod(floor(x), 2.)) == 1.) {\n return ceil(x) - x;\n } else {\n return x - floor(x);\n }\n }\n\n vec3 mapTextureSample0(vec3 position){\n vec3 uv = position + VOLUME_BBOX_SPAN;\n\n ${this.wrap3D===Three.RepeatWrapping?"return mod(uv, 1.);":""}\n ${0===this.wrap3D||this.wrap3D===Three.ClampToEdgeWrapping?"// NOTE Return UV":""}\n ${this.wrap3D===Three.MirroredRepeatWrapping?"\n uv.x = loopUV(uv.x);\n uv.y = loopUV(uv.y);\n uv.z = loopUV(uv.z);\n ":""}\n\n return uv;\n }\n\n vec3 mapTextureSample(vec3 position, vec3 offset){\n vec3 uv = position + VOLUME_BBOX_SPAN + offset;\n\n ${this.wrap3D===Three.RepeatWrapping?"return mod(uv, 1.);":""}\n ${0===this.wrap3D||this.wrap3D===Three.ClampToEdgeWrapping?"// NOTE Return UV":""}\n ${this.wrap3D===Three.MirroredRepeatWrapping?"\n uv.x = loopUV(uv.x);\n uv.y = loopUV(uv.y);\n uv.z = loopUV(uv.z);\n ":""}\n\n return uv;\n }\n\n float blendSample(float value) {\n return value * densityScale;\n \n // float fSmoothingThreshold = 0.1;\n // float fSmoothingBlend = 0.1;\n\n // return smoothstep(\n // fSmoothingThreshold - fSmoothingBlend,\n // fSmoothingThreshold + fSmoothingBlend,\n // value\n // ) * densityScale;\n }\n\n float clampedTexture(sampler3D source, vec3 position) {\n ${0===this.wrap3D||this.wrap3D===Three.ClampToEdgeWrapping?"\n vec3 vAbs = abs(position);\n\n if (max(vAbs.x, max(vAbs.y, vAbs.z)) > .5) {\n return 0.;\n }\n ":""}\n\n return texture(source, mapTextureSample(position, offset3D)).r;\n }\n\n float clampedMaskTexture(sampler3D source, vec3 position) {\n ${0===this.wrap3D||this.wrap3D===Three.ClampToEdgeWrapping?"\n vec3 vAbs = abs(position);\n\n if (max(vAbs.x, max(vAbs.y, vAbs.z)) > .5) {\n return 0.;\n }\n ":""}\n\n return texture(source, mapTextureSample0(position)).r;\n }\n\n vec3 clampedTextureRGB(sampler3D source, vec3 position) {\n return texture(source, mapTextureSample(position, offset3D)).rgb;\n }\n `;n.fragmentShader=n.fragmentShader.replace("#include <uv_pars_fragment>","// NOTE Override UV calculations").replace("#include <packing>","").replace("#include <common>",`\n precision highp float;\n precision highp sampler2D;\n precision highp sampler3D;\n\n in vec3 vOrigin;\n in vec3 vDirection;\n in vec3 vPosition;\n\n uniform sampler3D densityMap3D;\n uniform sampler3D emissiveMap3D;\n uniform sampler3D baseColorMap3D;\n uniform sampler3D maskMap3D;\n uniform vec3 offset3D;\n uniform int wrap3D;\n\n uniform float steps;\n uniform float absorbance;\n uniform float densityScale;\n uniform float densityCutoff;\n uniform vec3 baseColor;\n uniform vec3 scatterColor;\n uniform float resolution;\n\n uniform float cameraNear;\n uniform float cameraFar;\n uniform sampler2D depthScreenMap;\n uniform vec4 depthView;\n\n ${o}\n ${a}\n\n #include <common>\n #include <packing>\n #include <uv_pars_fragment>\n\n ${r}\n `).replace("#include <lights_fragment_begin>","// NOTE Override light calculations").replace("#include <lights_fragment_maps>","// NOTE Override light calculations").replace("#include <lights_fragment_end>","// NOTE Override light calculations").replace("#include <opaque_fragment>","\n vec3 vWorld = -vViewPosition;\n\n vec3 vRayDirection = normalize(vDirection);\n vec2 vBounds = getVolumeBbox(vRayDirection);\n vec2 vMaxBounds = getVolumeBbox(vOrigin);\n \n if (vBounds.x > vBounds.y) {\n discard;\n }\n\n vBounds.x = max(vBounds.x, 0.0);\n vMaxBounds.x = max(vMaxBounds.x, 0.0);\n\n // Volume movement\n\n vec3 vPoint = vOrigin + vBounds.x * vRayDirection;\n vec3 vPointStep = 1.0 / abs(vRayDirection);\n float delta = min(vPointStep.x, min(vPointStep.y, vPointStep.z)) / steps;\n vec3 vDirectionDeltaStep = vRayDirection * delta;\n\n // Density calculations\n\n float density = 0.0;\n vec3 albedo = vec3(0.);\n vec3 emissive = vec3(0.);\n float volumeSample;\n float emissiveSample;\n float maskSample = 1.;\n float noiseSample;\n float noiseFactor;\n vec3 fTest;\n\n // Light calculations\n \n vec3 lightAlbedo = vec3(0.);\n vec3 vLightProbe;\n vec3 lightDirection;\n vec3 vLightStep;\n vec3 vEnvMapScatter = vec3(0.0);\n float lightSample;\n float lightAbsorbance;\n float lightDistance;\n float stepAccumulation;\n\n // Utils\n\n float absorbanceDensityRatio = min(absorbance, densityScale); // NOTE When reducing densityScale, automatically reduce absorbance\n float inverseAbsorbance = 1.0 / absorbanceDensityRatio;\n float inverseDensityScale = 1.0 / densityScale;\n float eInverseAbsorbance = exp(-1. - inverseAbsorbance); // NOTE Wrong, but looks kinda better than exp(-inverseAbsorbance)\n float eDensityAbsorbance = exp(-absorbanceDensityRatio * delta);\n float eInverseDensityScale = exp(-1. - inverseDensityScale);\n\n // Depth Testing\n\n vec2 screenUV = gl_FragCoord.xy / depthView.zw;\n float worldDepth = unpackRGBAToDepth(texture2D(depthScreenMap, screenUV));\n float fogDepth = 0.;\n vec3 vProjPoint;\n\n \n float lightMarchLimit = resolution + 2.;\n int iLightMarchLimit = int(lightMarchLimit);\n\n vec3 vUnit = (mModelMatrix * vec4(1., 0., 0., 0.)).xyz;\n float vUnitLength = length(vUnit);\n\n vec3 iblIrradiance = vec3(0.0);\n \n\n vec3 lastNonSolidPoint = vec3(vPoint);\n\n for (float i = vBounds.x; i < vBounds.y; i += delta) {\n volumeSample = clampedTexture(densityMap3D, vPoint);\n \n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vPoint);\n #endif\n\n density += blendSample(volumeSample) * eDensityAbsorbance * maskSample;\n\n #ifdef USE_EMISSIVE_GRID\n emissiveSample = clampedTexture(emissiveMap3D, vPoint);\n emissive = max(emissive, density * vec3(emissiveSample));\n #endif\n\n if (density < 1.) {\n lastNonSolidPoint = vPoint;\n }\n\n if (density >= 1.) {\n break;\n }\n\n vProjPoint = (mModelMatrix * vec4(vPoint, 1.0)).xyz;\n fogDepth = length(vProjPoint - cameraPosition);\n fogDepth = (fogDepth - cameraNear) / (cameraFar - cameraNear);\n fogDepth = saturate(fogDepth);\n \n if (worldDepth < fogDepth) {\n break;\n }\n\n vPoint += vDirectionDeltaStep;\n }\n\n density = clamp(density, 0.0, 1.0);\n\n vPoint = lastNonSolidPoint;\n\n if (density <= densityCutoff) {\n discard;\n }\n\n if (density > 0.) {\n\n \n vec3 baseColorSample = baseColor.rgb;\n\n #ifdef USE_BASE_COLOR_GRID\n baseColorSample *= clampedTextureRGB(baseColorMap3D, vPoint);\n #endif\n\n albedo += ambientLightColor;\n \n\n #if VOLUME_USE_ENVIRONMENT\n \n vec3 viewDir = isOrthographic ? vec3(0.0, 0.0, 1.0) : normalize(vViewPosition);\n vec3 vUVCoords = normalize(lastNonSolidPoint * mInverseNormalMatrix).xyz;\n\n #if defined( USE_ENVMAP )\n vEnvMapScatter = getIBLIrradiance(vUVCoords) * RECIPROCAL_PI;\n\n albedo += density * getIBLRadiance(viewDir, vUVCoords, roughness) * RECIPROCAL_PI;\n #endif\n \n #endif\n\n #if NUM_HEMI_LIGHTS > 0 && VOLUME_USE_HEMI_LIGHTS\n \n lightAlbedo = vec3(0.);\n vec3 textureProbe;\n\n float absorbanceUp;\n float absorbanceDown;\n float stepAccumulationUp;\n float stepAccumulationDown;\n\n for (int lightIndex = 0; lightIndex < NUM_HEMI_LIGHTS; lightIndex++) {\n\n HemisphereLight hemiLight = hemisphereLights[lightIndex];\n vLightProbe = vec3(vPoint);\n\n absorbanceUp = 0.0;\n absorbanceDown = 0.0;\n\n stepAccumulationUp = 0.;\n stepAccumulationDown = 0.;\n\n lightDirection = -normalize((vec4(hemiLight.direction, 1.) * viewMatrix).xyz);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 1; lightMarch < int(lightMarchLimit / 2.); lightMarch++) {\n if (absorbanceUp < 1.) {\n textureProbe = vLightProbe - float(lightMarch) * vLightStep;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, textureProbe);\n #endif\n\n lightSample = clampedTexture(densityMap3D, textureProbe);\n\n absorbanceUp += lightSample * eInverseAbsorbance;\n stepAccumulationUp += 1.;\n }\n \n if (absorbanceDown < 1.) {\n textureProbe = vLightProbe + float(lightMarch) * vLightStep;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, textureProbe);\n #endif\n\n lightSample = clampedTexture(densityMap3D, textureProbe);\n\n absorbanceDown += blendSample(lightSample) * eInverseAbsorbance;\n stepAccumulationDown += 1.;\n }\n }\n\n absorbanceUp = min(1., absorbanceUp);\n absorbanceDown = min(1., absorbanceDown);\n\n lightAlbedo += mix(\n (1. - absorbanceUp) * hemiLight.skyColor,\n (1. - absorbanceDown) * hemiLight.groundColor,\n 0.5\n );\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_POINT_LIGHTS > 0 && VOLUME_USE_POINT_LIGHTS\n \n PointLight pointLight;\n lightAlbedo = vec3(0.);\n \n for (int lightIndex = 0; lightIndex < NUM_POINT_LIGHTS; lightIndex++) {\n pointLight = pointLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.;\n stepAccumulation = 1.;\n\n lightDirection = vPoint - (mInverseModelViewMatrix * vec4(pointLight.position, 1.)).xyz;\n lightDistance = length(lightDirection) * vUnitLength;\n lightDirection = normalize(lightDirection);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vLightProbe);\n #endif\n \n lightSample = clampedTexture(densityMap3D, vLightProbe) * sqrt(lightDistance);\n \n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * pointLight.color;\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_DIR_LIGHTS > 0 && VOLUME_USE_DIR_LIGHTS\n \n DirectionalLight directionalLight;\n lightAlbedo = vec3(0.);\n\n for (int lightIndex = 0; lightIndex < NUM_DIR_LIGHTS; lightIndex++) {\n directionalLight = directionalLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.0;\n stepAccumulation = 1.;\n\n lightDirection = -normalize((vec4(directionalLight.direction, 1.) * viewMatrix).xyz);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vLightProbe);\n #endif\n \n float lightSample = clampedTexture(densityMap3D, vLightProbe);\n\n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * directionalLight.color;\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_SPOT_LIGHTS > 0 && VOLUME_USE_SPOT_LIGHTS\n \n SpotLight spotLight;\n lightAlbedo = vec3(0.);\n\n float angleCos;\n float spotAttenuation;\n \n for (int lightIndex = 0; lightIndex < NUM_SPOT_LIGHTS; lightIndex++) {\n spotLight = spotLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.0;\n stepAccumulation = 1.;\n\n lightDirection = vPoint - (mInverseModelViewMatrix * vec4(spotLight.position, 1.)).xyz;\n lightDistance = length(lightDirection) * vUnitLength;\n lightDirection = normalize(lightDirection);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n angleCos = dot(normalize(-lightDirection), normalize(mInverseNormalMatrix * spotLight.direction));\n spotAttenuation = smoothstep(spotLight.coneCos, spotLight.penumbraCos, angleCos);\n\n if (spotAttenuation > 0.) {\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vLightProbe);\n #endif\n \n lightSample = clampedTexture(densityMap3D, vLightProbe);\n \n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * spotAttenuation * spotLight.color;\n }\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n albedo *= baseColorSample;\n }\n \n emissive = getBlackBodyRadiation(emissive.r);\n albedo += emissive;\n\n outgoingLight.rgb = max(max(scatterColor, vEnvMapScatter), albedo);\n diffuseColor.a = saturate(density) * opacity;\n\n #include <opaque_fragment>\n ");};}}
17
+ class VolumeBasicMaterial extends Three.MeshStandardMaterial{name="VolumeBasicMaterial";isVolumetricFogMaterial=!0;customProgramCacheKey=()=>getUuid(this);_uniforms={baseColor:{value:new Three.Color(16777215)},scatterColor:{value:new Three.Color(0)},densityMap3D:{value:null},emissiveMap3D:{value:null},baseColorMap3D:{value:null},maskMap3D:{value:null},steps:{value:100},absorbance:{value:1},roughness:{value:.5},densityScale:{value:1},densityCutoff:{value:0},resolution:{value:100},offset3D:{value:new Three.Vector3(0,0,0)},wrap3D:{value:Three.ClampToEdgeWrapping},lights:{value:lights.useDirectionalLights|lights.usePointLights|lights.useSpotLights|lights.useHemisphereLights|lights.useEnvironment},cameraPosition:{value:new Three.Vector3(0,0,0)},cameraNear:{value:1},cameraFar:{value:1}};set baseColor(e){this._uniforms.baseColor.value.set(e);}get baseColor(){return this._uniforms.baseColor.value}set scatterColor(e){this._uniforms.scatterColor.value.set(e);}get scatterColor(){return this._uniforms.scatterColor.value}set densityMap3D(e){this._uniforms.densityMap3D.value=e,e.offset3D=this._uniforms.offset3D.value;}get densityMap3D(){return this._uniforms.densityMap3D.value}set emissiveMap3D(e){this._uniforms.emissiveMap3D.value=e,e.offset3D=this._uniforms.offset3D.value,this.needsUpdate=!0;}get emissiveMap3D(){return this._uniforms.emissiveMap3D.value}set maskMap3D(e){this._uniforms.maskMap3D.value=e,this.needsUpdate=!0;}get maskMap3D(){return this._uniforms.maskMap3D.value}set baseColorMap3D(e){this._uniforms.baseColorMap3D.value=e,e.offset3D=this._uniforms.offset3D.value,this.needsUpdate=!0;}get baseColorMap3D(){return this._uniforms.baseColorMap3D.value}set steps(e){this._uniforms.steps.value=e;}get steps(){return this._uniforms.steps.value}set absorbance(e){this._uniforms.absorbance.value=e;}get absorbance(){return this._uniforms.absorbance.value}set roughness(e){this._uniforms&&(this._uniforms.roughness.value=e);}get roughness(){return this._uniforms?this._uniforms.roughness.value:.5}set resolution(e){this._uniforms.resolution.value=e;}get resolution(){return this._uniforms.resolution.value}set offset3D(e){this._uniforms.offset3D.value.copy(e);}get offset3D(){return this._uniforms.offset3D.value}set wrap3D(e){this._uniforms.wrap3D.value=e,this.needsUpdate=!0;}get wrap3D(){return this._uniforms.wrap3D.value}set densityScale(e){this._uniforms.densityScale.value=e;}get densityScale(){return this._uniforms.densityScale.value}set densityCutoff(e){this._uniforms.densityCutoff.value=e;}get densityCutoff(){return this._uniforms.densityCutoff.value}set lights(e){this._uniforms.lights.value=e,this.needsUpdate=!0;}get lights(){return this._uniforms.lights.value}set useDirectionalLights(e=!0){e?this._uniforms.lights.value|=lights.useDirectionalLights:this._uniforms.lights.value&=~lights.useDirectionalLights,this.needsUpdate=!0;}get useDirectionalLights(){return 0!=(this._uniforms.lights.value&lights.useDirectionalLights)}set usePointLights(e=!0){e?this._uniforms.lights.value|=lights.usePointLights:this._uniforms.lights.value&=~lights.usePointLights,this.needsUpdate=!0;}get usePointLights(){return 0!=(this._uniforms.lights.value&lights.usePointLights)}set useHemisphereLights(e=!0){e?this._uniforms.lights.value|=lights.useHemisphereLights:this._uniforms.lights.value&=~lights.useHemisphereLights,this.needsUpdate=!0;}get useHemisphereLights(){return 0!=(this._uniforms.lights.value&lights.useHemisphereLights)}set useSpotLights(e=!0){e?this._uniforms.lights.value|=lights.useSpotLights:this._uniforms.lights.value&=~lights.useSpotLights,this.needsUpdate=!0;}get useSpotLights(){return 0!=(this._uniforms.lights.value&lights.useSpotLights)}set useEnvironment(e=!0){e?this._uniforms.lights.value|=lights.useEnvironment:this._uniforms.lights.value&=~lights.useEnvironment,this.needsUpdate=!0;}get useEnvironment(){return 0!=(this._uniforms.lights.value&lights.useEnvironment)}constructor(e={}){super(),this.side=Three.BackSide,this.depthWrite=!1,this.depthTest=!1,this.transparent=!0,Object.keys(this._uniforms).forEach((n=>{void 0!==e[n]&&null!==e[n]&&(this[n]=e[n],e[n]instanceof Three.Texture&&(e[n].offset3D=this._uniforms.offset3D.value));})),this.onBeforeCompile=(n,t)=>{const i=getDepthUniforms();Object.entries({...this._uniforms,...i}).forEach((([e,t])=>{n.uniforms[e]=t;}));const o=`\n #define VOLUME_BBOX_SPAN 0.5\n ${e.emissiveMap3D?"#define USE_EMISSIVE_GRID 1":""}\n ${e.baseColorMap3D?"#define USE_BASE_COLOR_GRID 1":""}\n ${e.maskMap3D?"#define USE_MASK_GRID 1":""}\n\n #define VOLUME_USE_ENVIRONMENT ${Number(this.useEnvironment)} != 0\n #define VOLUME_USE_HEMI_LIGHTS ${Number(this.useHemisphereLights)} != 0\n #define VOLUME_USE_POINT_LIGHTS ${Number(this.usePointLights)} != 0\n #define VOLUME_USE_DIR_LIGHTS ${Number(this.useDirectionalLights)} != 0\n #define VOLUME_USE_SPOT_LIGHTS ${Number(this.useSpotLights)} != 0\n `;n.vertexShader=n.vertexShader.replace("#include <common>","\n out vec3 vOrigin;\n out vec3 vDirection;\n out vec3 vPosition;\n\n #include <common>\n ").replace("#include <worldpos_vertex>","\n #include <worldpos_vertex>\n\n vOrigin = vec3(inverse(modelMatrix) * vec4(cameraPosition, 1.0)).xyz;\n vDirection = position - vOrigin;\n vPosition = position;\n ");const a=`\n vec2 getVolumeBbox(vec3 vPointOfReference) {\n const vec3 vBoxMin = vec3(-0.5);\n const vec3 vBoxMax = vec3(0.5);\n vec3 vInvPointOfReference = 1.0 / vPointOfReference;\n \n vec3 vMinRange = (-vOrigin - VOLUME_BBOX_SPAN) * vInvPointOfReference;\n vec3 vMaxRange = (-vOrigin + VOLUME_BBOX_SPAN) * vInvPointOfReference;\n \n vec3 vMin = min(vMinRange, vMaxRange);\n vec3 vMax = max(vMinRange, vMaxRange);\n\n return vec2(\n max(vMin.x, max(vMin.y, vMin.z)),\n min(vMax.x, min(vMax.y, vMax.z))\n );\n }\n\n vec3 radiation = vec3(0.);\n vec3 getBlackBodyRadiation(float temperature) {\n if (temperature == 0.) {\n return vec3(0.);\n }\n\n #ifndef USE_EMISSIVE_GRID\n return vec3(0.);\n #endif\n\n // NOTE Blackbody radiation source - https://www.shadertoy.com/view/4tdGWM\n float temperatureScaled = temperature * 1000.;\n\n radiation.r += 1. / (exp(19E3 * 1. / temperatureScaled) - 1.);\n radiation.g += 3.375 / (exp(19E3 * 1.5 / temperatureScaled) - 1.);\n radiation.b += 8. / (exp(19E3 * 2. / temperatureScaled) - 1.);\n\n return (radiation / max(radiation.r,max(radiation.g,radiation.b)));\n }\n\n float loopUV(float x) {\n if (abs(mod(floor(x), 2.)) == 1.) {\n return ceil(x) - x;\n } else {\n return x - floor(x);\n }\n }\n\n vec3 mapTextureSample0(vec3 position){\n vec3 uv = position + VOLUME_BBOX_SPAN;\n\n ${this.wrap3D===Three.RepeatWrapping?"return mod(uv, 1.);":""}\n ${0===this.wrap3D||this.wrap3D===Three.ClampToEdgeWrapping?"// NOTE Return UV":""}\n ${this.wrap3D===Three.MirroredRepeatWrapping?"\n uv.x = loopUV(uv.x);\n uv.y = loopUV(uv.y);\n uv.z = loopUV(uv.z);\n ":""}\n\n return uv;\n }\n\n vec3 mapTextureSample(vec3 position, vec3 offset){\n vec3 uv = position + VOLUME_BBOX_SPAN + offset;\n\n ${this.wrap3D===Three.RepeatWrapping?"return mod(uv, 1.);":""}\n ${0===this.wrap3D||this.wrap3D===Three.ClampToEdgeWrapping?"// NOTE Return UV":""}\n ${this.wrap3D===Three.MirroredRepeatWrapping?"\n uv.x = loopUV(uv.x);\n uv.y = loopUV(uv.y);\n uv.z = loopUV(uv.z);\n ":""}\n\n return uv;\n }\n\n float blendSample(float value) {\n return value * densityScale;\n \n // float fSmoothingThreshold = 0.1;\n // float fSmoothingBlend = 0.1;\n\n // return smoothstep(\n // fSmoothingThreshold - fSmoothingBlend,\n // fSmoothingThreshold + fSmoothingBlend,\n // value\n // ) * densityScale;\n }\n\n float clampedTexture(sampler3D source, vec3 position) {\n ${0===this.wrap3D||this.wrap3D===Three.ClampToEdgeWrapping?"\n vec3 vAbs = abs(position);\n\n if (max(vAbs.x, max(vAbs.y, vAbs.z)) > .5) {\n return 0.;\n }\n ":""}\n\n return texture(source, mapTextureSample(position, offset3D)).r;\n }\n\n float clampedMaskTexture(sampler3D source, vec3 position) {\n ${0===this.wrap3D||this.wrap3D===Three.ClampToEdgeWrapping?"\n vec3 vAbs = abs(position);\n\n if (max(vAbs.x, max(vAbs.y, vAbs.z)) > .5) {\n return 0.;\n }\n ":""}\n\n return texture(source, mapTextureSample0(position)).r;\n }\n\n vec3 clampedTextureRGB(sampler3D source, vec3 position) {\n return texture(source, mapTextureSample(position, offset3D)).rgb;\n }\n `;n.fragmentShader=n.fragmentShader.replace("#include <uv_pars_fragment>","// NOTE Override UV calculations").replace("#include <packing>","").replace("#include <common>",`\n \n uniform mat4 modelMatrix;\n uniform mat4 modelViewMatrix;\n \n\n precision highp float;\n precision highp sampler2D;\n precision highp sampler3D;\n\n in vec3 vOrigin;\n in vec3 vDirection;\n in vec3 vPosition;\n\n uniform sampler3D densityMap3D;\n uniform sampler3D emissiveMap3D;\n uniform sampler3D baseColorMap3D;\n uniform sampler3D maskMap3D;\n uniform vec3 offset3D;\n uniform int wrap3D;\n\n uniform float steps;\n uniform float absorbance;\n uniform float densityScale;\n uniform float densityCutoff;\n uniform vec3 baseColor;\n uniform vec3 scatterColor;\n uniform float resolution;\n\n uniform float cameraNear;\n uniform float cameraFar;\n uniform sampler2D depthScreenMap;\n uniform vec4 depthView;\n\n ${o}\n\n #include <common>\n #include <packing>\n #include <uv_pars_fragment>\n\n ${a}\n `).replace("#include <lights_fragment_begin>","// NOTE Override light calculations").replace("#include <lights_fragment_maps>","// NOTE Override light calculations").replace("#include <lights_fragment_end>","// NOTE Override light calculations").replace("#include <opaque_fragment>","\n vec3 vWorld = -vViewPosition;\n\n vec3 vRayDirection = normalize(vDirection);\n vec2 vBounds = getVolumeBbox(vRayDirection);\n vec2 vMaxBounds = getVolumeBbox(vOrigin);\n \n if (vBounds.x > vBounds.y) {\n discard;\n }\n\n vBounds.x = max(vBounds.x, 0.0);\n vMaxBounds.x = max(vMaxBounds.x, 0.0);\n\n // Volume movement\n\n vec3 vPoint = vOrigin + vBounds.x * vRayDirection;\n vec3 vPointStep = 1.0 / abs(vRayDirection);\n float delta = min(vPointStep.x, min(vPointStep.y, vPointStep.z)) / steps;\n vec3 vDirectionDeltaStep = vRayDirection * delta;\n\n // Density calculations\n\n float density = 0.0;\n vec3 albedo = vec3(0.);\n vec3 emissive = vec3(0.);\n float volumeSample;\n float emissiveSample;\n float maskSample = 1.;\n float noiseSample;\n float noiseFactor;\n vec3 fTest;\n\n // Light calculations\n \n vec3 lightAlbedo = vec3(0.);\n vec3 vLightProbe;\n vec3 lightDirection;\n vec3 vLightStep;\n vec3 vEnvMapScatter = vec3(0.0);\n float lightSample;\n float lightAbsorbance;\n float lightDistance;\n float stepAccumulation;\n\n // Utils\n\n float absorbanceDensityRatio = min(absorbance, densityScale); // NOTE When reducing densityScale, automatically reduce absorbance\n float inverseAbsorbance = 1.0 / absorbanceDensityRatio;\n float inverseDensityScale = 1.0 / densityScale;\n float eInverseAbsorbance = exp(-1. - inverseAbsorbance); // NOTE Wrong, but looks kinda better than exp(-inverseAbsorbance)\n float eDensityAbsorbance = exp(-absorbanceDensityRatio * delta);\n float eInverseDensityScale = exp(-1. - inverseDensityScale);\n\n // Depth Testing\n\n vec2 screenUV = gl_FragCoord.xy / depthView.zw;\n float worldDepth = unpackRGBAToDepth(texture2D(depthScreenMap, screenUV));\n float fogDepth = 0.;\n vec3 vProjPoint;\n\n \n float lightMarchLimit = resolution + 2.;\n int iLightMarchLimit = int(lightMarchLimit);\n\n vec3 vUnit = (modelMatrix * vec4(1., 0., 0., 0.)).xyz;\n float vUnitLength = length(vUnit);\n\n vec3 iblIrradiance = vec3(0.0);\n \n\n vec3 lastNonSolidPoint = vec3(vPoint);\n\n for (float i = vBounds.x; i < vBounds.y; i += delta) {\n volumeSample = clampedTexture(densityMap3D, vPoint);\n \n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vPoint);\n #endif\n\n density += blendSample(volumeSample) * eDensityAbsorbance * maskSample;\n\n #ifdef USE_EMISSIVE_GRID\n emissiveSample = clampedTexture(emissiveMap3D, vPoint);\n emissive = max(emissive, density * vec3(emissiveSample));\n #endif\n\n if (density < 1.) {\n lastNonSolidPoint = vPoint;\n }\n\n if (density >= 1.) {\n break;\n }\n\n vProjPoint = (modelMatrix * vec4(vPoint, 1.0)).xyz;\n fogDepth = length(vProjPoint - cameraPosition);\n fogDepth = (fogDepth - cameraNear) / (cameraFar - cameraNear);\n fogDepth = saturate(fogDepth);\n \n if (worldDepth < fogDepth) {\n break;\n }\n\n vPoint += vDirectionDeltaStep;\n }\n\n density = clamp(density, 0.0, 1.0);\n\n vPoint = lastNonSolidPoint;\n\n if (density <= densityCutoff) {\n discard;\n }\n\n mat4 mInverseModelViewMatrix = inverse(modelViewMatrix);\n mat3 mInverseNormalMatrix = transpose(mat3(modelViewMatrix)); // NOTE Same as inverse(normalMatrix)\n\n if (density > 0.) {\n\n \n vec3 baseColorSample = baseColor.rgb;\n\n #ifdef USE_BASE_COLOR_GRID\n baseColorSample *= clampedTextureRGB(baseColorMap3D, vPoint);\n #endif\n\n albedo += ambientLightColor;\n \n\n #if VOLUME_USE_ENVIRONMENT\n \n vec3 viewDir = isOrthographic ? vec3(0.0, 0.0, 1.0) : normalize(vViewPosition);\n vec3 vUVCoords = normalize(lastNonSolidPoint * mInverseNormalMatrix).xyz;\n\n #if defined( USE_ENVMAP )\n vEnvMapScatter = getIBLIrradiance(vUVCoords) * RECIPROCAL_PI;\n\n albedo += density * getIBLRadiance(viewDir, vUVCoords, roughness) * RECIPROCAL_PI;\n #endif\n \n #endif\n\n #if NUM_HEMI_LIGHTS > 0 && VOLUME_USE_HEMI_LIGHTS\n \n lightAlbedo = vec3(0.);\n vec3 textureProbe;\n\n float absorbanceUp;\n float absorbanceDown;\n float stepAccumulationUp;\n float stepAccumulationDown;\n\n for (int lightIndex = 0; lightIndex < NUM_HEMI_LIGHTS; lightIndex++) {\n\n HemisphereLight hemiLight = hemisphereLights[lightIndex];\n vLightProbe = vec3(vPoint);\n\n absorbanceUp = 0.0;\n absorbanceDown = 0.0;\n\n stepAccumulationUp = 0.;\n stepAccumulationDown = 0.;\n\n lightDirection = -normalize((vec4(hemiLight.direction, 1.) * viewMatrix).xyz);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 1; lightMarch < int(lightMarchLimit / 2.); lightMarch++) {\n if (absorbanceUp < 1.) {\n textureProbe = vLightProbe - float(lightMarch) * vLightStep;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, textureProbe);\n #endif\n\n lightSample = clampedTexture(densityMap3D, textureProbe);\n\n absorbanceUp += lightSample * eInverseAbsorbance;\n stepAccumulationUp += 1.;\n }\n \n if (absorbanceDown < 1.) {\n textureProbe = vLightProbe + float(lightMarch) * vLightStep;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, textureProbe);\n #endif\n\n lightSample = clampedTexture(densityMap3D, textureProbe);\n\n absorbanceDown += blendSample(lightSample) * eInverseAbsorbance;\n stepAccumulationDown += 1.;\n }\n }\n\n absorbanceUp = min(1., absorbanceUp);\n absorbanceDown = min(1., absorbanceDown);\n\n lightAlbedo += mix(\n (1. - absorbanceUp) * hemiLight.skyColor,\n (1. - absorbanceDown) * hemiLight.groundColor,\n 0.5\n );\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_POINT_LIGHTS > 0 && VOLUME_USE_POINT_LIGHTS\n \n PointLight pointLight;\n lightAlbedo = vec3(0.);\n \n for (int lightIndex = 0; lightIndex < NUM_POINT_LIGHTS; lightIndex++) {\n pointLight = pointLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.;\n stepAccumulation = 1.;\n\n lightDirection = vPoint - (mInverseModelViewMatrix * vec4(pointLight.position, 1.)).xyz;\n lightDistance = length(lightDirection) * vUnitLength;\n lightDirection = normalize(lightDirection);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vLightProbe);\n #endif\n \n lightSample = clampedTexture(densityMap3D, vLightProbe) * sqrt(lightDistance);\n \n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * pointLight.color;\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_DIR_LIGHTS > 0 && VOLUME_USE_DIR_LIGHTS\n \n DirectionalLight directionalLight;\n lightAlbedo = vec3(0.);\n\n for (int lightIndex = 0; lightIndex < NUM_DIR_LIGHTS; lightIndex++) {\n directionalLight = directionalLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.0;\n stepAccumulation = 1.;\n\n lightDirection = -normalize((vec4(directionalLight.direction, 1.) * viewMatrix).xyz);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vLightProbe);\n #endif\n \n float lightSample = clampedTexture(densityMap3D, vLightProbe);\n\n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * directionalLight.color;\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_SPOT_LIGHTS > 0 && VOLUME_USE_SPOT_LIGHTS\n \n SpotLight spotLight;\n lightAlbedo = vec3(0.);\n\n float angleCos;\n float spotAttenuation;\n \n for (int lightIndex = 0; lightIndex < NUM_SPOT_LIGHTS; lightIndex++) {\n spotLight = spotLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.0;\n stepAccumulation = 1.;\n\n lightDirection = vPoint - (mInverseModelViewMatrix * vec4(spotLight.position, 1.)).xyz;\n lightDistance = length(lightDirection) * vUnitLength;\n lightDirection = normalize(lightDirection);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n angleCos = dot(normalize(-lightDirection), normalize(mInverseNormalMatrix * spotLight.direction));\n spotAttenuation = smoothstep(spotLight.coneCos, spotLight.penumbraCos, angleCos);\n\n if (spotAttenuation > 0.) {\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n\n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vLightProbe);\n #endif\n \n lightSample = clampedTexture(densityMap3D, vLightProbe);\n \n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * spotAttenuation * spotLight.color;\n }\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n albedo *= baseColorSample;\n }\n \n emissive = getBlackBodyRadiation(emissive.r);\n albedo += emissive;\n\n outgoingLight.rgb = max(max(scatterColor, vEnvMapScatter), albedo);\n diffuseColor.a = saturate(density) * opacity;\n\n #include <opaque_fragment>\n ");};}}
18
18
 
19
19
  const getDensity$1=(t,e)=>t.isLevelSet?.()?t.isActive(e)?1:0:MathUtils.clamp(t.getValue(e),0,1);
20
20
 
@@ -22,15 +22,15 @@ const textureTypes={uint8:"uint8",half:"half",float:"float"};const textureTypeMa
22
22
 
23
23
  const textureBuilds={sample:"sample",leaves:"leaves"};const textureFilters={nearest:"nearest",trilinear:"trilinear",max:"max"};function leafTextureKernel(){const e=new Float32Array(1),t=new Uint32Array(e.buffer),n=n=>{e[0]=n;const r=t[0],o=r>>>16&32768;let l=(r>>>23&255)-127+15,s=8388607&r;return l>=31?31744|o:l<=0?l<-10?o:(s=(8388608|s)>>1-l,o|s+4096>>13):(s+=4096,8388608&s&&(s=0,l++),l>=31?31744|o:o|l<<10|s>>13)};return {create:({resolution:e,start:t,step:r,filter:o,background:l,leafLog2Dim:s})=>{const a=e*e,i=a*e,u="trilinear"===o,c="max"===o,f=1<<s,d=1<<3*s,h=Math.ceil(d/32);let p=null,g=null,x=0;const m=e=>p?p[x*h+(e>>5)]>>>(31&e)&1:Math.min(Math.max(g[x*d+e],0),1),y=new Float32Array(i),k=u?new Float32Array(i):null;u||0===l||y.fill(l);const w=[0,1,2].map((()=>({texels:new Int32Array(2*e+8),voxels:new Int32Array(2*e+8),weights:new Float32Array(2*e+8),count:0,scatter:new Int32Array(f)}))),M=(n,o,l)=>{const s=w[n],a=t[n],i=r[n],c=o+l;let f=(o-1-a)/i,d=(c+1-a)/i;f>d&&([f,d]=[d,f]);const h=Math.max(0,Math.floor(f)),p=Math.min(e-1,Math.ceil(d));let g=0;for(let e=h;e<=p;e++){const t=a+e*i;if(u){const n=Math.floor(t),r=t-n;n>=o&&n<c&&(s.texels[g]=e,s.voxels[g]=n-o,s.weights[g]=1-r,g++),n+1>=o&&n+1<c&&(s.texels[g]=e,s.voxels[g]=n+1-o,s.weights[g]=r,g++);}else {const n=Math.round(t);n>=o&&n<c&&(s.texels[g]=e,s.voxels[g]=n-o,s.weights[g]=1,g++);}}s.count=g;},v=(n,o)=>{const l=Math.round((o-t[n])/r[n]);return l>=0&&l<e?l:-1},b=(e,t,n)=>{u?(y[e]+=t*n,k[e]+=n):y[e]=c?Math.max(y[e],t):t;},C=(n,o,s,i,u)=>{if(u===l)return;M(0,n,i),M(1,o,i),M(2,s,i);const[f,d,h]=w;for(let t=0;t<h.count;t++)for(let n=0;n<d.count;n++){const r=d.texels[n]*e+h.texels[t]*a,o=d.weights[n]*h.weights[t];for(let e=0;e<f.count;e++)b(f.texels[e]+r,u,f.weights[e]*o);}if(!c)return;const p=[n,o,s].map(((n,o)=>{const l=Math.round((n-t[o])/r[o]),s=Math.round((n+i-1-t[o])/r[o]);return [Math.max(0,Math.min(l,s)),Math.min(e-1,Math.max(l,s))]}));for(let t=p[2][0];t<=p[2][1];t++)for(let n=p[1][0];n<=p[1][1];n++)for(let r=p[0][0];r<=p[0][1];r++){const o=r+n*e+t*a;y[o]=Math.max(y[o],u);}},L=(t,n,r)=>{M(0,t,f),M(1,n,f),M(2,r,f);const[o,i,u]=w,h=2*s;for(let t=0;t<u.count;t++)for(let n=0;n<i.count;n++){const r=i.texels[n]*e+u.texels[t]*a,c=i.voxels[n]<<s|u.voxels[t],f=i.weights[n]*u.weights[t];for(let e=0;e<o.count;e++){const t=m(o.voxels[e]<<h|c);t!==l&&b(o.texels[e]+r,t,o.weights[e]*f);}}if(c){[t,n,r].forEach(((e,t)=>{for(let n=0;n<f;n++)w[t].scatter[n]=v(t,e+n);}));for(let t=0;t<d;t++){const n=m(t),r=o.scatter[t>>h],c=i.scatter[t>>s&f-1],d=u.scatter[t&f-1];if(n!==l&&-1!==r&&-1!==c&&-1!==d){const t=r+c*e+d*a;y[t]=Math.max(y[t],n);}}}};return {addChunk:e=>{const{leafCount:t,origins:n,tileCount:r,tiles:o}=e;for(p=e.leafWords,g=e.leafValues,x=0;x<t;x++)L(n[3*x],n[3*x+1],n[3*x+2]);for(let e=0;e<r;e++){const t=5*e;C(o[t],o[t+1],o[t+2],o[t+3],o[t+4]);}p=null,g=null;},finish:({textureType:t,radius:r})=>{let o=y;if(u)for(let e=0;e<i;e++)o[e]+=Math.max(0,1-k[e])*l;if(r&&(o=((t,n)=>{const r=new Float32Array(i);for(let o=0;o<e;o++)for(let l=0;l<e;l++)for(let s=0;s<e;s++){const i=s+l*e+o*a,u=t[i];if(0!==u)for(let t=-n;t<n;t++)for(let c=-n;c<n;c++)for(let f=-n;f<n;f++){if(s+t<0||s+t>=e||l+c<0||l+c>=e||o+f<0||o+f>=e)continue;const d=i+t+c*e+f*a,h=Math.max(0,Math.min(1,1-Math.hypot(t,c,f)/(n/2)));r[d]=Math.min(r[d]+h*u,1);}}return r})(o,r)),"float"===t)return o;if("half"===t){const e=new Uint16Array(i);for(let t=0;t<i;t++)e[t]=n(o[t]);return e}const s=new Uint8Array(i);for(let e=0;e<i;e++)s[e]=Math.min(255*o[e],255);return s}}}}}const workerSource$1=`\nconst kernel = (${leafTextureKernel.toString()})();\nlet build = null;\n\nself.onmessage = ({ data }) => {\n if (data.type === 'create') {\n build = kernel.create(data.config);\n } else if (data.type === 'chunk') {\n build.addChunk(data.chunk);\n self.postMessage({ type: 'chunk' });\n } else if (data.type === 'finish') {\n const result = build.finish(data.options);\n build = null;\n self.postMessage({ type: 'result', result }, [result.buffer]);\n }\n};\n`;const canBuildFromLeaves=e=>e instanceof GridDescriptor&&!!e.root&&"number"==typeof e.root.background;const getDensity=(e,t,n)=>n?t?1:0:Math.min(Math.max(e,0),1);function*packChunks(e,t,n){const r=e.isLevelSet(),o=1<<3*t,l=Math.ceil(o/32),s=getDensity(e.root.background,!1,r);let a;const i=()=>{a={leafCount:0,origins:new Int32Array(3072),leafValues:r?null:new Float32Array(1024*o),leafWords:r?new Uint32Array(1024*l):null,tileCount:0,tiles:new Float64Array(5120)};},u=()=>1024===a.leafCount||1024===a.tileCount,c=(e,t,s,i)=>{const u=a.leafCount++;a.origins[3*u]=t,a.origins[3*u+1]=s,a.origins[3*u+2]=i,r?a.leafWords.set(e.valueMask.words,u*l):e.values.length===o?a.leafValues.set(e.values,u*o):a.leafValues.fill(n,u*o,(u+1)*o);},f=(e,t,n,o,l,i)=>{const u=getDensity(l,i,r);if(u===s)return;const c=5*a.tileCount++;a.tiles[c]=e,a.tiles[c+1]=t,a.tiles[c+2]=n,a.tiles[c+3]=o,a.tiles[c+4]=u;};function*d(t,n,o,l){const s=t.log2dim,h=t.total-t.log2dim,p=1<<h,g=(1<<s)-1,x=t.childMask.words,m=t.valueMask.words;for(let y=0;y<x.length;y++){const k=x[y];let w=k|(r?m[y]&~k:~k);for(;0!==w;){const r=31-Math.clz32(w&-w),x=y<<5|r;if(w&=w-1,k>>>r&1){const e=t.table[x],r=n+e.origin.x,s=o+e.origin.y,a=l+e.origin.z;e.isLeaf()?c(e,r,s,a):yield*d(e,r,s,a);}else f(n+((x>>2*s&g)<<h),o+((x>>s&g)<<h),l+((x&g)<<h),p,t.values[x]??e.root.background,1==(m[y]>>>r&1));u()&&(yield a,i());}}}i();for(const t of e.root.table){const{x:e,y:n,z:r}=t.origin;t.isInternalNode()?yield*d(t,e,n,r):(t.isLeaf()?c(t,e,n,r):f(e,n,r,t.dim,t.value,t.active),u()&&(yield a,i()));}(a.leafCount||a.tileCount)&&(yield a);}const getChunkTransfer=e=>[e.origins,e.leafValues,e.leafWords,e.tiles].filter(Boolean).map((e=>e.buffer)),createWorkerBackend$1=()=>{if("undefined"==typeof Worker||"undefined"==typeof Blob||"undefined"==typeof URL)return null;const e=URL.createObjectURL(new Blob([workerSource$1],{type:"text/javascript"})),t=new Worker(e),n=[],r=(e,r=[])=>new Promise(((o,l)=>{n.push({resolve:o,reject:l}),t.postMessage(e,r);}));return t.onmessage=({data:e})=>{n.shift()?.resolve(e.result);},t.onerror=e=>{e.preventDefault(),n.splice(0).forEach((({reject:t})=>t(e.message||"Worker error")));},{create:e=>t.postMessage({type:"create",config:e}),addChunk:e=>r({type:"chunk",chunk:e},getChunkTransfer(e)),finish:e=>r({type:"finish",options:e}),dispose:()=>{t.terminate(),URL.revokeObjectURL(e),n.splice(0).forEach((({resolve:e})=>e(null)));}}},createLocalBackend$1=()=>{const e=leafTextureKernel();let t=null,n=performance.now();const r=e=>performance.now()-n<12?Promise.resolve().then(e):new Promise((t=>setTimeout((()=>{n=performance.now(),t(e());}),0)));return {create:n=>{t=e.create(n);},addChunk:e=>r((()=>t?.addChunk(e))),finish:e=>r((()=>t?.finish(e)??null)),dispose:()=>{t=null;}}};const buildLeafTexture=({grid:e,resolution:t,start:n,step:r,filter:o=textureFilters.nearest,textureType:l,radius:s,onProgress:a})=>{const{leafLog2Dim:i}=e.treeLayout,u=e.isLevelSet(),c=e.leavesCount*(1<<3*i);let f=!1,d=null;const h=async h=>{d=h(),d.create({resolution:t,start:[n.x,n.y,n.z],step:[r.x,r.y,r.z],filter:o,background:getDensity(e.root.background,!1,u),leafLog2Dim:i});const p=[];let g=0;for(const t of packChunks(e,i,e.root.background)){if(f)return null;g+=t.leafCount<<3*i,p.push(d.addChunk(t)),p.length>=2&&await p.shift(),a&&a({convertedVoxels:g,totalVoxels:c});}return await Promise.all(p),f?null:d.finish({textureType:l,radius:s})};return {promise:h((()=>createWorkerBackend$1()||createLocalBackend$1())).catch((e=>f?null:(console.warn("buildLeafTexture","Worker failed, building on the main thread.",{error:e}),d?.dispose(),h(createLocalBackend$1)))).finally((()=>{d?.dispose();})),cancel:()=>{f=!0,d?.dispose();}}};
24
24
 
25
- const getTextureMapping=(e,t)=>{const r=new Vector3,s=new Vector3,a=new Vector3,o=new Vector3,i=new Bbox;return i.set(...e.getPreciseWorldBbox()),i.getCenter(r),i.getSize(s),a.copy(s).multiplyScalar(1/t),e.transform.applyInverseJacobian(a),o.copy(r).sub(s.clone().multiplyScalar(.5)),e.transform.applyInverseTransformMap(o),o.add(a.clone().multiplyScalar(.5)),{center:r,size:s,start:o,step:a}};class VolumeToFog extends Three.Group{processes=[];leafBuilds=[];materials=[];isFogVolume=!0;constructor(e,t,r,s){super();const{resolution:a,progressive:o,emissiveGrid:i,baseColorGrid:n,maskGrid:l,radius:c,textureBuild:u=textureBuilds.sample,textureFilter:d=textureFilters.nearest,textureType:p=textureTypes.uint8}=t,m=getTextureScale(p);let h,x,f,T,y,w,g;if(this.frustumCulled=!1,h=e instanceof Array?e:void 0!==e.grids?Object.values(e.grids):[e],i)if(h=h.filter((e=>e!==i)),i instanceof Uint8Array)x=createScalarTexture3D(i,a,textureTypes.uint8);else {const e=createTextureData(p,Math.pow(a,3));x=createScalarTexture3D(e,a,p),f=(t,r,s)=>{const a=void 0!==s?s:getDensity$1(i,r)*m;return writeTexel(e,t,a),a};}if(n){if(n instanceof Uint8Array)T=new Three.Data3DTexture(n,a,a,a);else {const e=new Uint8Array(4*Math.pow(a,3));T=new Three.Data3DTexture(e,a,a,a);const t=new Three.Color;y=(r,s,a)=>{if(a)return e[4*r+0]=255*a.r,e[4*r+1]=255*a.g,e[4*r+2]=255*a.b,void(e[4*r+3]=255);const o=n.getValue(s);return o instanceof Three.Color?t.set(gridValue):o instanceof Vector3$1||o instanceof Three.Vector3?t.setRGB(o.x,o.y,o.z):t.setRGB(1,0,1),e[4*r+0]=255*t.r,e[4*r+1]=255*t.g,e[4*r+2]=255*t.b,e[4*r+3]=255,{r:t.r,g:t.g,b:t.b,a:t.a}};}T.format=Three.RGBAFormat,T.minFilter=Three.LinearFilter,T.magFilter=Three.LinearFilter,T.unpackAlignment=1,T.needsUpdate=!0;}if(l)if(l instanceof Uint8Array)w=createScalarTexture3D(l,a,textureTypes.uint8);else {const e=createTextureData(p,Math.pow(a,3));w=createScalarTexture3D(e,a,p),g=(t,r,s)=>{const a=void 0!==s?s:getDensity$1(l,r)*m;return writeTexel(e,t,a),a};}let D=0,v=0;const M=h.length,V=M*Math.pow(a,3),B=e=>!e||e instanceof Uint8Array||canBuildFromLeaves(e),F=u===textureBuilds.leaves&&h.every((e=>!(e instanceof GridDescriptor)||canBuildFromLeaves(e)))&&B(i)&&B(l)&&(!n||n instanceof Uint8Array);u!==textureBuilds.leaves||F||console.warn("VolumeToFog","textureBuild","Primitive or vector grids need the sample build. Using the sample build.");const b=[],U=[];let G=!1;const A=()=>{s&&s({convertedVoxels:U.reduce(((e,t)=>e+t.convertedVoxels),0),totalVoxels:U.reduce(((e,t)=>e+t.totalVoxels),0),convertedGrids:D,totalGrids:M});},S=(e,t,r,s)=>{const o={convertedVoxels:0,totalVoxels:0};U.push(o);const i=buildLeafTexture({grid:e,resolution:a,start:t.start,step:t.step,filter:d,textureType:p,radius:s,onProgress:({convertedVoxels:e,totalVoxels:t})=>{o.convertedVoxels=e,o.totalVoxels=t,A();}});return this.leafBuilds.push(i),i.promise.then((e=>!!e&&(r.image.data=e,r.needsUpdate=!0,!0))).catch((e=>(console.error("VolumeToFog","buildLeafTexture",{error:e}),!1)))};h.reverse().forEach((async(e,n)=>{if(e instanceof Uint8Array){const r=new Three.Data3DTexture(e,a,a,a);r.format=Three.RedFormat,r.minFilter=Three.LinearFilter,r.magFilter=Three.LinearFilter,r.unpackAlignment=1,r.needsUpdate=!0;const s=new Three.SphereGeometry(1),o=new VolumeBasicMaterial({...t,emissiveMap3D:x,densityMap3D:r,baseColorMap3D:T,maskMap3D:w}),i=new Three.Mesh(s,o);return i.frustumCulled=!1,i.onBeforeRender=updateFrameDepth,this.materials.push(o),void this.add(i)}if(!(e instanceof GridDescriptor))return;const u=createTextureData(p,Math.pow(a,3)),d=createScalarTexture3D(u,a,p),h=new Three.SphereGeometry(1),B=new VolumeBasicMaterial({...t,emissiveMap3D:x,densityMap3D:d,baseColorMap3D:T,maskMap3D:w}),U=new Three.Mesh(h,B);if(U.frustumCulled=!1,U.onBeforeRender=updateFrameDepth,this.materials.push(B),F){const t=getTextureMapping(e,a);return U.position.copy(t.center),U.scale.copy(t.size),o&&this.add(U),b.push(S(e,t,d,c).then((e=>{e&&(D++,o||this.add(U),A());}))),void(G||(G=!0,!i||i instanceof Uint8Array||b.push(S(i,t,x,0)),!l||l instanceof Uint8Array||b.push(S(l,t,w,0))))}const L=a,C=Math.pow(L,2);o&&this.add(U),await(t=>new Promise((a=>{const i=Math.pow(t,2),l=Math.pow(t,3),{center:p,size:h,start:B,step:F}=getTextureMapping(e,t);U.position.copy(p),U.scale.copy(h);let b=0,G=0,A=0;let S=function*(){for(let r=0;r<l;r++){const r=b+G*t+A*i,s=getDensity$1(e,B),a=f&&f(r,B),n=y&&y(r,B),l=g&&g(r,B),p=c;if(p)for(let e=-p;e<p;e++)for(let o=-p;o<p;o++)for(let i=-p;i<p;i++){if(b+e<0||b+e>=t||G+o<0||G+o>=t||A+i<0||A+i>=t)continue;const d=r+e+o*L+i*C,p=Math.max(0,Math.min(1,1-Math.hypot(e,o,i)/(c/2))),h=p*s*m;writeTexel(u,d,readTexel(u,d)+h),writeTexel(u,d,Math.min(readTexel(u,d),m)),f&&f(d,null,a*p*m),y&&y(d,null,n*p),g&&g(d,null,l*p*m);}else {const e=s*m;writeTexel(u,r,readTexel(u,r)+e),writeTexel(u,r,Math.min(readTexel(u,r),m));}if(v++,o&&(d.needsUpdate=!0,x&&(x.needsUpdate=!0),T&&(T.needsUpdate=!0),w&&(w.needsUpdate=!0)),A>=t){x.needsUpdate=!0,T.needsUpdate=!0,w.needsUpdate=!0;break}b++,B.x+=F.x,b>=t&&(b=0,B.x-=F.x*t,G++,B.y+=F.y),G>=t&&(G=0,B.y-=F.y*t,A++,B.z+=F.z),yield;}}();const z=()=>{let e=window.performance.now();for(;window.performance.now()-e<16&&S;){const{done:e}=S.next();s&&s({convertedVoxels:v,totalVoxels:V,convertedGrids:D,totalGrids:M}),e&&(D++,S=null,delete this.processes[n],a(),D===M&&(o||this.add(U),r&&r()));}S&&this.processes&&(this.processes[n]=setTimeout(z,0));};z();})))(a);})),F&&Promise.all(b).then((()=>{this.processes&&r&&r();}));}dispose(){this.leafBuilds.forEach((e=>e.cancel())),this.leafBuilds=[],this.processes&&(this.processes.forEach(((e,t)=>{cancelAnimationFrame(e),delete this.processes[t];})),this.processes=null);}}
25
+ const getTextureMapping=(e,t)=>{const r=new Vector3,s=new Vector3,o=new Vector3,i=new Vector3,a=new Bbox;return a.set(...e.getPreciseWorldBbox()),a.getCenter(r),a.getSize(s),o.copy(s).multiplyScalar(1/t),e.transform.applyInverseJacobian(o),i.copy(r).sub(s.clone().multiplyScalar(.5)),e.transform.applyInverseTransformMap(i),i.add(o.clone().multiplyScalar(.5)),{center:r,size:s,start:i,step:o}};class VolumeToFog extends Three.Group{processes=[];leafBuilds=[];materials=[];textures=[];geometries=[];isFogVolume=!0;constructor(e,t,r,s){super();const{resolution:o,progressive:i,emissiveGrid:a,baseColorGrid:n,maskGrid:l,radius:u,textureBuild:c=textureBuilds.sample,textureFilter:p=textureFilters.nearest,textureType:d=textureTypes.uint8}=t,h=getTextureScale(d);let m,x,f,T,y,g,w;if(this.frustumCulled=!1,m=e instanceof Array?e:void 0!==e.grids?Object.values(e.grids):[e],a){if(m=m.filter((e=>e!==a)),a instanceof Uint8Array)x=createScalarTexture3D(a,o,textureTypes.uint8);else {const e=createTextureData(d,Math.pow(o,3));x=createScalarTexture3D(e,o,d),f=(t,r,s)=>{const o=void 0!==s?s:getDensity$1(a,r)*h;return writeTexel(e,t,o),o};}this.textures.push(x);}if(n){if(n instanceof Uint8Array)T=new Three.Data3DTexture(n,o,o,o);else {const e=new Uint8Array(4*Math.pow(o,3));T=new Three.Data3DTexture(e,o,o,o);const t=new Three.Color;y=(r,s,o)=>{if(o)return e[4*r+0]=255*o.r,e[4*r+1]=255*o.g,e[4*r+2]=255*o.b,void(e[4*r+3]=255);const i=n.getValue(s);return i instanceof Three.Color?t.set(gridValue):i instanceof Vector3$1||i instanceof Three.Vector3?t.setRGB(i.x,i.y,i.z):t.setRGB(1,0,1),e[4*r+0]=255*t.r,e[4*r+1]=255*t.g,e[4*r+2]=255*t.b,e[4*r+3]=255,{r:t.r,g:t.g,b:t.b,a:t.a}};}this.textures.push(T),T.format=Three.RGBAFormat,T.minFilter=Three.LinearFilter,T.magFilter=Three.LinearFilter,T.unpackAlignment=1,T.needsUpdate=!0;}if(l){if(l instanceof Uint8Array)g=createScalarTexture3D(l,o,textureTypes.uint8);else {const e=createTextureData(d,Math.pow(o,3));g=createScalarTexture3D(e,o,d),w=(t,r,s)=>{const o=void 0!==s?s:getDensity$1(l,r)*h;return writeTexel(e,t,o),o};}this.textures.push(g);}let D=0,v=0;const M=m.length,V=M*Math.pow(o,3),B=e=>!e||e instanceof Uint8Array||canBuildFromLeaves(e),F=c===textureBuilds.leaves&&m.every((e=>!(e instanceof GridDescriptor)||canBuildFromLeaves(e)))&&B(a)&&B(l)&&(!n||n instanceof Uint8Array);c!==textureBuilds.leaves||F||console.warn("VolumeToFog","textureBuild","Primitive or vector grids need the sample build. Using the sample build.");const b=[],U=[];let G=!1;const S=()=>{s&&s({convertedVoxels:U.reduce(((e,t)=>e+t.convertedVoxels),0),totalVoxels:U.reduce(((e,t)=>e+t.totalVoxels),0),convertedGrids:D,totalGrids:M});},A=(e,t,r,s)=>{const i={convertedVoxels:0,totalVoxels:0};U.push(i);const a=buildLeafTexture({grid:e,resolution:o,start:t.start,step:t.step,filter:p,textureType:d,radius:s,onProgress:({convertedVoxels:e,totalVoxels:t})=>{i.convertedVoxels=e,i.totalVoxels=t,S();}});return this.leafBuilds.push(a),a.promise.then((e=>!!e&&(r.image.data=e,r.needsUpdate=!0,!0))).catch((e=>(console.error("VolumeToFog","buildLeafTexture",{error:e}),!1)))};m.reverse().forEach((async(e,n)=>{if(e instanceof Uint8Array){const r=new Three.Data3DTexture(e,o,o,o);r.format=Three.RedFormat,r.minFilter=Three.LinearFilter,r.magFilter=Three.LinearFilter,r.unpackAlignment=1,r.needsUpdate=!0;const s=new Three.SphereGeometry(1),i=new VolumeBasicMaterial({...t,emissiveMap3D:x,densityMap3D:r,baseColorMap3D:T,maskMap3D:g}),a=new Three.Mesh(s,i);return a.frustumCulled=!1,a.onBeforeRender=updateFrameDepth,this.materials.push(i),this.textures.push(r),this.geometries.push(s),void this.add(a)}if(!(e instanceof GridDescriptor))return;const c=createTextureData(d,Math.pow(o,3)),p=createScalarTexture3D(c,o,d),m=new Three.SphereGeometry(1),B=new VolumeBasicMaterial({...t,emissiveMap3D:x,densityMap3D:p,baseColorMap3D:T,maskMap3D:g}),U=new Three.Mesh(m,B);if(U.frustumCulled=!1,U.onBeforeRender=updateFrameDepth,this.materials.push(B),this.textures.push(p),this.geometries.push(m),F){const t=getTextureMapping(e,o);return U.position.copy(t.center),U.scale.copy(t.size),i&&this.add(U),b.push(A(e,t,p,u).then((e=>{e&&(D++,i||this.add(U),S());}))),void(G||(G=!0,!a||a instanceof Uint8Array||b.push(A(a,t,x,0)),!l||l instanceof Uint8Array||b.push(A(l,t,g,0))))}const L=o,C=Math.pow(L,2);i&&this.add(U),await(t=>new Promise((o=>{const a=Math.pow(t,2),l=Math.pow(t,3),{center:d,size:m,start:B,step:F}=getTextureMapping(e,t);U.position.copy(d),U.scale.copy(m);let b=0,G=0,S=0;let A=function*(){for(let r=0;r<l;r++){const r=b+G*t+S*a,s=getDensity$1(e,B),o=f&&f(r,B),n=y&&y(r,B),l=w&&w(r,B),d=u;if(d)for(let e=-d;e<d;e++)for(let i=-d;i<d;i++)for(let a=-d;a<d;a++){if(b+e<0||b+e>=t||G+i<0||G+i>=t||S+a<0||S+a>=t)continue;const p=r+e+i*L+a*C,d=Math.max(0,Math.min(1,1-Math.hypot(e,i,a)/(u/2))),m=d*s*h;writeTexel(c,p,readTexel(c,p)+m),writeTexel(c,p,Math.min(readTexel(c,p),h)),f&&f(p,null,o*d*h),y&&y(p,null,n*d),w&&w(p,null,l*d*h);}else {const e=s*h;writeTexel(c,r,readTexel(c,r)+e),writeTexel(c,r,Math.min(readTexel(c,r),h));}if(v++,i&&(p.needsUpdate=!0,x&&(x.needsUpdate=!0),T&&(T.needsUpdate=!0),g&&(g.needsUpdate=!0)),S>=t){x.needsUpdate=!0,T.needsUpdate=!0,g.needsUpdate=!0;break}b++,B.x+=F.x,b>=t&&(b=0,B.x-=F.x*t,G++,B.y+=F.y),G>=t&&(G=0,B.y-=F.y*t,S++,B.z+=F.z),yield;}}();const z=()=>{let e=window.performance.now();for(;window.performance.now()-e<16&&A;){const{done:e}=A.next();s&&s({convertedVoxels:v,totalVoxels:V,convertedGrids:D,totalGrids:M}),e&&(D++,A=null,delete this.processes[n],o(),D===M&&(i||this.add(U),r&&r()));}A&&this.processes&&(this.processes[n]=setTimeout(z,0));};z();})))(o);})),F&&Promise.all(b).then((()=>{this.processes&&r&&r();}));}dispose(){this.leafBuilds.forEach((e=>e.cancel())),this.leafBuilds=[],this.processes&&(this.processes.forEach((e=>clearTimeout(e))),this.processes=null),this.materials.forEach((e=>e.dispose())),this.textures.forEach((e=>e.dispose())),this.geometries.forEach((e=>e.dispose())),this.materials=[],this.textures=[],this.geometries=[];}}
26
26
 
27
- const triangleLUT=[[],[0,8,3],[0,1,9],[1,8,3,9,8,1],[1,2,10],[0,8,3,1,2,10],[9,2,10,0,2,9],[2,8,3,2,10,8,10,9,8],[3,11,2],[0,11,2,8,11,0],[1,9,0,2,3,11],[1,11,2,1,9,11,9,8,11],[3,10,1,11,10,3],[0,10,1,0,8,10,8,11,10],[3,9,0,3,11,9,11,10,9],[9,8,10,10,8,11],[4,7,8],[4,3,0,7,3,4],[0,1,9,8,4,7],[4,1,9,4,7,1,7,3,1],[1,2,10,8,4,7],[3,4,7,3,0,4,1,2,10],[9,2,10,9,0,2,8,4,7],[2,10,9,2,9,7,2,7,3,7,9,4],[8,4,7,3,11,2],[11,4,7,11,2,4,2,0,4],[9,0,1,8,4,7,2,3,11],[4,7,11,9,4,11,9,11,2,9,2,1],[3,10,1,3,11,10,7,8,4],[1,11,10,1,4,11,1,0,4,7,11,4],[4,7,8,9,0,11,9,11,10,11,0,3],[4,7,11,4,11,9,9,11,10],[9,5,4],[9,5,4,0,8,3],[0,5,4,1,5,0],[8,5,4,8,3,5,3,1,5],[1,2,10,9,5,4],[3,0,8,1,2,10,4,9,5],[5,2,10,5,4,2,4,0,2],[2,10,5,3,2,5,3,5,4,3,4,8],[9,5,4,2,3,11],[0,11,2,0,8,11,4,9,5],[0,5,4,0,1,5,2,3,11],[2,1,5,2,5,8,2,8,11,4,8,5],[10,3,11,10,1,3,9,5,4],[4,9,5,0,8,1,8,10,1,8,11,10],[5,4,0,5,0,11,5,11,10,11,0,3],[5,4,8,5,8,10,10,8,11],[9,7,8,5,7,9],[9,3,0,9,5,3,5,7,3],[0,7,8,0,1,7,1,5,7],[1,5,3,3,5,7],[9,7,8,9,5,7,10,1,2],[10,1,2,9,5,0,5,3,0,5,7,3],[8,0,2,8,2,5,8,5,7,10,5,2],[2,10,5,2,5,3,3,5,7],[7,9,5,7,8,9,3,11,2],[9,5,7,9,7,2,9,2,0,2,7,11],[2,3,11,0,1,8,1,7,8,1,5,7],[11,2,1,11,1,7,7,1,5],[9,5,8,8,5,7,10,1,3,10,3,11],[5,7,0,5,0,9,7,11,0,1,0,10,11,10,0],[11,10,0,11,0,3,10,5,0,8,0,7,5,7,0],[11,10,5,7,11,5],[10,6,5],[0,8,3,5,10,6],[9,0,1,5,10,6],[1,8,3,1,9,8,5,10,6],[1,6,5,2,6,1],[1,6,5,1,2,6,3,0,8],[9,6,5,9,0,6,0,2,6],[5,9,8,5,8,2,5,2,6,3,2,8],[2,3,11,10,6,5],[11,0,8,11,2,0,10,6,5],[0,1,9,2,3,11,5,10,6],[5,10,6,1,9,2,9,11,2,9,8,11],[6,3,11,6,5,3,5,1,3],[0,8,11,0,11,5,0,5,1,5,11,6],[3,11,6,0,3,6,0,6,5,0,5,9],[6,5,9,6,9,11,11,9,8],[5,10,6,4,7,8],[4,3,0,4,7,3,6,5,10],[1,9,0,5,10,6,8,4,7],[10,6,5,1,9,7,1,7,3,7,9,4],[6,1,2,6,5,1,4,7,8],[1,2,5,5,2,6,3,0,4,3,4,7],[8,4,7,9,0,5,0,6,5,0,2,6],[7,3,9,7,9,4,3,2,9,5,9,6,2,6,9],[3,11,2,7,8,4,10,6,5],[5,10,6,4,7,2,4,2,0,2,7,11],[0,1,9,4,7,8,2,3,11,5,10,6],[9,2,1,9,11,2,9,4,11,7,11,4,5,10,6],[8,4,7,3,11,5,3,5,1,5,11,6],[5,1,11,5,11,6,1,0,11,7,11,4,0,4,11],[0,5,9,0,6,5,0,3,6,11,6,3,8,4,7],[6,5,9,6,9,11,4,7,9,7,11,9],[10,4,9,6,4,10],[4,10,6,4,9,10,0,8,3],[10,0,1,10,6,0,6,4,0],[8,3,1,8,1,6,8,6,4,6,1,10],[1,4,9,1,2,4,2,6,4],[3,0,8,1,2,9,2,4,9,2,6,4],[0,2,4,4,2,6],[8,3,2,8,2,4,4,2,6],[10,4,9,10,6,4,11,2,3],[0,8,2,2,8,11,4,9,10,4,10,6],[3,11,2,0,1,6,0,6,4,6,1,10],[6,4,1,6,1,10,4,8,1,2,1,11,8,11,1],[9,6,4,9,3,6,9,1,3,11,6,3],[8,11,1,8,1,0,11,6,1,9,1,4,6,4,1],[3,11,6,3,6,0,0,6,4],[6,4,8,11,6,8],[7,10,6,7,8,10,8,9,10],[0,7,3,0,10,7,0,9,10,6,7,10],[10,6,7,1,10,7,1,7,8,1,8,0],[10,6,7,10,7,1,1,7,3],[1,2,6,1,6,8,1,8,9,8,6,7],[2,6,9,2,9,1,6,7,9,0,9,3,7,3,9],[7,8,0,7,0,6,6,0,2],[7,3,2,6,7,2],[2,3,11,10,6,8,10,8,9,8,6,7],[2,0,7,2,7,11,0,9,7,6,7,10,9,10,7],[1,8,0,1,7,8,1,10,7,6,7,10,2,3,11],[11,2,1,11,1,7,10,6,1,6,7,1],[8,9,6,8,6,7,9,1,6,11,6,3,1,3,6],[0,9,1,11,6,7],[7,8,0,7,0,6,3,11,0,11,6,0],[7,11,6],[7,6,11],[3,0,8,11,7,6],[0,1,9,11,7,6],[8,1,9,8,3,1,11,7,6],[10,1,2,6,11,7],[1,2,10,3,0,8,6,11,7],[2,9,0,2,10,9,6,11,7],[6,11,7,2,10,3,10,8,3,10,9,8],[7,2,3,6,2,7],[7,0,8,7,6,0,6,2,0],[2,7,6,2,3,7,0,1,9],[1,6,2,1,8,6,1,9,8,8,7,6],[10,7,6,10,1,7,1,3,7],[10,7,6,1,7,10,1,8,7,1,0,8],[0,3,7,0,7,10,0,10,9,6,10,7],[7,6,10,7,10,8,8,10,9],[6,8,4,11,8,6],[3,6,11,3,0,6,0,4,6],[8,6,11,8,4,6,9,0,1],[9,4,6,9,6,3,9,3,1,11,3,6],[6,8,4,6,11,8,2,10,1],[1,2,10,3,0,11,0,6,11,0,4,6],[4,11,8,4,6,11,0,2,9,2,10,9],[10,9,3,10,3,2,9,4,3,11,3,6,4,6,3],[8,2,3,8,4,2,4,6,2],[0,4,2,4,6,2],[1,9,0,2,3,4,2,4,6,4,3,8],[1,9,4,1,4,2,2,4,6],[8,1,3,8,6,1,8,4,6,6,10,1],[10,1,0,10,0,6,6,0,4],[4,6,3,4,3,8,6,10,3,0,3,9,10,9,3],[10,9,4,6,10,4],[4,9,5,7,6,11],[0,8,3,4,9,5,11,7,6],[5,0,1,5,4,0,7,6,11],[11,7,6,8,3,4,3,5,4,3,1,5],[9,5,4,10,1,2,7,6,11],[6,11,7,1,2,10,0,8,3,4,9,5],[7,6,11,5,4,10,4,2,10,4,0,2],[3,4,8,3,5,4,3,2,5,10,5,2,11,7,6],[7,2,3,7,6,2,5,4,9],[9,5,4,0,8,6,0,6,2,6,8,7],[3,6,2,3,7,6,1,5,0,5,4,0],[6,2,8,6,8,7,2,1,8,4,8,5,1,5,8],[9,5,4,10,1,6,1,7,6,1,3,7],[1,6,10,1,7,6,1,0,7,8,7,0,9,5,4],[4,0,10,4,10,5,0,3,10,6,10,7,3,7,10],[7,6,10,7,10,8,5,4,10,4,8,10],[6,9,5,6,11,9,11,8,9],[3,6,11,0,6,3,0,5,6,0,9,5],[0,11,8,0,5,11,0,1,5,5,6,11],[6,11,3,6,3,5,5,3,1],[1,2,10,9,5,11,9,11,8,11,5,6],[0,11,3,0,6,11,0,9,6,5,6,9,1,2,10],[11,8,5,11,5,6,8,0,5,10,5,2,0,2,5],[6,11,3,6,3,5,2,10,3,10,5,3],[5,8,9,5,2,8,5,6,2,3,8,2],[9,5,6,9,6,0,0,6,2],[1,5,8,1,8,0,5,6,8,3,8,2,6,2,8],[1,5,6,2,1,6],[1,3,6,1,6,10,3,8,6,5,6,9,8,9,6],[10,1,0,10,0,6,9,5,0,5,6,0],[0,3,8,5,6,10],[10,5,6],[11,5,10,7,5,11],[11,5,10,11,7,5,8,3,0],[5,11,7,5,10,11,1,9,0],[10,7,5,10,11,7,9,8,1,8,3,1],[11,1,2,11,7,1,7,5,1],[0,8,3,1,2,7,1,7,5,7,2,11],[9,7,5,9,2,7,9,0,2,2,11,7],[7,5,2,7,2,11,5,9,2,3,2,8,9,8,2],[2,5,10,2,3,5,3,7,5],[8,2,0,8,5,2,8,7,5,10,2,5],[9,0,1,5,10,3,5,3,7,3,10,2],[9,8,2,9,2,1,8,7,2,10,2,5,7,5,2],[1,3,5,3,7,5],[0,8,7,0,7,1,1,7,5],[9,0,3,9,3,5,5,3,7],[9,8,7,5,9,7],[5,8,4,5,10,8,10,11,8],[5,0,4,5,11,0,5,10,11,11,3,0],[0,1,9,8,4,10,8,10,11,10,4,5],[10,11,4,10,4,5,11,3,4,9,4,1,3,1,4],[2,5,1,2,8,5,2,11,8,4,5,8],[0,4,11,0,11,3,4,5,11,2,11,1,5,1,11],[0,2,5,0,5,9,2,11,5,4,5,8,11,8,5],[9,4,5,2,11,3],[2,5,10,3,5,2,3,4,5,3,8,4],[5,10,2,5,2,4,4,2,0],[3,10,2,3,5,10,3,8,5,4,5,8,0,1,9],[5,10,2,5,2,4,1,9,2,9,4,2],[8,4,5,8,5,3,3,5,1],[0,4,5,1,0,5],[8,4,5,8,5,3,9,0,5,0,3,5],[9,4,5],[4,11,7,4,9,11,9,10,11],[0,8,3,4,9,7,9,11,7,9,10,11],[1,10,11,1,11,4,1,4,0,7,4,11],[3,1,4,3,4,8,1,10,4,7,4,11,10,11,4],[4,11,7,9,11,4,9,2,11,9,1,2],[9,7,4,9,11,7,9,1,11,2,11,1,0,8,3],[11,7,4,11,4,2,2,4,0],[11,7,4,11,4,2,8,3,4,3,2,4],[2,9,10,2,7,9,2,3,7,7,4,9],[9,10,7,9,7,4,10,2,7,8,7,0,2,0,7],[3,7,10,3,10,2,7,4,10,1,10,0,4,0,10],[1,10,2,8,7,4],[4,9,1,4,1,7,7,1,3],[4,9,1,4,1,7,0,8,1,8,7,1],[4,0,3,7,4,3],[4,8,7],[9,10,8,10,11,8],[3,0,9,3,9,11,11,9,10],[0,1,10,0,10,8,8,10,11],[3,1,10,11,3,10],[1,2,11,1,11,9,9,11,8],[3,0,9,3,9,11,1,2,9,2,11,9],[0,2,11,8,0,11],[3,2,11],[2,3,8,2,8,10,10,8,9],[9,10,2,0,9,2],[2,3,8,2,8,10,0,1,8,1,10,8],[1,10,2],[1,3,8,9,1,8],[0,9,1],[0,3,8],[]];class VolumeToLevelSet extends Three.Group{processes=[];constructor(e,t,{resolution:r,progressive:o},s,n){let a;super(),a=e instanceof Array?e:void 0!==e.grids?Object.values(e.grids):[e];let i=0;const c=a.length,l=c*Math.pow(r,3);a.forEach(((e,a)=>{const y=new Three.BufferGeometry,p=[],h=[],m=[],g=new Three.Mesh(y,t),u=1/r,d=(Math.pow(r,3)),f=new Vector3(0,0,0),w=new Vector3(0,0,0),x=new Vector3(0,0,0),b=new Bbox,T=new Vector3;b.set(...e.getPreciseWorldBbox()),b.getCenter(g.position),b.getSize(g.scale),g.scale.multiplyScalar(1/r),b.getCenter(f),b.getSize(w),x.copy(w).multiplyScalar(u),e.transform.applyInverseJacobian(x),f.sub(w.clone().multiplyScalar(.5)),e.transform.applyInverseTransformMap(f),f.add(x.clone().multiplyScalar(.5));let D=0,z=0,A=0;let M=function*(){for(let t=0;t<d;t++){if(A>=r)return;D++,f.x+=x.x,D>=r&&(D=0,f.x-=x.x*r,z++,f.y+=x.y),z>=r&&(z=0,f.y-=x.y*r,A++,f.z+=x.z);const t=[];T.copy(f),t[0]=getDensity$1(e,T),T.copy(f),T.x+=x.x,t[1]=getDensity$1(e,T),T.copy(f),T.x+=x.x,T.z+=x.z,t[2]=getDensity$1(e,T),T.copy(f),T.z+=x.z,t[3]=getDensity$1(e,T),T.copy(f),T.y+=x.y,t[4]=getDensity$1(e,T),T.copy(f),T.x+=x.x,T.y+=x.y,t[5]=getDensity$1(e,T),T.copy(f),T.x+=x.x,T.y+=x.y,T.z+=x.z,t[6]=getDensity$1(e,T),T.copy(f),T.y+=x.y,T.z+=x.z,t[7]=getDensity$1(e,T);let o=0;t[0]&&(o|=1),t[1]&&(o|=2),t[2]&&(o|=4),t[3]&&(o|=8),t[4]&&(o|=16),t[5]&&(o|=32),t[6]&&(o|=64),t[7]&&(o|=128);const s=triangleLUT[o]||s[1],n=[[.5,0,0],[1,0,.5],[.5,0,1],[0,0,.5],[.5,1,0],[1,1,.5],[.5,1,1],[0,1,.5],[0,.5,0],[1,.5,0],[1,.5,1],[0,.5,1]];if(t.filter((e=>0!==e)).length){const e={r:Math.random(),g:Math.random(),b:Math.random()};s.forEach((t=>{const o=n[t];p.push(D-r/2+o[0],z-r/2+o[1],A-r/2+o[2]),m.push(e.r,e.g,e.b);const s=new Three.Vector3(Math.random(),Math.random(),Math.random()).normalize();h.push(s.x,s.y,s.z);}));}yield;}}();const U=()=>{M&&(this.processes[a]=requestAnimationFrame(U));let e=window.performance.now();for(;window.performance.now()-e<16&&M;){const{done:e}=M.next();if(n&&n({convertedVoxels:0,totalVoxels:l,convertedGrids:i,totalGrids:c}),e&&(i++,M=null,delete this.processes[a],i===c)){if(!o){const e=new Three.BufferAttribute(new Float32Array(p),3);e.setUsage(Three.DynamicDrawUsage),g.geometry.setAttribute("position",e);const t=new Three.BufferAttribute(new Float32Array(h),3);t.setUsage(Three.DynamicDrawUsage),g.geometry.setAttribute("normal",t);const r=new Three.BufferAttribute(new Float32Array(m),3);r.setUsage(Three.DynamicDrawUsage),g.geometry.setAttribute("color",r),this.add(g);}s&&s();}}};U(),o&&this.add(g);}));}dispose(){this.processes&&(this.processes.forEach(((e,t)=>{cancelAnimationFrame(e),delete this.processes[t];})),this.processes=null);}}
27
+ const triangleLUT=[[],[0,8,3],[0,1,9],[1,8,3,9,8,1],[1,2,10],[0,8,3,1,2,10],[9,2,10,0,2,9],[2,8,3,2,10,8,10,9,8],[3,11,2],[0,11,2,8,11,0],[1,9,0,2,3,11],[1,11,2,1,9,11,9,8,11],[3,10,1,11,10,3],[0,10,1,0,8,10,8,11,10],[3,9,0,3,11,9,11,10,9],[9,8,10,10,8,11],[4,7,8],[4,3,0,7,3,4],[0,1,9,8,4,7],[4,1,9,4,7,1,7,3,1],[1,2,10,8,4,7],[3,4,7,3,0,4,1,2,10],[9,2,10,9,0,2,8,4,7],[2,10,9,2,9,7,2,7,3,7,9,4],[8,4,7,3,11,2],[11,4,7,11,2,4,2,0,4],[9,0,1,8,4,7,2,3,11],[4,7,11,9,4,11,9,11,2,9,2,1],[3,10,1,3,11,10,7,8,4],[1,11,10,1,4,11,1,0,4,7,11,4],[4,7,8,9,0,11,9,11,10,11,0,3],[4,7,11,4,11,9,9,11,10],[9,5,4],[9,5,4,0,8,3],[0,5,4,1,5,0],[8,5,4,8,3,5,3,1,5],[1,2,10,9,5,4],[3,0,8,1,2,10,4,9,5],[5,2,10,5,4,2,4,0,2],[2,10,5,3,2,5,3,5,4,3,4,8],[9,5,4,2,3,11],[0,11,2,0,8,11,4,9,5],[0,5,4,0,1,5,2,3,11],[2,1,5,2,5,8,2,8,11,4,8,5],[10,3,11,10,1,3,9,5,4],[4,9,5,0,8,1,8,10,1,8,11,10],[5,4,0,5,0,11,5,11,10,11,0,3],[5,4,8,5,8,10,10,8,11],[9,7,8,5,7,9],[9,3,0,9,5,3,5,7,3],[0,7,8,0,1,7,1,5,7],[1,5,3,3,5,7],[9,7,8,9,5,7,10,1,2],[10,1,2,9,5,0,5,3,0,5,7,3],[8,0,2,8,2,5,8,5,7,10,5,2],[2,10,5,2,5,3,3,5,7],[7,9,5,7,8,9,3,11,2],[9,5,7,9,7,2,9,2,0,2,7,11],[2,3,11,0,1,8,1,7,8,1,5,7],[11,2,1,11,1,7,7,1,5],[9,5,8,8,5,7,10,1,3,10,3,11],[5,7,0,5,0,9,7,11,0,1,0,10,11,10,0],[11,10,0,11,0,3,10,5,0,8,0,7,5,7,0],[11,10,5,7,11,5],[10,6,5],[0,8,3,5,10,6],[9,0,1,5,10,6],[1,8,3,1,9,8,5,10,6],[1,6,5,2,6,1],[1,6,5,1,2,6,3,0,8],[9,6,5,9,0,6,0,2,6],[5,9,8,5,8,2,5,2,6,3,2,8],[2,3,11,10,6,5],[11,0,8,11,2,0,10,6,5],[0,1,9,2,3,11,5,10,6],[5,10,6,1,9,2,9,11,2,9,8,11],[6,3,11,6,5,3,5,1,3],[0,8,11,0,11,5,0,5,1,5,11,6],[3,11,6,0,3,6,0,6,5,0,5,9],[6,5,9,6,9,11,11,9,8],[5,10,6,4,7,8],[4,3,0,4,7,3,6,5,10],[1,9,0,5,10,6,8,4,7],[10,6,5,1,9,7,1,7,3,7,9,4],[6,1,2,6,5,1,4,7,8],[1,2,5,5,2,6,3,0,4,3,4,7],[8,4,7,9,0,5,0,6,5,0,2,6],[7,3,9,7,9,4,3,2,9,5,9,6,2,6,9],[3,11,2,7,8,4,10,6,5],[5,10,6,4,7,2,4,2,0,2,7,11],[0,1,9,4,7,8,2,3,11,5,10,6],[9,2,1,9,11,2,9,4,11,7,11,4,5,10,6],[8,4,7,3,11,5,3,5,1,5,11,6],[5,1,11,5,11,6,1,0,11,7,11,4,0,4,11],[0,5,9,0,6,5,0,3,6,11,6,3,8,4,7],[6,5,9,6,9,11,4,7,9,7,11,9],[10,4,9,6,4,10],[4,10,6,4,9,10,0,8,3],[10,0,1,10,6,0,6,4,0],[8,3,1,8,1,6,8,6,4,6,1,10],[1,4,9,1,2,4,2,6,4],[3,0,8,1,2,9,2,4,9,2,6,4],[0,2,4,4,2,6],[8,3,2,8,2,4,4,2,6],[10,4,9,10,6,4,11,2,3],[0,8,2,2,8,11,4,9,10,4,10,6],[3,11,2,0,1,6,0,6,4,6,1,10],[6,4,1,6,1,10,4,8,1,2,1,11,8,11,1],[9,6,4,9,3,6,9,1,3,11,6,3],[8,11,1,8,1,0,11,6,1,9,1,4,6,4,1],[3,11,6,3,6,0,0,6,4],[6,4,8,11,6,8],[7,10,6,7,8,10,8,9,10],[0,7,3,0,10,7,0,9,10,6,7,10],[10,6,7,1,10,7,1,7,8,1,8,0],[10,6,7,10,7,1,1,7,3],[1,2,6,1,6,8,1,8,9,8,6,7],[2,6,9,2,9,1,6,7,9,0,9,3,7,3,9],[7,8,0,7,0,6,6,0,2],[7,3,2,6,7,2],[2,3,11,10,6,8,10,8,9,8,6,7],[2,0,7,2,7,11,0,9,7,6,7,10,9,10,7],[1,8,0,1,7,8,1,10,7,6,7,10,2,3,11],[11,2,1,11,1,7,10,6,1,6,7,1],[8,9,6,8,6,7,9,1,6,11,6,3,1,3,6],[0,9,1,11,6,7],[7,8,0,7,0,6,3,11,0,11,6,0],[7,11,6],[7,6,11],[3,0,8,11,7,6],[0,1,9,11,7,6],[8,1,9,8,3,1,11,7,6],[10,1,2,6,11,7],[1,2,10,3,0,8,6,11,7],[2,9,0,2,10,9,6,11,7],[6,11,7,2,10,3,10,8,3,10,9,8],[7,2,3,6,2,7],[7,0,8,7,6,0,6,2,0],[2,7,6,2,3,7,0,1,9],[1,6,2,1,8,6,1,9,8,8,7,6],[10,7,6,10,1,7,1,3,7],[10,7,6,1,7,10,1,8,7,1,0,8],[0,3,7,0,7,10,0,10,9,6,10,7],[7,6,10,7,10,8,8,10,9],[6,8,4,11,8,6],[3,6,11,3,0,6,0,4,6],[8,6,11,8,4,6,9,0,1],[9,4,6,9,6,3,9,3,1,11,3,6],[6,8,4,6,11,8,2,10,1],[1,2,10,3,0,11,0,6,11,0,4,6],[4,11,8,4,6,11,0,2,9,2,10,9],[10,9,3,10,3,2,9,4,3,11,3,6,4,6,3],[8,2,3,8,4,2,4,6,2],[0,4,2,4,6,2],[1,9,0,2,3,4,2,4,6,4,3,8],[1,9,4,1,4,2,2,4,6],[8,1,3,8,6,1,8,4,6,6,10,1],[10,1,0,10,0,6,6,0,4],[4,6,3,4,3,8,6,10,3,0,3,9,10,9,3],[10,9,4,6,10,4],[4,9,5,7,6,11],[0,8,3,4,9,5,11,7,6],[5,0,1,5,4,0,7,6,11],[11,7,6,8,3,4,3,5,4,3,1,5],[9,5,4,10,1,2,7,6,11],[6,11,7,1,2,10,0,8,3,4,9,5],[7,6,11,5,4,10,4,2,10,4,0,2],[3,4,8,3,5,4,3,2,5,10,5,2,11,7,6],[7,2,3,7,6,2,5,4,9],[9,5,4,0,8,6,0,6,2,6,8,7],[3,6,2,3,7,6,1,5,0,5,4,0],[6,2,8,6,8,7,2,1,8,4,8,5,1,5,8],[9,5,4,10,1,6,1,7,6,1,3,7],[1,6,10,1,7,6,1,0,7,8,7,0,9,5,4],[4,0,10,4,10,5,0,3,10,6,10,7,3,7,10],[7,6,10,7,10,8,5,4,10,4,8,10],[6,9,5,6,11,9,11,8,9],[3,6,11,0,6,3,0,5,6,0,9,5],[0,11,8,0,5,11,0,1,5,5,6,11],[6,11,3,6,3,5,5,3,1],[1,2,10,9,5,11,9,11,8,11,5,6],[0,11,3,0,6,11,0,9,6,5,6,9,1,2,10],[11,8,5,11,5,6,8,0,5,10,5,2,0,2,5],[6,11,3,6,3,5,2,10,3,10,5,3],[5,8,9,5,2,8,5,6,2,3,8,2],[9,5,6,9,6,0,0,6,2],[1,5,8,1,8,0,5,6,8,3,8,2,6,2,8],[1,5,6,2,1,6],[1,3,6,1,6,10,3,8,6,5,6,9,8,9,6],[10,1,0,10,0,6,9,5,0,5,6,0],[0,3,8,5,6,10],[10,5,6],[11,5,10,7,5,11],[11,5,10,11,7,5,8,3,0],[5,11,7,5,10,11,1,9,0],[10,7,5,10,11,7,9,8,1,8,3,1],[11,1,2,11,7,1,7,5,1],[0,8,3,1,2,7,1,7,5,7,2,11],[9,7,5,9,2,7,9,0,2,2,11,7],[7,5,2,7,2,11,5,9,2,3,2,8,9,8,2],[2,5,10,2,3,5,3,7,5],[8,2,0,8,5,2,8,7,5,10,2,5],[9,0,1,5,10,3,5,3,7,3,10,2],[9,8,2,9,2,1,8,7,2,10,2,5,7,5,2],[1,3,5,3,7,5],[0,8,7,0,7,1,1,7,5],[9,0,3,9,3,5,5,3,7],[9,8,7,5,9,7],[5,8,4,5,10,8,10,11,8],[5,0,4,5,11,0,5,10,11,11,3,0],[0,1,9,8,4,10,8,10,11,10,4,5],[10,11,4,10,4,5,11,3,4,9,4,1,3,1,4],[2,5,1,2,8,5,2,11,8,4,5,8],[0,4,11,0,11,3,4,5,11,2,11,1,5,1,11],[0,2,5,0,5,9,2,11,5,4,5,8,11,8,5],[9,4,5,2,11,3],[2,5,10,3,5,2,3,4,5,3,8,4],[5,10,2,5,2,4,4,2,0],[3,10,2,3,5,10,3,8,5,4,5,8,0,1,9],[5,10,2,5,2,4,1,9,2,9,4,2],[8,4,5,8,5,3,3,5,1],[0,4,5,1,0,5],[8,4,5,8,5,3,9,0,5,0,3,5],[9,4,5],[4,11,7,4,9,11,9,10,11],[0,8,3,4,9,7,9,11,7,9,10,11],[1,10,11,1,11,4,1,4,0,7,4,11],[3,1,4,3,4,8,1,10,4,7,4,11,10,11,4],[4,11,7,9,11,4,9,2,11,9,1,2],[9,7,4,9,11,7,9,1,11,2,11,1,0,8,3],[11,7,4,11,4,2,2,4,0],[11,7,4,11,4,2,8,3,4,3,2,4],[2,9,10,2,7,9,2,3,7,7,4,9],[9,10,7,9,7,4,10,2,7,8,7,0,2,0,7],[3,7,10,3,10,2,7,4,10,1,10,0,4,0,10],[1,10,2,8,7,4],[4,9,1,4,1,7,7,1,3],[4,9,1,4,1,7,0,8,1,8,7,1],[4,0,3,7,4,3],[4,8,7],[9,10,8,10,11,8],[3,0,9,3,9,11,11,9,10],[0,1,10,0,10,8,8,10,11],[3,1,10,11,3,10],[1,2,11,1,11,9,9,11,8],[3,0,9,3,9,11,1,2,9,2,11,9],[0,2,11,8,0,11],[3,2,11],[2,3,8,2,8,10,10,8,9],[9,10,2,0,9,2],[2,3,8,2,8,10,0,1,8,1,10,8],[1,10,2],[1,3,8,9,1,8],[0,9,1],[0,3,8],[]];class VolumeToLevelSet extends Three.Group{processes=[];geometries=[];constructor(e,t,{resolution:r,progressive:o},s,n){let i;super(),i=e instanceof Array?e:void 0!==e.grids?Object.values(e.grids):[e];let a=0;const c=i.length,l=c*Math.pow(r,3);i.forEach(((e,i)=>{const y=new Three.BufferGeometry,p=[],h=[],m=[],g=new Three.Mesh(y,t);this.geometries.push(y);const u=1/r,f=(Math.pow(r,3)),d=new Vector3(0,0,0),w=new Vector3(0,0,0),x=new Vector3(0,0,0),b=new Bbox,T=new Vector3;b.set(...e.getPreciseWorldBbox()),b.getCenter(g.position),b.getSize(g.scale),g.scale.multiplyScalar(1/r),b.getCenter(d),b.getSize(w),x.copy(w).multiplyScalar(u),e.transform.applyInverseJacobian(x),d.sub(w.clone().multiplyScalar(.5)),e.transform.applyInverseTransformMap(d),d.add(x.clone().multiplyScalar(.5));let D=0,z=0,A=0;let M=function*(){for(let t=0;t<f;t++){if(A>=r)return;D++,d.x+=x.x,D>=r&&(D=0,d.x-=x.x*r,z++,d.y+=x.y),z>=r&&(z=0,d.y-=x.y*r,A++,d.z+=x.z);const t=[];T.copy(d),t[0]=getDensity$1(e,T),T.copy(d),T.x+=x.x,t[1]=getDensity$1(e,T),T.copy(d),T.x+=x.x,T.z+=x.z,t[2]=getDensity$1(e,T),T.copy(d),T.z+=x.z,t[3]=getDensity$1(e,T),T.copy(d),T.y+=x.y,t[4]=getDensity$1(e,T),T.copy(d),T.x+=x.x,T.y+=x.y,t[5]=getDensity$1(e,T),T.copy(d),T.x+=x.x,T.y+=x.y,T.z+=x.z,t[6]=getDensity$1(e,T),T.copy(d),T.y+=x.y,T.z+=x.z,t[7]=getDensity$1(e,T);let o=0;t[0]&&(o|=1),t[1]&&(o|=2),t[2]&&(o|=4),t[3]&&(o|=8),t[4]&&(o|=16),t[5]&&(o|=32),t[6]&&(o|=64),t[7]&&(o|=128);const s=triangleLUT[o]||s[1],n=[[.5,0,0],[1,0,.5],[.5,0,1],[0,0,.5],[.5,1,0],[1,1,.5],[.5,1,1],[0,1,.5],[0,.5,0],[1,.5,0],[1,.5,1],[0,.5,1]];if(t.filter((e=>0!==e)).length){const e={r:Math.random(),g:Math.random(),b:Math.random()};s.forEach((t=>{const o=n[t];p.push(D-r/2+o[0],z-r/2+o[1],A-r/2+o[2]),m.push(e.r,e.g,e.b);const s=new Three.Vector3(Math.random(),Math.random(),Math.random()).normalize();h.push(s.x,s.y,s.z);}));}yield;}}();const U=()=>{M&&(this.processes[i]=requestAnimationFrame(U));let e=window.performance.now();for(;window.performance.now()-e<16&&M;){const{done:e}=M.next();if(n&&n({convertedVoxels:0,totalVoxels:l,convertedGrids:a,totalGrids:c}),e&&(a++,M=null,delete this.processes[i],a===c)){if(!o){const e=new Three.BufferAttribute(new Float32Array(p),3);e.setUsage(Three.DynamicDrawUsage),g.geometry.setAttribute("position",e);const t=new Three.BufferAttribute(new Float32Array(h),3);t.setUsage(Three.DynamicDrawUsage),g.geometry.setAttribute("normal",t);const r=new Three.BufferAttribute(new Float32Array(m),3);r.setUsage(Three.DynamicDrawUsage),g.geometry.setAttribute("color",r),this.add(g);}s&&s();}}};U(),o&&this.add(g);}));}dispose(){this.processes&&(this.processes.forEach((e=>cancelAnimationFrame(e))),this.processes=null),this.geometries.forEach((e=>e.dispose())),this.geometries=[];}}
28
28
 
29
- class VolumeToPoints extends Three.Group{processes=[];constructor(e,t,{resolution:r,progressive:o},s,n){let i;super(),i=e instanceof Array?e:void 0!==e.grids?Object.values(e.grids):[e];let l=0;const a=i.length,c=a*Math.pow(r,3);i.forEach(((e,i)=>{const p=new Three.BufferGeometry,h=[],m=new Three.Points(p,new Three.PointsMaterial({color:16711935,size:1,...t||{}})),d=1/r,u=(Math.pow(r,3)),f=new Vector3(0,0,0),w=new Vector3(0,0,0),y=new Vector3(0,0,0),x=new Bbox;x.set(...e.getPreciseWorldBbox()),x.getCenter(m.position),x.getSize(m.scale),m.scale.multiplyScalar(1/r),x.getCenter(f),x.getSize(w),y.copy(w).multiplyScalar(d),e.transform.applyInverseJacobian(y),f.sub(w.clone().multiplyScalar(.5)),e.transform.applyInverseTransformMap(f),f.add(y.clone().multiplyScalar(.5));let g=0,b=0,T=0;let v=function*(){for(let t=0;t<u;t++){if(T>=r)return;g++,f.x+=y.x,g>=r&&(g=0,f.x-=y.x*r,b++,f.y+=y.y),b>=r&&(b=0,f.y-=y.y*r,T++,f.z+=y.z);getDensity$1(e,f)&&(h.push(g-r/2,b-r/2,T-r/2),p.setAttribute("position",new Three.BufferAttribute(new Float32Array(h),3))),yield;}}();const V=()=>{v&&(this.processes[i]=requestAnimationFrame(V));let e=window.performance.now();for(;window.performance.now()-e<16&&v;){const{done:e}=v.next();n&&n({convertedVoxels:0,totalVoxels:c,convertedGrids:l,totalGrids:a}),e&&(l++,v=null,delete this.processes[i],l===a&&(o||this.add(m),s&&s()));}};V(),o&&this.add(m);}));}dispose(){this.processes&&(this.processes.forEach(((e,t)=>{cancelAnimationFrame(e),delete this.processes[t];})),this.processes=null);}}
29
+ class VolumeToPoints extends Three.Group{processes=[];materials=[];geometries=[];constructor(e,t,{resolution:s,progressive:r},o,i){let n;super(),n=e instanceof Array?e:void 0!==e.grids?Object.values(e.grids):[e];let a=0;const l=n.length,c=l*Math.pow(s,3);n.forEach(((e,n)=>{const p=new Three.BufferGeometry,h=[],m=new Three.PointsMaterial({color:16711935,size:1,...t||{}}),u=new Three.Points(p,m);this.materials.push(m),this.geometries.push(p);const d=1/s,f=(Math.pow(s,3)),w=new Vector3(0,0,0),y=new Vector3(0,0,0),g=new Vector3(0,0,0),x=new Bbox;x.set(...e.getPreciseWorldBbox()),x.getCenter(u.position),x.getSize(u.scale),u.scale.multiplyScalar(1/s),x.getCenter(w),x.getSize(y),g.copy(y).multiplyScalar(d),e.transform.applyInverseJacobian(g),w.sub(y.clone().multiplyScalar(.5)),e.transform.applyInverseTransformMap(w),w.add(g.clone().multiplyScalar(.5));let b=0,T=0,v=0;let V=function*(){for(let t=0;t<f;t++){if(v>=s)return;b++,w.x+=g.x,b>=s&&(b=0,w.x-=g.x*s,T++,w.y+=g.y),T>=s&&(T=0,w.y-=g.y*s,v++,w.z+=g.z);getDensity$1(e,w)&&(h.push(b-s/2,T-s/2,v-s/2),p.setAttribute("position",new Three.BufferAttribute(new Float32Array(h),3))),yield;}}();const A=()=>{V&&(this.processes[n]=requestAnimationFrame(A));let e=window.performance.now();for(;window.performance.now()-e<16&&V;){const{done:e}=V.next();i&&i({convertedVoxels:0,totalVoxels:c,convertedGrids:a,totalGrids:l}),e&&(a++,V=null,delete this.processes[n],a===l&&(r||this.add(u),o&&o()));}};A(),r&&this.add(u);}));}dispose(){this.processes&&(this.processes.forEach((e=>cancelAnimationFrame(e))),this.processes=null),this.materials.forEach((e=>e.dispose())),this.geometries.forEach((e=>e.dispose())),this.materials=[],this.geometries=[];}}
30
30
 
31
31
  class MeshVolume extends GridDescriptor{static halfFloatGridPrefix=GridDescriptor.halfFloatGridPrefix;saveAsHalfFloat=!1;leavesCount=0;uniqueName;gridName;gridType;mesh=new Three.Object3D;constructor(e){super(),this.readGrid();const t=e.clone(),r=[],i=new Three.Vector3,s=new Three.Quaternion;t.position.setScalar(0,0,0),t.scale.setScalar(1),t.quaternion.identity(),t.updateMatrix(),t.updateMatrixWorld(),t.updateWorldMatrix(),t.traverse((e=>{if(e.geometry){const t=e.geometry.clone();e.getWorldQuaternion(s),t.applyQuaternion(s),e.getWorldScale(i),t.scale(i.x,i.y,i.z),e.getWorldPosition(i),t.translate(i.x,i.y,i.z),t.attributes={position:t.attributes.position,normal:t.attributes.normal},r.push(t);}}));const a=BufferGeometryUtils.mergeVertices(BufferGeometryUtils.mergeGeometries(r,!1));this.mesh=new Three.Mesh(a,new Three.MeshBasicMaterial({side:Three.DoubleSide})),this.cacheMeshGeometry();}readGrid(){this.readGridHeader(),this.readGridTransform();}readGridHeader(){this.uniqueName=`MeshVolume#${getUuid()}`,this.gridName=this.uniqueName.split("#")[0],this.gridType="fog volume";}readCompression(){}readMetadata(){}getGridValueType(){return floatType}getGridClass(){return "fog volume"}readGridTransform(){this.transform=new GridTransform;}readTopology(){}getWorldBbox(){return this.getLocalBbox()}getLocalBbox(){return [new Vector3$1(-.5,-.5,-.5),new Vector3$1(.5,.5,.5)]}getPreciseWorldBbox(){return this.getWorldBbox()}bbox=null;center=null;size=null;target=new Three.Vector3(0,0,0);raycaster=new Three.Raycaster;rayDirection=new Three.Vector3(1,0,0);cacheMeshGeometry(){this.bbox=new Three.Box3,this.bbox.expandByObject(this.mesh),this.center=new Three.Vector3,this.size=new Three.Vector3,this.bbox.getCenter(this.center),this.bbox.getSize(this.size);const e=Math.max(this.size.x,this.size.y,this.size.z),t=(new Three.Vector3).copy(this.size).divideScalar(e);this.size.divide(t),this.size.addScalar(.1),this.raycaster.near=0,this.raycaster.far=1e3;}getValue(e){this.target.copy(e).multiply(this.size).add(this.center),this.rayDirection.copy(e).normalize(),this.raycaster.set(this.target,this.rayDirection);const t=this.raycaster.intersectObjects([this.mesh],!0);return 0===t.length||t.length%2==0?0:1}getLeafAt(e){return null}}
32
32
 
33
- class VolumeNormalMaterial extends Three.MeshStandardMaterial{name="VolumeNormalMaterial";isVolumetricFogMaterial=!0;customProgramCacheKey=()=>getUuid(this);_uniforms={baseColor:{value:new Three.Color(16777215)},scatterColor:{value:new Three.Color(0)},densityMap3D:{value:null},emissiveMap3D:{value:null},baseColorMap3D:{value:null},maskMap3D:{value:null},steps:{value:100},absorbance:{value:1},densityScale:{value:1},resolution:{value:100},offset3D:{value:new Three.Vector3(0,0,0)},wrap3D:{value:Three.ClampToEdgeWrapping},lights:{value:lights.useDirectionalLights|lights.usePointLights|lights.useSpotLights|lights.useHemisphereLights|lights.useEnvironment},cameraPosition:{value:new Three.Vector3(0,0,0)},cameraNear:{value:1},cameraFar:{value:1}};set baseColor(e){this._uniforms.baseColor.value.set(e);}get baseColor(){return this._uniforms.baseColor.value}set scatterColor(e){this._uniforms.scatterColor.value.set(e);}get scatterColor(){return this._uniforms.scatterColor.value}set densityMap3D(e){this._uniforms.densityMap3D.value=e,e.offset3D=this._uniforms.offset3D.value;}get densityMap3D(){return this._uniforms.densityMap3D.value}set emissiveMap3D(e){this._uniforms.emissiveMap3D.value=e,e.offset3D=this._uniforms.offset3D.value,this.needsUpdate=!0;}get emissiveMap3D(){return this._uniforms.emissiveMap3D.value}set maskMap3D(e){this._uniforms.maskMap3D.value=e,this.needsUpdate=!0;}get maskMap3D(){return this._uniforms.maskMap3D.value}set baseColorMap3D(e){this._uniforms.baseColorMap3D.value=e,e.offset3D=this._uniforms.offset3D.value,this.needsUpdate=!0;}get baseColorMap3D(){return this._uniforms.baseColorMap3D.value}set steps(e){this._uniforms.steps.value=e;}get steps(){return this._uniforms.steps.value}set absorbance(e){this._uniforms.absorbance.value=e;}get absorbance(){return this._uniforms.absorbance.value}set resolution(e){this._uniforms.resolution.value=e;}get resolution(){return this._uniforms.resolution.value}set offset3D(e){this._uniforms.offset3D.value.copy(e);}get offset3D(){return this._uniforms.offset3D.value}set wrap3D(e){this._uniforms.wrap3D.value=e,this.needsUpdate=!0;}get wrap3D(){return this._uniforms.wrap3D.value}set densityScale(e){this._uniforms.densityScale.value=e;}get densityScale(){return this._uniforms.densityScale.value}set lights(e){this._uniforms.lights.value=e,this.needsUpdate=!0;}get lights(){return this._uniforms.lights.value}set useDirectionalLights(e=!0){e?this._uniforms.lights.value|=lights.useDirectionalLights:this._uniforms.lights.value&=~lights.useDirectionalLights,this.needsUpdate=!0;}get useDirectionalLights(){return 0!=(this._uniforms.lights.value&lights.useDirectionalLights)}set usePointLights(e=!0){e?this._uniforms.lights.value|=lights.usePointLights:this._uniforms.lights.value&=~lights.usePointLights,this.needsUpdate=!0;}get usePointLights(){return 0!=(this._uniforms.lights.value&lights.usePointLights)}set useHemisphereLights(e=!0){e?this._uniforms.lights.value|=lights.useHemisphereLights:this._uniforms.lights.value&=~lights.useHemisphereLights,this.needsUpdate=!0;}get useHemisphereLights(){return 0!=(this._uniforms.lights.value&lights.useHemisphereLights)}set useSpotLights(e=!0){e?this._uniforms.lights.value|=lights.useSpotLights:this._uniforms.lights.value&=~lights.useSpotLights,this.needsUpdate=!0;}get useSpotLights(){return 0!=(this._uniforms.lights.value&lights.useSpotLights)}set useEnvironment(e=!0){e?this._uniforms.lights.value|=lights.useEnvironment:this._uniforms.lights.value&=~lights.useEnvironment,this.needsUpdate=!0;}get useEnvironment(){return 0!=(this._uniforms.lights.value&lights.useEnvironment)}constructor(e={}){super(),this.side=Three.BackSide,this.depthWrite=!1,this.depthTest=!1,this.transparent=!0,Object.keys(this._uniforms).forEach((n=>{void 0!==e[n]&&null!==e[n]&&(this[n]=e[n],e[n]instanceof Three.Texture&&(e[n].offset3D=this._uniforms.offset3D.value));})),this.onBeforeCompile=(n,i)=>{const t=getDepthUniforms();Object.entries({...this._uniforms,...t}).forEach((([e,i])=>{n.uniforms[e]=i;}));const o=`\n #define VOLUME_BBOX_SPAN 0.5\n ${e.emissiveMap3D?"#define USE_EMISSIVE_GRID 1":""}\n ${e.baseColorMap3D?"#define USE_BASE_COLOR_GRID 1":""}\n ${e.maskMap3D?"#define USE_MASK_GRID 1":""}\n\n #define VOLUME_USE_ENVIRONMENT ${Number(this.useEnvironment)} != 0\n #define VOLUME_USE_HEMI_LIGHTS ${Number(this.useHemisphereLights)} != 0\n #define VOLUME_USE_POINT_LIGHTS ${Number(this.usePointLights)} != 0\n #define VOLUME_USE_DIR_LIGHTS ${Number(this.useDirectionalLights)} != 0\n #define VOLUME_USE_SPOT_LIGHTS ${Number(this.useSpotLights)} != 0\n `,a="\n varying mat4 mModelMatrix;\n varying mat4 mViewMatrix;\n varying mat4 mProjectionMatrix;\n varying mat4 mModelViewMatrix;\n varying mat4 mInverseModelViewMatrix;\n varying mat3 mInverseNormalMatrix;\n ";n.vertexShader=n.vertexShader.replace("#include <common>",`\n ${a}\n\n out vec3 vOrigin;\n out vec3 vDirection;\n out vec3 vPosition;\n\n #include <common>\n `).replace("#include <worldpos_vertex>","\n #include <worldpos_vertex>\n\n vOrigin = vec3(inverse(modelMatrix) * vec4(cameraPosition, 1.0)).xyz;\n vDirection = position - vOrigin;\n vPosition = position;\n\n mModelMatrix = modelMatrix;\n mViewMatrix = viewMatrix;\n mProjectionMatrix = projectionMatrix;\n mModelViewMatrix = modelViewMatrix;\n mInverseModelViewMatrix = inverse(modelViewMatrix);\n mInverseNormalMatrix = inverse(normalMatrix);\n ");const r=`\n vec2 getVolumeBbox(vec3 vPointOfReference) {\n const vec3 vBoxMin = vec3(-0.5);\n const vec3 vBoxMax = vec3(0.5);\n vec3 vInvPointOfReference = 1.0 / vPointOfReference;\n \n vec3 vMinRange = (-vOrigin - VOLUME_BBOX_SPAN) * vInvPointOfReference;\n vec3 vMaxRange = (-vOrigin + VOLUME_BBOX_SPAN) * vInvPointOfReference;\n \n vec3 vMin = min(vMinRange, vMaxRange);\n vec3 vMax = max(vMinRange, vMaxRange);\n\n return vec2(\n max(vMin.x, max(vMin.y, vMin.z)),\n min(vMax.x, min(vMax.y, vMax.z))\n );\n }\n\n vec3 radiation = vec3(0.);\n vec3 getBlackBodyRadiation(float temperature) {\n if (temperature == 0.) {\n return vec3(0.);\n }\n\n #ifndef USE_EMISSIVE_GRID\n return vec3(0.);\n #endif\n\n // NOTE Blackbody radiation source - https://www.shadertoy.com/view/4tdGWM\n float temperatureScaled = temperature * 1000.;\n\n radiation.r += 1. / (exp(19E3 * 1. / temperatureScaled) - 1.);\n radiation.g += 3.375 / (exp(19E3 * 1.5 / temperatureScaled) - 1.);\n radiation.b += 8. / (exp(19E3 * 2. / temperatureScaled) - 1.);\n\n return (radiation / max(radiation.r,max(radiation.g,radiation.b)));\n }\n\n float loopUV(float x) {\n if (abs(mod(floor(x), 2.)) == 1.) {\n return ceil(x) - x;\n } else {\n return x - floor(x);\n }\n }\n\n vec3 mapTextureSample0(vec3 position){\n vec3 uv = position + VOLUME_BBOX_SPAN;\n\n ${this.wrap3D===Three.RepeatWrapping?"return mod(uv, 1.);":""}\n ${0===this.wrap3D||this.wrap3D===Three.ClampToEdgeWrapping?"// NOTE Return UV":""}\n ${this.wrap3D===Three.MirroredRepeatWrapping?"\n uv.x = loopUV(uv.x);\n uv.y = loopUV(uv.y);\n uv.z = loopUV(uv.z);\n ":""}\n\n return uv;\n }\n\n vec3 mapTextureSample(vec3 position, vec3 offset){\n vec3 uv = position + VOLUME_BBOX_SPAN + offset;\n\n ${this.wrap3D===Three.RepeatWrapping?"return mod(uv, 1.);":""}\n ${0===this.wrap3D||this.wrap3D===Three.ClampToEdgeWrapping?"// NOTE Return UV":""}\n ${this.wrap3D===Three.MirroredRepeatWrapping?"\n uv.x = loopUV(uv.x);\n uv.y = loopUV(uv.y);\n uv.z = loopUV(uv.z);\n ":""}\n\n return uv;\n }\n\n float blendSample(float value) {\n float fSmoothingThreshold = 0.25;\n float fSmoothingBlend = 0.25;\n\n return smoothstep(\n fSmoothingThreshold - fSmoothingBlend,\n fSmoothingThreshold + fSmoothingBlend,\n value\n ) * densityScale;\n }\n\n float clampedTexture(sampler3D source, vec3 position) {\n ${0===this.wrap3D||this.wrap3D===Three.ClampToEdgeWrapping?"\n vec3 vAbs = abs(position);\n\n if (max(vAbs.x, max(vAbs.y, vAbs.z)) > .5) {\n return 0.;\n }\n ":""}\n\n return texture(source, mapTextureSample(position, offset3D)).r;\n }\n\n float clampedMaskTexture(sampler3D source, vec3 position) {\n ${0===this.wrap3D||this.wrap3D===Three.ClampToEdgeWrapping?"\n vec3 vAbs = abs(position);\n\n if (max(vAbs.x, max(vAbs.y, vAbs.z)) > .5) {\n return 0.;\n }\n ":""}\n\n return texture(source, mapTextureSample0(position)).r;\n }\n\n vec3 clampedTextureRGB(sampler3D source, vec3 position) {\n return texture(source, mapTextureSample(position, offset3D)).rgb;\n }\n `;n.fragmentShader=n.fragmentShader.replace("#include <uv_pars_fragment>","// NOTE Override UV calculations").replace("#include <packing>","").replace("#include <common>",`\n precision highp float;\n precision highp sampler2D;\n precision highp sampler3D;\n\n in vec3 vOrigin;\n in vec3 vDirection;\n in vec3 vPosition;\n\n uniform sampler3D densityMap3D;\n uniform sampler3D emissiveMap3D;\n uniform sampler3D baseColorMap3D;\n uniform sampler3D maskMap3D;\n uniform vec3 offset3D;\n uniform int wrap3D;\n\n uniform float steps;\n uniform float absorbance;\n uniform float densityScale;\n uniform vec3 baseColor;\n uniform vec3 scatterColor;\n uniform float resolution;\n\n uniform float cameraNear;\n uniform float cameraFar;\n uniform sampler2D depthScreenMap;\n uniform vec4 depthView;\n\n ${o}\n ${a}\n\n #include <common>\n #include <packing>\n #include <uv_pars_fragment>\n\n ${r}\n `).replace("#include <lights_fragment_begin>","// NOTE Override light calculations").replace("#include <lights_fragment_maps>","// NOTE Override light calculations").replace("#include <lights_fragment_end>","// NOTE Override light calculations").replace("#include <opaque_fragment>","\n vec3 vWorld = -vViewPosition;\n\n vec3 vRayDirection = normalize(vDirection);\n vec2 vBounds = getVolumeBbox(vRayDirection);\n vec2 vMaxBounds = getVolumeBbox(vOrigin);\n \n if (vBounds.x > vBounds.y) {\n discard;\n }\n\n vBounds.x = max(vBounds.x, 0.0);\n vMaxBounds.x = max(vMaxBounds.x, 0.0);\n\n // Volume movement\n\n vec3 vPoint = vOrigin + vBounds.x * vRayDirection;\n vec3 vPointStep = 1.0 / abs(vRayDirection);\n float delta = min(vPointStep.x, min(vPointStep.y, vPointStep.z)) / steps;\n vec3 vDirectionDeltaStep = vRayDirection * delta;\n\n // Density calculations\n\n float density = 0.0;\n vec3 albedo = vec3(0.);\n vec3 emissive = vec3(0.);\n float volumeSample;\n float emissiveSample;\n float maskSample = 1.;\n float noiseSample;\n float noiseFactor;\n vec3 fTest;\n\n // Light calculations\n \n vec3 lightAlbedo = vec3(0.);\n vec3 vLightProbe;\n vec3 lightDirection;\n vec3 vLightStep;\n vec3 vEnvMapScatter = vec3(0.0);\n float lightSample;\n float lightAbsorbance;\n float lightDistance;\n float stepAccumulation;\n\n // Utils\n\n float absorbanceDensityRatio = min(absorbance, densityScale); // NOTE When reducing densityScale, automatically reduce absorbance\n float inverseAbsorbance = 1.0 / absorbanceDensityRatio;\n float inverseDensityScale = 1.0 / densityScale;\n float eInverseAbsorbance = exp(-1. - inverseAbsorbance); // NOTE Wrong, but looks kinda better than exp(-inverseAbsorbance)\n float eDensityAbsorbance = exp(-absorbanceDensityRatio * delta);\n float eInverseDensityScale = exp(-1. - inverseDensityScale);\n\n // Depth Testing\n\n vec2 screenUV = gl_FragCoord.xy / depthView.zw;\n float worldDepth = unpackRGBAToDepth(texture2D(depthScreenMap, screenUV));\n float fogDepth = 0.;\n vec3 vProjPoint;\n\n \n float lightMarchLimit = resolution + 2.;\n int iLightMarchLimit = int(lightMarchLimit);\n\n vec3 vUnit = (mModelMatrix * vec4(1., 0., 0., 0.)).xyz;\n float vUnitLength = length(vUnit);\n\n vec3 iblIrradiance = vec3(0.0);\n \n\n vec3 lastNonSolidPoint = vec3(vPoint);\n\n for (float i = vBounds.x; i < vBounds.y; i += delta) {\n volumeSample = clampedTexture(densityMap3D, vPoint);\n \n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vPoint);\n #endif\n\n density += blendSample(volumeSample) * eDensityAbsorbance * maskSample;\n\n #ifdef USE_EMISSIVE_GRID\n emissiveSample = clampedTexture(emissiveMap3D, vPoint);\n emissive = max(emissive, density * vec3(emissiveSample));\n #endif\n\n if (density < 1.) {\n lastNonSolidPoint = vPoint;\n }\n\n if (density >= 1.) {\n break;\n }\n\n vProjPoint = (mModelMatrix * vec4(vPoint, 1.0)).xyz;\n fogDepth = length(vProjPoint - cameraPosition);\n fogDepth = (fogDepth - cameraNear) / (cameraFar - cameraNear);\n fogDepth = saturate(fogDepth);\n \n if (worldDepth < fogDepth) {\n break;\n }\n\n vPoint += vDirectionDeltaStep;\n }\n\n density = clamp(density, 0.0, 1.0);\n\n vPoint = lastNonSolidPoint;\n\n if (density > 0.) {\n\n \n vec3 baseColorSample = baseColor.rgb;\n\n #ifdef USE_BASE_COLOR_GRID\n baseColorSample *= clampedTextureRGB(baseColorMap3D, vPoint);\n #endif\n\n albedo += ambientLightColor;\n \n\n #if VOLUME_USE_ENVIRONMENT\n \n vec3 viewDir = isOrthographic ? vec3(0.0, 0.0, 1.0) : normalize(vViewPosition);\n vec3 vUVCoords = normalize(lastNonSolidPoint * mInverseNormalMatrix).xyz;\n\n #if defined( USE_ENVMAP )\n vEnvMapScatter = getIBLIrradiance(vUVCoords) * RECIPROCAL_PI;\n\n albedo += density * getIBLRadiance(viewDir, vUVCoords, .5) * RECIPROCAL_PI;\n #endif\n \n #endif\n\n #if NUM_HEMI_LIGHTS > 0 && VOLUME_USE_HEMI_LIGHTS\n \n lightAlbedo = vec3(0.);\n vec3 textureProbe;\n\n float absorbanceUp;\n float absorbanceDown;\n float stepAccumulationUp;\n float stepAccumulationDown;\n\n for (int lightIndex = 0; lightIndex < NUM_HEMI_LIGHTS; lightIndex++) {\n\n HemisphereLight hemiLight = hemisphereLights[lightIndex];\n vLightProbe = vec3(vPoint);\n\n absorbanceUp = 0.0;\n absorbanceDown = 0.0;\n\n stepAccumulationUp = 0.;\n stepAccumulationDown = 0.;\n\n lightDirection = -normalize((vec4(hemiLight.direction, 1.) * viewMatrix).xyz);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 1; lightMarch < int(lightMarchLimit / 2.); lightMarch++) {\n if (absorbanceUp < 1.) {\n textureProbe = vLightProbe - float(lightMarch) * vLightStep;\n lightSample = clampedTexture(densityMap3D, textureProbe);\n\n absorbanceUp += lightSample * eInverseAbsorbance;\n stepAccumulationUp += 1.;\n }\n \n if (absorbanceDown < 1.) {\n textureProbe = vLightProbe + float(lightMarch) * vLightStep;\n lightSample = clampedTexture(densityMap3D, textureProbe);\n\n absorbanceDown += blendSample(lightSample) * eInverseAbsorbance;\n stepAccumulationDown += 1.;\n }\n }\n\n absorbanceUp = min(1., absorbanceUp);\n absorbanceDown = min(1., absorbanceDown);\n\n lightAlbedo += mix(\n (1. - absorbanceUp) * hemiLight.skyColor,\n (1. - absorbanceDown) * hemiLight.groundColor,\n 0.5\n );\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_POINT_LIGHTS > 0 && VOLUME_USE_POINT_LIGHTS\n \n PointLight pointLight;\n lightAlbedo = vec3(0.);\n \n for (int lightIndex = 0; lightIndex < NUM_POINT_LIGHTS; lightIndex++) {\n pointLight = pointLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.;\n stepAccumulation = 1.;\n\n lightDirection = vPoint - (mInverseModelViewMatrix * vec4(pointLight.position, 1.)).xyz;\n lightDistance = length(lightDirection) * vUnitLength;\n lightDirection = normalize(lightDirection);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n \n lightSample = clampedTexture(densityMap3D, vLightProbe);\n \n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * pointLight.color;\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_DIR_LIGHTS > 0 && VOLUME_USE_DIR_LIGHTS\n \n DirectionalLight directionalLight;\n lightAlbedo = vec3(0.);\n\n for (int lightIndex = 0; lightIndex < NUM_DIR_LIGHTS; lightIndex++) {\n directionalLight = directionalLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.0;\n stepAccumulation = 1.;\n\n lightDirection = -normalize((vec4(directionalLight.direction, 1.) * viewMatrix).xyz);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n \n float lightSample = clampedTexture(densityMap3D, vLightProbe);\n\n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * directionalLight.color;\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_SPOT_LIGHTS > 0 && VOLUME_USE_SPOT_LIGHTS\n \n SpotLight spotLight;\n lightAlbedo = vec3(0.);\n\n float angleCos;\n float spotAttenuation;\n \n for (int lightIndex = 0; lightIndex < NUM_SPOT_LIGHTS; lightIndex++) {\n spotLight = spotLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.0;\n stepAccumulation = 1.;\n\n lightDirection = vPoint - (mInverseModelViewMatrix * vec4(spotLight.position, 1.)).xyz;\n lightDistance = length(lightDirection) * vUnitLength;\n lightDirection = normalize(lightDirection);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n angleCos = dot(normalize(-lightDirection), normalize(mInverseNormalMatrix * spotLight.direction));\n spotAttenuation = smoothstep(spotLight.coneCos, spotLight.penumbraCos, angleCos);\n\n if (spotAttenuation > 0.) {\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n \n lightSample = clampedTexture(densityMap3D, vLightProbe);\n \n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * spotAttenuation * spotLight.color;\n }\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n albedo *= baseColorSample;\n }\n \n emissive = getBlackBodyRadiation(emissive.r);\n albedo += emissive;\n\n outgoingLight.rgb = vPoint.xyz;\n diffuseColor.a = saturate(density * opacity);\n\n if (density <= 0.) {\n discard;\n }\n\n #include <opaque_fragment>\n ");};}}
33
+ class VolumeNormalMaterial extends Three.MeshStandardMaterial{name="VolumeNormalMaterial";isVolumetricFogMaterial=!0;customProgramCacheKey=()=>getUuid(this);_uniforms={baseColor:{value:new Three.Color(16777215)},scatterColor:{value:new Three.Color(0)},densityMap3D:{value:null},emissiveMap3D:{value:null},baseColorMap3D:{value:null},maskMap3D:{value:null},steps:{value:100},absorbance:{value:1},densityScale:{value:1},resolution:{value:100},offset3D:{value:new Three.Vector3(0,0,0)},wrap3D:{value:Three.ClampToEdgeWrapping},lights:{value:lights.useDirectionalLights|lights.usePointLights|lights.useSpotLights|lights.useHemisphereLights|lights.useEnvironment},cameraPosition:{value:new Three.Vector3(0,0,0)},cameraNear:{value:1},cameraFar:{value:1}};set baseColor(e){this._uniforms.baseColor.value.set(e);}get baseColor(){return this._uniforms.baseColor.value}set scatterColor(e){this._uniforms.scatterColor.value.set(e);}get scatterColor(){return this._uniforms.scatterColor.value}set densityMap3D(e){this._uniforms.densityMap3D.value=e,e.offset3D=this._uniforms.offset3D.value;}get densityMap3D(){return this._uniforms.densityMap3D.value}set emissiveMap3D(e){this._uniforms.emissiveMap3D.value=e,e.offset3D=this._uniforms.offset3D.value,this.needsUpdate=!0;}get emissiveMap3D(){return this._uniforms.emissiveMap3D.value}set maskMap3D(e){this._uniforms.maskMap3D.value=e,this.needsUpdate=!0;}get maskMap3D(){return this._uniforms.maskMap3D.value}set baseColorMap3D(e){this._uniforms.baseColorMap3D.value=e,e.offset3D=this._uniforms.offset3D.value,this.needsUpdate=!0;}get baseColorMap3D(){return this._uniforms.baseColorMap3D.value}set steps(e){this._uniforms.steps.value=e;}get steps(){return this._uniforms.steps.value}set absorbance(e){this._uniforms.absorbance.value=e;}get absorbance(){return this._uniforms.absorbance.value}set resolution(e){this._uniforms.resolution.value=e;}get resolution(){return this._uniforms.resolution.value}set offset3D(e){this._uniforms.offset3D.value.copy(e);}get offset3D(){return this._uniforms.offset3D.value}set wrap3D(e){this._uniforms.wrap3D.value=e,this.needsUpdate=!0;}get wrap3D(){return this._uniforms.wrap3D.value}set densityScale(e){this._uniforms.densityScale.value=e;}get densityScale(){return this._uniforms.densityScale.value}set lights(e){this._uniforms.lights.value=e,this.needsUpdate=!0;}get lights(){return this._uniforms.lights.value}set useDirectionalLights(e=!0){e?this._uniforms.lights.value|=lights.useDirectionalLights:this._uniforms.lights.value&=~lights.useDirectionalLights,this.needsUpdate=!0;}get useDirectionalLights(){return 0!=(this._uniforms.lights.value&lights.useDirectionalLights)}set usePointLights(e=!0){e?this._uniforms.lights.value|=lights.usePointLights:this._uniforms.lights.value&=~lights.usePointLights,this.needsUpdate=!0;}get usePointLights(){return 0!=(this._uniforms.lights.value&lights.usePointLights)}set useHemisphereLights(e=!0){e?this._uniforms.lights.value|=lights.useHemisphereLights:this._uniforms.lights.value&=~lights.useHemisphereLights,this.needsUpdate=!0;}get useHemisphereLights(){return 0!=(this._uniforms.lights.value&lights.useHemisphereLights)}set useSpotLights(e=!0){e?this._uniforms.lights.value|=lights.useSpotLights:this._uniforms.lights.value&=~lights.useSpotLights,this.needsUpdate=!0;}get useSpotLights(){return 0!=(this._uniforms.lights.value&lights.useSpotLights)}set useEnvironment(e=!0){e?this._uniforms.lights.value|=lights.useEnvironment:this._uniforms.lights.value&=~lights.useEnvironment,this.needsUpdate=!0;}get useEnvironment(){return 0!=(this._uniforms.lights.value&lights.useEnvironment)}constructor(e={}){super(),this.side=Three.BackSide,this.depthWrite=!1,this.depthTest=!1,this.transparent=!0,Object.keys(this._uniforms).forEach((n=>{void 0!==e[n]&&null!==e[n]&&(this[n]=e[n],e[n]instanceof Three.Texture&&(e[n].offset3D=this._uniforms.offset3D.value));})),this.onBeforeCompile=(n,i)=>{const t=getDepthUniforms();Object.entries({...this._uniforms,...t}).forEach((([e,i])=>{n.uniforms[e]=i;}));const o=`\n #define VOLUME_BBOX_SPAN 0.5\n ${e.emissiveMap3D?"#define USE_EMISSIVE_GRID 1":""}\n ${e.baseColorMap3D?"#define USE_BASE_COLOR_GRID 1":""}\n ${e.maskMap3D?"#define USE_MASK_GRID 1":""}\n\n #define VOLUME_USE_ENVIRONMENT ${Number(this.useEnvironment)} != 0\n #define VOLUME_USE_HEMI_LIGHTS ${Number(this.useHemisphereLights)} != 0\n #define VOLUME_USE_POINT_LIGHTS ${Number(this.usePointLights)} != 0\n #define VOLUME_USE_DIR_LIGHTS ${Number(this.useDirectionalLights)} != 0\n #define VOLUME_USE_SPOT_LIGHTS ${Number(this.useSpotLights)} != 0\n `;n.vertexShader=n.vertexShader.replace("#include <common>","\n out vec3 vOrigin;\n out vec3 vDirection;\n out vec3 vPosition;\n\n #include <common>\n ").replace("#include <worldpos_vertex>","\n #include <worldpos_vertex>\n\n vOrigin = vec3(inverse(modelMatrix) * vec4(cameraPosition, 1.0)).xyz;\n vDirection = position - vOrigin;\n vPosition = position;\n ");const a=`\n vec2 getVolumeBbox(vec3 vPointOfReference) {\n const vec3 vBoxMin = vec3(-0.5);\n const vec3 vBoxMax = vec3(0.5);\n vec3 vInvPointOfReference = 1.0 / vPointOfReference;\n \n vec3 vMinRange = (-vOrigin - VOLUME_BBOX_SPAN) * vInvPointOfReference;\n vec3 vMaxRange = (-vOrigin + VOLUME_BBOX_SPAN) * vInvPointOfReference;\n \n vec3 vMin = min(vMinRange, vMaxRange);\n vec3 vMax = max(vMinRange, vMaxRange);\n\n return vec2(\n max(vMin.x, max(vMin.y, vMin.z)),\n min(vMax.x, min(vMax.y, vMax.z))\n );\n }\n\n vec3 radiation = vec3(0.);\n vec3 getBlackBodyRadiation(float temperature) {\n if (temperature == 0.) {\n return vec3(0.);\n }\n\n #ifndef USE_EMISSIVE_GRID\n return vec3(0.);\n #endif\n\n // NOTE Blackbody radiation source - https://www.shadertoy.com/view/4tdGWM\n float temperatureScaled = temperature * 1000.;\n\n radiation.r += 1. / (exp(19E3 * 1. / temperatureScaled) - 1.);\n radiation.g += 3.375 / (exp(19E3 * 1.5 / temperatureScaled) - 1.);\n radiation.b += 8. / (exp(19E3 * 2. / temperatureScaled) - 1.);\n\n return (radiation / max(radiation.r,max(radiation.g,radiation.b)));\n }\n\n float loopUV(float x) {\n if (abs(mod(floor(x), 2.)) == 1.) {\n return ceil(x) - x;\n } else {\n return x - floor(x);\n }\n }\n\n vec3 mapTextureSample0(vec3 position){\n vec3 uv = position + VOLUME_BBOX_SPAN;\n\n ${this.wrap3D===Three.RepeatWrapping?"return mod(uv, 1.);":""}\n ${0===this.wrap3D||this.wrap3D===Three.ClampToEdgeWrapping?"// NOTE Return UV":""}\n ${this.wrap3D===Three.MirroredRepeatWrapping?"\n uv.x = loopUV(uv.x);\n uv.y = loopUV(uv.y);\n uv.z = loopUV(uv.z);\n ":""}\n\n return uv;\n }\n\n vec3 mapTextureSample(vec3 position, vec3 offset){\n vec3 uv = position + VOLUME_BBOX_SPAN + offset;\n\n ${this.wrap3D===Three.RepeatWrapping?"return mod(uv, 1.);":""}\n ${0===this.wrap3D||this.wrap3D===Three.ClampToEdgeWrapping?"// NOTE Return UV":""}\n ${this.wrap3D===Three.MirroredRepeatWrapping?"\n uv.x = loopUV(uv.x);\n uv.y = loopUV(uv.y);\n uv.z = loopUV(uv.z);\n ":""}\n\n return uv;\n }\n\n float blendSample(float value) {\n float fSmoothingThreshold = 0.25;\n float fSmoothingBlend = 0.25;\n\n return smoothstep(\n fSmoothingThreshold - fSmoothingBlend,\n fSmoothingThreshold + fSmoothingBlend,\n value\n ) * densityScale;\n }\n\n float clampedTexture(sampler3D source, vec3 position) {\n ${0===this.wrap3D||this.wrap3D===Three.ClampToEdgeWrapping?"\n vec3 vAbs = abs(position);\n\n if (max(vAbs.x, max(vAbs.y, vAbs.z)) > .5) {\n return 0.;\n }\n ":""}\n\n return texture(source, mapTextureSample(position, offset3D)).r;\n }\n\n float clampedMaskTexture(sampler3D source, vec3 position) {\n ${0===this.wrap3D||this.wrap3D===Three.ClampToEdgeWrapping?"\n vec3 vAbs = abs(position);\n\n if (max(vAbs.x, max(vAbs.y, vAbs.z)) > .5) {\n return 0.;\n }\n ":""}\n\n return texture(source, mapTextureSample0(position)).r;\n }\n\n vec3 clampedTextureRGB(sampler3D source, vec3 position) {\n return texture(source, mapTextureSample(position, offset3D)).rgb;\n }\n `;n.fragmentShader=n.fragmentShader.replace("#include <uv_pars_fragment>","// NOTE Override UV calculations").replace("#include <packing>","").replace("#include <common>",`\n \n uniform mat4 modelMatrix;\n uniform mat4 modelViewMatrix;\n \n\n precision highp float;\n precision highp sampler2D;\n precision highp sampler3D;\n\n in vec3 vOrigin;\n in vec3 vDirection;\n in vec3 vPosition;\n\n uniform sampler3D densityMap3D;\n uniform sampler3D emissiveMap3D;\n uniform sampler3D baseColorMap3D;\n uniform sampler3D maskMap3D;\n uniform vec3 offset3D;\n uniform int wrap3D;\n\n uniform float steps;\n uniform float absorbance;\n uniform float densityScale;\n uniform vec3 baseColor;\n uniform vec3 scatterColor;\n uniform float resolution;\n\n uniform float cameraNear;\n uniform float cameraFar;\n uniform sampler2D depthScreenMap;\n uniform vec4 depthView;\n\n ${o}\n\n #include <common>\n #include <packing>\n #include <uv_pars_fragment>\n\n ${a}\n `).replace("#include <lights_fragment_begin>","// NOTE Override light calculations").replace("#include <lights_fragment_maps>","// NOTE Override light calculations").replace("#include <lights_fragment_end>","// NOTE Override light calculations").replace("#include <opaque_fragment>","\n vec3 vWorld = -vViewPosition;\n\n vec3 vRayDirection = normalize(vDirection);\n vec2 vBounds = getVolumeBbox(vRayDirection);\n vec2 vMaxBounds = getVolumeBbox(vOrigin);\n \n if (vBounds.x > vBounds.y) {\n discard;\n }\n\n vBounds.x = max(vBounds.x, 0.0);\n vMaxBounds.x = max(vMaxBounds.x, 0.0);\n\n // Volume movement\n\n vec3 vPoint = vOrigin + vBounds.x * vRayDirection;\n vec3 vPointStep = 1.0 / abs(vRayDirection);\n float delta = min(vPointStep.x, min(vPointStep.y, vPointStep.z)) / steps;\n vec3 vDirectionDeltaStep = vRayDirection * delta;\n\n // Density calculations\n\n float density = 0.0;\n vec3 albedo = vec3(0.);\n vec3 emissive = vec3(0.);\n float volumeSample;\n float emissiveSample;\n float maskSample = 1.;\n float noiseSample;\n float noiseFactor;\n vec3 fTest;\n\n // Light calculations\n \n vec3 lightAlbedo = vec3(0.);\n vec3 vLightProbe;\n vec3 lightDirection;\n vec3 vLightStep;\n vec3 vEnvMapScatter = vec3(0.0);\n float lightSample;\n float lightAbsorbance;\n float lightDistance;\n float stepAccumulation;\n\n // Utils\n\n float absorbanceDensityRatio = min(absorbance, densityScale); // NOTE When reducing densityScale, automatically reduce absorbance\n float inverseAbsorbance = 1.0 / absorbanceDensityRatio;\n float inverseDensityScale = 1.0 / densityScale;\n float eInverseAbsorbance = exp(-1. - inverseAbsorbance); // NOTE Wrong, but looks kinda better than exp(-inverseAbsorbance)\n float eDensityAbsorbance = exp(-absorbanceDensityRatio * delta);\n float eInverseDensityScale = exp(-1. - inverseDensityScale);\n\n // Depth Testing\n\n vec2 screenUV = gl_FragCoord.xy / depthView.zw;\n float worldDepth = unpackRGBAToDepth(texture2D(depthScreenMap, screenUV));\n float fogDepth = 0.;\n vec3 vProjPoint;\n\n \n float lightMarchLimit = resolution + 2.;\n int iLightMarchLimit = int(lightMarchLimit);\n\n vec3 vUnit = (modelMatrix * vec4(1., 0., 0., 0.)).xyz;\n float vUnitLength = length(vUnit);\n\n vec3 iblIrradiance = vec3(0.0);\n \n\n vec3 lastNonSolidPoint = vec3(vPoint);\n\n for (float i = vBounds.x; i < vBounds.y; i += delta) {\n volumeSample = clampedTexture(densityMap3D, vPoint);\n \n #ifdef USE_MASK_GRID\n maskSample = clampedMaskTexture(maskMap3D, vPoint);\n #endif\n\n density += blendSample(volumeSample) * eDensityAbsorbance * maskSample;\n\n #ifdef USE_EMISSIVE_GRID\n emissiveSample = clampedTexture(emissiveMap3D, vPoint);\n emissive = max(emissive, density * vec3(emissiveSample));\n #endif\n\n if (density < 1.) {\n lastNonSolidPoint = vPoint;\n }\n\n if (density >= 1.) {\n break;\n }\n\n vProjPoint = (modelMatrix * vec4(vPoint, 1.0)).xyz;\n fogDepth = length(vProjPoint - cameraPosition);\n fogDepth = (fogDepth - cameraNear) / (cameraFar - cameraNear);\n fogDepth = saturate(fogDepth);\n \n if (worldDepth < fogDepth) {\n break;\n }\n\n vPoint += vDirectionDeltaStep;\n }\n\n density = clamp(density, 0.0, 1.0);\n\n vPoint = lastNonSolidPoint;\n\n mat4 mInverseModelViewMatrix = inverse(modelViewMatrix);\n mat3 mInverseNormalMatrix = transpose(mat3(modelViewMatrix)); // NOTE Same as inverse(normalMatrix)\n\n if (density > 0.) {\n\n \n vec3 baseColorSample = baseColor.rgb;\n\n #ifdef USE_BASE_COLOR_GRID\n baseColorSample *= clampedTextureRGB(baseColorMap3D, vPoint);\n #endif\n\n albedo += ambientLightColor;\n \n\n #if VOLUME_USE_ENVIRONMENT\n \n vec3 viewDir = isOrthographic ? vec3(0.0, 0.0, 1.0) : normalize(vViewPosition);\n vec3 vUVCoords = normalize(lastNonSolidPoint * mInverseNormalMatrix).xyz;\n\n #if defined( USE_ENVMAP )\n vEnvMapScatter = getIBLIrradiance(vUVCoords) * RECIPROCAL_PI;\n\n albedo += density * getIBLRadiance(viewDir, vUVCoords, .5) * RECIPROCAL_PI;\n #endif\n \n #endif\n\n #if NUM_HEMI_LIGHTS > 0 && VOLUME_USE_HEMI_LIGHTS\n \n lightAlbedo = vec3(0.);\n vec3 textureProbe;\n\n float absorbanceUp;\n float absorbanceDown;\n float stepAccumulationUp;\n float stepAccumulationDown;\n\n for (int lightIndex = 0; lightIndex < NUM_HEMI_LIGHTS; lightIndex++) {\n\n HemisphereLight hemiLight = hemisphereLights[lightIndex];\n vLightProbe = vec3(vPoint);\n\n absorbanceUp = 0.0;\n absorbanceDown = 0.0;\n\n stepAccumulationUp = 0.;\n stepAccumulationDown = 0.;\n\n lightDirection = -normalize((vec4(hemiLight.direction, 1.) * viewMatrix).xyz);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 1; lightMarch < int(lightMarchLimit / 2.); lightMarch++) {\n if (absorbanceUp < 1.) {\n textureProbe = vLightProbe - float(lightMarch) * vLightStep;\n lightSample = clampedTexture(densityMap3D, textureProbe);\n\n absorbanceUp += lightSample * eInverseAbsorbance;\n stepAccumulationUp += 1.;\n }\n \n if (absorbanceDown < 1.) {\n textureProbe = vLightProbe + float(lightMarch) * vLightStep;\n lightSample = clampedTexture(densityMap3D, textureProbe);\n\n absorbanceDown += blendSample(lightSample) * eInverseAbsorbance;\n stepAccumulationDown += 1.;\n }\n }\n\n absorbanceUp = min(1., absorbanceUp);\n absorbanceDown = min(1., absorbanceDown);\n\n lightAlbedo += mix(\n (1. - absorbanceUp) * hemiLight.skyColor,\n (1. - absorbanceDown) * hemiLight.groundColor,\n 0.5\n );\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_POINT_LIGHTS > 0 && VOLUME_USE_POINT_LIGHTS\n \n PointLight pointLight;\n lightAlbedo = vec3(0.);\n \n for (int lightIndex = 0; lightIndex < NUM_POINT_LIGHTS; lightIndex++) {\n pointLight = pointLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.;\n stepAccumulation = 1.;\n\n lightDirection = vPoint - (mInverseModelViewMatrix * vec4(pointLight.position, 1.)).xyz;\n lightDistance = length(lightDirection) * vUnitLength;\n lightDirection = normalize(lightDirection);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n \n lightSample = clampedTexture(densityMap3D, vLightProbe);\n \n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * pointLight.color;\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_DIR_LIGHTS > 0 && VOLUME_USE_DIR_LIGHTS\n \n DirectionalLight directionalLight;\n lightAlbedo = vec3(0.);\n\n for (int lightIndex = 0; lightIndex < NUM_DIR_LIGHTS; lightIndex++) {\n directionalLight = directionalLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.0;\n stepAccumulation = 1.;\n\n lightDirection = -normalize((vec4(directionalLight.direction, 1.) * viewMatrix).xyz);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n \n float lightSample = clampedTexture(densityMap3D, vLightProbe);\n\n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * directionalLight.color;\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n #if NUM_SPOT_LIGHTS > 0 && VOLUME_USE_SPOT_LIGHTS\n \n SpotLight spotLight;\n lightAlbedo = vec3(0.);\n\n float angleCos;\n float spotAttenuation;\n \n for (int lightIndex = 0; lightIndex < NUM_SPOT_LIGHTS; lightIndex++) {\n spotLight = spotLights[lightIndex];\n\n vLightProbe = vec3(vPoint);\n lightAbsorbance = 0.0;\n stepAccumulation = 1.;\n\n lightDirection = vPoint - (mInverseModelViewMatrix * vec4(spotLight.position, 1.)).xyz;\n lightDistance = length(lightDirection) * vUnitLength;\n lightDirection = normalize(lightDirection);\n vLightStep = (lightDirection * VOLUME_BBOX_SPAN) / lightMarchLimit;\n\n angleCos = dot(normalize(-lightDirection), normalize(mInverseNormalMatrix * spotLight.direction));\n spotAttenuation = smoothstep(spotLight.coneCos, spotLight.penumbraCos, angleCos);\n\n if (spotAttenuation > 0.) {\n for (int lightMarch = 0; lightMarch < iLightMarchLimit; lightMarch++) {\n vLightProbe -= vLightStep;\n stepAccumulation += 1.;\n \n lightSample = clampedTexture(densityMap3D, vLightProbe);\n \n lightAbsorbance += blendSample(lightSample) * eInverseAbsorbance;\n\n if (lightAbsorbance >= 1.) {\n lightAbsorbance = 1.;\n break;\n }\n }\n\n lightAlbedo += (1. - lightAbsorbance) * spotAttenuation * spotLight.color;\n }\n }\n\n lightAlbedo *= density;\n albedo += lightAlbedo * baseColor.rgb * RECIPROCAL_PI;\n \n #endif\n\n albedo *= baseColorSample;\n }\n \n emissive = getBlackBodyRadiation(emissive.r);\n albedo += emissive;\n\n outgoingLight.rgb = vPoint.xyz;\n diffuseColor.a = saturate(density * opacity);\n\n if (density <= 0.) {\n discard;\n }\n\n #include <opaque_fragment>\n ");};}}
34
34
 
35
35
  const froxelPars="\n uniform vec3 froxelGrid;\n uniform float froxelColumns;\n uniform vec2 froxelRange;\n uniform mat4 froxelProjectionInverse;\n uniform int froxelSubSteps;\n\n float frameSliceDepth(float t) {\n return froxelRange.x * pow(froxelRange.y / froxelRange.x, t);\n }\n\n vec3 frameGetViewPosition(vec2 uv, float viewDepth) {\n vec4 nearPoint = froxelProjectionInverse * vec4(uv * 2.0 - 1.0, -1.0, 1.0);\n vec4 farPoint = froxelProjectionInverse * vec4(uv * 2.0 - 1.0, 1.0, 1.0);\n nearPoint /= nearPoint.w;\n farPoint /= farPoint.w;\n\n float t = (viewDepth + nearPoint.z) / (nearPoint.z - farPoint.z);\n\n return mix(nearPoint.xyz, farPoint.xyz, t);\n }\n";const noisePars="\n uniform float frameJitter;\n\n // NOTE Returns 0.5 when jitter is off (frameJitter is negative)\n float frameNoise(vec2 pixel) {\n if (frameJitter < 0.0) {\n return 0.5;\n }\n\n float noise = fract(52.9829189 * fract(dot(pixel, vec2(0.06711056, 0.00583715))));\n\n return fract(noise + frameJitter);\n }\n";const volumePars=`\n #define FRAME_WRAP_REPEAT ${Three.RepeatWrapping}\n #define FRAME_WRAP_MIRROR ${Three.MirroredRepeatWrapping}\n\n uniform mat4 frameViewToLocal;\n uniform mat4 frameLocalToView;\n uniform mat4 frameWorldToLocal;\n uniform float frameWorldScale;\n\n uniform highp sampler3D frameDensityMap;\n uniform highp sampler3D frameEmissiveMap;\n uniform highp sampler3D frameBaseColorMap;\n uniform highp sampler3D frameMaskMap;\n uniform bool frameUseEmissiveMap;\n uniform bool frameUseBaseColorMap;\n uniform bool frameUseMaskMap;\n\n uniform vec3 frameBaseColor;\n uniform vec3 frameScatterColor;\n uniform float frameExtinction;\n uniform float frameDensityScale;\n uniform float frameDensityCutoff;\n uniform float frameAbsorbance;\n uniform float frameOpacity;\n uniform float frameEmissiveIntensity;\n uniform vec2 frameTemperatureRange;\n uniform vec3 frameOffset3D;\n uniform int frameWrap3D;\n\n float frameMirrorAxis(float value) {\n float period = mod(value, 2.0);\n\n return period > 1.0 ? 2.0 - period : period;\n }\n\n vec3 frameWrapUv(vec3 uv) {\n if (frameWrap3D == FRAME_WRAP_REPEAT) {\n return fract(uv);\n }\n\n if (frameWrap3D == FRAME_WRAP_MIRROR) {\n return vec3(frameMirrorAxis(uv.x), frameMirrorAxis(uv.y), frameMirrorAxis(uv.z));\n }\n\n return uv;\n }\n\n // NOTE Mip level for a footprint of the given size in local units of the longest box side\n float frameLod(float localSize) {\n vec3 texels = vec3(textureSize(frameDensityMap, 0));\n\n return max(0.0, log2(localSize * max(texels.x, max(texels.y, texels.z))));\n }\n\n // NOTE Returns extinction for each world unit\n float frameSampleExtinction(vec3 localPosition, float lod) {\n vec3 uv = frameWrapUv(localPosition + 0.5 + frameOffset3D);\n float value = textureLod(frameDensityMap, uv, lod).r;\n\n if (frameUseMaskMap) {\n value *= textureLod(frameMaskMap, localPosition + 0.5, lod).r;\n }\n\n value = value > frameDensityCutoff ? value : 0.0;\n\n return value * frameDensityScale * frameExtinction / frameWorldScale;\n }\n\n bool frameInsideBox(vec3 localPosition) {\n vec3 localAbs = abs(localPosition);\n\n return max(localAbs.x, max(localAbs.y, localAbs.z)) <= 0.5;\n }\n\n float frameBoxExit(vec3 origin, vec3 direction) {\n vec3 inverseDirection = 1.0 / direction;\n vec3 tMin = (vec3(-0.5) - origin) * inverseDirection;\n vec3 tMax = (vec3(0.5) - origin) * inverseDirection;\n vec3 tFar = max(tMin, tMax);\n\n return max(0.0, min(tFar.x, min(tFar.y, tFar.z)));\n }\n\n // NOTE Returns the part of the segment from start to end (0.0 to 1.0) that is in the box\n vec2 frameBoxSegment(vec3 start, vec3 end) {\n vec3 inverseDirection = 1.0 / (end - start);\n vec3 tMin = (vec3(-0.5) - start) * inverseDirection;\n vec3 tMax = (vec3(0.5) - start) * inverseDirection;\n vec3 tNear = min(tMin, tMax);\n vec3 tFar = max(tMin, tMax);\n\n return vec2(\n max(0.0, max(tNear.x, max(tNear.y, tNear.z))),\n min(1.0, min(tFar.x, min(tFar.y, tFar.z)))\n );\n }\n\n vec3 frameSampleAlbedo(vec3 localPosition, float lod) {\n vec3 albedo = frameBaseColor;\n\n if (frameUseBaseColorMap) {\n albedo *= textureLod(frameBaseColorMap, frameWrapUv(localPosition + 0.5 + frameOffset3D), lod).rgb;\n }\n\n return albedo;\n }\n\n // NOTE Blackbody color, temperature in Kelvin. Source - https://www.shadertoy.com/view/4tdGWM\n vec3 frameBlackBody(float temperature) {\n if (temperature <= 0.0) {\n return vec3(0.0);\n }\n\n vec3 radiation = vec3(\n 1.0 / (exp(19E3 * 1.0 / temperature) - 1.0),\n 3.375 / (exp(19E3 * 1.5 / temperature) - 1.0),\n 8.0 / (exp(19E3 * 2.0 / temperature) - 1.0)\n );\n\n float peak = max(radiation.r, max(radiation.g, radiation.b));\n\n return peak > 0.0 ? radiation / peak : vec3(0.0);\n }\n\n vec3 frameSampleEmission(vec3 localPosition, float lod) {\n if (!frameUseEmissiveMap) {\n return vec3(0.0);\n }\n\n float value = textureLod(frameEmissiveMap, frameWrapUv(localPosition + 0.5 + frameOffset3D), lod).r;\n float temperature = mix(frameTemperatureRange.x, frameTemperatureRange.y, value);\n\n // NOTE Emitted power increases with temperature^4 (Stefan-Boltzmann). Range top gives 1.0.\n float power = pow(temperature / max(frameTemperatureRange.y, 1.0), 4.0);\n\n return frameBlackBody(temperature) * power * frameEmissiveIntensity;\n }\n`;const frameMaxShadowCascades=4;const gridPars="\n #define FRAME_MAX_CASCADES 4\n\n uniform vec3 frameGridMin[FRAME_MAX_CASCADES];\n uniform vec3 frameGridSize[FRAME_MAX_CASCADES];\n uniform vec3 frameGridVoxel[FRAME_MAX_CASCADES];\n uniform int frameGridCount;\n uniform float frameGridResolution;\n uniform float frameGridColumns;\n uniform vec2 frameGridAtlasSize;\n\n vec3 frameGridUvw(int cascade, vec3 worldPosition) {\n return (worldPosition - frameGridMin[cascade]) / frameGridSize[cascade];\n }\n\n // NOTE Half a voxel inside the cascade. Then the linear filter does not read the clamped edge.\n bool frameInsideGrid(vec3 gridUvw) {\n float inset = 0.5 / frameGridResolution;\n\n return all(greaterThanEqual(gridUvw, vec3(inset))) && all(lessThanEqual(gridUvw, vec3(1.0 - inset)));\n }\n\n // NOTE Finest cascade from the first cascade that contains the position, or -1\n int frameFindCascade(vec3 worldPosition, int firstCascade) {\n for (int i = 0; i < FRAME_MAX_CASCADES; i++) {\n if (i >= frameGridCount) {\n break;\n }\n\n if (i >= firstCascade && frameInsideGrid(frameGridUvw(i, worldPosition))) {\n return i;\n }\n }\n\n return -1;\n }\n\n // NOTE Hardware bilinear filter in the slice, manual linear filter between slices\n vec4 frameSampleGrid(sampler2D atlas, int cascade, vec3 gridUvw) {\n float resolution = frameGridResolution;\n float z = gridUvw.z * resolution - 0.5;\n float z0 = clamp(floor(z), 0.0, resolution - 1.0);\n float z1 = min(z0 + 1.0, resolution - 1.0);\n float blend = clamp(z - z0, 0.0, 1.0);\n vec2 xy = clamp(gridUvw.xy * resolution, vec2(0.5), vec2(resolution - 0.5));\n\n float tileOffset = float(cascade) * resolution;\n float tile0Index = tileOffset + z0;\n float tile1Index = tileOffset + z1;\n vec2 tile0 = vec2(mod(tile0Index, frameGridColumns), floor(tile0Index / frameGridColumns));\n vec2 tile1 = vec2(mod(tile1Index, frameGridColumns), floor(tile1Index / frameGridColumns));\n\n vec4 sample0 = texture(atlas, (tile0 * resolution + xy) / frameGridAtlasSize);\n vec4 sample1 = texture(atlas, (tile1 * resolution + xy) / frameGridAtlasSize);\n\n return mix(sample0, sample1, blend);\n }\n\n // NOTE Transmittance of the shadow slots at a world position. 1.0 outside the grid.\n void frameSampleShadows(\n sampler2D map0,\n sampler2D map1,\n vec3 worldPosition,\n out vec4 shadow0,\n out vec4 shadow1\n ) {\n shadow0 = vec4(1.0);\n shadow1 = vec4(1.0);\n\n int cascade = frameFindCascade(worldPosition, 0);\n\n if (cascade < 0) {\n return;\n }\n\n vec3 gridUvw = frameGridUvw(cascade, worldPosition);\n\n shadow0 = frameSampleGrid(map0, cascade, gridUvw);\n shadow1 = frameSampleGrid(map1, cascade, gridUvw);\n\n // NOTE Blend to the next cascade near the sides of this cascade. Then cascade borders are not visible.\n vec3 sideDistance = min(gridUvw, 1.0 - gridUvw);\n float blend = 1.0 - smoothstep(0.0, 0.1, min(sideDistance.x, min(sideDistance.y, sideDistance.z)));\n int nextCascade = blend > 0.0 ? frameFindCascade(worldPosition, cascade + 1) : -1;\n\n if (nextCascade >= 0) {\n vec3 nextUvw = frameGridUvw(nextCascade, worldPosition);\n\n shadow0 = mix(shadow0, frameSampleGrid(map0, nextCascade, nextUvw), blend);\n shadow1 = mix(shadow1, frameSampleGrid(map1, nextCascade, nextUvw), blend);\n }\n }\n\n // NOTE Cascade and grid position of an atlas pixel\n int frameGetGridCell(vec2 fragCoord, out vec3 gridUvw) {\n float resolution = frameGridResolution;\n vec2 tile = floor(fragCoord / resolution);\n float tileIndex = tile.y * frameGridColumns + tile.x;\n float cascade = floor(tileIndex / resolution);\n float z = tileIndex - cascade * resolution;\n\n gridUvw = vec3(fragCoord - tile * resolution, z + 0.5) / resolution;\n\n return int(cascade);\n }\n";const tileQuadVertexShader="\n uniform vec4 frameRect;\n uniform float frameSliceStart;\n uniform vec2 frameTileSize;\n uniform float frameTileColumns;\n uniform vec2 frameTargetSize;\n\n void main() {\n float slice = frameSliceStart + float(gl_InstanceID);\n vec2 tile = vec2(mod(slice, frameTileColumns), floor(slice / frameTileColumns));\n vec2 cell = mix(frameRect.xy, frameRect.zw, position.xy * 0.5 + 0.5);\n\n gl_Position = vec4((tile * frameTileSize + cell) / frameTargetSize * 2.0 - 1.0, 0.0, 1.0);\n }\n";const createVolumeUniforms=()=>({frameViewToLocal:{value:new Three.Matrix4},frameLocalToView:{value:new Three.Matrix4},frameWorldToLocal:{value:new Three.Matrix4},frameWorldScale:{value:1},frameDensityMap:{value:null},frameEmissiveMap:{value:null},frameBaseColorMap:{value:null},frameMaskMap:{value:null},frameUseEmissiveMap:{value:!1},frameUseBaseColorMap:{value:!1},frameUseMaskMap:{value:!1},frameBaseColor:{value:new Three.Color(16777215)},frameScatterColor:{value:new Three.Color(0)},frameExtinction:{value:100},frameDensityScale:{value:1},frameDensityCutoff:{value:0},frameAbsorbance:{value:1},frameOpacity:{value:1},frameEmissiveIntensity:{value:1},frameTemperatureRange:{value:new Three.Vector2(0,6500)},frameOffset3D:{value:new Three.Vector3},frameWrap3D:{value:Three.ClampToEdgeWrapping}});const setVolumeUniforms=(e,a,n)=>{e.frameViewToLocal.value.copy(n.viewToLocal),e.frameLocalToView.value.copy(n.localToView),e.frameWorldToLocal.value.copy(n.worldToLocal),e.frameWorldScale.value=n.matrixWorld.getMaxScaleOnAxis(),e.frameDensityMap.value=n.densityMap3D,e.frameEmissiveMap.value=n.emissiveMap3D,e.frameBaseColorMap.value=n.baseColorMap3D,e.frameMaskMap.value=n.maskMap3D,e.frameUseEmissiveMap.value=!!n.emissiveMap3D,e.frameUseBaseColorMap.value=!!n.baseColorMap3D,e.frameUseMaskMap.value=!!n.maskMap3D,e.frameBaseColor.value.copy(a.baseColor),e.frameScatterColor.value.copy(a.scatterColor),e.frameExtinction.value=a.extinction,e.frameDensityScale.value=a.densityScale,e.frameDensityCutoff.value=a.densityCutoff,e.frameAbsorbance.value=a.absorbance,e.frameOpacity.value=a.opacity,e.frameEmissiveIntensity.value=a.emissiveIntensity,e.frameTemperatureRange.value.copy(a.temperatureRange),e.frameOffset3D.value.copy(a.offset3D),e.frameWrap3D.value=a.wrap3D;};
36
36
 
@@ -58,10 +58,10 @@ const gridOptions$1=["emissiveGrid","baseColorGrid","maskGrid"],findGrid=(e,o)=>
58
58
 
59
59
  function wvdbKernel(e,t){const n=512,r={},a={},s={},l=e=>255*Math.min(Math.max(e,0),1)|0,o=(e,t)=>{if(1===t.length)return e.fill(t[0]);for(let n=0;n<e.length;n+=t.length)e.set(t,n);return e},i=(e,[t,r,a],s,l,o=1)=>{const i=Math.ceil(t/8),c=Math.ceil(r/8);for(let d=0;d<s.length;d++){const f=s[d],h=f%i*8,g=Math.floor(f/i)%c*8,u=8*Math.floor(f/(i*c));let p=d*n;for(let n=0;n<8;n++)for(let s=0;s<8;s++){const i=(g+s)*t+(u+n)*t*r,c=g+s<r&&u+n<a;for(let n=0;n<8;n++,p++)if(c&&h+n<t){const t=(h+n+i)*o,r=p*o;for(let n=0;n<o;n++)e[t+n]=l[r+n];}}}},c=e=>e.backgroundTexel.length;return {fillTexels:o,create:({readerId:e,config:t})=>{r[e]=t;},release:({readerId:e})=>{delete r[e],[a,s].forEach((t=>Object.keys(t).filter((t=>t.startsWith(`${e}:`))).forEach((e=>delete t[e]))));},chunk:async({readerId:d,gridIndex:f,lod:h,kind:g,firstIndex:u,bytes:p})=>{const m=r[d];if(!m)return null;const w=m.grids[f],y=w.lods[h],M=`${d}:${f}`,k=`${M}:${h}`,x=c(w),[b,I,A]=y.dimensions,T=await(async e=>{const t=new Blob([e]).stream().pipeThrough(new DecompressionStream("deflate"));return new Uint8Array(await new Response(t).arrayBuffer())})(p);a[k]||(a[k]={data:o(new Uint8Array(b*I*A*x),w.backgroundTexel),pagesLeft:y.pages,mask:null});const U=a[k];if("mask"===g)U.mask=t(T);else {const t=s[M];t&&U.pagesLeft===y.pages&&(((e,[t,n,r],a,[s,l,o],i)=>{const c=t*n,d=(e,t)=>{const n=new Int32Array(e),r=new Int32Array(e),a=new Float32Array(e);for(let s=0;s<e;s++){const l=Math.min(Math.max((s+.5)/e*t-.5,0),t-1);n[s]=Math.floor(l),r[s]=Math.min(n[s]+1,t-1),a[s]=l-n[s];}return {lower:n,upper:r,weight:a}},f=d(s,t),h=d(l,n),g=d(o,r);let u=0;for(let n=0;n<o;n++){const r=g.lower[n]*c,o=g.upper[n]*c,d=g.weight[n];for(let n=0;n<l;n++){const l=h.lower[n]*t,c=h.upper[n]*t,g=h.weight[n];for(let t=0;t<s;t++){const n=f.lower[t],s=f.upper[t],h=f.weight[t];for(let t=0;t<i;t++){const f=n=>e[n*i+t],p=f(n+l+r)+(f(s+l+r)-f(n+l+r))*h,m=f(n+c+r)+(f(s+c+r)-f(n+c+r))*h,w=f(n+l+o)+(f(s+l+o)-f(n+l+o))*h,y=p+(m-p)*g,M=w+(f(n+c+o)+(f(s+c+o)-f(n+c+o))*h-w)*g;a[u++]=y+(M-y)*d+.5;}}}}})(t.data,t.dimensions,U.data,y.dimensions,x),delete s[M],U.mask&&((e,t,r)=>{const a=c(t),s=o(new Uint8Array(n*a),t.backgroundTexel),l=Math.ceil(r[0]/8)*Math.ceil(r[1]/8)*Math.ceil(r[2]/8);for(let t=0;t<l;t++)e.mask[t>>>5]&1<<(31&t)||i(e.data,r,[t],s,a);})(U,w,y.dimensions));const{indices:r,values:a}=e(T,{layout:y.layout,bits:m.bits,firstIndex:u,channels:w.channels});i(U.data,y.dimensions,r,((e,t,{class:r,background:a,channels:s,scale:o})=>{if(3===s){const r=new Uint8Array(t*n*4);for(let a=0;a<t;a++){const t=a*n*3,s=a*n*4;for(let a=0;a<n;a++){for(let o=0;o<3;o++)r[s+4*a+o]=l(e[t+o*n+a]);r[s+4*a+3]=255;}}return r}const i=new Uint8Array(e.length),c="level set"===r;for(let t=0;t<e.length;t++)i[t]=c?Math.abs(e[t])<a?255:0:l(e[t]/o);return i})(a,r.length,w),x),U.pagesLeft--;}const $=0===U.pagesLeft;if(!$&&("mask"===g||1===y.pages))return null;if($){delete a[k];const e=w.lods[h+1];e&&e.pages>1&&(s[M]={data:U.data.slice(),dimensions:y.dimensions});}return {type:"texture",gridIndex:f,lod:h,complete:$,data:$?U.data:U.data.slice()}}}}
60
60
 
61
- const{fillTexels:fillTexels}=wvdbKernel(decodeWVDBPage,decodeWVDBMask),workerSource=`\nconst kernel = (${wvdbKernel.toString()})(\n (${decodeWVDBPage.toString()}),\n (${decodeWVDBMask.toString()})\n);\nlet queue = Promise.resolve();\n\nself.onmessage = ({ data }) => {\n queue = queue.then(async () => {\n try {\n const result = await kernel[data.type](data.payload);\n\n self.postMessage({ result }, result && result.data ? [result.data.buffer] : []);\n } catch (error) {\n self.postMessage({ error: String((error && error.message) || error) });\n }\n });\n};\n`,createWorkerBackend=e=>{const t=new Worker(e),a=[];return t.onmessage=({data:e})=>{const t=a.shift();e.error?t?.reject(e.error):t?.resolve(e.result);},t.onerror=e=>{e.preventDefault(),a.splice(0).forEach((({reject:t})=>t(e.message||"Worker error")));},{request:(e,s,r=[])=>new Promise(((n,d)=>{a.push({resolve:n,reject:d}),t.postMessage({type:e,payload:s},r);}))}},createLocalBackend=()=>{const e=wvdbKernel(decodeWVDBPage,decodeWVDBMask);let t=Promise.resolve();return {request:(a,s)=>{const r=t.then((()=>e[a](s)));return t=r.catch((()=>null)),r}}};let backends=null,nextReaderId=0;const getBackend=e=>{if(!backends)if("undefined"!=typeof Worker&&"undefined"!=typeof Blob&&"undefined"!=typeof URL){const e=URL.createObjectURL(new Blob([workerSource],{type:"text/javascript"})),t="undefined"!=typeof navigator&&navigator.hardwareConcurrency||2;backends=Array.from({length:Math.min(Math.max(t-1,1),4)},(()=>createWorkerBackend(e)));}else backends=[createLocalBackend()];return backends[e%backends.length]},toByte=e=>255*Math.min(Math.max(e,0),1)|0,getBackgroundTexel=({class:e,background:t,channels:a=1},s)=>3===a?[...t.map(toByte),255]:["level set"===e?0:toByte(t/s)];class WVDBReader{url=null;header=null;grids={};finalLod=0;loadedLod=-1;bytesLoaded=0;bytesTotal=0;done=!1;cancelled=!1;dataStart=0;streamReader=null;constructor({maxLod:e=1/0,scales:t={}}={}){this.maxLod=e,this.scales=t,this.readerId=nextReaderId++,this.backend=getBackend(this.readerId);}async load(e,{onHeader:t,onUpdate:a,untilLod:s=1/0}={}){return this.url=e,await this.read(s,{onHeader:t,onUpdate:a}),this}async loadMore(e,{onUpdate:t}={}){if(!this.header)throw new Error("Call load() first.");return await this.read(e,{onUpdate:t}),this}async read(e,{onHeader:t,onUpdate:a}){if(this.cancelled||this.header&&this.loadedLod>=Math.min(e,this.finalLod))return;let s=null,r=0,n={};if(this.header){s=this.getChunks(e),r=this.dataStart+s[0].offset;const t=this.dataStart+Math.max(...s.map((({offset:e,length:t})=>e+t)));n={Range:`bytes=${r}-${t-1}`};}const d=await fetch(this.url,{headers:n}).catch((e=>{if(n.Range)return fetch(this.url);throw e}));if(!d.ok)throw new Error(`Could not load ${this.url} (${d.status}).`);if(!d.body)throw new Error("Streams are not supported.");const o=206===d.status?r:0;this.streamReader=d.body.getReader();let l=new Uint8Array(0),i=0,h=0;const c=[],u=e=>{if(i+e.length>l.length){const t=new Uint8Array(Math.max(2*l.length,i+e.length));t.set(l.subarray(0,i)),l=t;}l.set(e,i),i+=e.length;},g=()=>{for(;h<s.length;){const e=s[h],t=this.dataStart+e.offset-o;if(t+e.length>i)break;h++;const r=l.slice(t,t+e.length),n=this.backend.request("chunk",{readerId:this.readerId,gridIndex:e.gridIndex,lod:e.lod,kind:e.kind,firstIndex:e.firstIndex,bytes:r},[r.buffer]);c.push(n.then((t=>{this.cancelled||this.apply(e,t,a);})));}};for(;!this.cancelled;){const{done:r,value:n}=await this.streamReader.read();if(r||this.cancelled)break;if(u(n),this.bytesLoaded+=n.length,!this.header){const r=readWVDBHeader(l.subarray(0,i));if(!r)continue;this.header=r.header,this.dataStart=r.dataStart,this.prepare(),s=this.getChunks(e),t&&t(this),s.length||(this.done=!0,this.loadedLod=this.finalLod,a&&a({reader:this,grid:null,lod:-1,lodComplete:!0,done:!0}));}if(g(),h===s.length){this.streamReader.cancel().catch((()=>{}));break}}if(!this.cancelled&&(!s||h<s.length))throw new Error("The WVDB file is incomplete.");await Promise.all(c),s.length&&(this.loadedLod=Math.max(...s.map((({lod:e})=>e)))),this.done=this.loadedLod>=this.finalLod,this.done&&this.release();}getChunks(e){return getWVDBChunks(this.header,Math.min(e,this.finalLod)).filter((({lod:e})=>e>this.loadedLod))}prepare(){const{grids:e,bake:t}=this.header,a=({name:e})=>this.scales[e]||1;this.finalLod=Math.min(Math.max(Math.floor(this.maxLod),0),t.lods-1),this.bytesTotal=this.dataStart+getWVDBChunks(this.header,this.finalLod).reduce(((e,{offset:t,length:a})=>Math.max(e,t+a)),0),e.forEach((e=>{const t=getBackgroundTexel(e,a(e));this.grids[e.name]={name:e.name,class:e.class,channels:e.channels||1,bbox:e.bbox,texelSize:t.length,dimensions:[1,1,1],data:fillTexels(new Uint8Array(t.length),t),lod:-1,chunksLeft:e.lods.map((({mask:e,pages:t})=>t.length+(e?1:0)))};})),this.backend.request("create",{readerId:this.readerId,config:{bits:t.bits,grids:e.map((e=>({class:e.class,background:e.background,channels:e.channels||1,scale:a(e),backgroundTexel:getBackgroundTexel(e,a(e)),lods:e.lods.map((({dimensions:e,mask:t,pages:a})=>({dimensions:e,layout:t?wvdbLayouts.sparse:wvdbLayouts.dense,pages:a.length})))})))}});}apply(e,t,a){const s=this.grids[this.header.grids[e.gridIndex].name],{lod:r}=e;s.chunksLeft[r]--,t&&(s.data=t.data,s.dimensions=this.header.grids[e.gridIndex].lods[r].dimensions,s.lod=r);const n=0===s.chunksLeft[r];a&&(t||n)&&a({reader:this,grid:s,lod:r,lodComplete:n,done:this.isComplete()});}isComplete(){return Object.values(this.grids).every((({chunksLeft:e})=>e.slice(0,this.finalLod+1).every((e=>0===e))))}release(){this.backend.request("release",{readerId:this.readerId}).catch((()=>{}));}cancel(){this.cancelled||(this.cancelled=!0,this.streamReader?.cancel().catch((()=>{})),this.release());}}
61
+ const{fillTexels:fillTexels}=wvdbKernel(decodeWVDBPage,decodeWVDBMask),workerSource=`\nconst kernel = (${wvdbKernel.toString()})(\n (${decodeWVDBPage.toString()}),\n (${decodeWVDBMask.toString()})\n);\nlet queue = Promise.resolve();\n\nself.onmessage = ({ data }) => {\n queue = queue.then(async () => {\n try {\n const result = await kernel[data.type](data.payload);\n\n self.postMessage({ result }, result && result.data ? [result.data.buffer] : []);\n } catch (error) {\n self.postMessage({ error: String((error && error.message) || error) });\n }\n });\n};\n`,createWorkerBackend=e=>{const t=new Worker(e),s=[];return t.onmessage=({data:e})=>{const t=s.shift();e.error?t?.reject(e.error):t?.resolve(e.result);},t.onerror=e=>{e.preventDefault(),s.splice(0).forEach((({reject:t})=>t(e.message||"Worker error")));},{request:(e,r,a=[])=>new Promise(((i,d)=>{s.push({resolve:i,reject:d}),t.postMessage({type:e,payload:r},a);}))}},createLocalBackend=()=>{const e=wvdbKernel(decodeWVDBPage,decodeWVDBMask);let t=Promise.resolve();return {request:(s,r)=>{const a=t.then((()=>e[s](r)));return t=a.catch((()=>null)),a}}};let backends=null,nextReaderId=0;const getBackend=e=>{if(!backends)if("undefined"!=typeof Worker&&"undefined"!=typeof Blob&&"undefined"!=typeof URL){const e=URL.createObjectURL(new Blob([workerSource],{type:"text/javascript"})),t="undefined"!=typeof navigator&&navigator.hardwareConcurrency||2;backends=Array.from({length:Math.min(Math.max(t-1,1),4)},(()=>createWorkerBackend(e)));}else backends=[createLocalBackend()];return backends[e%backends.length]},toByte=e=>255*Math.min(Math.max(e,0),1)|0,getBackgroundTexel=({class:e,background:t,channels:s=1},r)=>3===s?[...t.map(toByte),255]:["level set"===e?0:toByte(t/r)];class WVDBReader{url=null;header=null;grids={};finalLod=0;loadedLod=-1;bytesLoaded=0;bytesTotal=0;done=!1;cancelled=!1;dataStart=0;chunks=[];streamReader=null;buffer=new Uint8Array(0);received=0;waiters=[];streamError=null;streamEnded=!1;constructor({maxLod:e=1/0,scales:t={}}={}){this.maxLod=e,this.scales=t,this.readerId=nextReaderId++,this.backend=getBackend(this.readerId),this.headerReady=new Promise(((e,t)=>{this.resolveHeader=e,this.rejectHeader=t;})),this.headerReady.catch((()=>{}));}async load(e,{onHeader:t,onUpdate:s,untilLod:r=1/0}={}){return this.start(e,{onHeader:t}),await this.decode(r,{onUpdate:s}),this}async loadMore(e,{onUpdate:t}={}){return await this.decode(e,{onUpdate:t}),this}start(e,{onHeader:t}={}){return this.streaming||(this.url=e,this.streaming=this.stream(t).catch((e=>{this.streamError=e,this.rejectHeader(e),this.wakeWaiters();}))),this.streaming}async stream(e){const t=await fetch(this.url);if(!t.ok)throw new Error(`Could not load ${this.url} (${t.status}).`);if(!t.body)throw new Error("Streams are not supported.");for(this.streamReader=t.body.getReader();!this.cancelled;){const{done:t,value:s}=await this.streamReader.read();if(t||this.cancelled)break;if(this.receive(s,e),this.received>=this.bytesTotal){this.streamReader.cancel().catch((()=>{}));break}}if(this.streamEnded=!0,!this.header&&!this.cancelled)throw new Error("Not a complete WVDB file.");this.wakeWaiters();}receive(e,t){if(this.append(e),!this.header){const e=readWVDBHeader(this.buffer.subarray(0,this.received));e&&(this.header=e.header,this.dataStart=e.dataStart,this.prepare(),this.chunks=getWVDBChunks(this.header,this.finalLod),t&&t(this),this.resolveHeader());}this.wakeWaiters();}async loadLods(e,t,{onHeader:s,onUpdate:r}={}){this.url=e;const a=this.fetchPrefix(t,s).catch((e=>{this.streamError=e,this.rejectHeader(e),this.wakeWaiters();}));return await this.decode(t,{onUpdate:r}),await a,this}getLodEnd(e){return this.chunks.filter((t=>t.lod<=e)).reduce(((e,{offset:t,length:s})=>Math.max(e,this.dataStart+t+s)),this.dataStart)}async fetchPrefix(e,t){const s=()=>this.header?this.getLodEnd(Math.min(e,this.finalLod)):16384;for(;!this.cancelled&&this.received<s();){const e=this.received;if(await this.fetchRange(s,t),this.received===e&&!this.cancelled)throw new Error("The WVDB file is incomplete.")}}async fetchRange(e,t){const s=e(),r=s+Math.ceil(s/1e3)+1024-1,a=await fetch(this.url,{headers:{Range:`bytes=0-${r}`}});if(!a.ok)throw new Error(`Could not load ${this.url} (${a.status}).`);this.streamReader=a.body.getReader();let i=0;try{for(;!this.cancelled;){const{done:s,value:r}=await this.streamReader.read();if(s)break;const a=Math.min(Math.max(this.received-i,0),r.length);if(i+=r.length,this.bytesLoaded+=a,a<r.length&&this.receive(r.subarray(a),t),this.header&&this.received>=e()){this.streamReader.cancel().catch((()=>{}));break}}}catch(t){if(!this.header||this.received<e())throw t}}append(e){if(!this.buffer)return;const t=this.header?this.bytesTotal:1/0,s=Math.max(0,Math.min(e.length,t-this.received));if(this.received+s>this.buffer.length){const e=this.header?this.bytesTotal:Math.max(2*this.buffer.length,this.received+s),t=new Uint8Array(e);t.set(this.buffer.subarray(0,this.received)),this.buffer=t;}this.buffer.set(e.subarray(0,s),this.received),this.received+=s,this.bytesLoaded+=e.length;}wakeWaiters(){this.waiters=this.waiters.filter((({end:e,resolve:t,reject:s})=>{if(this.received>=e)t();else {if(!(this.streamError||this.cancelled||this.streamEnded))return !0;s(this.streamError||new Error("The WVDB file is incomplete."));}return !1}));}waitForBytes(e){return new Promise(((t,s)=>{this.waiters.push({end:e,resolve:t,reject:s}),this.wakeWaiters();}))}async decode(e,{onUpdate:t}={}){await this.headerReady;const s=Math.min(e,this.finalLod);if(this.cancelled||s<=this.loadedLod)return;const r=this.chunks.filter((e=>e.lod>this.loadedLod&&e.lod<=s)),a=[];this.loadedLod=s,!this.chunks.length&&t&&t({reader:this,grid:null,lod:-1,lodComplete:!0,done:!0});for(const e of r){const s=this.dataStart+e.offset;if(await this.waitForBytes(s+e.length),this.cancelled)return;const r=this.buffer.slice(s,s+e.length),i=this.backend.request("chunk",{readerId:this.readerId,gridIndex:e.gridIndex,lod:e.lod,kind:e.kind,firstIndex:e.firstIndex,bytes:r},[r.buffer]);a.push(i.then((s=>{this.cancelled||this.apply(e,s,t);})));}await Promise.all(a),this.done=this.loadedLod>=this.finalLod,this.done&&(this.buffer=null,this.release());}prepare(){const{grids:e,bake:t}=this.header,s=({name:e})=>this.scales[e]||1;this.finalLod=Math.min(Math.max(Math.floor(this.maxLod),0),t.lods-1),this.bytesTotal=this.dataStart+getWVDBChunks(this.header,this.finalLod).reduce(((e,{offset:t,length:s})=>Math.max(e,t+s)),0),e.forEach((e=>{const t=getBackgroundTexel(e,s(e));this.grids[e.name]={name:e.name,class:e.class,channels:e.channels||1,bbox:e.bbox,texelSize:t.length,dimensions:[1,1,1],data:fillTexels(new Uint8Array(t.length),t),lod:-1,chunksLeft:e.lods.map((({mask:e,pages:t})=>t.length+(e?1:0)))};})),this.backend.request("create",{readerId:this.readerId,config:{bits:t.bits,grids:e.map((e=>({class:e.class,background:e.background,channels:e.channels||1,scale:s(e),backgroundTexel:getBackgroundTexel(e,s(e)),lods:e.lods.map((({dimensions:e,mask:t,pages:s})=>({dimensions:e,layout:t?wvdbLayouts.sparse:wvdbLayouts.dense,pages:s.length})))})))}});}apply(e,t,s){const r=this.grids[this.header.grids[e.gridIndex].name],{lod:a}=e;r.chunksLeft[a]--,t&&(r.data=t.data,r.dimensions=this.header.grids[e.gridIndex].lods[a].dimensions,r.lod=a);const i=0===r.chunksLeft[a];s&&(t||i)&&s({reader:this,grid:r,lod:a,lodComplete:i,done:this.isComplete()});}isComplete(){return Object.values(this.grids).every((({chunksLeft:e})=>e.slice(0,this.finalLod+1).every((e=>0===e))))}release(){this.backend.request("release",{readerId:this.readerId}).catch((()=>{}));}cancel(){this.cancelled||(this.cancelled=!0,this.streamReader?.cancel().catch((()=>{})),this.rejectHeader(new Error("Cancelled.")),this.wakeWaiters(),this.release());}}
62
62
 
63
63
  const textureKeys=["densityMap3D","emissiveMap3D","baseColorMap3D","maskMap3D"],updateTexture=(e,{data:t,dimensions:[s,r,a]})=>{const{image:d}=e,o=d.width!==s||d.height!==r||d.depth!==a;e.image={data:t,width:s,height:r,depth:a},o&&e.dispose(),e.needsUpdate=!0;};class WVDBVolumeData{grids={};bindings=[];constructor(e){this.reader=e;}readGrids(){Object.values(this.reader.grids).forEach((e=>{const{name:t,class:s,channels:r,bbox:a}=e;this.grids[t]={name:t,isLevelSet:"level set"===s,isColor:3===r,bbox:a?new Three.Box3(new Three.Vector3(...a[0]),new Three.Vector3(...a[1])):new Three.Box3,get data(){return e.data},get dimensions(){return e.dimensions},get resolution(){return e.dimensions[0]},get lod(){return e.lod},source:e};}));}get lod(){return Math.min(...Object.values(this.reader.grids).map((({lod:e})=>e)))}get lods(){return this.reader.finalLod+1}get done(){return this.reader.done}attach(e){const t=Object.values(this.grids);return [...e.volumes||[],...e.materials||[]].forEach((s=>{textureKeys.forEach((r=>{const a=s[r],d=a&&t.find((({data:e})=>e===a.image?.data));d&&this.bindings.push({object:e,texture:a,grid:d.source});}));})),e}detach(e){this.bindings=this.bindings.filter((t=>t.object!==e));}updateTextures(){this.bindings.forEach((({texture:e,grid:t})=>{e.image.data!==t.data&&updateTexture(e,t);}));}dispose(){this.reader.cancel(),this.bindings=[];}}class WVDBLoader extends Three.Loader{maxLod=1/0;scales={};constructor(e){super(e);}setMaxLod(e){return this.maxLod=e,this}setGridScales(e){return this.scales={...e},this}load(e,t,s,r){const a=new WVDBReader({maxLod:this.maxLod,scales:this.scales}),d=new WVDBVolumeData(a);let o=!1;return a.load(e,{onHeader:()=>d.readGrids(),onUpdate:e=>{d.updateTextures(),!o&&d.lod>=0&&(o=!0,t&&t(d)),o&&s&&s({lod:e.lod,lods:d.lods,lodComplete:e.lodComplete,done:e.done,bytesLoaded:a.bytesLoaded,bytesTotal:a.bytesTotal});}}).catch((e=>{a.cancelled||(r?r(e):console.error(e));})),d}}
64
64
 
65
- const gridOptions=["emissiveGrid","baseColorGrid","maskGrid"];class WVDBAnimationLoader extends Three.Loader{options={};maxLod=1/0;concurrency=6;constructor(e){super(e);}setOptions(e){return this.options={...e},this}setMaxLod(e){return this.maxLod=e,this}setConcurrency(e){return this.concurrency=Math.max(1,e),this}load(e,o,a,t){const r=new FrameVolumeAnimation;return r.totalFrames=e.length,this.loadFrames(r,e,o,a).catch((e=>{r.disposed||(t?t(e):console.error(e));})),r}async loadFrames(e,o,a,t){const{emissiveGrid:r,emissiveScale:n=1}=this.options,i="string"==typeof r?{[r]:n}:{},s=o.map((()=>new WVDBReader({maxLod:this.maxLod,scales:i}))),m=s.map((e=>new WVDBVolumeData(e))),d=new Array(o.length);let l=0;const c=[],u=()=>(e.disposed&&s.forEach((e=>e.cancel())),e.disposed),h=e=>{c[e]=(c[e]||0)+1,t&&t({lod:e,loadedFrames:c[e],totalFrames:o.length});},p=async e=>{let a=0;await Promise.all(Array.from({length:Math.min(this.concurrency,o.length)},(async()=>{for(;a<o.length&&!u();)await e(a++);})));},f=e=>()=>{u()||m[e].updateTextures();};if(await p((async a=>{await s[a].load(o[a],{untilLod:0,onHeader:()=>m[a].readGrids(),onUpdate:f(a)}),u()||(d[a]=this.createFrame(m[a],e),(()=>{for(;l<d.length&&d[l];)e.addFrame(d[l]),l++,e.loadedFrames=l;})(),h(0));})),u())return;a&&a(e);const y=Math.max(0,...s.map((({finalLod:e})=>e)));for(let e=1;e<=y;e++)if(await p((async o=>{s[o].finalLod<e||(await s[o].loadMore(e,{onUpdate:f(o)}),u()||h(e));})),u())return}createFrame(e,o){const{grids:a}=e,t={...this.options};gridOptions.forEach((e=>{const o="string"==typeof t[e]?a[t[e]]:null;delete t[e],o&&(t[e]=o);}));const r=gridOptions.map((e=>t[e])).filter(Boolean),n=a.density&&!r.includes(a.density)&&a.density||Object.values(a).find((e=>!e.isColor&&!r.includes(e)));return n?(gridOptions.forEach((e=>{t[e]&&(t[e]=t[e].data);})),n.bbox.isEmpty()||o.bbox.union(n.bbox),e.attach(new FrameVolume(n.data,{...t,resolution:n.resolution,bbox:n.bbox}))):new FrameVolume(new Uint8Array(1),{resolution:1})}}
65
+ const gridOptions=["emissiveGrid","baseColorGrid","maskGrid"];class WVDBAnimationLoader extends Three.Loader{options={};maxLod=1/0;concurrency=1/0;constructor(e){super(e);}setOptions(e){return this.options={...e},this}setMaxLod(e){return this.maxLod=e,this}setConcurrency(e){return this.concurrency=Math.max(1,e),this}load(e,o,a,t){const r=new FrameVolumeAnimation;r.totalFrames=e.length;const n=e.map((()=>new WVDBReader({maxLod:this.maxLod,scales:this.getScales()})));return this.loadFrames(r,e,n,o,a).catch((e=>{n.forEach((e=>e.cancel())),r.disposed||(t?t(e):console.error(e));})),r}getScales(){const{emissiveGrid:e,emissiveScale:o=1}=this.options;return "string"==typeof e?{[e]:o}:{}}async loadFrames(e,o,a,t,r){const n=a.map((e=>new WVDBVolumeData(e))),s=new Array(o.length);let i=0;const m=[],d=()=>(e.disposed&&a.forEach((e=>e.cancel())),e.disposed),l=e=>{m[e]=(m[e]||0)+1,r&&r({lod:e,loadedFrames:m[e],totalFrames:o.length});},c=async e=>{let a=0;await Promise.all(Array.from({length:Math.min(this.concurrency,o.length)},(async()=>{for(;a<o.length&&!d();)await e(a++);})));},u=e=>()=>{d()||n[e].updateTextures();};if(await c((async t=>{await a[t].loadLods(o[t],0,{onHeader:()=>n[t].readGrids(),onUpdate:u(t)}),d()||(s[t]=this.createFrame(n[t],e),(()=>{for(;i<s.length&&s[i];)e.addFrame(s[i]),i++,e.loadedFrames=i;})(),l(0));})),d())return;t&&t(e);const h=Math.max(0,...a.map((({finalLod:e})=>e)));for(let e=1;e<=h;e++)if(await c((async t=>{a[t].finalLod<e||(await a[t].loadLods(o[t],e,{onUpdate:u(t)}),d()||l(e));})),d())return}createFrame(e,o){const{grids:a}=e,t={...this.options};gridOptions.forEach((e=>{const o="string"==typeof t[e]?a[t[e]]:null;delete t[e],o&&(t[e]=o);}));const r=gridOptions.map((e=>t[e])).filter(Boolean),n=a.density&&!r.includes(a.density)&&a.density||Object.values(a).find((e=>!e.isColor&&!r.includes(e)));return n?(gridOptions.forEach((e=>{t[e]&&(t[e]=t[e].data);})),n.bbox.isEmpty()||o.bbox.union(n.bbox),e.attach(new FrameVolume(n.data,{...t,resolution:n.resolution,bbox:n.bbox}))):new FrameVolume(new Uint8Array(1),{resolution:1})}}
66
66
 
67
67
  export { VolumeToBbox as Bbox, VolumeToFog as FogVolume, FrameVolume, FrameVolumeAnimation, VolumeToLevelSet as LevelSet, MeshVolume, VolumeToPoints as Points, VDBAnimationLoader, VDBLoader, VolumeBasicMaterial, VolumeNormalMaterial, WVDBAnimationLoader, WVDBLoader, WVDBReader, frameLights, frameRenderer, lights };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-openvdb",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "three.js rendering of OpenVDB volumes: loaders, fog and frame volumes, materials.",
5
5
  "main": "./index.cjs.js",
6
6
  "module": "./index.js",
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "scripts": {},
14
14
  "dependencies": {
15
- "openvdb": "^0.5.0"
15
+ "openvdb": "^0.6.0"
16
16
  },
17
17
  "peerDependencies": {
18
18
  "three": ">=0.186.0"
@@ -39,5 +39,10 @@
39
39
  "url": "https://discourse.threejs.org/u/notchris"
40
40
  }
41
41
  ],
42
- "license": "MIT"
42
+ "license": "MIT",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/mjurczyk/openvdb.git",
46
+ "directory": "src/three-openvdb"
47
+ }
43
48
  }