UNPKG

@arcgis/core

Version:

ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API

3 lines (2 loc) • 12.4 kB
/* COPYRIGHT Esri - https://js.arcgis.com/5.0.8/LICENSE.txt */ import{__decorate as e}from"tslib";import"../../../core/has.js";import t from"../../../core/Logger.js";import{removeMaybe as s}from"../../../core/maybe.js";import{watch as i,syncAndInitial as r}from"../../../core/reactiveUtils.js";import{unitFromRESTJSON as a,convertUnit as n}from"../../../core/units.js";import{property as o,subclass as l}from"../../../core/accessorSupport/decorators.js";import{fromValues as h}from"../../../core/libs/gl-matrix-2/factories/quatf64.js";import{getReferenceEllipsoid as u}from"../../../geometry/ellipsoidUtils.js";import d from"../../../geometry/HeightModelInfo.js";import{isCompatible as m}from"../../../geometry/support/heightModelInfoUtils.js";import{isPlateCarree as p,isWebMercator as c,equals as f,isGeographic as g}from"../../../geometry/support/spatialReferenceUtils.js";import y from"../../../geometry/support/WKIDUnitConversion.js";import{getHeightModelInfoFromVCS as _}from"../../../layers/support/tiles3DUtils.js";import{getElevationOffsetInMeters as w}from"../../../support/elevationInfoUtils.js";import{getMetersPerUnit as v}from"../../../symbols/support/unitConversionUtils.js";import{GaussianSplatLayerViewPerformanceInfo as b}from"./GaussianSplatLayerViewPerformanceInfo.js";import{LayerView3D as S}from"./LayerView3D.js";import{addLayerViewToWasm as C,getLyr3DWasm as x,removeLayerViewFromWasm as M}from"./Lyr3DWasm.js";import{LayerElevationProvider as H}from"./i3s/LayerElevationProvider.js";import{ElevationRange as j}from"../support/ElevationRange.js";import{Obb as E}from"../support/orientedBoundingBox.js";import{splatPageSizeInU32 as P,elementsPerSplatPage as U,packedGaussianSizeInU32 as G,splatPageSizeInU32WithoutHeader as V,gaussiansPerSplatPage as A,splatAtlasTextureWidth as I}from"../support/gaussianSplatting/GaussianSplatAtlasPages.js";import{GaussianSplatDataStore as R}from"../support/gaussianSplatting/GaussianSplatDataStore.js";import{GaussianSplatFadeHelper as T}from"../support/gaussianSplatting/GaussianSplatFadeHelper.js";import{GaussianSplatIntersectionHandler as O}from"../support/gaussianSplatting/GaussianSplatIntersectionHandler.js";import{GaussianTile as L}from"../support/gaussianSplatting/GaussianTile.js";import{GaussianSplatRenderNode as F}from"../webgl-engine/lib/GaussianSplatRenderNode.js";import k from"../../layers/LayerView.js";import{spatialReferenceIncompatibleError as B,isInEffectiveScaleRange as D}from"../../support/layerViewUtils.js";import{TaskPriority as z}from"../../support/Scheduler.js";const q=()=>t.getLogger("esri.views.3d.layers.GaussianSplatLayerView3D");let N=class extends(S(k)){constructor(e){super(e),this.type="gaussian-splat-3d",this.ignoresMemoryFactor=!1,this._tileHandles=new Map,this._pageBuffer=new Uint32Array(P),this._tmpObbsWithChangedVisibility=new Array,this._frameTask=null,this._wasmLayerId=-1,this._metersPerVCSUnit=1,this._usedMemory=0,this._cacheMemory=0,this._useEsriCrs=!1,this.fullExtentInLocalViewSpatialReference=null,this._suspendedHandle=null,this._conversionBuffer=new ArrayBuffer(4),this._u32View=new Uint32Array(this._conversionBuffer),this._f32View=new Float32Array(this._conversionBuffer);const{view:t}=e;this._memCache=t.resourceController.memoryController.newCache(`GaussianSplat-${this.uid}`,e=>this._deleteTile(e))}initialize(){if(!this._canProjectWithoutEngine())throw B("layer",this.layer.spatialReference.wkid,this.view.renderSpatialReference?.wkid);const e=C(this).then(e=>{this._wasmLayerId=e,this._renderNode=new F({view:this.view,layerView:this}),this.data=new R(this._renderNode),this.fadeHelper=new T(this),this._intersectionHandler=new O(this),this.view.sceneIntersectionHelper.addIntersectionHandler(this._intersectionHandler),this._elevationProvider=new H({view:this.view,layerElevationSource:this,intersectionHandler:this._intersectionHandler}),this.view.elevationProvider.register(2,this._elevationProvider),this.addHandles([i(()=>this.layer.elevationInfo,e=>this._elevationInfoChanged(e))]),this._suspendedHandle=i(()=>this.suspended,e=>this._wasm?.setEnabled(this,!e),r),this.setMaximumGaussianCount(this.view.qualitySettings.gaussianSplat.maximumNumberOfGaussians)});this.addHandles([i(()=>this.view.qualitySettings.fadeDuration,e=>{this.fadeHelper.onFadeDurationChanged(e),this.data.fadingTexture.updateTexture(this.data.textureAtlas.pageAllocator.pageCount)}),i(()=>this.view.qualitySettings.gaussianSplat.maximumNumberOfGaussians,e=>this.setMaximumGaussianCount(e*this.view.quality)),i(()=>this.view.quality,e=>this.setMaximumGaussianCount(this.view.qualitySettings.gaussianSplat.maximumNumberOfGaussians*e))]),this.addResolvingPromise(e),this._frameTask=this.view.resourceController.scheduler.registerTask(z.GAUSSIAN_SPLAT_TEXTURE_ATLAS)}get wasmLayerId(){return this._wasmLayerId}get metersPerVCSUnit(){return this._metersPerVCSUnit}get tileHandles(){return this._tileHandles}get _wasm(){return x(this.view)}get usedMemory(){return this._usedMemory}get cachedMemory(){return this._cacheMemory}get unloadedMemory(){return 0}get useEsriCrs(){return this._useEsriCrs}get elevationProvider(){return this._elevationProvider}get elevationOffset(){return w(this.layer.elevationInfo)}get elevationRange(){const e=this.fullExtent;return e?.zmin&&e?.zmax?new j(e.zmin,e.zmax):null}getElevationRange(e){return this._intersectionHandler.getElevationRange(e)}get fullExtent(){return this.layer.fullExtent}get visibleAtCurrentScale(){return D(this.layer.effectiveScaleRange,this.view.scale)}isUpdating(){const e=this._wasm;return!(this._wasmLayerId<0||null==e)&&(e.isUpdating(this._wasmLayerId)||this.data.updating||this.fadeHelper.updating)}updatingFlagChanged(){this.notifyChange("updating")}async createRenderable(e){const t=e.meshData;if(null==t.data)throw new Error("meshData.data undefined");if(t.desc=JSON.parse(t.desc),null==t.desc)throw new Error("meshData.desc undefined");const s=t.desc.prims[0],i=s.vertexCount,r=s.atrbs[0].view,a=s.atrbs[0].view.byteCount,n=s.atrbs[0].view.byteOffset;let o=null;if("U32"!==r.type)return q().warnOnce("unexpected meshData.data format"),{memUsageBytes:0,numGaussians:0};o=new Uint32Array(t.data.buffer,n,a/4);const l=this.extractHeader(o),d=Math.ceil(i/A),m=new Uint32Array(i),p=new Array;let c=!1,f=0;const g=async e=>{for(;f<d&&!e.done&&!c;f++){let t=this.data.textureAtlas.requestPage();if(null===t&&(this._freeInvisibleTiles(),t=this.data.textureAtlas.requestPage()),null!==t){p.push(t);const s=i-f*A,r=Math.min(s,A),a=f*A;for(let e=0;e<r;e++)m[e+a]=e+U*t;const n=f*V;this._pageBuffer.set(o.subarray(n,n+r*G)),this._pageBuffer.set(l.packedHeader,V);const h=t*U,u=h%I,d=Math.floor(h/I);this.data.textureAtlas.update(u,d,this._pageBuffer),e.madeProgress()}else c=!0}f<d&&!c&&await this._frameTask.schedule(g)};if(await this._frameTask.schedule(g),c)return q().warnOnce("ran out of gaussian splat memory"),{memUsageBytes:0,numGaussians:0};const y=new Float64Array(3*i),_=new Float32Array(i),w=2.048,v=l.tileOrigin.x*w,b=l.tileOrigin.y*w,S=l.tileOrigin.z*w,C=l.invPosScale,x=new j,M=this.view.state.isGlobal,H=M?u(this.view.spatialReference).radius:0;let P=0,R=0,T=0;const O=async e=>{for(;T<i&&!e.done;T++){const t=T*G,{rawX:s,rawY:i,rawZ:r}=this._extractGaussianPosition(o,t),a=this._extractGaussianSphericalScale(o,t),n=s*C+v,l=i*C+b,h=r*C+S;y[P]=n,y[P+1]=l,y[P+2]=h;const u=M?Math.sqrt(n*n+l*l+h*h)-H:h;x.expandElevationRangeValues(u,u),_[T]=a*a,R=Math.max(R,a),P+=3,e.madeProgress()}T<i&&await this._frameTask.schedule(O)};await this._frameTask.schedule(O);let F=null;if(t.desc.obb){const e=t.desc.obb.quaternion;F=new E(t.desc.obb.center,t.desc.obb.halfSize,h(...e))}F||(q().warnOnce("encountered tile without a bounding box"),F=new E);const{fullExtent:k}=this.layer;k?.hasZ&&k.zmax&&k.zmin&&(x.minElevation=Math.max(x.minElevation,k.zmin),x.maxElevation=Math.min(x.maxElevation,k.zmax));const B=new L(e.handle,F,m,p,y,_,R,x);return this._memCache.put(`${B.handle}`,B),this._tileHandles.set(e.handle,B),this._cacheMemory+=B.usedMemory,{memUsageBytes:B.usedMemory,numGaussians:i}}_extractGaussianPosition(e,t){const s=e[t+1];return{rawX:16383&s,rawY:s>>>14&16383,rawZ:s>>>28&15|(1023&e[t+2])<<4}}_extractGaussianSphericalScale(e,t){const s=e[t+2],i=s>>>10&255,r=s>>>18&255,a=s>>>26&63|(3&e[t+3])<<6,n=Math.exp(i/16-10),o=Math.exp(r/16-10),l=Math.exp(a/16-10);return Math.max(n,o,l)}freeRenderable(e){const t=this._tileHandles.get(e);t&&(t.isVisible&&!this.fadeHelper.isTileFadingOut(t)?this._usedMemory-=t.usedMemory:this._cacheMemory-=t.usedMemory,t.pageIds.forEach(e=>this.data.textureAtlas.freePage(e)),this.freeObject(t),this._tileHandles.delete(e)),this.updateGaussians()}freeObject(e){this._memCache.pop(`${e.handle}`)}setRenderableVisibility(e,t,s){const i=this._tmpObbsWithChangedVisibility;i.length=0;for(let r=0;r<s;r++){if(!t[r])continue;const s=e[r],a=this._tileHandles.get(s);a&&(a.isVisible&&!this.fadeHelper.isTileFadingOut(a)||(a.isVisible=!0,i.push(a.obb),this.fadeHelper.isTileFadingOut(a)||this._popTileFromCache(a),this.fadeHelper.fadeTile(a,0)))}for(let r=0;r<s;r++){if(t[r])continue;const s=e[r],a=this._tileHandles.get(s);a&&a.isVisible&&(i.push(a.obb),this.fadeHelper.fadeTile(a,1))}i.length>0&&this._elevationProvider&&this._elevationProvider.notifyObjectsChanged(i),this.updateGaussians()}_popTileFromCache(e){this._usedMemory+=e.usedMemory,this._cacheMemory-=e.usedMemory,this._memCache.pop(`${e.handle}`)}moveTileToCache(e){this._usedMemory-=e.usedMemory,this._cacheMemory+=e.usedMemory,this._memCache.put(`${e.handle}`,e)}destroy(){M(this),this._suspendedHandle&&(this._suspendedHandle=s(this._suspendedHandle)),this._intersectionHandler&&(this.view.sceneIntersectionHelper.removeIntersectionHandler(this._intersectionHandler),this._intersectionHandler=null),this._elevationProvider&&this.view.elevationProvider&&(this._elevationProvider.notifyObjectsChangedFunctional(e=>{for(const t of this._tileHandles.values())e(t.obb)}),this.view.elevationProvider.unregister(this._elevationProvider),this._elevationProvider=null),this._frameTask.remove(),this._renderNode.destroy(),this.data.destroy(),this._memCache.destroy()}_canProjectWithoutEngine(){if(1===this.view.state.viewingMode)return!0;if(p(this.view.renderSpatialReference)||c(this.view.renderSpatialReference))return!0;if(this.layer.esriCrsSpatialReference&&f(this.layer.esriCrsSpatialReference,this.view.renderSpatialReference)){if(115700===this.layer.esriCrsSpatialReference.vcsWkid)return!1;let e=_(this.layer.esriCrsSpatialReference);if(!e){const t=this.layer.esriCrsSpatialReference;let s="meters";!g(t)&&t.wkid&&-1!==t.wkid&&(s=a(y.units[y[t.wkid]])),s&&(e=new d({heightModel:"gravity-related-height",heightUnit:s}))}const t=this.view.heightModelInfo;return this._useEsriCrs=0===m(e,t,!1),this._useEsriCrs&&(e&&(this._metersPerVCSUnit=n(1,"meters",e.heightUnit)),this.fullExtentInLocalViewSpatialReference=this.layer.esriCrsFullExtent),this._useEsriCrs}return!1}_elevationInfoChanged(e){if(e?.offset)if(this._useEsriCrs){const t=v(e?.unit)/this._metersPerVCSUnit,s=e?.offset??0;this._wasm?.setLayerOffset(this,s*t)}else this._wasm?.setLayerOffset(this,w(e));else this._wasm?.setLayerOffset(this,0)}updateGaussians(){const e=new Array;for(const t of this._tileHandles.values())t.isVisible&&e.push(t);this.data.updateGaussianVisibility(e),this.notifyChange("updating")}setMaximumGaussianCount(e){this._wasm?.setMaximumGaussianSplatCount(e)}_freeInvisibleTiles(){for(const e of this._tileHandles.values())e.isVisible||this._deleteTile(e)}extractHeader(e){const t=e.length-4,s=this.reinterpretU32AsFloat(e[t]),i=this.reinterpretU32AsFloat(e[t+1]),r=this.reinterpretU32AsFloat(e[t+2]),a=1/(1<<(255&e[t+3]));return{packedHeader:e.subarray(t,t+4),tileOrigin:{x:s,y:i,z:r},invPosScale:a}}_deleteTile(e){this._wasm?.onRenderableEvicted(this,e.handle,e.usedMemory),this.freeRenderable(e.handle)}reinterpretU32AsFloat(e){return this._u32View[0]=e,this._f32View[0]}get performanceInfo(){let e=0,t=0;return this._tileHandles.forEach(s=>{s.isVisible?e++:t++}),new b(this.usedMemory,e,t,this.cachedMemory)}get test(){}};e([o()],N.prototype,"layer",void 0),e([o()],N.prototype,"elevationOffset",null),e([o({readOnly:!0})],N.prototype,"visibleAtCurrentScale",null),e([o()],N.prototype,"fullExtentInLocalViewSpatialReference",void 0),N=e([l("esri.views.3d.layers.GaussianSplatLayerView3D")],N);const W=N;export{W as default};