@smarterplan/ngx-smarterplan-core 1.4.21 → 1.4.22
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 +24 -24
- package/esm2022/lib/components/csv-export/csv-export.component.mjs +3 -3
- package/esm2022/lib/components/loader/loader.component.mjs +3 -3
- package/esm2022/lib/components/menu-bar/avatar/avatar.component.mjs +1 -1
- package/esm2022/lib/components/menu-bar/menu-bar.component.mjs +3 -3
- package/esm2022/lib/components/menu-bar/navigation-bar/navigation-bar.component.mjs +3 -3
- package/esm2022/lib/components/modal-switch-visit/modal-switch-visit.component.mjs +3 -3
- package/esm2022/lib/components/search-bar/search-bar.component.mjs +3 -3
- package/esm2022/lib/components/splat-viewer/splat-viewer.component.mjs +361 -0
- package/esm2022/lib/components/support-modal/support-modal.component.mjs +3 -3
- package/esm2022/lib/config.mjs +1 -1
- package/esm2022/lib/helpers.service.mjs +1 -1
- package/esm2022/lib/matterport-extensions/nest-thermostat/CanvasImage.mjs +1 -1
- package/esm2022/lib/matterport-extensions/nest-thermostat/CanvasRenderer.mjs +1 -1
- package/esm2022/lib/matterport-extensions/nest-thermostat/NestThermostat.mjs +1 -1
- package/esm2022/lib/matterport-extensions/nest-thermostat/PlaneRenderer.mjs +1 -1
- package/esm2022/lib/matterport-extensions/scene-component/SceneComponent.mjs +1 -1
- package/esm2022/lib/matterport-extensions/security-camera/SecurityCamera.mjs +22 -22
- package/esm2022/lib/matterport-extensions/tv-player/TvPlayer.mjs +1 -1
- package/esm2022/lib/matterport-extensions/view-frustum-mesh/ViewFrustumMesh.mjs +22 -22
- package/esm2022/lib/mattertagData.mjs +1 -1
- package/esm2022/lib/ngx-smarterplan-core.module.mjs +9 -4
- package/esm2022/lib/ngx-smarterplan-core.service.mjs +1 -1
- package/esm2022/lib/pipes/duration-to-string.pipe.mjs +1 -1
- package/esm2022/lib/pipes/format-date-number-to-digits.pipe.mjs +1 -1
- package/esm2022/lib/pipes/hashtag-from-id.pipe.mjs +1 -1
- package/esm2022/lib/pipes/safe-url.pipe.mjs +1 -1
- package/esm2022/lib/pipes/time-date-to-local-string.pipe.mjs +1 -1
- package/esm2022/lib/pipes/username-from-id.pipe.mjs +1 -1
- package/esm2022/lib/services/amplify-cache.service.mjs +1 -1
- package/esm2022/lib/services/base-tab.service.mjs +1 -1
- package/esm2022/lib/services/baseVisibility.service.mjs +1 -1
- package/esm2022/lib/services/content.service.mjs +1 -1
- package/esm2022/lib/services/filter.service.mjs +1 -1
- package/esm2022/lib/services/intervention.service.mjs +1 -1
- package/esm2022/lib/services/locale.service.mjs +1 -1
- package/esm2022/lib/services/matterport-import.service.mjs +1 -1
- package/esm2022/lib/services/matterport-measurement.service.mjs +1 -1
- package/esm2022/lib/services/matterport-navigation.service.mjs +1 -1
- package/esm2022/lib/services/matterport-pointer.service.mjs +6 -6
- package/esm2022/lib/services/matterport-tag-copy.service.mjs +1 -1
- package/esm2022/lib/services/matterport-tag.service.mjs +12 -7
- package/esm2022/lib/services/matterport.service.mjs +17 -17
- package/esm2022/lib/services/models/affectation.service.mjs +1 -1
- package/esm2022/lib/services/models/base-object.service.mjs +1 -1
- package/esm2022/lib/services/models/capture.service.mjs +1 -1
- package/esm2022/lib/services/models/comment.service.mjs +1 -1
- package/esm2022/lib/services/models/domain.service.mjs +1 -1
- package/esm2022/lib/services/models/equipment.service.mjs +1 -1
- package/esm2022/lib/services/models/event.service.mjs +1 -1
- package/esm2022/lib/services/models/feature.service.mjs +1 -1
- package/esm2022/lib/services/models/hashtag.service.mjs +1 -1
- package/esm2022/lib/services/models/layer.service.mjs +1 -1
- package/esm2022/lib/services/models/measurement.service.mjs +1 -1
- package/esm2022/lib/services/models/mission.service.mjs +1 -1
- package/esm2022/lib/services/models/navigation.service.mjs +1 -1
- package/esm2022/lib/services/models/node.service.mjs +1 -1
- package/esm2022/lib/services/models/object3D.service.mjs +1 -1
- package/esm2022/lib/services/models/operation.service.mjs +1 -1
- package/esm2022/lib/services/models/organisation.service.mjs +1 -1
- package/esm2022/lib/services/models/plan.service.mjs +1 -1
- package/esm2022/lib/services/models/poi.service.mjs +27 -27
- package/esm2022/lib/services/models/profile.service.mjs +1 -1
- package/esm2022/lib/services/models/property.service.mjs +1 -1
- package/esm2022/lib/services/models/space.service.mjs +1 -1
- package/esm2022/lib/services/models/template.service.mjs +1 -1
- package/esm2022/lib/services/models/ticket.service.mjs +1 -1
- package/esm2022/lib/services/models/visit.service.mjs +1 -1
- package/esm2022/lib/services/models/zone.service.mjs +1 -1
- package/esm2022/lib/services/navigator.service.mjs +1 -1
- package/esm2022/lib/services/obj-to-glb.mjs +649 -0
- package/esm2022/lib/services/s3.service.mjs +43 -3
- package/esm2022/lib/services/search.service.mjs +1 -1
- package/esm2022/lib/services/support.service.mjs +1 -1
- package/esm2022/lib/services/sweep-locator.service.mjs +1 -1
- package/esm2022/lib/services/tag.service.mjs +16 -16
- package/esm2022/lib/services/threejs-import.service.mjs +6 -4
- package/esm2022/lib/services/threejs.service.mjs +867 -95
- package/esm2022/lib/services/user.service.mjs +1 -1
- package/esm2022/lib/services/utils/matterport-transform.util.mjs +1 -1
- package/esm2022/lib/services/validators.service.mjs +1 -1
- package/esm2022/lib/services/viewer.service.mjs +189 -7
- package/esm2022/lib/services/zone-drawer.service.mjs +1 -1
- package/esm2022/lib/services/zoneChange.service.mjs +1 -1
- package/esm2022/lib/tokens/matterport-sdk-key.token.mjs +1 -1
- package/esm2022/lib/validators/email.directive.mjs +1 -1
- package/esm2022/lib/validators/no-empty.directive.mjs +1 -1
- package/esm2022/lib/validators/number.directive.mjs +1 -1
- package/esm2022/lib/validators/text.directive.mjs +1 -1
- package/esm2022/public-api.mjs +3 -1
- package/fesm2022/smarterplan-ngx-smarterplan-core.mjs +2239 -233
- package/fesm2022/smarterplan-ngx-smarterplan-core.mjs.map +1 -1
- package/lib/components/splat-viewer/splat-viewer.component.d.ts +98 -0
- package/lib/ngx-smarterplan-core.module.d.ts +7 -6
- package/lib/services/matterport-tag.service.d.ts +5 -5
- package/lib/services/obj-to-glb.d.ts +66 -0
- package/lib/services/s3.service.d.ts +7 -0
- package/lib/services/threejs.service.d.ts +336 -16
- package/lib/services/viewer.service.d.ts +110 -8
- package/package.json +1 -1
- package/public-api.d.ts +2 -0
- package/src/assets/viewers/splat/chunk-BYLI4YBI.js +17037 -0
- package/src/assets/viewers/splat/chunk-LDV6YPMG.js +1 -0
- package/src/assets/viewers/splat/chunk-WZ5Z4TU3.js +105719 -0
- package/src/assets/viewers/splat/convert-4DRLH77X.js +1 -0
- package/src/assets/viewers/splat/convert.html +11 -0
- package/src/assets/viewers/splat/converter-SLBB5MHG.js +1 -0
- package/src/assets/viewers/splat/index.html +90 -0
- package/src/assets/viewers/splat/spz-DEYRea_v-QE6YSGIB.js +5292 -0
- package/src/assets/viewers/splat/viewer-W4X3FWQJ.js +2704 -0
- package/src/assets/viewers/splat/viewer.css +1096 -0
- package/src/assets/viewers/splat/webp.wasm +0 -0
- package/src/assets/viewers/splat/worker.mjs +1383 -0
- package/src/assets/vs-app.json +67 -67
|
@@ -0,0 +1,2704 @@
|
|
|
1
|
+
import{$a as ua,A as Fn,Aa as ie,B as pe,Ba as Ss,C as In,Ca as Cs,D as Vn,Da as Ye,E as Pi,Ea as $t,F as Li,Fa as ke,G as Ue,Ga as Qi,H as oe,Ha as qn,I as ns,Ia as Ji,J as os,Ja as Ze,K as xt,L as G,La as ea,M as On,Ma as Ps,N as X,Na as ht,O as Bt,Oa as ce,P as Z,Pa as ta,Q as Ei,Qa as sa,R as Ri,Ra as na,S as lt,Sa as oa,T as is,Ta as ia,U as ze,Ua as aa,V as fe,Va as ra,W as Xe,Wa as la,X as Ai,Xa as ca,Y as q,Ya as ha,Z as Nt,Za as da,_ as ct,_a as Ls,a as Tn,aa as Ti,ab as $n,b as gi,ba as Fe,bb as pa,c as Ot,ca as Di,cb as fa,d as vi,da as zi,db as ma,e as Dn,ea as Fi,eb as ga,f as Y,fa as wt,fb as Hn,g as K,ga as Ms,gb as va,ha as Bn,hb as ya,i as x,ia as Ii,ib as xa,ja as Vi,k as yt,ka as qt,l as yi,la as Oi,m as U,ma as Bi,n as Q,na as Ni,o as xi,oa as qi,p as wi,pa as $i,q as bi,qa as Hi,r as ss,ra as ji,s as ks,sa as Wi,t as zn,ta as Gi,u as _i,ua as Nn,v as ki,va as Ui,w as Mi,wa as Xi,x as Si,xa as Yi,y as Ci,ya as Zi,z as rt,za as Ki}from"./chunk-BYLI4YBI.js";import"./chunk-LDV6YPMG.js";var Es=class extends ht{static scriptName="xrControllers";basePath="https://cdn.jsdelivr.net/npm/@webxr-input-profiles/assets/dist/profiles";controllers=new Map;_pendingInputSources=new Set;_models=new Map;_visible=!0;_handlers=null;initialize(){if(!this.app.xr){console.error("XrControllers script requires XR to be enabled on the application");return}this._handlers={onAdd:this._onInputSourceAdd.bind(this),onRemove:this._onInputSourceRemove.bind(this),onXrEnd:this._onXrEnd.bind(this)},this.app.xr.input.on("add",this._handlers.onAdd),this.app.xr.input.on("remove",this._handlers.onRemove),this.app.xr.on("end",this._handlers.onXrEnd),this.once("destroy",()=>{this._onDestroy()})}_onDestroy(){this._handlers&&this.app.xr&&(this.app.xr.input.off("add",this._handlers.onAdd),this.app.xr.input.off("remove",this._handlers.onRemove),this.app.xr.off("end",this._handlers.onXrEnd)),this._destroyAllControllers(),this._handlers=null,this._pendingInputSources.clear();for(let e of this._models.values())e.asset?this._releaseModel(e.asset):e.load.then(t=>this._releaseModel(t),()=>{});this._models.clear()}_onXrEnd(){this._destroyAllControllers(),this._pendingInputSources.clear()}_destroyController(e){let t=this.controllers.get(e);t&&(t.entity.destroy(),this.controllers.delete(e),this.app.fire("xr:controller:remove",e))}_destroyAllControllers(){for(let e of this.controllers.keys())this._destroyController(e)}async _tryLoadProfiles(e,t,s=0){if(s>=t.length||!this._pendingInputSources.has(e))return null;let n=await this._loadProfile(e,t[s]);return n||this._tryLoadProfiles(e,t,s+1)}async _onInputSourceAdd(e){if(!e.profiles?.length){console.warn("XrControllers: No profiles available for input source");return}this._pendingInputSources.add(e);let t=await this._tryLoadProfiles(e,e.profiles);if(this._pendingInputSources.has(e))if(this._pendingInputSources.delete(e),t){let{asset:s}=t,o=s.resource.instantiateRenderEntity();this.app.root.addChild(o),o.enabled=this._visible;let r=new Map;if(e.hand)for(let a of e.hand.joints){let l=o.findByName(a.id);l&&r.set(a,l)}this.controllers.set(e,{entity:o,jointMap:r,asset:s,tracked:!0,enabledWhenTracked:!0}),this.app.fire("xr:controller:add",e,o)}else console.warn("XrControllers: No compatible profiles found for input source")}async _loadProfile(e,t){let s=`${this.basePath}/${t}/profile.json`;try{let n=await fetch(s);if(!n.ok)return null;let o=await n.json(),r=o.layouts[e.handedness]?.assetPath||"",a=`${this.basePath}/${o.profileId}/${e.handedness}${r.replace(/^\/?(left|right)/,"")}`;if(!this._pendingInputSources.has(e))return null;let l=await this._loadModel(a);return{profileId:t,asset:l}}catch{return null}}_loadModel(e){let t=this._models.get(e);if(t)return t.load;let s={load:new Promise((n,o)=>{this.app.assets.loadFromUrl(e,"container",(r,a)=>{r?o(r):n(a)})}),asset:null};return this._models.set(e,s),s.load.then(n=>{s.asset=n},()=>{this._models.get(e)===s&&this._models.delete(e)}),s.load}_releaseModel(e){let{assets:t}=this.app;t&&(t.remove(e),e.unload())}_onInputSourceRemove(e){this._pendingInputSources.delete(e),this._destroyController(e)}set visible(e){if(this._visible!==e){this._visible=e;for(let[,t]of this.controllers)t.tracked?t.entity.enabled=e:t.enabledWhenTracked=e}}get visible(){return this._visible}update(e){if(!this.app.xr?.active||!this._visible)return;let t=this.app.xr.visibilityState==="visible";for(let[s,n]of this.controllers){let o=t&&(!s.hand||s.hand.tracking);if(n.tracked!==o&&(n.tracked=o,o?n.entity.enabled=n.enabledWhenTracked:(n.enabledWhenTracked=n.entity.enabled,n.entity.enabled=!1)),!o)continue;let{entity:r,jointMap:a}=n;if(s.hand)for(let[l,c]of a)c.setPosition(l.getPosition()),c.setRotation(l.getRotation());else{let l=s.getPosition(),c=s.getRotation();l&&r.setPosition(l),c&&r.setRotation(c)}}}};var Rs=.001,As=1e-4,bt=new x,_t=new x,jn=new x,Ie=new x,kt=new x,Ke=new x,Qe=new x,Me=new x,Ts=new Q,Ds=new Q,zs=(i,e)=>{let t=Math.cos(e),s=Math.sin(e),n=i.x*t+i.z*s;return i.z=-i.x*s+i.z*t,i.x=n,i},Wn=i=>(i%=Math.PI*2,i>Math.PI?i-=Math.PI*2:i<=-Math.PI&&(i+=Math.PI*2),i),Fs=class extends ht{static scriptName="xrManipulation";target=null;enableTranslate=!0;enableRotate=!0;enableScale=!0;scaleThreshold=.15;scalePivot="feet";minScale=.2;maxScale=5;resetEvent="xr:manipulation:reset";_inputSources=new Set;_grabbing=!1;_leftSource=null;_rightSource=null;_grabTarget=null;_framePos=new x;_frameYaw=0;_frameScale=1;_targetPos0=new x;_targetRot0=new Q;_targetScale0=new x;_midPose0=new x;_grabDist=0;_grabHeading=null;_prevLeft=new x;_prevRight=new x;_prevTranslateEnabled=!0;_menuActive=!1;_resetEventName="";_baselineTarget=null;_initPosition=new x;_initRotation=new Q;_initScale=new x(1,1,1);initialize(){if(!this.app.xr){console.error("XrManipulation script requires XR to be enabled on the application");return}this.target||console.warn("XrManipulation: No target entity assigned. Grabbing will not work until one is set."),!this.enableTranslate&&!this.enableRotate&&!this.enableScale&&console.warn("XrManipulation: All manipulation modes are disabled. Grabbing will not work."),this._captureBaseline(this.target);let e=n=>{this._inputSources.add(n)};this.app.xr.input.on("add",e);let t=n=>{this._inputSources.delete(n),this._grabbing&&(n===this._leftSource||n===this._rightSource)&&this._endGrab()};this.app.xr.input.on("remove",t);let s=n=>{this._menuActive=!!n,this._menuActive&&this._endGrab()};this.app.on("xr:menu:active",s),this._resetEventName=this.resetEvent,this._resetEventName&&this.app.on(this._resetEventName,this.reset,this),this.once("destroy",()=>{this.app.xr.input.off("add",e),this.app.xr.input.off("remove",t),this.app.off("xr:menu:active",s),this._resetEventName&&this.app.off(this._resetEventName,this.reset,this),this._inputSources.clear(),this._endGrab()})}reset(){this._endGrab();let e=this._baselineTarget;e&&(e.setPosition(this._initPosition),e.setRotation(this._initRotation),e.setLocalScale(this._initScale))}_captureBaseline(e){this._baselineTarget=e,e&&(this._initPosition.copy(e.getPosition()),this._initRotation.copy(e.getRotation()),this._initScale.copy(e.getLocalScale()))}_hasUsablePose(e){return e.grip||!!(e.hand&&e.hand.tracking&&e.hand.wrist)}_canContinueGrab(e){return!!e&&this._inputSources.has(e)&&e.squeezing&&(e.grip||!!e.hand)}_getRigSpacePosition(e,t){if(e.grip)t.copy(e.getLocalPosition());else{t.copy(e.hand.wrist.getPosition());let s=this.app.xr.camera?.parent;if(s){let n=s.getPosition(),o=s.forward,r=Math.atan2(-o.x,-o.z),a=s.getLocalScale().x||1;t.sub(n),zs(t,-r),t.mulScalar(1/a)}}return t}_captureFrame(){let e=this.app.xr.camera?.parent;if(e){this._framePos.copy(e.getPosition());let t=e.forward;this._frameYaw=Math.atan2(-t.x,-t.z),this._frameScale=e.getLocalScale().x||1}else this._framePos.set(0,0,0),this._frameYaw=0,this._frameScale=1}_frameStale(){let e=this.app.xr.camera?.parent;if(!e)return!1;let t=e.forward,s=Math.atan2(-t.x,-t.z),n=e.getPosition(),o=n.x-this._framePos.x,r=n.y-this._framePos.y,a=n.z-this._framePos.z;return o*o+r*r+a*a>1e-12||Math.abs(Wn(s-this._frameYaw))>1e-6}_frameToWorld(e){return zs(e,this._frameYaw),e.mulScalar(this._frameScale).add(this._framePos),e}_captureGrabState(e,t){let s=this._grabTarget;this._targetPos0.copy(s.getPosition()),this._targetRot0.copy(s.getRotation()),this._targetScale0.copy(s.getLocalScale()),this._midPose0.add2(e,t).mulScalar(.5),Me.sub2(t,e),this._grabDist=Me.length();let n=Math.sqrt(Me.x*Me.x+Me.z*Me.z);this._grabHeading=n>As?Math.atan2(Me.x,Me.z):null}_startGrab(e,t,s){this._grabTarget=e,this._leftSource=t,this._rightSource=s,this._captureFrame(),this._getRigSpacePosition(t,this._prevLeft),this._getRigSpacePosition(s,this._prevRight),this._captureGrabState(this._prevLeft,this._prevRight),this._prevTranslateEnabled=this.enableTranslate,this._grabbing=!0,this.app.fire("xr:manipulation:active",!0)}_endGrab(){this._grabbing&&(this._leftSource=null,this._rightSource=null,this._grabTarget=null,this._grabbing=!1,this.app.fire("xr:manipulation:active",!1))}_applyScaleDeadZone(e){let t=Y.clamp(this.scaleThreshold,0,.9);return t<=0?e:e>1+t?e/(1+t):e<1-t?e/(1-t):1}_clampScaleFactor(e,t){let s=this._initScale.x||1,n=Y.clamp(t*e/s,this.minScale,this.maxScale);return t>0?n*s/t:1}update(e){let t=this.target;if(t!==this._baselineTarget&&this._captureBaseline(t),!this.app.xr?.active){this._endGrab();return}if(!t||this._menuActive||!this.enableTranslate&&!this.enableRotate&&!this.enableScale){this._endGrab();return}if(this._grabbing&&(t!==this._grabTarget||!this._canContinueGrab(this._leftSource)||!this._canContinueGrab(this._rightSource))&&this._endGrab(),!this._grabbing){let r=null,a=null,l=null,c=null;for(let h of this._inputSources)!h.squeezing||!this._hasUsablePose(h)||(h.handedness==="left"&&!r?r=h:h.handedness==="right"&&!a&&(a=h),l?c||(c=h):l=h);r&&a?this._startGrab(t,r,a):l&&c&&this._startGrab(t,l,c);return}this._getRigSpacePosition(this._leftSource,bt),this._getRigSpacePosition(this._rightSource,_t),jn.add2(bt,_t).mulScalar(.5),Ie.sub2(_t,bt);let s=Ie.length(),n=Math.sqrt(Ie.x*Ie.x+Ie.z*Ie.z),o=n>As?Math.atan2(Ie.x,Ie.z):null;this._frameStale()&&(this._captureFrame(),this._captureGrabState(bt,_t)),this.enableTranslate!==this._prevTranslateEnabled&&(this._captureGrabState(bt,_t),this._prevTranslateEnabled=this.enableTranslate),this.enableTranslate?this._solveAnchored(s,o):this._solveIncremental(s,n),this._prevLeft.copy(bt),this._prevRight.copy(_t)}_solveAnchored(e,t){let s=0,n=!1;this.enableRotate&&(t!==null&&this._grabHeading!==null?s=Wn(t-this._grabHeading):n=!0);let o=1,r=!1,a=!1;if(this.enableScale)if(e>Rs&&this._grabDist>Rs){let l=this._applyScaleDeadZone(e/this._grabDist);o=this._clampScaleFactor(l,this._targetScale0.x),a=o!==l}else r=!0;if(Ke.copy(this._midPose0),this._frameToWorld(Ke),Me.copy(jn),this._frameToWorld(Me),Qe.sub2(this._targetPos0,Ke),zs(Qe,s),Qe.mulScalar(o).add(Me),this.scalePivot!=="hands"){let l=this._framePos.y;Qe.y=l+o*(this._targetPos0.y-l)+(Me.y-Ke.y)}Ts.setFromEulerAngles(0,s*Y.RAD_TO_DEG,0),Ds.mul2(Ts,this._targetRot0),this._grabTarget.setPosition(Qe),this._grabTarget.setRotation(Ds),this._grabTarget.setLocalScale(this._targetScale0.x*o,this._targetScale0.y*o,this._targetScale0.z*o),(n||r||a)&&this._captureGrabState(bt,_t)}_solveIncremental(e,t){let s=this._grabTarget.getLocalScale();kt.sub2(this._prevRight,this._prevLeft);let n=Math.sqrt(kt.x*kt.x+kt.z*kt.z),o=0;this.enableRotate&&t>As&&n>As&&(o=Wn(Math.atan2(Ie.x,Ie.z)-Math.atan2(kt.x,kt.z)));let r=1;if(this.enableScale&&e>Rs&&this._grabDist>Rs&&s.x>0){let a=this._applyScaleDeadZone(e/this._grabDist);r=this._clampScaleFactor(a,this._targetScale0.x)*this._targetScale0.x/s.x}Ke.copy(jn),this._frameToWorld(Ke),this.scalePivot!=="hands"&&(Ke.y=this._framePos.y),Qe.sub2(this._grabTarget.getPosition(),Ke),zs(Qe,o),Qe.mulScalar(r).add(Ke),Ts.setFromEulerAngles(0,o*Y.RAD_TO_DEG,0),Ds.mul2(Ts,this._grabTarget.getRotation()),this._grabTarget.setPosition(Qe),this._grabTarget.setRotation(Ds),this._grabTarget.setLocalScale(s.x*r,s.y*r,s.z*r)}};var Al=9.81,Se=new yt,as=new yt,Is=new x,Gn=new x,wa=new x,Ve=new x,Mt=new x,St=new x,ba=new x,Un=new x,_a=new x,Tl=1e3,ka=`
|
|
2
|
+
attribute vec3 vertex_position;
|
|
3
|
+
attribute vec2 aUv0;
|
|
4
|
+
|
|
5
|
+
uniform mat4 matrix_model;
|
|
6
|
+
uniform mat4 matrix_viewProjection;
|
|
7
|
+
|
|
8
|
+
varying vec2 uv0;
|
|
9
|
+
|
|
10
|
+
void main(void) {
|
|
11
|
+
gl_Position = matrix_viewProjection * matrix_model * vec4(vertex_position, 1.0);
|
|
12
|
+
uv0 = aUv0;
|
|
13
|
+
}
|
|
14
|
+
`,Dl=`
|
|
15
|
+
uniform vec4 uColor;
|
|
16
|
+
|
|
17
|
+
varying vec2 uv0;
|
|
18
|
+
|
|
19
|
+
void main(void) {
|
|
20
|
+
float edge = 1.0 - abs(uv0.y * 2.0 - 1.0);
|
|
21
|
+
edge *= edge;
|
|
22
|
+
float ends = smoothstep(0.0, 0.1, uv0.x) * (1.0 - smoothstep(0.9, 1.0, uv0.x));
|
|
23
|
+
gl_FragColor = vec4(uColor.rgb, uColor.a * edge * ends);
|
|
24
|
+
}
|
|
25
|
+
`,Ma=`
|
|
26
|
+
attribute vertex_position: vec3f;
|
|
27
|
+
attribute aUv0: vec2f;
|
|
28
|
+
|
|
29
|
+
uniform matrix_model: mat4x4f;
|
|
30
|
+
uniform matrix_viewProjection: mat4x4f;
|
|
31
|
+
|
|
32
|
+
varying uv0: vec2f;
|
|
33
|
+
|
|
34
|
+
@vertex
|
|
35
|
+
fn vertexMain(input: VertexInput) -> VertexOutput {
|
|
36
|
+
var output: VertexOutput;
|
|
37
|
+
output.position = uniform.matrix_viewProjection * uniform.matrix_model * vec4f(input.vertex_position, 1.0);
|
|
38
|
+
output.uv0 = input.aUv0;
|
|
39
|
+
return output;
|
|
40
|
+
}
|
|
41
|
+
`,zl=`
|
|
42
|
+
uniform uColor: vec4f;
|
|
43
|
+
|
|
44
|
+
varying uv0: vec2f;
|
|
45
|
+
|
|
46
|
+
@fragment
|
|
47
|
+
fn fragmentMain(input: FragmentInput) -> FragmentOutput {
|
|
48
|
+
var output: FragmentOutput;
|
|
49
|
+
let edgeBase = 1.0 - abs(input.uv0.y * 2.0 - 1.0);
|
|
50
|
+
let edge = edgeBase * edgeBase;
|
|
51
|
+
let ends = smoothstep(0.0, 0.1, input.uv0.x) * (1.0 - smoothstep(0.9, 1.0, input.uv0.x));
|
|
52
|
+
output.color = vec4f(uniform.uColor.rgb, uniform.uColor.a * edge * ends);
|
|
53
|
+
return output;
|
|
54
|
+
}
|
|
55
|
+
`,Fl=`
|
|
56
|
+
uniform vec4 uColor;
|
|
57
|
+
|
|
58
|
+
varying vec2 uv0;
|
|
59
|
+
|
|
60
|
+
void main(void) {
|
|
61
|
+
float d = length(uv0 * 2.0 - 1.0);
|
|
62
|
+
float ring = smoothstep(0.55, 0.75, d) * (1.0 - smoothstep(0.85, 1.0, d));
|
|
63
|
+
float fill = (1.0 - smoothstep(0.0, 0.85, d)) * 0.15;
|
|
64
|
+
gl_FragColor = vec4(uColor.rgb, uColor.a * (ring + fill));
|
|
65
|
+
}
|
|
66
|
+
`,Il=`
|
|
67
|
+
uniform uColor: vec4f;
|
|
68
|
+
|
|
69
|
+
varying uv0: vec2f;
|
|
70
|
+
|
|
71
|
+
@fragment
|
|
72
|
+
fn fragmentMain(input: FragmentInput) -> FragmentOutput {
|
|
73
|
+
var output: FragmentOutput;
|
|
74
|
+
let d = length(input.uv0 * 2.0 - 1.0);
|
|
75
|
+
let ring = smoothstep(0.55, 0.75, d) * (1.0 - smoothstep(0.85, 1.0, d));
|
|
76
|
+
let fill = (1.0 - smoothstep(0.0, 0.85, d)) * 0.15;
|
|
77
|
+
output.color = vec4f(uniform.uColor.rgb, uniform.uColor.a * (ring + fill));
|
|
78
|
+
return output;
|
|
79
|
+
}
|
|
80
|
+
`,Vs=class extends ht{static scriptName="xrNavigation";enableTeleport=!0;enableMove=!0;movementSpeed=1.5;turnMode="snap";rotateSpeed=45;movementThreshold=.1;rotateThreshold=.5;rotateResetThreshold=.25;smoothTurnSpeed=90;smoothTurnThreshold=.15;maxTeleportDistance=10;groundHeight=0;teleportArcSpeed=8;arcWidth=.05;arcSegments=32;teleportIndicatorRadius=.2;validTeleportColor=new K(.5,.8,.95);invalidTeleportColor=new K(.9,.4,.35);enableSnapVertical=!0;snapVerticalHeight=.5;snapVerticalBoostHeight=2;snapVerticalThreshold=.5;snapVerticalResetThreshold=.25;castRay=null;_inputSources=new Set;_activePointers=new Map;_inputHandlers=new Map;_rotateSnap={last:0};_verticalSnap={last:0};_cameraEntity=null;_arcMaterial=null;_ringMaterial=null;_ringMesh=null;_arcVisuals=new Map;_arcHits=new Map;_arcPoints=[];_currentGroundY=0;_unfocusedTime=-1/0;initialize(){if(!this.app.xr){console.error("XrNavigation script requires XR to be enabled on the application");return}!this.enableTeleport&&!this.enableMove&&!this.enableSnapVertical&&console.warn("XrNavigation: All navigation methods are disabled. Navigation will not work.");for(let n=0;n<=this.arcSegments;n++)this._arcPoints.push(new x);this._currentGroundY=this.groundHeight,this._arcMaterial=new Ze({uniqueName:"xr-navigation-arc",vertexGLSL:ka,fragmentGLSL:Dl,vertexWGSL:Ma,fragmentWGSL:zl,attributes:{vertex_position:pe,aUv0:In}}),this._arcMaterial.blendType=wt,this._arcMaterial.cull=0,this._arcMaterial.depthWrite=!1,this._arcMaterial.update(),this._ringMaterial=new Ze({uniqueName:"xr-navigation-ring",vertexGLSL:ka,fragmentGLSL:Fl,vertexWGSL:Ma,fragmentWGSL:Il,attributes:{vertex_position:pe,aUv0:In}}),this._ringMaterial.blendType=wt,this._ringMaterial.cull=0,this._ringMaterial.depthWrite=!1,this._ringMaterial.update(),this._ringMesh=Ye.fromGeometry(this.app.graphicsDevice,new ea),this._ringMesh.incRefCount();let e=()=>{this._destroyAllArcVisuals()};this.app.xr.on("end",e),this._cameraEntity=this.entity.findComponent("camera")?.entity??null,this._cameraEntity||console.error("XrNavigation: No camera entity found. Movement calculations may not work correctly.");let t=n=>{let o=()=>{this._isSystemSelect(n)||(this._activePointers.set(n,!0),this._arcHits.delete(n))},r=()=>{this._activePointers.get(n)&&(this._activePointers.set(n,!1),this.enableTeleport&&this.tryTeleport(n))};n.on("selectstart",o),n.on("selectend",r),this._inputHandlers.set(n,{handleSelectStart:o,handleSelectEnd:r}),this._inputSources.add(n)};this.app.xr.input.on("add",t);let s=n=>{let o=this._inputHandlers.get(n);o&&(n.off("selectstart",o.handleSelectStart),n.off("selectend",o.handleSelectEnd),this._inputHandlers.delete(n)),this._activePointers.delete(n),this._inputSources.delete(n),this._destroyArcVisual(n)};this.app.xr.input.on("remove",s),this.once("destroy",()=>{this.app.xr.off("end",e),this.app.xr.input.off("add",t),this.app.xr.input.off("remove",s);for(let[n,o]of this._inputHandlers)n.off("selectstart",o.handleSelectStart),n.off("selectend",o.handleSelectEnd);this._inputHandlers.clear(),this._activePointers.clear(),this._inputSources.clear(),this._destroyAllArcVisuals(),this._arcMaterial&&(this._arcMaterial.destroy(),this._arcMaterial=null),this._ringMaterial&&(this._ringMaterial.destroy(),this._ringMaterial=null),this._ringMesh&&(this._ringMesh.destroy(),this._ringMesh=null)})}_getAimRay(e){St.copy(e.getDirection()),Mt.copy(e.getOrigin());let t=e.grip?e.getPosition():null;if(!t)return;let s=this.maxTeleportDistance;if(!this.castRay&&St.y<-.001){let n=(Mt.y-this.groundHeight)/-St.y;n>0&&n<this.maxTeleportDistance*2&&(s=n)}ba.copy(St).mulScalar(s).add(Mt),Mt.copy(t),St.sub2(ba,Mt).normalize()}_computeArcHit(e,t,s){let n=this._arcPoints.length-1,o=Al,r=t.x*this.teleportArcSpeed,a=t.y*this.teleportArcSpeed,l=t.z*this.teleportArcSpeed;s.valid=!1;let c,h=!1;if(this.castRay){let d=this.maxTeleportDistance+Math.max(0,e.y-this.entity.getPosition().y);c=(a+Math.sqrt(a*a+2*o*d))/o}else{let d=a*a+2*o*(e.y-this.groundHeight);c=d>=0?(a+Math.sqrt(d))/o:0,h=c>.001,h||(c=2*this.teleportArcSpeed/o)}for(let d=0;d<=n;d++){let u=c*d/n;this._arcPoints[d].set(e.x+r*u,e.y+a*u-.5*o*u*u,e.z+l*u)}if(this.castRay)for(let d=0;d<n;d++){let u=this.castRay(this._arcPoints[d],this._arcPoints[d+1]);if(u){s.point.copy(u);for(let p=d+1;p<=n;p++)this._arcPoints[p].copy(s.point);s.valid=this.isValidTeleportDistance(s.point);return}}else h&&(s.point.copy(this._arcPoints[n]),s.point.y=this.groundHeight,s.valid=this.isValidTeleportDistance(s.point))}_getOrCreateHit(e){let t=this._arcHits.get(e);return t||(t={point:new x,valid:!1},this._arcHits.set(e,t)),t}tryTeleport(e){let t=this._arcHits.get(e);if(t||(t=this._getOrCreateHit(e),this._getAimRay(e),this._computeArcHit(Mt,St,t)),!t.valid)return;let s=Is.copy(t.point),n=this.entity.getPosition();if(this._cameraEntity){let o=this._cameraEntity.getPosition();s.x-=o.x-n.x,s.z-=o.z-n.z}s.y=n.y+(t.point.y-this._currentGroundY),this._currentGroundY=t.point.y,this.entity.setPosition(s)}update(e){this.app.xr?.active&&this.app.xr.visibilityState!=="visible"&&(this._unfocusedTime=Dn());for(let t of this._inputSources)this._hasThumbsticks(t)&&(t.handedness==="left"?this.enableMove&&this._cameraEntity&&this._handleMovement(t,e):t.handedness==="right"&&(this.enableMove&&this._cameraEntity&&(this.turnMode==="smooth"?this._handleSmoothTurning(t,e):this.turnMode==="snap"&&this._handleSnapTurning(t)),this.enableSnapVertical&&this._handleSnapVertical(t)));this.enableTeleport&&this._handleTeleportation()}_isSystemSelect(e){if(this.app.xr.visibilityState!=="visible"||Dn()-this._unfocusedTime<Tl)return!0;let t=e.hand,s=t?.tracking?t.wrist:null;return!!(s&&this._cameraEntity&&(s.getRotation().transformVector(Un.set(0,-1,0),Un),_a.sub2(this._cameraEntity.getPosition(),s.getPosition()).normalize(),Un.dot(_a)>0))}_hasThumbsticks(e){return!!e.gamepad&&e.gamepad.axes.length>=4}_snapTrigger(e,t,s,n){return(e.last>0&&t<n||e.last<0&&t>-n)&&(e.last=0),e.last===0&&Math.abs(t)>s?(e.last=Math.sign(t),e.last):0}_rotateRigAroundCamera(e){Is.copy(this._cameraEntity.getLocalPosition()),this.entity.translateLocal(Is),this.entity.rotateLocal(0,e,0),this.entity.translateLocal(Is.mulScalar(-1))}_handleMovement(e,t){if(Se.set(e.gamepad.axes[2],e.gamepad.axes[3]),Se.length()<=this.movementThreshold)return;Se.normalize();let s=this._cameraEntity.forward;as.x=s.x,as.y=s.z,as.normalize();let n=Math.atan2(as.x,as.y)-Math.PI/2,o=Se.x*Math.sin(n)-Se.y*Math.cos(n);Se.y=Se.y*Math.sin(n)+Se.x*Math.cos(n),Se.x=o,Se.mulScalar(this.movementSpeed*t),this.entity.translate(Se.x,0,Se.y)}_handleSnapTurning(e){let t=-e.gamepad.axes[2],s=this._snapTrigger(this._rotateSnap,t,this.rotateThreshold,this.rotateResetThreshold);s&&this._rotateRigAroundCamera(s*this.rotateSpeed)}_handleSmoothTurning(e,t){let s=-e.gamepad.axes[2];Math.abs(s)<=this.smoothTurnThreshold||this._rotateRigAroundCamera(s*this.smoothTurnSpeed*t)}_handleSnapVertical(e){let t=-e.gamepad.axes[3],s=this._snapTrigger(this._verticalSnap,t,this.snapVerticalThreshold,this.snapVerticalResetThreshold);if(s){let n=e.gamepad.buttons[1]?.pressed;this.entity.translate(0,s*(n?this.snapVerticalBoostHeight:this.snapVerticalHeight),0)}}_handleTeleportation(){for(let e of this._inputSources){if(!this._activePointers.get(e)){let n=this._arcVisuals.get(e);n&&(n.entity.enabled=!1,n.ringEntity.enabled=!1);continue}let t=this._getOrCreateHit(e);this._getAimRay(e),this._computeArcHit(Mt,St,t);let s=this._getArcVisual(e);if(s.entity.enabled=!0,this._updateArcVisual(s,t.valid),s.ringEntity.enabled=t.valid,t.valid){s.ringEntity.setPosition(t.point.x,t.point.y+.01,t.point.z);let n=this.teleportIndicatorRadius*2;s.ringEntity.setLocalScale(n,1,n)}}}_getArcVisual(e){let t=this._arcVisuals.get(e);if(t)return t;let s=this._arcPoints.length-1,n=(s+1)*2,o=new Float32Array(n*3),r=new Float32Array(n*2),a=new Uint16Array(s*6);for(let g=0;g<=s;g++){let m=g/s;r[g*4+0]=m,r[g*4+1]=0,r[g*4+2]=m,r[g*4+3]=1}for(let g=0;g<s;g++){let m=g*2;a[g*6+0]=m,a[g*6+1]=m+1,a[g*6+2]=m+2,a[g*6+3]=m+1,a[g*6+4]=m+3,a[g*6+5]=m+2}let l=new Ye(this.app.graphicsDevice);l.clear(!0,!1),l.setPositions(o),l.setUvs(0,r),l.setIndices(a),l.update(rt);let c=new ke(l,this._arcMaterial);c.pick=!1;let h=new ce("XrNavigationArc");h.addComponent("render",{castShadows:!1}),h.render.meshInstances=[c],this.app.root.addChild(h);let d=new Float32Array(4),u=new ke(this._ringMesh,this._ringMaterial);u.pick=!1,u.setParameter("uColor",d);let p=new ce("XrNavigationRing");return p.addComponent("render",{castShadows:!1}),p.render.meshInstances=[u],p.enabled=!1,this.app.root.addChild(p),t={entity:h,meshInstance:c,mesh:l,positions:o,colorParam:d,ringEntity:p},this._arcVisuals.set(e,t),t}_updateArcVisual(e,t){let s=this._arcPoints,n=s.length-1,o=e.positions,r=this._cameraEntity?this._cameraEntity.getPosition():this.entity.getPosition(),a=this.arcWidth*.5;for(let c=0;c<=n;c++){let h=s[c];c<n?Gn.sub2(s[c+1],h):Gn.sub2(h,s[c-1]),wa.sub2(r,h),Ve.cross(Gn,wa);let d=Ve.length();d>1e-6?Ve.mulScalar(a/d):Ve.set(a,0,0);let u=c*6;o[u+0]=h.x-Ve.x,o[u+1]=h.y-Ve.y,o[u+2]=h.z-Ve.z,o[u+3]=h.x+Ve.x,o[u+4]=h.y+Ve.y,o[u+5]=h.z+Ve.z}e.mesh.setPositions(o),e.mesh.update(rt);let l=t?this.validTeleportColor:this.invalidTeleportColor;e.colorParam[0]=l.r,e.colorParam[1]=l.g,e.colorParam[2]=l.b,e.colorParam[3]=l.a,e.meshInstance.setParameter("uColor",e.colorParam)}_destroyArcVisual(e){let t=this._arcVisuals.get(e);t&&(t.entity.destroy(),t.ringEntity.destroy(),this._arcVisuals.delete(e),this._arcHits.delete(e))}_destroyAllArcVisuals(){for(let e of this._arcVisuals.keys())this._destroyArcVisual(e)}isValidTeleportDistance(e){return e.distance(this.entity.getPosition())<=this.maxTeleportDistance}};var to=class extends ta{constructor(e,t){super(e);let s=new sa;s.graphicsDevice=t.graphicsDevice,s.componentSystems=[oa,ia,na,ra,aa],s.resourceHandlers=[ca,da,ha,la],s.mouse=t.mouse,s.touch=t.touch,s.keyboard=t.keyboard,s.xr=Ls,this.init(s)}},Nl=.05,Rt=1e-4,ql=4;function dn(i,e,t,s,n,o,r){let a=i,l=e,c=t,h=0,d=0,u=0,p=!1,g=0;for(let v=0;v<ql&&s(a,l,c,o);v++){p=!0;let y=o.x,k=o.y,w=o.z;for(let S=0;S<g;S++){let b=n[S],z=y*b.x+k*b.y+w*b.z;z<0&&(y-=z*b.x,k-=z*b.y,w-=z*b.z)}let P=Math.sqrt(o.x*o.x+o.y*o.y+o.z*o.z);if(P>Rt&&g<3){let S=1/P,b=n[g];b.x=o.x*S,b.y=o.y*S,b.z=o.z*S,g++}a+=y,l+=k,c+=w,h+=y,d+=k,u+=w}let m=h*h+d*d+u*u,f=p&&m>Rt*Rt;return f&&(r.x=h,r.y=d,r.z=u),f}var $l=4;function Hl(i,e,t){let s=i.indices[e];t.minX=Math.min(i.v0x[s],i.v1x[s],i.v2x[s]),t.minY=Math.min(i.v0y[s],i.v1y[s],i.v2y[s]),t.minZ=Math.min(i.v0z[s],i.v1z[s],i.v2z[s]),t.maxX=Math.max(i.v0x[s],i.v1x[s],i.v2x[s]),t.maxY=Math.max(i.v0y[s],i.v1y[s],i.v2y[s]),t.maxZ=Math.max(i.v0z[s],i.v1z[s],i.v2z[s])}function so(i,e,t){let s={minX:1/0,minY:1/0,minZ:1/0,maxX:-1/0,maxY:-1/0,maxZ:-1/0},n={minX:0,minY:0,minZ:0,maxX:0,maxY:0,maxZ:0};for(let p=e;p<e+t;p++)Hl(i,p,n),s.minX=Math.min(s.minX,n.minX),s.minY=Math.min(s.minY,n.minY),s.minZ=Math.min(s.minZ,n.minZ),s.maxX=Math.max(s.maxX,n.maxX),s.maxY=Math.max(s.maxY,n.maxY),s.maxZ=Math.max(s.maxZ,n.maxZ);if(t<=$l)return{...s,left:null,right:null,triStart:e,triCount:t};let o=s.maxX-s.minX,r=s.maxY-s.minY,a=s.maxZ-s.minZ,l=o>=r&&o>=a?0:r>=a?1:2,c=l===0?(s.minX+s.maxX)*.5:l===1?(s.minY+s.maxY)*.5:(s.minZ+s.maxZ)*.5,h=e,d=e+t-1;for(;h<=d;){let p=i.indices[h];if((l===0?(i.v0x[p]+i.v1x[p]+i.v2x[p])/3:l===1?(i.v0y[p]+i.v1y[p]+i.v2y[p])/3:(i.v0z[p]+i.v1z[p]+i.v2z[p])/3)<c)h++;else{let m=i.indices[h];i.indices[h]=i.indices[d],i.indices[d]=m,d--}}let u=h-e;return(u===0||u===t)&&(u=t>>1),{...s,left:so(i,e,u),right:so(i,e+u,t-u),triStart:0,triCount:0}}function Xn(i,e,t,s,n,o,r,a,l,c,h,d,u){let p=(r-i)*s,g=(c-i)*s,m=(a-e)*n,f=(h-e)*n,v=(l-t)*o,y=(d-t)*o,k=Math.max(Math.min(p,g),Math.min(m,f),Math.min(v,y)),w=Math.min(Math.max(p,g),Math.max(m,f),Math.max(v,y));return w<0||k>w||k>u?-1:k>=0?k:0}function jl(i,e,t,s,n,o,r,a,l,c,h,d,u,p,g){let m=c-r,f=h-a,v=d-l,y=u-r,k=p-a,w=g-l,P=n*w-o*k,S=o*y-s*w,b=s*k-n*y,z=m*P+f*S+v*b;if(Math.abs(z)<1e-10)return-1;let _=1/z,R=i-r,L=e-a,M=t-l,T=(R*P+L*S+M*b)*_;if(T<0||T>1)return-1;let V=L*v-M*f,B=M*m-R*v,I=R*f-L*m,C=(s*V+n*B+o*I)*_;if(C<0||T+C>1)return-1;let A=(y*V+k*B+w*I)*_;return A>=0?A:-1}function Wl(i,e,t,s,n,o,r,a,l,c){let h=Math.max(n,Math.min(i,a)),d=Math.max(o,Math.min(e,l)),u=Math.max(r,Math.min(t,c)),p=i-h,g=e-d,m=t-u;return p*p+g*g+m*m<=s*s}function no(i,e,t,s,n,o,r,a,l,c,h,d,u){let p=r-s,g=a-n,m=l-o,f=c-s,v=h-n,y=d-o,k=i-s,w=e-n,P=t-o,S=p*k+g*w+m*P,b=f*k+v*w+y*P;if(S<=0&&b<=0){u.x=s,u.y=n,u.z=o;return}let z=i-r,_=e-a,R=t-l,L=p*z+g*_+m*R,M=f*z+v*_+y*R;if(L>=0&&M<=L){u.x=r,u.y=a,u.z=l;return}let T=S*M-L*b;if(T<=0&&S>=0&&L<=0){let j=S/(S-L);u.x=s+p*j,u.y=n+g*j,u.z=o+m*j;return}let V=i-c,B=e-h,I=t-d,C=p*V+g*B+m*I,A=f*V+v*B+y*I;if(A>=0&&C<=A){u.x=c,u.y=h,u.z=d;return}let D=C*b-S*A;if(D<=0&&b>=0&&A<=0){let j=b/(b-A);u.x=s+f*j,u.y=n+v*j,u.z=o+y*j;return}let O=L*A-C*M;if(O<=0&&M-L>=0&&C-A>=0){let j=(M-L)/(M-L+(C-A));u.x=r+(c-r)*j,u.y=a+(h-a)*j,u.z=l+(d-l)*j;return}let H=1/(O+D+T),N=D*H,E=T*H;u.x=s+p*N+f*E,u.y=n+g*N+v*E,u.z=o+m*N+y*E}function Gl(i,e,t,s,n,o,r,a,l,c){let h=r-s,d=a-n,u=l-o,p=h*h+d*d+u*u;if(p<1e-20){c.x=s,c.y=n,c.z=o;return}let g=i-s,m=e-n,f=t-o,v=(g*h+m*d+f*u)/p;v=Math.max(0,Math.min(1,v)),c.x=s+h*v,c.y=n+d*v,c.z=o+u*v}var et={x:0,y:0,z:0},Ce={x:0,y:0,z:0},Oe={x:0,y:0,z:0},he={x:0,y:0,z:0};function Ul(i,e,t,s,n,o,r,a,l,c,h,d,u,p,g,m,f){let y=1/0;for(let b=0;b<=5;b++){let z=b/5,_=i+(s-i)*z,R=e+(n-e)*z,L=t+(o-t)*z;no(_,R,L,r,a,l,c,h,d,u,p,g,he);let M=_-he.x,T=R-he.y,V=L-he.z,B=M*M+T*T+V*V;B<y&&(y=B,et.x=_,et.y=R,et.z=L,Ce.x=he.x,Ce.y=he.y,Ce.z=he.z)}Gl(Ce.x,Ce.y,Ce.z,i,e,t,s,n,o,Oe),no(Oe.x,Oe.y,Oe.z,r,a,l,c,h,d,u,p,g,he);let k=Oe.x-he.x,w=Oe.y-he.y,P=Oe.z-he.z,S=k*k+w*w+P*P;return S<y&&(y=S,et.x=Oe.x,et.y=Oe.y,et.z=Oe.z,Ce.x=he.x,Ce.y=he.y,Ce.z=he.z),m.x=et.x,m.y=et.y,m.z=et.z,f.x=Ce.x,f.y=Ce.y,f.z=Ce.z,y}var Bs={x:0,y:0,z:0},Ns={x:0,y:0,z:0},qs={x:0,y:0,z:0},un=class i{voxelResolution=Nl;_tris;_root;_normalResult={nx:0,ny:0,nz:0};_push={x:0,y:0,z:0};_constraintNormals=[{x:0,y:0,z:0},{x:0,y:0,z:0},{x:0,y:0,z:0}];_stack=[];_rayResult={t:-1,triIdx:-1};constructor(e,t){let s=Math.floor(t.length/3),n={v0x:new Float32Array(s),v0y:new Float32Array(s),v0z:new Float32Array(s),v1x:new Float32Array(s),v1y:new Float32Array(s),v1z:new Float32Array(s),v2x:new Float32Array(s),v2y:new Float32Array(s),v2z:new Float32Array(s),nx:new Float32Array(s),ny:new Float32Array(s),nz:new Float32Array(s),indices:new Uint32Array(s),count:s};for(let o=0;o<s;o++){let r=t[o*3]*3,a=t[o*3+1]*3,l=t[o*3+2]*3;n.v0x[o]=e[r],n.v0y[o]=e[r+1],n.v0z[o]=e[r+2],n.v1x[o]=e[a],n.v1y[o]=e[a+1],n.v1z[o]=e[a+2],n.v2x[o]=e[l],n.v2y[o]=e[l+1],n.v2z[o]=e[l+2];let c=n.v1x[o]-n.v0x[o],h=n.v1y[o]-n.v0y[o],d=n.v1z[o]-n.v0z[o],u=n.v2x[o]-n.v0x[o],p=n.v2y[o]-n.v0y[o],g=n.v2z[o]-n.v0z[o],m=h*g-d*p,f=d*u-c*g,v=c*p-h*u,y=Math.sqrt(m*m+f*f+v*v);if(y>1e-10){let k=1/y;m*=k,f*=k,v*=k}n.nx[o]=m,n.ny[o]=f,n.nz[o]=v,n.indices[o]=o}this._tris=n,this._root=so(n,0,s)}get triangles(){return this._tris}queryRay(e,t,s,n,o,r,a){let l=Math.sqrt(n*n+o*o+r*r);if(l<1e-10)return null;let c=1/l;n*=c,o*=c,r*=c;let h=1/(Math.abs(n)>1e-12?n:n>=0?1e-12:-1e-12),d=1/(Math.abs(o)>1e-12?o:o>=0?1e-12:-1e-12),u=1/(Math.abs(r)>1e-12?r:r>=0?1e-12:-1e-12),p=this._queryRayBVH(e,t,s,n,o,r,h,d,u,a);return p?{x:e+n*p.t,y:t+o*p.t,z:s+r*p.t}:null}querySphere(e,t,s,n,o){return dn(e,t,s,(r,a,l,c)=>this._deepestSpherePenetration(r,a,l,n,c),this._constraintNormals,this._push,o)}queryCapsule(e,t,s,n,o,r){return dn(e,t,s,(a,l,c,h)=>this._deepestCapsulePenetration(a,l,c,n,o,h),this._constraintNormals,this._push,r)}isFreeAt(e,t,s){return!this._deepestSpherePenetration(e,t,s,this.voxelResolution*.5,this._push)}querySurfaceNormal(e,t,s,n,o,r){let a=Math.sqrt(n*n+o*o+r*r);if(a<1e-10)return this._normalResult.nx=0,this._normalResult.ny=1,this._normalResult.nz=0,this._normalResult;let l=1/a,c=n*l,h=o*l,d=r*l,u=1/(Math.abs(c)>1e-12?c:c>=0?1e-12:-1e-12),p=1/(Math.abs(h)>1e-12?h:h>=0?1e-12:-1e-12),g=1/(Math.abs(d)>1e-12?d:d>=0?1e-12:-1e-12),m=this._queryRayBVH(e,t,s,c,h,d,u,p,g,1),f=this._normalResult;if(m){let v=m.triIdx;f.nx=this._tris.nx[v],f.ny=this._tris.ny[v],f.nz=this._tris.nz[v],f.nx*c+f.ny*h+f.nz*d>0&&(f.nx=-f.nx,f.ny=-f.ny,f.nz=-f.nz)}else f.nx=0,f.ny=1,f.nz=0;return f}_queryRayBVH(e,t,s,n,o,r,a,l,c,h){let d=this._root;if(Xn(e,t,s,a,l,c,d.minX,d.minY,d.minZ,d.maxX,d.maxY,d.maxZ,h)<0)return null;let u=this._stack,p=0;u[p++]=d;let g=h+1,m=-1,{_tris:f}=this;for(;p>0;){let y=u[--p];if(y.left===null){for(let P=y.triStart;P<y.triStart+y.triCount;P++){let S=f.indices[P],b=jl(e,t,s,n,o,r,f.v0x[S],f.v0y[S],f.v0z[S],f.v1x[S],f.v1y[S],f.v1z[S],f.v2x[S],f.v2y[S],f.v2z[S]);b>=0&&b<=h&&b<g&&(g=b,m=S)}continue}let k=Xn(e,t,s,a,l,c,y.left.minX,y.left.minY,y.left.minZ,y.left.maxX,y.left.maxY,y.left.maxZ,g),w=Xn(e,t,s,a,l,c,y.right.minX,y.right.minY,y.right.minZ,y.right.maxX,y.right.maxY,y.right.maxZ,g);k>=0&&w>=0?k<=w?(u[p++]=y.right,u[p++]=y.left):(u[p++]=y.left,u[p++]=y.right):k>=0?u[p++]=y.left:w>=0&&(u[p++]=y.right)}if(m<0)return null;let v=this._rayResult;return v.t=g,v.triIdx=m,v}_deepestSpherePenetration(e,t,s,n,o){let r=Rt,a=0,l=0,c=0,h=!1;return this._sphereBVH(this._root,e,t,s,n,d=>{let u=this._tris;no(e,t,s,u.v0x[d],u.v0y[d],u.v0z[d],u.v1x[d],u.v1y[d],u.v1z[d],u.v2x[d],u.v2y[d],u.v2z[d],Bs);let p=e-Bs.x,g=t-Bs.y,m=s-Bs.z,f=p*p+g*g+m*m;if(f>=n*n)return;let v=Math.sqrt(f),y=n-v;if(y>r){if(r=y,v>1e-10){let k=1/v;a=p*k*y,l=g*k*y,c=m*k*y}else a=u.nx[d]*y,l=u.ny[d]*y,c=u.nz[d]*y;h=!0}}),h&&(o.x=a,o.y=l,o.z=c),h}_deepestCapsulePenetration(e,t,s,n,o,r){let a=Rt,l=0,c=0,h=0,d=!1,u=e,p=t-n,g=s,m=e,f=t+n,v=s,y=o,k=t,w=n+o;return this._capsuleBVH(this._root,e,k,s,w,y,P=>{let S=this._tris;Ul(u,p,g,m,f,v,S.v0x[P],S.v0y[P],S.v0z[P],S.v1x[P],S.v1y[P],S.v1z[P],S.v2x[P],S.v2y[P],S.v2z[P],Ns,qs);let b=Ns.x-qs.x,z=Ns.y-qs.y,_=Ns.z-qs.z,R=b*b+z*z+_*_;if(R>=o*o)return;let L=Math.sqrt(R),M=o-L;if(M>a){if(a=M,L>1e-10){let T=1/L;l=b*T*M,c=z*T*M,h=_*T*M}else l=S.nx[P]*M,c=S.ny[P]*M,h=S.nz[P]*M;d=!0}}),d&&(r.x=l,r.y=c,r.z=h),d}_sphereBVH(e,t,s,n,o,r){let a=this._stack,l=0;for(a[l++]=e;l>0;){let c=a[--l];if(Wl(t,s,n,o,c.minX,c.minY,c.minZ,c.maxX,c.maxY,c.maxZ)){if(c.left===null){let{_tris:h}=this;for(let d=c.triStart;d<c.triStart+c.triCount;d++)r(h.indices[d]);continue}a[l++]=c.right,a[l++]=c.left}}}_capsuleBVH(e,t,s,n,o,r,a){let l=t-r,c=t+r,h=s-o,d=s+o,u=n-r,p=n+r,g=this._stack,m=0;for(g[m++]=e;m>0;){let f=g[--m];if(!(c<f.minX||l>f.maxX||d<f.minY||h>f.maxY||p<f.minZ||u>f.maxZ)){if(f.left===null){let{_tris:v}=this;for(let y=f.triStart;y<f.triStart+f.triCount;y++)a(v.indices[y]);continue}g[m++]=f.right,g[m++]=f.left}}}static fromGlb(e,t){return new Promise((s,n)=>{let o=new Ps(t,"container",{url:t}),r=()=>{e.assets.remove(o),o.unload()};o.on("load",()=>{let a=o.resource.renders;if(!a||a.length===0){r(),n(new Error("GLB contains no mesh data"));return}let l=[],c=[],h=0;for(let u of a){let p=u.resource;for(let g=0;g<p.meshes.length;g++){let m=p.meshes[g],f=m.vertexBuffer,v=m.indexBuffer[0];if(!f||!v)continue;let y=f.format,k=null;for(let _=0;_<y.elements.length;_++)if(y.elements[_].name===pe){k=y.elements[_];break}if(!k)continue;let w=new Float32Array(f.storage),P=y.size/4,S=k.offset/4,b=f.numVertices;for(let _=0;_<b;_++){let R=_*P+S;l.push(w[R],w[R+1],w[R+2])}let z=v.format===2?new Uint32Array(v.storage):new Uint16Array(v.storage);for(let _ of m.primitive)for(let R=0;R<_.count;R++)c.push(z[_.base+R]+h);h+=b}}if(c.length===0){r(),n(new Error("GLB meshes contain no triangle data"));return}let d=new i(new Float32Array(l),new Uint32Array(c));r(),s(d)}),o.on("error",a=>{r(),n(new Error(a))}),e.assets.add(o),e.assets.load(o)})}},La=4278190080,$s=2,Xl=1/Math.sqrt(2),Ea=[[1,0,0,0,1,0,0,0,1],[0,1,0,1,0,0,0,0,1],[0,0,1,1,0,0,0,1,0],[1,0,1,0,1,0,-1,0,1],[1,0,-1,0,1,0,1,0,1],[1,1,0,0,0,1,-1,1,0],[1,-1,0,0,0,1,1,1,0],[0,1,1,1,0,0,0,-1,1],[0,1,-1,1,0,0,0,1,1]];function Yl(i,e,t,s,n,o,r,a,l,c,h,d,u){let p=0;for(let g=1;g>=-1;g--){let m=0;for(let f=-$s;f<=$s;f++)for(let v=-$s;v<=$s;v++){let y=e+f*a+v*h-n*g,k=t+f*l+v*d-o*g,w=s+f*c+v*u-r*g;i.isVoxelSolid(y,k,w)&&!i.isVoxelSolid(y+n,k+o,w+r)&&m++}m>p&&(p=m)}return p}function Zl(i){return i>>>=0,i-=i>>>1&1431655765,i=(i&858993459)+(i>>>2&858993459),(i+(i>>>4)&252645135)*16843009>>>24}var hs=class{_gridMinX;_gridMinY;_gridMinZ;_numVoxelsX;_numVoxelsY;_numVoxelsZ;_voxelResolution;_leafSize;_treeDepth;_nodes;_leafData;_push={x:0,y:0,z:0};_normalResult={nx:0,ny:0,nz:0};_constraintNormals=[{x:0,y:0,z:0},{x:0,y:0,z:0},{x:0,y:0,z:0}];constructor(e,t,s){this._gridMinX=e.gridBounds.min[0],this._gridMinY=e.gridBounds.min[1],this._gridMinZ=e.gridBounds.min[2];let n=e.voxelResolution;this._numVoxelsX=Math.round((e.gridBounds.max[0]-e.gridBounds.min[0])/n),this._numVoxelsY=Math.round((e.gridBounds.max[1]-e.gridBounds.min[1])/n),this._numVoxelsZ=Math.round((e.gridBounds.max[2]-e.gridBounds.min[2])/n),this._voxelResolution=n,this._leafSize=e.leafSize,this._treeDepth=e.treeDepth,this._nodes=t,this._leafData=s}get gridMinX(){return this._gridMinX}get gridMinY(){return this._gridMinY}get gridMinZ(){return this._gridMinZ}get numVoxelsX(){return this._numVoxelsX}get numVoxelsY(){return this._numVoxelsY}get numVoxelsZ(){return this._numVoxelsZ}get voxelResolution(){return this._voxelResolution}get leafSize(){return this._leafSize}get treeDepth(){return this._treeDepth}get nodes(){return this._nodes}get leafData(){return this._leafData}get flipXY(){return!1}isFreeAt(e,t,s){if(this._nodes.length===0)return!1;let n=this._voxelResolution,o=Math.floor((e-this._gridMinX)/n),r=Math.floor((t-this._gridMinY)/n),a=Math.floor((s-this._gridMinZ)/n);return o<0||r<0||a<0||o>=this._numVoxelsX||r>=this._numVoxelsY||a>=this._numVoxelsZ?!1:!this.isVoxelSolid(o,r,a)}querySurfaceNormal(e,t,s,n,o,r){let a=this._voxelResolution*.25,l=Math.floor((e+Math.sign(n)*a-this._gridMinX)/this._voxelResolution),c=Math.floor((t+Math.sign(o)*a-this._gridMinY)/this._voxelResolution),h=Math.floor((s+Math.sign(r)*a-this._gridMinZ)/this._voxelResolution),d=this._normalResult,u=-1,p=0,g=1,m=0;for(let f=0;f<Ea.length;f++){let v=Ea[f],y=v[0],k=v[1],w=v[2],P=n*y+o*k+r*w;if(Math.abs(P)<1e-6)continue;let S=P<0?1:-1,b=y*S,z=k*S,_=w*S,R=Yl(this,l,c,h,b,z,_,v[3],v[4],v[5],v[6],v[7],v[8]);if(R>u){u=R;let L=Math.abs(y)+Math.abs(k)+Math.abs(w)>1?Xl:1;p=b*L,g=z*L,m=_*L}}return d.nx=p,d.ny=g,d.nz=m,d}queryRay(e,t,s,n,o,r,a){if(this._nodes.length===0)return null;let l=this._voxelResolution,c=this._gridMinX,h=this._gridMinY,d=this._gridMinZ,u=c+this._numVoxelsX*l,p=h+this._numVoxelsY*l,g=d+this._numVoxelsZ*l,m=1e-12,f=0,v=a;if(Math.abs(n)>m){let N=(c-e)/n,E=(u-e)/n;if(N>E){let j=N;N=E,E=j}if(N>f&&(f=N),v=Math.min(v,E),f>v)return null}else if(e<c||e>=u)return null;if(Math.abs(o)>m){let N=(h-t)/o,E=(p-t)/o;if(N>E){let j=N;N=E,E=j}if(N>f&&(f=N),v=Math.min(v,E),f>v)return null}else if(t<h||t>=p)return null;if(Math.abs(r)>m){let N=(d-s)/r,E=(g-s)/r;if(N>E){let j=N;N=E,E=j}if(N>f&&(f=N),v=Math.min(v,E),f>v)return null}else if(s<d||s>=g)return null;let y=e+n*f,k=t+o*f,w=s+r*f,P=Math.max(0,Math.min(Math.floor((y-c)/l),this._numVoxelsX-1)),S=Math.max(0,Math.min(Math.floor((k-h)/l),this._numVoxelsY-1)),b=Math.max(0,Math.min(Math.floor((w-d)/l),this._numVoxelsZ-1)),z=n>0?1:n<0?-1:0,_=o>0?1:o<0?-1:0,R=r>0?1:r<0?-1:0,L=Math.abs(n)>m?1/n:0,M=Math.abs(o)>m?1/o:0,T=Math.abs(r)>m?1/r:0,V=Math.abs(n)>m?(c+(P+(n>0?1:0))*l-e)*L:1/0,B=Math.abs(o)>m?(h+(S+(o>0?1:0))*l-t)*M:1/0,I=Math.abs(r)>m?(d+(b+(r>0?1:0))*l-s)*T:1/0,C=Math.abs(n)>m?l*Math.abs(L):1/0,A=Math.abs(o)>m?l*Math.abs(M):1/0,D=Math.abs(r)>m?l*Math.abs(T):1/0,O=f,H=this._numVoxelsX+this._numVoxelsY+this._numVoxelsZ;for(let N=0;N<H;N++){if(this.isVoxelSolid(P,S,b))return{x:e+n*O,y:t+o*O,z:s+r*O};if(V<B?V<I?(O=V,P+=z,V+=C):(O=I,b+=R,I+=D):B<I?(O=B,S+=_,B+=A):(O=I,b+=R,I+=D),P<0||S<0||b<0||P>=this._numVoxelsX||S>=this._numVoxelsY||b>=this._numVoxelsZ||O>a)return null}return null}querySphere(e,t,s,n,o){return this.nodes.length===0?!1:dn(e,t,s,(r,a,l,c)=>this.resolveDeepestPenetration(r,a,l,n,c),this._constraintNormals,this._push,o)}queryCapsule(e,t,s,n,o,r){return this.nodes.length===0?!1:dn(e,t,s,(a,l,c,h)=>this.resolveDeepestPenetrationCapsule(a,l,c,n,o,h),this._constraintNormals,this._push,r)}resolveDeepestPenetration(e,t,s,n,o){let{voxelResolution:r,gridMinX:a,gridMinY:l,gridMinZ:c}=this,h=n*n,d=Math.floor((e-n-a)/r),u=Math.floor((t-n-l)/r),p=Math.floor((s-n-c)/r),g=Math.floor((e+n-a)/r),m=Math.floor((t+n-l)/r),f=Math.floor((s+n-c)/r),v=0,y=0,k=0,w=Rt,P=!1;for(let S=p;S<=f;S++)for(let b=u;b<=m;b++)for(let z=d;z<=g;z++){if(!this.isVoxelSolid(z,b,S))continue;let _=a+z*r,R=l+b*r,L=c+S*r,M=_+r,T=R+r,V=L+r,B=Math.max(_,Math.min(e,M)),I=Math.max(R,Math.min(t,T)),C=Math.max(L,Math.min(s,V)),A=e-B,D=t-I,O=s-C,H=A*A+D*D+O*O;if(H>=h)continue;let N,E,j,$;if(H>1e-12){let F=Math.sqrt(H);$=n-F;let W=1/F;N=A*W*$,E=D*W*$,j=O*W*$}else{let F=e-_,W=M-e,_e=t-R,J=T-t,ne=s-L,ue=V-s,ee=F<W?-(F+n):W+n,Te=_e<J?-(_e+n):J+n,De=ne<ue?-(ne+n):ue+n,Ae=Math.abs(ee),It=Math.abs(Te),Vt=Math.abs(De);N=0,E=0,j=0,Ae<=It&&Ae<=Vt?(N=ee,$=Ae):It<=Vt?(E=Te,$=It):(j=De,$=Vt)}$>w&&(w=$,v=N,y=E,k=j,P=!0)}return P&&(o.x=v,o.y=y,o.z=k),P}resolveDeepestPenetrationCapsule(e,t,s,n,o,r){let{voxelResolution:a,gridMinX:l,gridMinY:c,gridMinZ:h}=this,d=o*o,u=t-n,p=t+n,g=Math.floor((e-o-l)/a),m=Math.floor((u-o-c)/a),f=Math.floor((s-o-h)/a),v=Math.floor((e+o-l)/a),y=Math.floor((p+o-c)/a),k=Math.floor((s+o-h)/a),w=0,P=0,S=0,b=Rt,z=!1;for(let _=f;_<=k;_++)for(let R=m;R<=y;R++)for(let L=g;L<=v;L++){if(!this.isVoxelSolid(L,R,_))continue;let M=l+L*a,T=c+R*a,V=h+_*a,B=M+a,I=T+a,C=V+a,A;if(p<T)A=p;else if(u>I)A=u;else{let ne=(T+I)*.5;A=Math.max(u,Math.min(p,ne))}let D=Math.max(M,Math.min(e,B)),O=Math.max(T,Math.min(A,I)),H=Math.max(V,Math.min(s,C)),N=e-D,E=A-O,j=s-H,$=N*N+E*E+j*j;if($>=d)continue;let F,W,_e,J;if($>1e-12){let ne=Math.sqrt($);J=o-ne;let ue=1/ne;F=N*ue*J,W=E*ue*J,_e=j*ue*J}else{let ne=e-M,ue=B-e,ee=A-T,Te=I-A,De=s-V,Ae=C-s,It=ne<ue?-(ne+o):ue+o,Vt=ee<Te?-(ee+o):Te+o,mi=De<Ae?-(De+o):Ae+o,En=Math.abs(It),Rn=Math.abs(Vt),An=Math.abs(mi);F=0,W=0,_e=0,En<=Rn&&En<=An?(F=It,J=En):Rn<=An?(W=Vt,J=Rn):(_e=mi,J=An)}J>b&&(b=J,w=F,P=W,S=_e,z=!0)}return z&&(r.x=w,r.y=P,r.z=S),z}isVoxelSolid(e,t,s){if(this.nodes.length===0||e<0||t<0||s<0||e>=this.numVoxelsX||t>=this.numVoxelsY||s>=this.numVoxelsZ)return!1;let{leafSize:n,treeDepth:o}=this,r=Math.floor(e/n),a=Math.floor(t/n),l=Math.floor(s/n),c=0;for(let d=o-1;d>=0;d--){let u=this.nodes[c]>>>0;if(u===La)return!0;let p=u>>>24&255;if(p===0)return this.checkLeafByIndex(u,e,t,s);let g=r>>>d&1,m=a>>>d&1,v=(l>>>d&1)<<2|m<<1|g;if(!(p&1<<v))return!1;let y=u&16777215,k=(1<<v)-1,w=Zl(p&k);c=y+w}let h=this.nodes[c]>>>0;return h===La?!0:this.checkLeafByIndex(h,e,t,s)}checkLeafByIndex(e,t,s,n){let o=e&16777215,r=t&3,a=s&3,c=(n&3)*16+a*4+r;return c<32?(this.leafData[o*2]>>>0>>>c&1)===1:(this.leafData[o*2+1]>>>0>>>c-32&1)===1}},oo=class extends hs{get flipXY(){return!0}querySurfaceNormal(e,t,s,n,o,r){let a=super.querySurfaceNormal(-e,-t,s,-n,-o,r);return a.nx=-a.nx,a.ny=-a.ny,a}queryRay(e,t,s,n,o,r,a){let l=super.queryRay(-e,-t,s,-n,-o,r,a);return l&&(l.x=-l.x,l.y=-l.y),l}querySphere(e,t,s,n,o){let r=super.querySphere(-e,-t,s,n,o);return r&&(o.x=-o.x,o.y=-o.y),r}queryCapsule(e,t,s,n,o,r){let a=super.queryCapsule(-e,-t,s,n,o,r);return a&&(r.x=-r.x,r.y=-r.y),a}isFreeAt(e,t,s){return super.isFreeAt(-e,-t,s)}},Kl=async i=>{let e=await fetch(i);if(!e.ok)throw new Error(`Failed to fetch voxel metadata: ${e.statusText}`);let t=await e.json(),s=i.replace(".voxel.json",".voxel.bin"),n=await fetch(s);if(!n.ok)throw new Error(`Failed to fetch voxel binary: ${n.statusText}`);let o=await n.arrayBuffer(),r=new Uint32Array(o),a=r.slice(0,t.nodeCount),l=r.slice(t.nodeCount,t.nodeCount+t.leafDataCount);return!t.version||parseFloat(t.version)<1.1?new oo(t,a,l):new hs(t,a,l)},Ql=(i,e)=>{let t=new Set(Object.keys(e));return new Proxy(e,{set(s,n,o,r){if(typeof n=="symbol")return console.error("Cannot set symbol property on target"),!1;if(!t.has(n))return console.error("Cannot set new property on target"),!1;if(s[n]!==o){let a=s[n];s[n]=o,i.fire(`${n}:changed`,o,a)}return!0}})},Jl={"settings.performance-mode":"Leistungsmodus","settings.gaming-controls":"Gaming-Steuerung","settings.show-annotations":"Anmerkungen anzeigen","settings.show-collision":"Kollision anzeigen","settings.frame":"Einpassen","settings.reset":"Zur\xFCcksetzen","tooltip.play":"Wiedergabe","tooltip.pause":"Pause","tooltip.orbit-camera":"Umlaufkamera","tooltip.fly-camera":"Flugkamera","tooltip.walk-mode":"Gehmodus","tooltip.reset-camera":"Kamera zur\xFCcksetzen","tooltip.frame-scene":"Szene einpassen","tooltip.settings":"Einstellungen","tooltip.help":"Info","tooltip.controls":"Steuerung","tooltip.enter-ar":"AR starten","tooltip.enter-vr":"VR starten","tooltip.fullscreen":"Vollbild","help.section.orbit":"Umlaufmodus","help.section.fly":"Flugmodus","help.section.walk":"Gehmodus","help.section.anim":"Animation","help.section.modes":"Modus wechseln","help.action.orbit":"Umkreisen","help.action.pan":"Schwenken","help.action.zoom":"Zoomen","help.action.set-focus":"Fokus setzen","help.action.fly-to":"Fliegen zu","help.action.fly-to-point":"Zum Punkt fliegen","help.action.walk-to":"Gehen zu","help.action.look-around":"Umsehen","help.action.move":"Bewegen","help.action.vertical":"Vertikal","help.action.jump":"Springen","help.action.run-slow":"Rennen / Gehen","help.action.focus-point":"Fokuspunkt","help.action.frame-scene":"Szene einpassen","help.action.reset-camera":"Kamera zur\xFCcksetzen","help.action.orbit-mode":"Umlaufmodus","help.action.fly-mode":"Flugmodus","help.action.toggle-walk":"Gehen umschalten","help.action.toggle-gaming":"Gaming-Steuerung umschalten","help.action.show-collision":"Kollision anzeigen","help.action.play-pause":"Wiedergabe / Pause","help.action.toggle-controls":"Steuerung umschalten","help.action.toggle-help":"Info umschalten","help.action.exit-cancel":"Beenden / Abbrechen","help.action.release-mouse":"Maus freigeben","help.action.exit-walk":"Gehmodus beenden","help.key.double-tap":"Doppeltippen","help.key.tap":"Tippen","help.key.touch-drag":"Ber\xFChren + Ziehen","help.key.one-finger-drag":"Mit einem Finger ziehen","help.key.two-finger-drag":"Mit zwei Fingern ziehen","help.key.pinch":"Zwei-Finger-Zoom","help.key.pinch-two-finger":"Zwei-Finger-Zoom / Ziehen","help.key.joystick":"Joystick","help.key.space":"Leertaste","help.key.esc":"Esc","xr-modal.title":"WebGL erforderlich","xr-modal.message":"AR und VR werden vom WebGPU-Renderer nicht unterst\xFCtzt. Mit OK wird der Viewer mit WebGL neu geladen.","xr-modal.cancel":"Abbrechen","xr-modal.ok":"OK"},ec={"settings.performance-mode":"Performance Mode","settings.gaming-controls":"Gaming Controls","settings.show-annotations":"Show Annotations","settings.show-collision":"Show Collision","settings.frame":"Frame","settings.reset":"Reset","tooltip.play":"Play","tooltip.pause":"Pause","tooltip.orbit-camera":"Orbit Camera","tooltip.fly-camera":"Fly Camera","tooltip.walk-mode":"Walk Mode","tooltip.reset-camera":"Reset Camera","tooltip.frame-scene":"Frame Scene","tooltip.settings":"Settings","tooltip.help":"Info","tooltip.controls":"Controls","tooltip.enter-ar":"Enter AR","tooltip.enter-vr":"Enter VR","tooltip.fullscreen":"Fullscreen","help.section.orbit":"Orbit Mode","help.section.fly":"Fly Mode","help.section.walk":"Walk Mode","help.section.anim":"Animation","help.section.modes":"Mode Switching","help.action.orbit":"Orbit","help.action.pan":"Pan","help.action.zoom":"Zoom","help.action.set-focus":"Set Focus","help.action.fly-to":"Fly To","help.action.fly-to-point":"Fly to Point","help.action.walk-to":"Walk To","help.action.look-around":"Look Around","help.action.move":"Move","help.action.vertical":"Vertical","help.action.jump":"Jump","help.action.run-slow":"Run / Slow","help.action.focus-point":"Focus Point","help.action.frame-scene":"Frame Scene","help.action.reset-camera":"Reset Camera","help.action.orbit-mode":"Orbit Mode","help.action.fly-mode":"Fly Mode","help.action.toggle-walk":"Toggle Walk","help.action.toggle-gaming":"Toggle Gaming Controls","help.action.show-collision":"Show Collision","help.action.play-pause":"Play / Pause","help.action.toggle-controls":"Toggle Controls","help.action.toggle-help":"Toggle Info","help.action.exit-cancel":"Exit / Cancel","help.action.release-mouse":"Release Mouse","help.action.exit-walk":"Exit Walk","help.key.double-tap":"Double-tap","help.key.tap":"Tap","help.key.touch-drag":"Touch + Drag","help.key.one-finger-drag":"One Finger Drag","help.key.two-finger-drag":"Two Finger Drag","help.key.pinch":"Pinch","help.key.pinch-two-finger":"Pinch / Two Finger Drag","help.key.joystick":"Joystick","help.key.space":"Space","help.key.esc":"Esc","xr-modal.title":"WebGL Required","xr-modal.message":"AR and VR are not supported on the WebGPU renderer. Pressing OK will reload the viewer with WebGL.","xr-modal.cancel":"Cancel","xr-modal.ok":"OK"},tc={"settings.performance-mode":"Modo rendimiento","settings.gaming-controls":"Controles de juego","settings.show-annotations":"Mostrar anotaciones","settings.show-collision":"Mostrar colisi\xF3n","settings.frame":"Encuadrar","settings.reset":"Restablecer","tooltip.play":"Reproducir","tooltip.pause":"Pausar","tooltip.orbit-camera":"C\xE1mara orbital","tooltip.fly-camera":"C\xE1mara de vuelo","tooltip.walk-mode":"Modo caminar","tooltip.reset-camera":"Restablecer c\xE1mara","tooltip.frame-scene":"Encuadrar escena","tooltip.settings":"Ajustes","tooltip.help":"Informaci\xF3n","tooltip.controls":"Controles","tooltip.enter-ar":"Entrar en AR","tooltip.enter-vr":"Entrar en VR","tooltip.fullscreen":"Pantalla completa","help.section.orbit":"Modo orbital","help.section.fly":"Modo vuelo","help.section.walk":"Modo caminar","help.section.anim":"Animaci\xF3n","help.section.modes":"Cambio de modo","help.action.orbit":"Orbitar","help.action.pan":"Desplazar","help.action.zoom":"Zoom","help.action.set-focus":"Fijar foco","help.action.fly-to":"Volar a","help.action.fly-to-point":"Volar al punto","help.action.walk-to":"Caminar a","help.action.look-around":"Mirar alrededor","help.action.move":"Mover","help.action.vertical":"Vertical","help.action.jump":"Saltar","help.action.run-slow":"Correr / Lento","help.action.focus-point":"Punto focal","help.action.frame-scene":"Encuadrar escena","help.action.reset-camera":"Restablecer c\xE1mara","help.action.orbit-mode":"Modo orbital","help.action.fly-mode":"Modo vuelo","help.action.toggle-walk":"Alternar caminar","help.action.toggle-gaming":"Alternar controles de juego","help.action.show-collision":"Mostrar colisi\xF3n","help.action.play-pause":"Reproducir / Pausar","help.action.toggle-controls":"Alternar controles","help.action.toggle-help":"Alternar informaci\xF3n","help.action.exit-cancel":"Salir / Cancelar","help.action.release-mouse":"Liberar rat\xF3n","help.action.exit-walk":"Salir de caminar","help.key.double-tap":"Doble toque","help.key.tap":"Toque","help.key.touch-drag":"Toque + arrastrar","help.key.one-finger-drag":"Arrastre con un dedo","help.key.two-finger-drag":"Arrastre con dos dedos","help.key.pinch":"Pellizcar","help.key.pinch-two-finger":"Pellizcar / Arrastre con dos dedos","help.key.joystick":"Joystick","help.key.space":"Espacio","help.key.esc":"Esc","xr-modal.title":"Se requiere WebGL","xr-modal.message":"AR y VR no son compatibles con el renderizador WebGPU. Al pulsar OK se recargar\xE1 el visor con WebGL.","xr-modal.cancel":"Cancelar","xr-modal.ok":"OK"},sc={"settings.performance-mode":"Mode performance","settings.gaming-controls":"Commandes de jeu","settings.show-annotations":"Afficher les annotations","settings.show-collision":"Afficher les collisions","settings.frame":"Cadrer","settings.reset":"R\xE9initialiser","tooltip.play":"Lecture","tooltip.pause":"Pause","tooltip.orbit-camera":"Cam\xE9ra orbitale","tooltip.fly-camera":"Cam\xE9ra volante","tooltip.walk-mode":"Mode marche","tooltip.reset-camera":"R\xE9initialiser la cam\xE9ra","tooltip.frame-scene":"Cadrer la sc\xE8ne","tooltip.settings":"Param\xE8tres","tooltip.help":"Infos","tooltip.controls":"Commandes","tooltip.enter-ar":"Entrer en AR","tooltip.enter-vr":"Entrer en VR","tooltip.fullscreen":"Plein \xE9cran","help.section.orbit":"Mode orbital","help.section.fly":"Mode vol","help.section.walk":"Mode marche","help.section.anim":"Animation","help.section.modes":"Changement de mode","help.action.orbit":"Orbiter","help.action.pan":"Panoramique","help.action.zoom":"Zoom","help.action.set-focus":"D\xE9finir le focus","help.action.fly-to":"Voler vers","help.action.fly-to-point":"Voler vers le point","help.action.walk-to":"Marcher vers","help.action.look-around":"Regarder autour","help.action.move":"Se d\xE9placer","help.action.vertical":"Vertical","help.action.jump":"Sauter","help.action.run-slow":"Courir / Lent","help.action.focus-point":"Point focal","help.action.frame-scene":"Cadrer la sc\xE8ne","help.action.reset-camera":"R\xE9initialiser la cam\xE9ra","help.action.orbit-mode":"Mode orbital","help.action.fly-mode":"Mode vol","help.action.toggle-walk":"Basculer la marche","help.action.toggle-gaming":"Basculer les commandes de jeu","help.action.show-collision":"Afficher les collisions","help.action.play-pause":"Lecture / Pause","help.action.toggle-controls":"Basculer les commandes","help.action.toggle-help":"Basculer les infos","help.action.exit-cancel":"Quitter / Annuler","help.action.release-mouse":"Lib\xE9rer la souris","help.action.exit-walk":"Quitter la marche","help.key.double-tap":"Double tape","help.key.tap":"Tape","help.key.touch-drag":"Toucher + glisser","help.key.one-finger-drag":"Glisser \xE0 un doigt","help.key.two-finger-drag":"Glisser \xE0 deux doigts","help.key.pinch":"Pincer","help.key.pinch-two-finger":"Pincer / Glisser \xE0 deux doigts","help.key.joystick":"Joystick","help.key.space":"Espace","help.key.esc":"\xC9chap","xr-modal.title":"WebGL requis","xr-modal.message":"L'AR et la VR ne sont pas prises en charge par le moteur de rendu WebGPU. Cliquer sur OK rechargera le visualiseur avec WebGL.","xr-modal.cancel":"Annuler","xr-modal.ok":"OK"},nc={"settings.performance-mode":"\u30D1\u30D5\u30A9\u30FC\u30DE\u30F3\u30B9\u30E2\u30FC\u30C9","settings.gaming-controls":"\u30B2\u30FC\u30E0\u30B3\u30F3\u30C8\u30ED\u30FC\u30EB","settings.show-annotations":"\u6CE8\u91C8\u3092\u8868\u793A","settings.show-collision":"\u30B3\u30EA\u30B8\u30E7\u30F3\u3092\u8868\u793A","settings.frame":"\u30D5\u30A3\u30C3\u30C8","settings.reset":"\u30EA\u30BB\u30C3\u30C8","tooltip.play":"\u518D\u751F","tooltip.pause":"\u4E00\u6642\u505C\u6B62","tooltip.orbit-camera":"\u30AA\u30FC\u30D3\u30C3\u30C8\u30AB\u30E1\u30E9","tooltip.fly-camera":"\u30D5\u30E9\u30A4\u30AB\u30E1\u30E9","tooltip.walk-mode":"\u30A6\u30A9\u30FC\u30AF\u30E2\u30FC\u30C9","tooltip.reset-camera":"\u30AB\u30E1\u30E9\u3092\u30EA\u30BB\u30C3\u30C8","tooltip.frame-scene":"\u30B7\u30FC\u30F3\u306B\u30D5\u30A3\u30C3\u30C8","tooltip.settings":"\u8A2D\u5B9A","tooltip.help":"\u60C5\u5831","tooltip.controls":"\u64CD\u4F5C","tooltip.enter-ar":"AR\u3092\u958B\u59CB","tooltip.enter-vr":"VR\u3092\u958B\u59CB","tooltip.fullscreen":"\u5168\u753B\u9762\u8868\u793A","help.section.orbit":"\u30AA\u30FC\u30D3\u30C3\u30C8\u30E2\u30FC\u30C9","help.section.fly":"\u30D5\u30E9\u30A4\u30E2\u30FC\u30C9","help.section.walk":"\u30A6\u30A9\u30FC\u30AF\u30E2\u30FC\u30C9","help.section.anim":"\u30A2\u30CB\u30E1\u30FC\u30B7\u30E7\u30F3","help.section.modes":"\u30E2\u30FC\u30C9\u5207\u308A\u66FF\u3048","help.action.orbit":"\u30AA\u30FC\u30D3\u30C3\u30C8","help.action.pan":"\u30D1\u30F3","help.action.zoom":"\u30BA\u30FC\u30E0","help.action.set-focus":"\u30D5\u30A9\u30FC\u30AB\u30B9\u3092\u8A2D\u5B9A","help.action.fly-to":"\u98DB\u3076","help.action.fly-to-point":"\u30DD\u30A4\u30F3\u30C8\u3078\u98DB\u3076","help.action.walk-to":"\u6B69\u304F","help.action.look-around":"\u5468\u308A\u3092\u898B\u308B","help.action.move":"\u79FB\u52D5","help.action.vertical":"\u5782\u76F4","help.action.jump":"\u30B8\u30E3\u30F3\u30D7","help.action.run-slow":"\u8D70\u308B / \u6B69\u304F","help.action.focus-point":"\u30D5\u30A9\u30FC\u30AB\u30B9\u30DD\u30A4\u30F3\u30C8","help.action.frame-scene":"\u30B7\u30FC\u30F3\u306B\u30D5\u30A3\u30C3\u30C8","help.action.reset-camera":"\u30AB\u30E1\u30E9\u3092\u30EA\u30BB\u30C3\u30C8","help.action.orbit-mode":"\u30AA\u30FC\u30D3\u30C3\u30C8\u30E2\u30FC\u30C9","help.action.fly-mode":"\u30D5\u30E9\u30A4\u30E2\u30FC\u30C9","help.action.toggle-walk":"\u30A6\u30A9\u30FC\u30AF\u3092\u5207\u308A\u66FF\u3048","help.action.toggle-gaming":"\u30B2\u30FC\u30E0\u30B3\u30F3\u30C8\u30ED\u30FC\u30EB\u3092\u5207\u308A\u66FF\u3048","help.action.show-collision":"\u30B3\u30EA\u30B8\u30E7\u30F3\u3092\u8868\u793A","help.action.play-pause":"\u518D\u751F / \u4E00\u6642\u505C\u6B62","help.action.toggle-controls":"\u64CD\u4F5C\u306E\u5207\u308A\u66FF\u3048","help.action.toggle-help":"\u60C5\u5831\u3092\u5207\u308A\u66FF\u3048","help.action.exit-cancel":"\u7D42\u4E86 / \u30AD\u30E3\u30F3\u30BB\u30EB","help.action.release-mouse":"\u30DE\u30A6\u30B9\u3092\u89E3\u653E","help.action.exit-walk":"\u30A6\u30A9\u30FC\u30AF\u3092\u7D42\u4E86","help.key.double-tap":"\u30C0\u30D6\u30EB\u30BF\u30C3\u30D7","help.key.tap":"\u30BF\u30C3\u30D7","help.key.touch-drag":"\u30BF\u30C3\u30C1 + \u30C9\u30E9\u30C3\u30B0","help.key.one-finger-drag":"1\u672C\u6307\u3067\u30C9\u30E9\u30C3\u30B0","help.key.two-finger-drag":"2\u672C\u6307\u3067\u30C9\u30E9\u30C3\u30B0","help.key.pinch":"\u30D4\u30F3\u30C1","help.key.pinch-two-finger":"\u30D4\u30F3\u30C1 / 2\u672C\u6307\u3067\u30C9\u30E9\u30C3\u30B0","help.key.joystick":"\u30B8\u30E7\u30A4\u30B9\u30C6\u30A3\u30C3\u30AF","help.key.space":"\u30B9\u30DA\u30FC\u30B9","help.key.esc":"Esc","xr-modal.title":"WebGL\u304C\u5FC5\u8981\u3067\u3059","xr-modal.message":"AR\u3068VR\u306FWebGPU\u30EC\u30F3\u30C0\u30E9\u30FC\u3067\u306F\u5229\u7528\u3067\u304D\u307E\u305B\u3093\u3002OK\u3092\u62BC\u3059\u3068WebGL\u3067\u30D3\u30E5\u30FC\u30A2\u30FC\u3092\u518D\u8AAD\u307F\u8FBC\u307F\u3057\u307E\u3059\u3002","xr-modal.cancel":"\u30AD\u30E3\u30F3\u30BB\u30EB","xr-modal.ok":"OK"},oc={"settings.performance-mode":"\uC131\uB2A5 \uBAA8\uB4DC","settings.gaming-controls":"\uAC8C\uC784 \uCEE8\uD2B8\uB864","settings.show-annotations":"\uC8FC\uC11D \uD45C\uC2DC","settings.show-collision":"\uCDA9\uB3CC \uD45C\uC2DC","settings.frame":"\uB9DE\uCDA4","settings.reset":"\uC7AC\uC124\uC815","tooltip.play":"\uC7AC\uC0DD","tooltip.pause":"\uC77C\uC2DC \uC815\uC9C0","tooltip.orbit-camera":"\uC624\uBE57 \uCE74\uBA54\uB77C","tooltip.fly-camera":"\uD50C\uB77C\uC774 \uCE74\uBA54\uB77C","tooltip.walk-mode":"\uC6CC\uD06C \uBAA8\uB4DC","tooltip.reset-camera":"\uCE74\uBA54\uB77C \uC7AC\uC124\uC815","tooltip.frame-scene":"\uC52C\uC5D0 \uB9DE\uCDA4","tooltip.settings":"\uC124\uC815","tooltip.help":"\uC815\uBCF4","tooltip.controls":"\uC870\uC791","tooltip.enter-ar":"AR \uC2DC\uC791","tooltip.enter-vr":"VR \uC2DC\uC791","tooltip.fullscreen":"\uC804\uCCB4 \uD654\uBA74","help.section.orbit":"\uC624\uBE57 \uBAA8\uB4DC","help.section.fly":"\uD50C\uB77C\uC774 \uBAA8\uB4DC","help.section.walk":"\uC6CC\uD06C \uBAA8\uB4DC","help.section.anim":"\uC560\uB2C8\uBA54\uC774\uC158","help.section.modes":"\uBAA8\uB4DC \uC804\uD658","help.action.orbit":"\uC624\uBE57","help.action.pan":"\uD32C","help.action.zoom":"\uC90C","help.action.set-focus":"\uD3EC\uCEE4\uC2A4 \uC124\uC815","help.action.fly-to":"\uBE44\uD589","help.action.fly-to-point":"\uC9C0\uC810\uC73C\uB85C \uBE44\uD589","help.action.walk-to":"\uAC77\uAE30","help.action.look-around":"\uB458\uB7EC\uBCF4\uAE30","help.action.move":"\uC774\uB3D9","help.action.vertical":"\uC218\uC9C1","help.action.jump":"\uC810\uD504","help.action.run-slow":"\uB2EC\uB9AC\uAE30 / \uCC9C\uCC9C\uD788","help.action.focus-point":"\uD3EC\uCEE4\uC2A4 \uD3EC\uC778\uD2B8","help.action.frame-scene":"\uC52C\uC5D0 \uB9DE\uCDA4","help.action.reset-camera":"\uCE74\uBA54\uB77C \uC7AC\uC124\uC815","help.action.orbit-mode":"\uC624\uBE57 \uBAA8\uB4DC","help.action.fly-mode":"\uD50C\uB77C\uC774 \uBAA8\uB4DC","help.action.toggle-walk":"\uC6CC\uD06C \uC804\uD658","help.action.toggle-gaming":"\uAC8C\uC784 \uCEE8\uD2B8\uB864 \uC804\uD658","help.action.show-collision":"\uCDA9\uB3CC \uD45C\uC2DC","help.action.play-pause":"\uC7AC\uC0DD / \uC77C\uC2DC \uC815\uC9C0","help.action.toggle-controls":"\uC870\uC791 \uC804\uD658","help.action.toggle-help":"\uC815\uBCF4 \uC804\uD658","help.action.exit-cancel":"\uB098\uAC00\uAE30 / \uCDE8\uC18C","help.action.release-mouse":"\uB9C8\uC6B0\uC2A4 \uD574\uC81C","help.action.exit-walk":"\uC6CC\uD06C \uC885\uB8CC","help.key.double-tap":"\uB354\uBE14 \uD0ED","help.key.tap":"\uD0ED","help.key.touch-drag":"\uD130\uCE58 + \uB4DC\uB798\uADF8","help.key.one-finger-drag":"\uD55C \uC190\uAC00\uB77D \uB4DC\uB798\uADF8","help.key.two-finger-drag":"\uB450 \uC190\uAC00\uB77D \uB4DC\uB798\uADF8","help.key.pinch":"\uD540\uCE58","help.key.pinch-two-finger":"\uD540\uCE58 / \uB450 \uC190\uAC00\uB77D \uB4DC\uB798\uADF8","help.key.joystick":"\uC870\uC774\uC2A4\uD2F1","help.key.space":"\uC2A4\uD398\uC774\uC2A4","help.key.esc":"Esc","xr-modal.title":"WebGL \uD544\uC694","xr-modal.message":"AR\uACFC VR\uC740 WebGPU \uB80C\uB354\uB7EC\uC5D0\uC11C \uC9C0\uC6D0\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uD655\uC778\uC744 \uB204\uB974\uBA74 WebGL\uB85C \uBDF0\uC5B4\uB97C \uB2E4\uC2DC \uBD88\uB7EC\uC635\uB2C8\uB2E4.","xr-modal.cancel":"\uCDE8\uC18C","xr-modal.ok":"\uD655\uC778"},ic={"settings.performance-mode":"Modo de desempenho","settings.gaming-controls":"Controles de jogo","settings.show-annotations":"Mostrar anota\xE7\xF5es","settings.show-collision":"Mostrar colis\xE3o","settings.frame":"Enquadrar","settings.reset":"Redefinir","tooltip.play":"Reproduzir","tooltip.pause":"Pausar","tooltip.orbit-camera":"C\xE2mera orbital","tooltip.fly-camera":"C\xE2mera de voo","tooltip.walk-mode":"Modo caminhar","tooltip.reset-camera":"Redefinir c\xE2mera","tooltip.frame-scene":"Enquadrar cena","tooltip.settings":"Configura\xE7\xF5es","tooltip.help":"Informa\xE7\xF5es","tooltip.controls":"Controles","tooltip.enter-ar":"Entrar em AR","tooltip.enter-vr":"Entrar em VR","tooltip.fullscreen":"Tela cheia","help.section.orbit":"Modo orbital","help.section.fly":"Modo voo","help.section.walk":"Modo caminhar","help.section.anim":"Anima\xE7\xE3o","help.section.modes":"Troca de modo","help.action.orbit":"Orbitar","help.action.pan":"Deslocar","help.action.zoom":"Zoom","help.action.set-focus":"Definir foco","help.action.fly-to":"Voar para","help.action.fly-to-point":"Voar at\xE9 o ponto","help.action.walk-to":"Caminhar para","help.action.look-around":"Olhar em volta","help.action.move":"Mover","help.action.vertical":"Vertical","help.action.jump":"Pular","help.action.run-slow":"Correr / Devagar","help.action.focus-point":"Ponto focal","help.action.frame-scene":"Enquadrar cena","help.action.reset-camera":"Redefinir c\xE2mera","help.action.orbit-mode":"Modo orbital","help.action.fly-mode":"Modo voo","help.action.toggle-walk":"Alternar caminhar","help.action.toggle-gaming":"Alternar controles de jogo","help.action.show-collision":"Mostrar colis\xE3o","help.action.play-pause":"Reproduzir / Pausar","help.action.toggle-controls":"Alternar controles","help.action.toggle-help":"Alternar informa\xE7\xF5es","help.action.exit-cancel":"Sair / Cancelar","help.action.release-mouse":"Liberar mouse","help.action.exit-walk":"Sair da caminhada","help.key.double-tap":"Toque duplo","help.key.tap":"Toque","help.key.touch-drag":"Toque + arrastar","help.key.one-finger-drag":"Arrastar com um dedo","help.key.two-finger-drag":"Arrastar com dois dedos","help.key.pinch":"Pin\xE7ar","help.key.pinch-two-finger":"Pin\xE7ar / Arrastar com dois dedos","help.key.joystick":"Joystick","help.key.space":"Espa\xE7o","help.key.esc":"Esc","xr-modal.title":"WebGL necess\xE1rio","xr-modal.message":"AR e VR n\xE3o s\xE3o compat\xEDveis com o renderizador WebGPU. Ao pressionar OK, o visualizador ser\xE1 recarregado com WebGL.","xr-modal.cancel":"Cancelar","xr-modal.ok":"OK"},ac={"settings.performance-mode":"\u0420\u0435\u0436\u0438\u043C \u043F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u0438","settings.gaming-controls":"\u0418\u0433\u0440\u043E\u0432\u043E\u0435 \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435","settings.show-annotations":"\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0430\u043D\u043D\u043E\u0442\u0430\u0446\u0438\u0438","settings.show-collision":"\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043A\u043E\u043B\u043B\u0438\u0437\u0438\u044E","settings.frame":"\u041A\u0430\u0434\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C","settings.reset":"\u0421\u0431\u0440\u043E\u0441\u0438\u0442\u044C","tooltip.play":"\u0412\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u0435\u0441\u0442\u0438","tooltip.pause":"\u041F\u0430\u0443\u0437\u0430","tooltip.orbit-camera":"\u041E\u0440\u0431\u0438\u0442\u0430\u043B\u044C\u043D\u0430\u044F \u043A\u0430\u043C\u0435\u0440\u0430","tooltip.fly-camera":"\u041F\u043E\u043B\u0451\u0442\u043D\u0430\u044F \u043A\u0430\u043C\u0435\u0440\u0430","tooltip.walk-mode":"\u0420\u0435\u0436\u0438\u043C \u0445\u043E\u0434\u044C\u0431\u044B","tooltip.reset-camera":"\u0421\u0431\u0440\u043E\u0441\u0438\u0442\u044C \u043A\u0430\u043C\u0435\u0440\u0443","tooltip.frame-scene":"\u041A\u0430\u0434\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0441\u0446\u0435\u043D\u0443","tooltip.settings":"\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438","tooltip.help":"\u0418\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044F","tooltip.controls":"\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435","tooltip.enter-ar":"\u0412\u043E\u0439\u0442\u0438 \u0432 AR","tooltip.enter-vr":"\u0412\u043E\u0439\u0442\u0438 \u0432 VR","tooltip.fullscreen":"\u041F\u043E\u043B\u043D\u044B\u0439 \u044D\u043A\u0440\u0430\u043D","help.section.orbit":"\u041E\u0440\u0431\u0438\u0442\u0430\u043B\u044C\u043D\u044B\u0439 \u0440\u0435\u0436\u0438\u043C","help.section.fly":"\u0420\u0435\u0436\u0438\u043C \u043F\u043E\u043B\u0451\u0442\u0430","help.section.walk":"\u0420\u0435\u0436\u0438\u043C \u0445\u043E\u0434\u044C\u0431\u044B","help.section.anim":"\u0410\u043D\u0438\u043C\u0430\u0446\u0438\u044F","help.section.modes":"\u0421\u043C\u0435\u043D\u0430 \u0440\u0435\u0436\u0438\u043C\u043E\u0432","help.action.orbit":"\u0412\u0440\u0430\u0449\u0435\u043D\u0438\u0435","help.action.pan":"\u041F\u0430\u043D\u043E\u0440\u0430\u043C\u0430","help.action.zoom":"\u041C\u0430\u0441\u0448\u0442\u0430\u0431","help.action.set-focus":"\u0417\u0430\u0434\u0430\u0442\u044C \u0444\u043E\u043A\u0443\u0441","help.action.fly-to":"\u041B\u0435\u0442\u0435\u0442\u044C \u043A","help.action.fly-to-point":"\u041B\u0435\u0442\u0435\u0442\u044C \u043A \u0442\u043E\u0447\u043A\u0435","help.action.walk-to":"\u0418\u0434\u0442\u0438 \u043A","help.action.look-around":"\u041E\u0441\u043C\u043E\u0442\u0440\u0435\u0442\u044C\u0441\u044F","help.action.move":"\u0414\u0432\u0438\u0436\u0435\u043D\u0438\u0435","help.action.vertical":"\u0412\u0435\u0440\u0442\u0438\u043A\u0430\u043B\u044C\u043D\u043E","help.action.jump":"\u041F\u0440\u044B\u0436\u043E\u043A","help.action.run-slow":"\u0411\u0435\u0433 / \u041C\u0435\u0434\u043B\u0435\u043D\u043D\u043E","help.action.focus-point":"\u0422\u043E\u0447\u043A\u0430 \u0444\u043E\u043A\u0443\u0441\u0430","help.action.frame-scene":"\u041A\u0430\u0434\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0441\u0446\u0435\u043D\u0443","help.action.reset-camera":"\u0421\u0431\u0440\u043E\u0441\u0438\u0442\u044C \u043A\u0430\u043C\u0435\u0440\u0443","help.action.orbit-mode":"\u041E\u0440\u0431\u0438\u0442\u0430\u043B\u044C\u043D\u044B\u0439 \u0440\u0435\u0436\u0438\u043C","help.action.fly-mode":"\u0420\u0435\u0436\u0438\u043C \u043F\u043E\u043B\u0451\u0442\u0430","help.action.toggle-walk":"\u041F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0445\u043E\u0434\u044C\u0431\u0443","help.action.toggle-gaming":"\u041F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0438\u0433\u0440\u043E\u0432\u043E\u0435 \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435","help.action.show-collision":"\u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043A\u043E\u043B\u043B\u0438\u0437\u0438\u044E","help.action.play-pause":"\u0412\u043E\u0441\u043F\u0440\u043E\u0438\u0437\u0432\u0435\u0434\u0435\u043D\u0438\u0435 / \u041F\u0430\u0443\u0437\u0430","help.action.toggle-controls":"\u041F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0443\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435","help.action.toggle-help":"\u041F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0438\u043D\u0444\u043E\u0440\u043C\u0430\u0446\u0438\u044E","help.action.exit-cancel":"\u0412\u044B\u0445\u043E\u0434 / \u041E\u0442\u043C\u0435\u043D\u0430","help.action.release-mouse":"\u041E\u0441\u0432\u043E\u0431\u043E\u0434\u0438\u0442\u044C \u043C\u044B\u0448\u044C","help.action.exit-walk":"\u0412\u044B\u0439\u0442\u0438 \u0438\u0437 \u0445\u043E\u0434\u044C\u0431\u044B","help.key.double-tap":"\u0414\u0432\u043E\u0439\u043D\u043E\u0439 \u0442\u0430\u043F","help.key.tap":"\u0422\u0430\u043F","help.key.touch-drag":"\u041A\u0430\u0441\u0430\u043D\u0438\u0435 + \u043F\u0435\u0440\u0435\u0442\u0430\u0441\u043A\u0438\u0432\u0430\u043D\u0438\u0435","help.key.one-finger-drag":"\u041F\u0435\u0440\u0435\u0442\u0430\u0441\u043A\u0438\u0432\u0430\u043D\u0438\u0435 \u043E\u0434\u043D\u0438\u043C \u043F\u0430\u043B\u044C\u0446\u0435\u043C","help.key.two-finger-drag":"\u041F\u0435\u0440\u0435\u0442\u0430\u0441\u043A\u0438\u0432\u0430\u043D\u0438\u0435 \u0434\u0432\u0443\u043C\u044F \u043F\u0430\u043B\u044C\u0446\u0430\u043C\u0438","help.key.pinch":"\u0429\u0438\u043F\u043E\u043A","help.key.pinch-two-finger":"\u0429\u0438\u043F\u043E\u043A / \u041F\u0435\u0440\u0435\u0442\u0430\u0441\u043A\u0438\u0432\u0430\u043D\u0438\u0435 \u0434\u0432\u0443\u043C\u044F \u043F\u0430\u043B\u044C\u0446\u0430\u043C\u0438","help.key.joystick":"\u0414\u0436\u043E\u0439\u0441\u0442\u0438\u043A","help.key.space":"\u041F\u0440\u043E\u0431\u0435\u043B","help.key.esc":"Esc","xr-modal.title":"\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F WebGL","xr-modal.message":"AR \u0438 VR \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044E\u0442\u0441\u044F \u0440\u0435\u043D\u0434\u0435\u0440\u043E\u043C WebGPU. \u041D\u0430\u0436\u043C\u0438\u0442\u0435 OK, \u0447\u0442\u043E\u0431\u044B \u043F\u0435\u0440\u0435\u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440\u0449\u0438\u043A \u0441 WebGL.","xr-modal.cancel":"\u041E\u0442\u043C\u0435\u043D\u0430","xr-modal.ok":"OK"},rc={"settings.performance-mode":"\u6027\u80FD\u6A21\u5F0F","settings.gaming-controls":"\u6E38\u620F\u63A7\u5236","settings.show-annotations":"\u663E\u793A\u6CE8\u91CA","settings.show-collision":"\u663E\u793A\u78B0\u649E","settings.frame":"\u9002\u5E94","settings.reset":"\u91CD\u7F6E","tooltip.play":"\u64AD\u653E","tooltip.pause":"\u6682\u505C","tooltip.orbit-camera":"\u73AF\u7ED5\u76F8\u673A","tooltip.fly-camera":"\u98DE\u884C\u76F8\u673A","tooltip.walk-mode":"\u884C\u8D70\u6A21\u5F0F","tooltip.reset-camera":"\u91CD\u7F6E\u76F8\u673A","tooltip.frame-scene":"\u9002\u5E94\u573A\u666F","tooltip.settings":"\u8BBE\u7F6E","tooltip.help":"\u4FE1\u606F","tooltip.controls":"\u64CD\u4F5C","tooltip.enter-ar":"\u8FDB\u5165 AR","tooltip.enter-vr":"\u8FDB\u5165 VR","tooltip.fullscreen":"\u5168\u5C4F","help.section.orbit":"\u73AF\u7ED5\u6A21\u5F0F","help.section.fly":"\u98DE\u884C\u6A21\u5F0F","help.section.walk":"\u884C\u8D70\u6A21\u5F0F","help.section.anim":"\u52A8\u753B","help.section.modes":"\u6A21\u5F0F\u5207\u6362","help.action.orbit":"\u73AF\u7ED5","help.action.pan":"\u5E73\u79FB","help.action.zoom":"\u7F29\u653E","help.action.set-focus":"\u8BBE\u7F6E\u7126\u70B9","help.action.fly-to":"\u98DE\u81F3","help.action.fly-to-point":"\u98DE\u81F3\u8BE5\u70B9","help.action.walk-to":"\u8D70\u5230","help.action.look-around":"\u73AF\u987E","help.action.move":"\u79FB\u52A8","help.action.vertical":"\u5782\u76F4","help.action.jump":"\u8DF3\u8DC3","help.action.run-slow":"\u5954\u8DD1 / \u6162\u8D70","help.action.focus-point":"\u7126\u70B9","help.action.frame-scene":"\u9002\u5E94\u573A\u666F","help.action.reset-camera":"\u91CD\u7F6E\u76F8\u673A","help.action.orbit-mode":"\u73AF\u7ED5\u6A21\u5F0F","help.action.fly-mode":"\u98DE\u884C\u6A21\u5F0F","help.action.toggle-walk":"\u5207\u6362\u884C\u8D70","help.action.toggle-gaming":"\u5207\u6362\u6E38\u620F\u63A7\u5236","help.action.show-collision":"\u663E\u793A\u78B0\u649E","help.action.play-pause":"\u64AD\u653E / \u6682\u505C","help.action.toggle-controls":"\u5207\u6362\u64CD\u4F5C","help.action.toggle-help":"\u5207\u6362\u4FE1\u606F","help.action.exit-cancel":"\u9000\u51FA / \u53D6\u6D88","help.action.release-mouse":"\u91CA\u653E\u9F20\u6807","help.action.exit-walk":"\u9000\u51FA\u884C\u8D70","help.key.double-tap":"\u53CC\u51FB","help.key.tap":"\u70B9\u51FB","help.key.touch-drag":"\u89E6\u6478 + \u62D6\u52A8","help.key.one-finger-drag":"\u5355\u6307\u62D6\u52A8","help.key.two-finger-drag":"\u53CC\u6307\u62D6\u52A8","help.key.pinch":"\u634F\u5408","help.key.pinch-two-finger":"\u634F\u5408 / \u53CC\u6307\u62D6\u52A8","help.key.joystick":"\u6447\u6746","help.key.space":"\u7A7A\u683C","help.key.esc":"Esc","xr-modal.title":"\u9700\u8981 WebGL","xr-modal.message":"WebGPU \u6E32\u67D3\u5668\u4E0D\u652F\u6301 AR \u548C VR\u3002\u6309\u4E0B\u786E\u5B9A\u5C06\u4EE5 WebGL \u91CD\u65B0\u52A0\u8F7D\u67E5\u770B\u5668\u3002","xr-modal.cancel":"\u53D6\u6D88","xr-modal.ok":"\u786E\u5B9A"},Tr=ec,Dr={de:Jl,en:Tr,es:tc,fr:sc,ja:nc,ko:oc,"pt-BR":ic,ru:ac,"zh-CN":rc},lc=i=>{let e=[i,...navigator.languages??[navigator.language]],t=Object.keys(Dr);for(let s of e){if(!s)continue;let n=s.toLowerCase(),o=n.split("-")[0],r=t.find(a=>a.toLowerCase()===n)??t.find(a=>a.toLowerCase()===o)??t.find(a=>a.toLowerCase().split("-")[0]===o);if(r)return r}return"en"},cc=(i,e)=>{let t=lc(i),s=Dr[t],n=o=>s[o]??Tr[o]??o;return e.lang=t,e.querySelectorAll("[data-i18n]").forEach(o=>{o.textContent=n(o.dataset.i18n)}),n},hc=i=>{let e={performanceMode:i,gamingControls:!1,showAnnotations:!0};try{let t=localStorage.getItem("retinaDisplay"),s=localStorage.getItem("performanceMode");if(t!==null&&s===null){s=String(t==="false");try{localStorage.setItem("performanceMode",s),localStorage.removeItem("retinaDisplay")}catch{}}return{performanceMode:s===null?i:s==="true",gamingControls:localStorage.getItem("gamingControls")==="true",showAnnotations:localStorage.getItem("showAnnotations")!=="false"}}catch{return e}},dc=i=>{let e=["performanceMode","gamingControls","showAnnotations"].map(t=>i.on(`${t}:changed`,s=>{try{localStorage.setItem(t,String(s))}catch{}}));return()=>e.forEach(t=>t.off())},io=85,uc=()=>({sharpness:{enabled:!1,amount:0},bloom:{enabled:!1,intensity:.1,blurLevel:2},grading:{enabled:!1,brightness:1,contrast:1,saturation:1,tint:[1,1,1]},vignette:{enabled:!1,intensity:.5,inner:.3,outer:.75,curvature:1},fringing:{enabled:!1,intensity:.5}}),us=i=>(i!==null&&typeof i=="object"&&(Object.values(i).forEach(us),Object.freeze(i)),i);us({min:10,max:120,step:1});us({sharpness:{amount:{min:0,max:1,step:.01}},bloom:{intensity:{min:0,max:.1,step:.01},blurLevel:{min:1,max:16,step:1}},grading:{brightness:{min:0,max:3,step:.01},contrast:{min:.5,max:1.5,step:.01},saturation:{min:0,max:2,step:.01}},vignette:{intensity:{min:0,max:1,step:.01},inner:{min:0,max:3,step:.01},outer:{min:0,max:3,step:.01},curvature:{min:.01,max:10,step:.01}},fringing:{intensity:{min:0,max:100,step:1}}});us({duration:{min:.1,max:600},frameRate:{min:1,max:120},smoothness:{min:0,max:1},maxKeyframes:1e3,nameMax:120,maxTracks:10});us({maxCount:25,titleMax:60,textMax:280});var pc=i=>{let e=structuredClone(i);return e.animTracks?e.animTracks?.forEach(t=>{if(!t.frameRate){t.frameRate=30;let n=t.keyframes.times;for(let o=0;o<n.length;o++)n[o]*=30}t.hasOwnProperty("smoothness")||(t.smoothness=0)}):e.animTracks=[],e},fc=(i,e)=>({name:i.name,duration:i.duration,frameRate:i.frameRate,loopMode:i.loopMode,interpolation:i.interpolation,smoothness:i.smoothness,keyframes:{times:i.keyframes.times,values:{position:i.keyframes.values.position,target:i.keyframes.values.target,fov:new Array(i.keyframes.times.length).fill(e)}}}),mc=i=>({version:2,tonemapping:"none",highPrecisionRendering:!1,background:{color:i.background.color||[0,0,0]},postEffectSettings:uc(),animTracks:i.animTracks.map(e=>fc(e,i.camera.fov||io)),cameras:i.camera.position&&i.camera.target?[{initial:{position:i.camera.position,target:i.camera.target,fov:i.camera.fov||io}}]:[],annotations:[],startMode:i.camera.startAnim==="animTrack"?"animTrack":"default"}),gc=i=>{let e,t=i.version;if(t===void 0)e=mc(pc(i));else if(t===2)e=i;else throw new Error(`Unsupported experience settings version: ${t}`);return e},zr="1.37.0",ao=class{register;unregister;destroy;constructor(e){let{style:t}=e;t.display="none";let s=new Map,n=0;this.register=(o,r,a="bottom")=>{let l=()=>{let p=e.parentElement.getBoundingClientRect(),g=o.getBoundingClientRect(),m=g.left-p.left,f=g.right-p.left,v=g.top-p.top,y=g.bottom-p.top,k=Math.floor((m+f)*.5),w=Math.floor((v+y)*.5);switch(a){case"left":t.left=`${m}px`,t.top=`${w}px`,t.transform="translate(calc(-100% - 10px), -50%)";break;case"right":t.left=`${f}px`,t.top=`${w}px`,t.transform="translate(10px, -50%)";break;case"top":t.left=`${k}px`,t.top=`${v}px`,t.transform="translate(-50%, calc(-100% - 10px))";break;case"bottom":t.left=`${k}px`,t.top=`${y}px`,t.transform="translate(-50%, 10px)";break}e.textContent=r,t.display="inline"},c=p=>{n=window.setTimeout(()=>{p(),n=-1},250)},h=()=>{n>=0&&(clearTimeout(n),n=-1)},d=()=>{h(),t.display==="inline"?l():c(()=>l())},u=()=>{h(),t.display==="inline"&&c(()=>{t.display="none"})};o.addEventListener("pointerenter",d),o.addEventListener("pointerleave",u),s.set(o,{enter:d,leave:u})},this.unregister=o=>{let r=s.get(o);r&&(o.removeEventListener("pointerenter",r.enter),o.removeEventListener("pointerleave",r.leave),s.delete(o))},this.destroy=()=>{for(let o of s.keys())this.unregister(o)}}},vc=(i,e)=>{let{state:t,events:s,annotations:n}=i,o=e.querySelector(".sse-annotationNav"),r=e.querySelector(".sse-annotationInfo"),a=e.querySelector(".sse-annotationNavNumber"),l=e.querySelector(".sse-annotationNavTitle"),c=e.querySelector(".sse-annotationPrev"),h=e.querySelector(".sse-annotationNext"),d=e.querySelector(".sse-annotationsRow"),u=e.querySelector(".sse-annotationsCheck"),p=t.selectedAnnotation??0,g=()=>{p=t.selectedAnnotation??p,a.textContent=n[p]?String(p+1):"",l.textContent=n[p]?.title??"",o.classList.toggle("sse-hidden",!t.loaded||!t.showAnnotations||n.length<2),o.classList.toggle("sse-unselected",t.selectedAnnotation===null),o.classList.toggle("sse-desktop",t.inputMode==="desktop"),o.classList.toggle("sse-touch",t.inputMode==="touch"),o.classList.toggle("sse-faded-in",!t.controlsHidden),o.classList.toggle("sse-faded-out",t.controlsHidden),c.disabled=h.disabled=!t.loaded||n.length<2,d.classList.toggle("sse-hidden",n.length===0),u.classList.toggle("sse-active",t.showAnnotations)},m=w=>{w.stopPropagation(),i.selectAnnotation((p-1+n.length)%n.length)},f=w=>{w.stopPropagation(),i.selectAnnotation((p+1)%n.length)},v=w=>{w.stopPropagation(),t.loaded&&t.selectedAnnotation===null&&n[p]&&i.selectAnnotation(p)},y=w=>{w.stopPropagation(),t.showAnnotations=!t.showAnnotations};c.addEventListener("click",m),h.addEventListener("click",f),r.addEventListener("click",v),d.addEventListener("click",y);let k=[s.on("loaded:changed",g),s.on("selectedAnnotation:changed",g),s.on("showAnnotations:changed",g),s.on("inputMode:changed",g),s.on("controlsHidden:changed",g)];return g(),()=>{for(let w of k)w.off();c.removeEventListener("click",m),h.removeEventListener("click",f),r.removeEventListener("click",v),d.removeEventListener("click",y)}},st=1/60,At=15,yc=new x,pn=(i,e,t,s,n=new x)=>{let{system:o,fov:r,aspectRatio:a,horizontalFov:l,projection:c,orthoHeight:h}=i,{width:d,height:u}=o.app.graphicsDevice.clientRect;n.set(-(e/d)*2,t/u*2,0);let p=yc.set(0,0,0);if(c===Vi){let g=s*Math.tan(.5*r*Y.DEG_TO_RAD);l?p.set(g,g/a,0):p.set(g*a,g,0)}else p.set(h*a,h,0);return n.mul(p),n},xc=(i,e)=>i==="orbit"?-e:e,Yn=new x,ro=class extends ht{static scriptName="annotation";context;label;title;text;hotspotDom=null;screen=null;destroyed=!1;initialize(){let e=this.context;this.hotspotDom=document.createElement("div"),this.hotspotDom.className="sse-annotation-hotspot sse-occluded",this.hotspotDom.textContent=this.label;let t=n=>{n.stopPropagation(),this.fire("select")};this.hotspotDom.addEventListener("click",t),e.hotspotsDom.appendChild(this.hotspotDom);let s=()=>this._update();this.app.on("prerender",s),this.on("destroy",()=>{this.destroyed=!0,this.app.off("prerender",s),this.hotspotDom.removeEventListener("click",t),this.hotspotDom.remove(),e.activeAnnotation===this&&this.hideTooltip()})}_update(){if(this.destroyed)return;let{camera:e}=this.context,t=this.entity.getPosition();if(e.camera.viewMatrix.transformPoint(t,Yn),Yn.z>=0){this.screen=null,this._hideElements();return}let s=e.camera.worldToScreen(t),n=e.getPosition().distance(t);this.screen={x:s.x,y:s.y,distance:n,depth:-Yn.z},this._updatePositions(s,n)}setOccluded(e){this.hotspotDom.classList.toggle("sse-occluded",e)}showTooltip(){let e=this.context;e.activeAnnotation=this,e.revealed=!1,this.hotspotDom.classList.add("sse-active")}revealTooltip(){let e=this.context;e.revealed=!0,e.tooltipOwner!==this&&(e.tooltipOwner=this,e.titleDom.textContent=this.title,e.textDom.textContent=this.text,e.tooltipDom.classList.remove("sse-arrow-left","sse-arrow-right")),this._update()}hideTooltip(){let e=this.context;e.activeAnnotation=null,e.tooltipDom.classList.remove("sse-visible"),this.hotspotDom.classList.remove("sse-active")}_hideElements(){this.hotspotDom.style.display="none",this.context.tooltipOwner===this&&this.context.tooltipDom.classList.remove("sse-visible")}_updatePositions(e,t){let s=this.hotspotDom;s.style.display="flex",s.style.transform=`translate(${e.x}px, ${e.y}px) translate(-50%, -50%)`,s.style.zIndex=String(Math.round(1e5/(1+t)));let n=this.context;if(n.tooltipOwner!==this)return;let o=n.tooltipDom,r=n.canvas.clientWidth,a=n.canvas.clientHeight,l=e.x>=0&&e.x<=r&&e.y>=0&&e.y<=a;if(n.activeAnnotation!==this||!n.revealed){o.classList.remove("sse-visible");let k=n.tooltipOffset;k&&(o.style.transform=`translate(${e.x+k.x}px, ${e.y+k.y}px)`);return}let c=8,h=24,d=o.offsetWidth,u=o.offsetHeight,p=e.x+h,g=e.x-h-d,m=o.classList.contains("sse-arrow-left");(m?g<c&&p+d<=r-c:p+d>r-c)&&(m=!m);let f=m?g:p,v=e.y-u/2;f=Math.max(c,Math.min(f,r-d-c)),v=Math.max(c,Math.min(v,a-u-c));let y=Math.max(16,Math.min(e.y-v,u-16));o.style.setProperty("--arrow-top",`${y}px`),o.classList.toggle("sse-arrow-right",!m),o.classList.toggle("sse-arrow-left",m),o.style.transform=`translate(${f}px, ${v}px)`,o.classList.add("sse-visible"),n.tooltipOffset=l?{x:f-e.x,y:v-e.y}:null}},lo=class{parentDom;hotspotsDom;canvas;camera;tooltipDom;titleDom;textDom;activeAnnotation=null;revealed=!1;tooltipOwner=null;tooltipOffset=null;constructor(e,t,s){this.parentDom=s,this.canvas=e,this.camera=t,this.hotspotsDom=document.createElement("div"),this.hotspotsDom.className="sse-annotation-hotspots",s.appendChild(this.hotspotsDom),this.tooltipDom=document.createElement("div"),this.tooltipDom.className="sse-annotation",this.titleDom=document.createElement("div"),this.titleDom.className="sse-annotation-title",this.textDom=document.createElement("div"),this.textDom.className="sse-annotation-text",this.tooltipDom.append(this.titleDom,this.textDom),s.appendChild(this.tooltipDom)}},wc=150,bc=.5,_c=.1,kc=13,Mc=.9,co=class{parentDom;parent;subscriptions;removeClick;removeOcclusion;constructor(e,t,s,n,o){let{app:r,state:a,events:l,annotations:c}=e,h=document.createElement("div");h.className="sse-annotations",t.querySelector(".sse-sceneLayer").appendChild(h),this.parentDom=h;let d=r.graphicsDevice.canvas,u=new lo(d,s,h),p=new ce("annotations",r);r.root.addChild(p),this.parent=p;let g=[];for(let C=0;C<c.length;C++){let A=c[C],D=new ce("annotation",r);D.addComponent("script"),D.script.create(ro);let O=D.script.annotation;O.context=u,O.label=(C+1).toString(),O.title=A.title,O.text=A.text,D.setPosition(A.position[0],A.position[1],A.position[2]),p.addChild(D),g.push(O),O.on("select",()=>e.selectAnnotation(C))}let m=!1,f=null,v=0,y=new U,k=new U,w=async()=>{f=null;let C=n();if(m||!C||h.style.display==="none")return;let A=d.clientWidth,D=d.clientHeight,O=g.filter(({screen:E})=>E&&E.x>=0&&E.x<A&&E.y>=0&&E.y<D);if(O.length===0)return;let H=v,N;try{N=await C.pickVisibility(O.map(({screen:E})=>({x:E.x/A,y:E.y/D,depth:E.depth*(1-_c)})),kc/D)}catch{return}m||H!==v||O.forEach((E,j)=>{E.setOccluded((N[j]??0)>=bc)})},P=()=>{f&&clearTimeout(f),f=setTimeout(w,wc)},S=1,b=()=>{let C=o();C<S&&(u.revealed=!1),S=C};r.on("framerender",b);let z=()=>{let C=s.getWorldTransform(),{projectionMatrix:A}=s.camera,D=y.equals(C)&&k.equals(A),O=o(),{activeAnnotation:H}=u,N=H?.screen,E=N&&N.x>=0&&N.x<=d.clientWidth&&N.y>=0&&N.y<=d.clientHeight;H&&!u.revealed&&(D||O>=Mc&&E)&&H.revealTooltip(),!D&&(y.copy(C),k.copy(A),v++,P())};r.on("frameend",z);let _=r.systems.gsplat,R=!1,L=(C,A,D)=>{C===s.camera&&(D&&!R&&(v++,P()),R=D)};_.on("frame:ready",L),this.removeOcclusion=()=>{m=!0,f&&clearTimeout(f),r.off("framerender",b),r.off("frameend",z),_.off("frame:ready",L)};let M=()=>{let C=(a.cameraMode==="walk"||a.cameraMode==="fly")&&a.gamingControls,A=!a.loaded||!a.showAnnotations||a.controlsHidden||C,D=h.style.display==="none";h.style.display=A?"none":"block",D&&!A&&P();let O=A||a.selectedAnnotation===null?null:g[a.selectedAnnotation];O!==u.activeAnnotation&&(u.activeAnnotation?.hideTooltip(),O?.showTooltip()),r.renderNextFrame=!0};this.subscriptions=[l.on("loaded:changed",M),l.on("selectedAnnotation:changed",M),l.on("controlsHidden:changed",M),l.on("showAnnotations:changed",M),l.on("cameraMode:changed",M),l.on("gamingControls:changed",M)],M();let T=0,V=()=>{T=0},B=C=>{C.buttons&&(T+=Math.abs(C.movementX)+Math.abs(C.movementY))},I=()=>{let C=T>=At;T=0,!C&&a.loaded&&a.selectedAnnotation!==null&&e.selectAnnotation(null)};t.addEventListener("pointerdown",V),t.addEventListener("pointermove",B),t.addEventListener("click",I),this.removeClick=()=>{t.removeEventListener("pointerdown",V),t.removeEventListener("pointermove",B),t.removeEventListener("click",I)}}destroy(){for(let e of this.subscriptions)e.off();this.removeClick(),this.removeOcclusion(),this.parent.destroy(),this.parentDom.remove()}},Sc=(i,e)=>{let{state:t,events:s}=i,n=e.querySelector(".sse-orbitCamera"),o=e.querySelector(".sse-flyCamera"),r=e.querySelector(".sse-fpsCamera"),a=e.querySelector(".sse-reset"),l=e.querySelector(".sse-frame"),c=()=>{n.classList.toggle("sse-active",t.cameraMode==="orbit"),o.classList.toggle("sse-active",t.cameraMode==="fly"),r.classList.toggle("sse-active",t.cameraMode==="walk"),r.classList.toggle("sse-hidden",!t.walkAllowed),o.classList.toggle("sse-middle",t.walkAllowed),o.classList.toggle("sse-right",!t.walkAllowed);for(let f of[n,o,r,a,l])f.disabled=!t.loaded},h=()=>{t.cameraMode="orbit"},d=()=>{t.cameraMode="fly"},u=()=>i.toggleWalk(),p=()=>i.resetCamera(),g=()=>i.frameScene();n.addEventListener("click",h),o.addEventListener("click",d),r.addEventListener("click",u),a.addEventListener("click",p),l.addEventListener("click",g);let m=[s.on("cameraMode:changed",c),s.on("walkAllowed:changed",c),s.on("loaded:changed",c)];return c(),()=>{for(let f of m)f.off();n.removeEventListener("click",h),o.removeEventListener("click",d),r.removeEventListener("click",u),a.removeEventListener("click",p),l.removeEventListener("click",g)}},Fr="controlsPanelOpen",Cc=()=>{try{return localStorage.getItem(Fr)!=="false"}catch{return!0}},Pc=i=>{try{localStorage.setItem(Fr,String(i))}catch{}},Pe=i=>({kind:"key",label:i}),de=(i,e)=>({kind:"mouse",button:i,suffix:e}),Hs=["W","A","S","D"].map(Pe),Lc={kind:"or"},Ec={left:'<path class="sse-mouseActive" d="M1 9a8 8 0 0 1 8-8v8Z"/>',right:'<path class="sse-mouseActive" d="M17 9a8 8 0 0 0-8-8v8Z"/>',wheel:'<rect class="sse-mouseActive" x="7.5" y="3.5" width="3" height="6" rx="1.5"/>',none:""},Rc=i=>`<svg class="sse-mouseIcon" viewBox="0 0 18 26" aria-hidden="true">${Ec[i]}<rect class="sse-mouseBody" x="1" y="1" width="16" height="24" rx="8"/><path class="sse-mouseSplit" d="M9 1v8M1 9h16"/></svg>`,Ac='<svg class="sse-dragIcon" viewBox="0 0 12 12" aria-hidden="true"><path d="M6 1v10M1 6h10M4 3l2-2 2 2M4 9l2 2 2-2M3 4 1 6l2 2M9 4l2 2-2 2"/></svg>',Tc={orbit:"help.section.orbit",fly:"help.section.fly",walk:"help.section.walk",anim:"help.section.anim"},Dc={orbit:"#orbitIcon",fly:"#flyIcon",walk:"#walkIcon",anim:"#playIcon"},Ir=i=>i.gamingControls&&(i.cameraMode==="fly"||i.cameraMode==="walk"),zc=(i,e)=>{let{cameraMode:t}=i,s=Ir(i),n=Pe(e("help.key.space")),o=Pe(e("help.key.esc")),r=["run-slow",Pe("Shift"),Lc,Pe("Ctrl")],a=["reset-camera",Pe("R")],l=h=>({kind:"gesture",label:e(`help.key.${h}`)}),c;if(i.inputMode==="desktop"){switch(t){case"orbit":c=[["orbit",de("left","drag")],["pan",de("right","drag")],["zoom",de("wheel")],["set-focus",de("left")],["fly-to-point",de("left","double")],["frame-scene",Pe("F")],a];break;case"fly":c=s?[["look-around",de("none","drag")],["move",...Hs],["vertical",Pe("Q"),Pe("E")],r,a,["release-mouse",o]]:[["fly-to",de("left")],["look-around",de("left","drag")],["pan",de("right","drag")],["focus-point",de("left","double")],["move",...Hs],r,a];break;case"walk":c=s?[["look-around",de("none","drag")],["move",...Hs],["jump",n],r,a,["release-mouse",o]]:[["walk-to",de("left")],["look-around",de("left","drag")],["fly-to-point",de("left","double")],["move",...Hs],r,a,["exit-walk",o]];break;case"anim":c=[["play-pause",n],["exit-cancel",o]];break}s||c.push(null,["toggle-controls",Pe("H")],["toggle-help",Pe("Shift"),Pe("H")])}else switch(t){case"orbit":c=[["orbit",l("one-finger-drag")],["pan",l("two-finger-drag")],["zoom",l("pinch")],["set-focus",l("tap")],["fly-to-point",l("double-tap")]];break;case"fly":c=s?[["look-around",l("touch-drag")],["move",l("joystick")]]:[["fly-to",l("tap")],["look-around",l("touch-drag")],["move",l("pinch-two-finger")],["focus-point",l("double-tap")]];break;case"walk":c=s?[["look-around",l("touch-drag")],["move",l("joystick")],["jump",l("tap")]]:[["walk-to",l("tap")],["look-around",l("touch-drag")],["fly-to-point",l("double-tap")]];break;case"anim":c=[];break}return{title:e(Tc[t]),subtitle:s?e("settings.gaming-controls"):"",icon:Dc[t],rows:c}},Fc=i=>{let e=(s,n)=>{let o=document.createElement("span");return o.className=s,n&&(o.textContent=n),o},t=s=>{let n=document.createElement("kbd");return n.className="sse-key",n.textContent=s,n};switch(i.kind){case"key":return t(i.label);case"mouse":{let s=e("sse-mouse");return s.innerHTML=(i.suffix==="drag"?Ac:"")+Rc(i.button),i.suffix==="double"&&s.prepend(e("sse-mouseCount","\xD72")),s}case"gesture":return e("sse-gesture",i.label);case"or":return e("sse-or","/")}},Ic=(i,e,t)=>{let{state:s,events:n}=i,o=e.querySelector(".sse-controlsHintIcon > use"),r=e.querySelector(".sse-controlsHintTitle"),a=e.querySelector(".sse-controlsHintSubtitle"),l=e.querySelector(".sse-controlsHintRows"),c="",h=p=>{o.setAttribute("href",p.icon),r.textContent=p.title,a.textContent=p.subtitle,l.replaceChildren(...p.rows.map(g=>{let m=document.createElement("div");if(!g)return m.className="sse-divider",m;let[f,...v]=g,y=document.createElement("span");y.className="sse-controlsHintAction",y.textContent=t(`help.action.${f}`);let k=document.createElement("span");return k.className="sse-controlsHintInputs",k.append(...v.map(Fc)),m.className="sse-controlsHintItem",m.append(y,k),m}))},d=()=>{let p=`${s.cameraMode}:${s.inputMode}:${Ir(s)}`;p!==c&&(c=p,h(zc(s,t)))},u=[n.on("cameraMode:changed",d),n.on("inputMode:changed",d),n.on("gamingControls:changed",d)];return d(),()=>{for(let p of u)p.off()}},Vc=(i,e)=>{let t=e.querySelector(".sse-enterFullscreen"),s=e.querySelector(".sse-exitFullscreen"),n=()=>{t.classList.toggle("sse-hidden",i.state.isFullscreen),s.classList.toggle("sse-hidden",!i.state.isFullscreen)},o=()=>{i.requestFullscreen().catch(()=>{})},r=()=>{i.exitFullscreen().catch(()=>{})},a=()=>{screen.orientation.type.startsWith("landscape")?o():r()};t.addEventListener("click",o),s.addEventListener("click",r),screen.orientation?.addEventListener("change",a);let l=i.events.on("isFullscreen:changed",n);return n(),()=>{l.off(),t.removeEventListener("click",o),s.removeEventListener("click",r),screen.orientation?.removeEventListener("change",a)}},Oc=(i,e)=>{let{state:t,events:s}=i,n={joystickBase:e.querySelector(".sse-joystickBase"),joystick:e.querySelector(".sse-joystick")},o=!1,r=100,a=40,l=(r-a)/2,c=(r-a)/2,h=l,d=null,u=0,p=0,g="2d",m=0,f=()=>{let b=d;d=null,u=p=0,n.joystick.style.top=`${l}px`,n.joystick.style.left=g==="2d"?`${c}px`:"8px",b!==null&&(o||i.setMoveInput(0,0),n.joystickBase.hasPointerCapture(b)&&n.joystickBase.releasePointerCapture(b))},v=()=>{f(),(t.cameraMode==="fly"||t.cameraMode==="walk")&&t.inputMode==="touch"&&t.gamingControls&&t.loaded&&t.xrMode===null?(n.joystickBase.classList.remove("sse-hidden"),n.joystickBase.classList.toggle("sse-mode-2d",g==="2d"),n.joystick.style.top=`${l}px`,g==="2d"?n.joystick.style.left=`${c}px`:n.joystick.style.left="8px"):n.joystickBase.classList.add("sse-hidden")},y=["cameraMode","inputMode","gamingControls","loaded","xrMode"].map(b=>s.on(`${b}:changed`,v)),k=(b,z)=>{let _=n.joystickBase.getBoundingClientRect(),R=_.top+_.height/2,L=z-R,M=Math.max(-h,Math.min(h,L));if(p=M/h,n.joystick.style.top=`${l+M}px`,g==="2d"){let T=_.left+_.width/2,V=b-T,B=Math.max(-h,Math.min(h,V));u=B/h,n.joystick.style.left=`${c+B}px`}else u=0;i.setMoveInput(u,-p)},w=b=>{if(d!==null)return;let z=Date.now();z-m<300?(g=g==="1d"?"2d":"1d",v(),m=0):m=z,d=b.pointerId,n.joystickBase.setPointerCapture(b.pointerId),k(b.clientX,b.clientY),b.preventDefault(),b.stopPropagation()},P=b=>{b.pointerId===d&&(k(b.clientX,b.clientY),b.preventDefault())},S=b=>{b.pointerId===d&&f()};return n.joystickBase.addEventListener("pointerdown",w),n.joystickBase.addEventListener("pointermove",P),n.joystickBase.addEventListener("pointerup",S),n.joystickBase.addEventListener("pointercancel",S),n.joystickBase.addEventListener("lostpointercapture",S),window.addEventListener("blur",f),document.addEventListener("visibilitychange",f),v(),()=>{o=!0,f();for(let b of y)b.off();n.joystickBase.removeEventListener("pointerdown",w),n.joystickBase.removeEventListener("pointermove",P),n.joystickBase.removeEventListener("pointerup",S),n.joystickBase.removeEventListener("pointercancel",S),n.joystickBase.removeEventListener("lostpointercapture",S),window.removeEventListener("blur",f),document.removeEventListener("visibilitychange",f)}},Bc=(i,e,t)=>{let{state:s,events:n}=i,o=e.querySelector(".sse-play"),r=e.querySelector(".sse-pause"),a=e.querySelector(".sse-timelineContainer"),l=e.querySelector(".sse-handle"),c=e.querySelector(".sse-time"),h=()=>s.loaded&&s.hasAnimation,d=()=>{let P=s.cameraMode==="anim"&&!s.animationPaused;o.classList.toggle("sse-hidden",!h()||P),r.classList.toggle("sse-hidden",!h()||!P),a.classList.toggle("sse-hidden",!h()||s.cameraMode!=="anim")},u=()=>{let P=s.animationDuration>0?s.animationTime/s.animationDuration*100:0;l.style.left=`${P}%`,c.style.left=`${P}%`,c.innerText=`${s.animationTime.toFixed(1)}s`},p=()=>{h()&&(s.cameraMode="anim",s.animationPaused=!1)},g=()=>{h()&&(s.cameraMode="anim",s.animationPaused=!0)},m=P=>{let S=a.getBoundingClientRect();if(S.width<=0)return;let b=Math.max(0,Math.min(1,(P.clientX-S.left)/S.width));i.seek(s.animationDuration*b),t()},f=null,v=P=>{f!==null||P.button!==0||!h()||(s.animationPaused=!0,m(P),f=P.pointerId,a.setPointerCapture(f),c.classList.remove("sse-hidden"))},y=P=>{P.pointerId===f&&m(P)},k=P=>{P.pointerId===f&&(f=null,c.classList.add("sse-hidden"),a.hasPointerCapture(P.pointerId)&&a.releasePointerCapture(P.pointerId))};o.addEventListener("click",p),r.addEventListener("click",g),a.addEventListener("pointerdown",v),a.addEventListener("pointermove",y),a.addEventListener("pointerup",k),a.addEventListener("pointercancel",k),a.addEventListener("lostpointercapture",k);let w=[n.on("loaded:changed",d),n.on("hasAnimation:changed",d),n.on("cameraMode:changed",d),n.on("animationPaused:changed",d),n.on("animationTime:changed",u),n.on("animationDuration:changed",u)];return d(),u(),()=>{for(let P of w)P.off();o.removeEventListener("click",p),r.removeEventListener("click",g),a.removeEventListener("pointerdown",v),a.removeEventListener("pointermove",y),a.removeEventListener("pointerup",k),a.removeEventListener("pointercancel",k),a.removeEventListener("lostpointercapture",k),f!==null&&a.hasPointerCapture(f)&&a.releasePointerCapture(f)}},Nc=(i,e)=>{let{state:t,events:s}=i,n=e.querySelector(".sse-arMode"),o=e.querySelector(".sse-vrMode"),r=e.querySelector(".sse-xrModal"),a=e.querySelector(".sse-xrModalOk"),l=e.querySelector(".sse-xrModalCancel"),c=()=>{n.classList.toggle("sse-hidden",!t.hasAR),o.classList.toggle("sse-hidden",!t.hasVR),n.disabled=o.disabled=!t.loaded||t.xrMode!==null},h=f=>{(f==="ar"?t.canStartAR:t.canStartVR)?i.startXR(f).catch(()=>{}):(f==="ar"?t.hasAR:t.hasVR)&&r.classList.remove("sse-hidden")},d=()=>h("ar"),u=()=>h("vr"),p=()=>r.classList.add("sse-hidden"),g=()=>{let f=new URL(location.href);f.searchParams.set("webgl",""),location.replace(f.toString())};n.addEventListener("click",d),o.addEventListener("click",u),a.addEventListener("click",g),l.addEventListener("click",p),r.addEventListener("pointerdown",p);let m=[s.on("loaded:changed",c),s.on("hasAR:changed",c),s.on("hasVR:changed",c),s.on("xrMode:changed",c)];return c(),()=>{for(let f of m)f.off();n.removeEventListener("click",d),o.removeEventListener("click",u),a.removeEventListener("click",g),l.removeEventListener("click",p),r.removeEventListener("pointerdown",p)}},qc=(i,e,t)=>{let s=i.querySelector(".sse-poster");s.style.setProperty("--poster-url",`url(${e.src})`),s.style.display="block",s.style.filter="blur(40px)",i.style.setProperty("--canvas-opacity","0"),t.on("loaded:changed",()=>{s.style.display="none",i.style.setProperty("--canvas-opacity","1")});let n=o=>{s.style.filter=`blur(${Math.floor((100-o)*.4)}px)`};t.on("progress:changed",n)},$c=i=>{let{gpuAdapter:e,unmaskedRenderer:t}=i,s=e?.info;return s?s.description||[s.vendor,s.architecture,s.device].filter(Boolean).join(" / "):t??""},Hc=(i,e,t,s)=>{let{root:n,localize:o}=i,{events:r,state:a}=e,l=[],c=(E,j)=>{let $=r.on(E,j);l.push(()=>$.off())},h=["ui","sceneLayer","controlsWrap","annotationNav","arMode","vrMode","enterFullscreen","exitFullscreen","info","infoPanel","infoClose","infoShortcuts","rendererName","gpuName","engineVersionLabel","buttonContainer","play","pause","settings","settingsPanel","orbitCamera","flyCamera","fpsCamera","performanceModeRow","performanceModeCheck","performanceModeOption","gamingControlsRow","gamingControlsCheck","gamingControlsOption","controlsButton","controlsHint","controlsHintClose","reset","frame","loadingWrap","loadingText","loadingBar","showCollisionRow","showCollisionCheck","showCollisionShortcut","walkShortcut","playShortcut","tooltip","viewerBranding","appVersionLabel"].reduce((E,j)=>(E[j]=n.querySelector(`.sse-${j}`),E),{});h.appVersionLabel.textContent=zr,h.engineVersionLabel.textContent=Tn,h.rendererName.textContent=i.renderer==="webgpu"?"WebGPU":"WebGL 2",h.gpuName.textContent=$c(i.app.graphicsDevice),h.ui.addEventListener("click",()=>{document.activeElement?.blur()});let d=i.app.graphicsDevice.canvas,u=E=>{E.preventDefault();let j=new WheelEvent(E.type,E),$=E;for(let F of["wheelDelta","wheelDeltaX","wheelDeltaY"])typeof $[F]=="number"&&Object.defineProperty(j,F,{value:$[F],configurable:!0});d.dispatchEvent(j)};for(let E of[h.ui,h.sceneLayer])E.addEventListener("wheel",u,{passive:!1});let p=E=>{h.loadingText.textContent=`${E}%`,E<100?h.loadingBar.style.backgroundImage=`linear-gradient(90deg, #F60 0%, #F60 ${E}%, white ${E}%, white 100%)`:h.loadingBar.style.backgroundImage="linear-gradient(90deg, #F60 0%, #F60 100%)"};c("progress:changed",p),p(a.progress),c("loaded:changed",()=>{h.loadingWrap.classList.add("sse-hidden")}),h.performanceModeRow.addEventListener("click",()=>{a.performanceMode=!a.performanceMode});let g=()=>{h.performanceModeCheck.classList.toggle("sse-active",a.performanceMode)};c("performanceMode:changed",g),g(),h.gamingControlsRow.addEventListener("click",()=>{a.gamingControls=!a.gamingControls});let m=()=>{let E=a.inputMode==="desktop";h.gamingControlsRow.classList.toggle("sse-hidden",E)};c("inputMode:changed",m),m();let f=()=>{h.gamingControlsCheck.classList.toggle("sse-active",a.gamingControls)};c("gamingControls:changed",f),f();let v=E=>!E.classList.contains("sse-hidden"),y=Cc(),k=()=>{let E=y&&a.loaded&&!v(h.settingsPanel);h.controlsHint.classList.toggle("sse-hidden",!E),h.controlsButton.classList.toggle("sse-active",E)},w=E=>{h.settingsPanel.classList.toggle("sse-hidden",!E),h.settings.classList.toggle("sse-active",E),k()},P=E=>{y=E,Pc(E),E&&w(!1),k()};c("loaded:changed",k),k();let S=()=>P(!v(h.controlsHint));h.controlsButton.addEventListener("click",()=>{S(),A()}),h.controlsHintClose.addEventListener("click",()=>{P(!1),A()});let b=E=>{h.infoShortcuts.classList.toggle("sse-hidden",a.inputMode!=="desktop"),h.walkShortcut.classList.toggle("sse-hidden",!a.walkAllowed),h.playShortcut.classList.toggle("sse-hidden",!a.hasAnimation),h.infoPanel.classList.toggle("sse-hidden",!E),h.info.classList.toggle("sse-active",E)},z=()=>b(!v(h.infoPanel));h.info.addEventListener("click",()=>{z(),A()}),h.infoPanel.addEventListener("pointerdown",()=>{b(!1),A()}),h.infoClose.addEventListener("click",()=>{b(!1),A()}),c("inputEvent",E=>{E==="toggleHelp"?z():E==="toggleControls"?S():E==="cancel"?(b(!1),w(!1)):E==="interrupt"&&w(!1)}),c("controlsHidden:changed",E=>{h.controlsWrap.classList.toggle("sse-faded-out",E),h.controlsWrap.classList.toggle("sse-faded-in",!E)});let _=null,R=null,L=!1;l.push(()=>{_&&(clearTimeout(_),_=null),R&&(clearTimeout(R),R=null)});let M=()=>a.inputMode==="desktop"&&a.gamingControls&&(a.cameraMode==="walk"||a.cameraMode==="fly"),T=E=>{h.controlsWrap.classList.toggle("sse-dimmed",E),h.annotationNav.classList.toggle("sse-dimmed",E)},V=()=>{R&&(clearTimeout(R),R=null),T(!1)},B=()=>{_&&(clearTimeout(_),_=null),V(),L=!1,b(!1),w(!1),a.controlsHidden=!0},I=()=>{V(),T(!0),R=setTimeout(B,2500)},C=()=>{_&&(clearTimeout(_),_=null),b(!1),w(!1),a.controlsHidden=!1,I()},A=()=>{if(M()){R?(b(!1),w(!1)):B();return}_&&clearTimeout(_),V(),a.controlsHidden=!1,_=setTimeout(()=>{_=null,!(L||v(h.settingsPanel)||v(h.infoPanel)||v(h.controlsHint))&&(a.selectedAnnotation===null||!a.showAnnotations)&&I()},3e3)};h.controlsWrap.addEventListener("pointerenter",()=>{L=!0,A()}),h.controlsWrap.addEventListener("pointerleave",()=>{L=!1,A()}),c("loaded:changed",()=>{h.controlsWrap.classList.remove("sse-hidden"),A()}),c("inputEvent",A);let D=!1,O=()=>{let E=M();E&&!D?C():E||(V(),A()),D=E};if(c("cameraMode:changed",O),c("inputMode:changed",O),c("gamingControls:changed",O),c("selectedAnnotation:changed",A),l.push(Bc(e,n,A)),l.push(Sc(e,n)),l.push(Ic(e,n,o)),l.push(Vc(e,n)),l.push(Nc(e,n)),c("hasCollisionOverlay:changed",E=>{h.showCollisionRow.classList.toggle("sse-hidden",!E),h.showCollisionShortcut.classList.toggle("sse-hidden",!E)}),h.showCollisionRow.addEventListener("click",()=>{a.collisionOverlayEnabled=!a.collisionOverlayEnabled}),c("collisionOverlayEnabled:changed",E=>{h.showCollisionCheck.classList.toggle("sse-active",E)}),h.settings.addEventListener("click",()=>{w(!v(h.settingsPanel)),A()}),l.push(Oc(e,n)),l.push(vc(e,n)),e.annotations.length>0){let E=new co(e,n,i.camera,t,s);l.push(()=>E.destroy())}let H=new ao(h.tooltip);l.push(H.destroy),H.register(h.play,o("tooltip.play"),"top"),H.register(h.pause,o("tooltip.pause"),"top"),H.register(h.orbitCamera,o("tooltip.orbit-camera"),"top"),H.register(h.flyCamera,o("tooltip.fly-camera"),"top"),H.register(h.fpsCamera,o("tooltip.walk-mode"),"top"),H.register(h.reset,o("tooltip.reset-camera"),"bottom"),H.register(h.frame,o("tooltip.frame-scene"),"bottom"),H.register(h.settings,o("tooltip.settings"),"top"),H.register(h.info,o("tooltip.help"),"top"),H.register(h.controlsButton,o("tooltip.controls"),"top"),H.register(h.arMode,o("tooltip.enter-ar"),"top"),H.register(h.vrMode,o("tooltip.enter-vr"),"top"),H.register(h.enterFullscreen,o("tooltip.fullscreen"),"top"),H.register(h.exitFullscreen,o("tooltip.fullscreen"),"top");let N=()=>{try{return window.location.hostname!==window.parent.location.hostname}catch{return!0}};if(window.parent!==window&&N()){let E=new URL(window.location.href);E.pathname==="/s"&&(E.pathname="/view"),h.viewerBranding.href=E.toString(),h.viewerBranding.classList.remove("sse-hidden")}return()=>{for(let E of l)E()}},jc=`
|
|
81
|
+
<canvas></canvas>
|
|
82
|
+
|
|
83
|
+
<div class="sse-sceneLayer"></div>
|
|
84
|
+
|
|
85
|
+
<div class="sse-ui">
|
|
86
|
+
<div class="sse-poster"></div>
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
<a class="sse-viewerBranding sse-hidden" target="_blank" rel="noopener noreferrer">
|
|
90
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="64 64 384 384" role="img" aria-label="SuperSplat">
|
|
91
|
+
<path
|
|
92
|
+
fill="#F26722"
|
|
93
|
+
d="M129.83,217c9.75,0,17.64-7.9,17.64-17.64s-7.9-17.64-17.64-17.64-17.64,7.9-17.64,17.64,7.9,17.64,17.64,17.64Z"
|
|
94
|
+
/>
|
|
95
|
+
<path
|
|
96
|
+
fill="#F26722"
|
|
97
|
+
d="M388.74,253.94c-12.46,0-22.57,10.11-22.57,22.57s10.11,22.57,22.57,22.57,22.57-10.11,22.57-22.57-10.11-22.57-22.57-22.57h0Z"
|
|
98
|
+
/>
|
|
99
|
+
<path
|
|
100
|
+
fill="#F26722"
|
|
101
|
+
d="M345.26,161.1h.02c.37-.05.65-.22.97-.33,16.78-2.32,29.75-16.57,29.75-33.99,0-19.04-15.43-34.46-34.46-34.46s-34.46,15.43-34.46,34.46c0,4.29.88,8.35,2.32,12.14.04.12-.01.25.04.38,10.55,29.18-22.33,23.62-39.07,20.74-.49-.09-.88-.07-1.32-.11-4.27-.56-8.59-.97-13.03-.97-53.93,0-97.64,43.71-97.64,97.64,0,9.84,1.48,19.32,4.2,28.28.04.1,0,.17.05.27,8.97,30.15-14.83,25.49-25.52,25.69-1.04-.13-2.09-.32-3.18-.32-13.51,0-24.45,10.94-24.45,24.45s10.94,24.45,24.45,24.45c11.69,0,21.44-8.22,23.85-19.19h.02c6.31-26.57,25.9-18.02,31.15-12.67.2.2.4.26.59.42,17.44,16.25,40.75,26.26,66.47,26.26.67,0,1.34-.09,2.01-.1.66.01,1.31.04,2.04.04,9.7-.04,17.82,10.91,4.94,29.68h.13c-4.42,6.01-7.1,13.36-7.1,21.41,0,20.09,16.29,36.39,36.39,36.39s36.39-16.29,36.39-36.39c0-15.4-9.61-28.49-23.12-33.81-.32-.15-.61-.29-.98-.45-12.28-4.94-25.43-17.69-6.89-27.1l-.04-.1c31.93-16.06,53.88-49.02,53.88-87.2,0-21.15-6.8-40.68-18.23-56.66-.13-.23-.23-.47-.44-.74-23.15-31.67-2.37-36.07,10.3-38.09h-.01Z"
|
|
102
|
+
/>
|
|
103
|
+
<path
|
|
104
|
+
fill="#fff"
|
|
105
|
+
d="M230.79,284.71c0,7.95-6.44,14.38-14.38,14.38s-14.38-6.44-14.38-14.38v-22.95c0-7.95,6.44-14.38,14.38-14.38s14.38,6.44,14.38,14.38v22.95Z"
|
|
106
|
+
/>
|
|
107
|
+
<path
|
|
108
|
+
fill="#fff"
|
|
109
|
+
d="M309.78,284.71c0,7.95-6.44,14.38-14.38,14.38s-14.38-6.44-14.38-14.38v-22.95c0-7.95,6.44-14.38,14.38-14.38s14.38,6.44,14.38,14.38v22.95Z"
|
|
110
|
+
/>
|
|
111
|
+
</svg>
|
|
112
|
+
<span>SuperSplat</span>
|
|
113
|
+
</a>
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
<div class="sse-loadingWrap">
|
|
117
|
+
<div class="sse-loadingText"></div>
|
|
118
|
+
<div class="sse-loadingBar"></div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<div class="sse-controlsWrap sse-hidden">
|
|
122
|
+
|
|
123
|
+
<div class="sse-timelineContainer sse-hidden">
|
|
124
|
+
<div class="sse-line"></div>
|
|
125
|
+
<div class="sse-handle"></div>
|
|
126
|
+
<div class="sse-time sse-hidden">0:00</div>
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
<div class="sse-buttonsContainer">
|
|
131
|
+
<div class="sse-buttonGroup">
|
|
132
|
+
<button class="sse-play sse-controlButton sse-hidden">
|
|
133
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
|
134
|
+
<g class="sse-stroke"><use href="#playIcon" /></g>
|
|
135
|
+
<g class="sse-fill"><use href="#playIcon" /></g>
|
|
136
|
+
</svg>
|
|
137
|
+
</button>
|
|
138
|
+
<button class="sse-pause sse-controlButton sse-hidden">
|
|
139
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px">
|
|
140
|
+
<g class="sse-stroke"><use href="#pauseIcon" /></g>
|
|
141
|
+
<g class="sse-fill"><use href="#pauseIcon" /></g>
|
|
142
|
+
</svg>
|
|
143
|
+
</button>
|
|
144
|
+
</div>
|
|
145
|
+
|
|
146
|
+
<div class="sse-spacer"></div>
|
|
147
|
+
|
|
148
|
+
<div class="sse-buttonGroup">
|
|
149
|
+
<button class="sse-orbitCamera sse-controlButton sse-toggle sse-left">
|
|
150
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
|
151
|
+
<g class="sse-stroke"><use href="#orbitIcon" /></g>
|
|
152
|
+
<g class="sse-fill"><use href="#orbitIcon" /></g>
|
|
153
|
+
</svg>
|
|
154
|
+
</button>
|
|
155
|
+
<button class="sse-flyCamera sse-controlButton sse-toggle sse-right">
|
|
156
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
|
157
|
+
<g class="sse-stroke"><use href="#flyIcon" /></g>
|
|
158
|
+
<g class="sse-fill"><use href="#flyIcon" /></g>
|
|
159
|
+
</svg>
|
|
160
|
+
</button>
|
|
161
|
+
<button class="sse-fpsCamera sse-controlButton sse-toggle sse-right sse-hidden">
|
|
162
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
|
163
|
+
<g class="sse-stroke"><use href="#walkIcon" /></g>
|
|
164
|
+
<g class="sse-fill"><use href="#walkIcon" /></g>
|
|
165
|
+
</svg>
|
|
166
|
+
</button>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<div class="sse-buttonGroup">
|
|
170
|
+
|
|
171
|
+
<button class="sse-settings sse-controlButton">
|
|
172
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
|
173
|
+
<g class="sse-stroke"><use href="#settingsIcon" /></g>
|
|
174
|
+
<g class="sse-fill"><use href="#settingsIcon" /></g>
|
|
175
|
+
</svg>
|
|
176
|
+
</button>
|
|
177
|
+
<button class="sse-controlsButton sse-controlButton">
|
|
178
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
|
179
|
+
<g class="sse-stroke"><use href="#keyboardIcon" /></g>
|
|
180
|
+
<g class="sse-fill"><use href="#keyboardIcon" /></g>
|
|
181
|
+
</svg>
|
|
182
|
+
</button>
|
|
183
|
+
<button class="sse-info sse-controlButton">
|
|
184
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
|
185
|
+
<g class="sse-stroke"><use href="#infoIcon" /></g>
|
|
186
|
+
<g class="sse-fill"><use href="#infoIcon" /></g>
|
|
187
|
+
</svg>
|
|
188
|
+
</button>
|
|
189
|
+
<button class="sse-arMode sse-controlButton sse-hidden">
|
|
190
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
|
191
|
+
<g class="sse-stroke"><use href="#arIcon" /></g>
|
|
192
|
+
<g class="sse-fill"><use href="#arIcon" /></g>
|
|
193
|
+
</svg>
|
|
194
|
+
</button>
|
|
195
|
+
<button class="sse-vrMode sse-controlButton sse-hidden">
|
|
196
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
|
197
|
+
<g class="sse-stroke"><use href="#vrIcon" /></g>
|
|
198
|
+
<g class="sse-fill"><use href="#vrIcon" /></g>
|
|
199
|
+
</svg>
|
|
200
|
+
</button>
|
|
201
|
+
<button class="sse-enterFullscreen sse-controlButton">
|
|
202
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
|
203
|
+
<g class="sse-stroke"><use href="#enterFullscreenIcon" /></g>
|
|
204
|
+
<g class="sse-fill"><use href="#enterFullscreenIcon" /></g>
|
|
205
|
+
</svg>
|
|
206
|
+
</button>
|
|
207
|
+
<button class="sse-exitFullscreen sse-controlButton sse-hidden">
|
|
208
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
|
|
209
|
+
<g class="sse-stroke"><use href="#exitFullscreenIcon" /></g>
|
|
210
|
+
<g class="sse-fill"><use href="#exitFullscreenIcon" /></g>
|
|
211
|
+
</svg>
|
|
212
|
+
</button>
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
<div class="sse-joystickBase sse-hidden">
|
|
219
|
+
<div class="sse-joystick"></div>
|
|
220
|
+
</div>
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
<div class="sse-annotationNav sse-hidden">
|
|
224
|
+
<button class="sse-annotationPrev">
|
|
225
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20">
|
|
226
|
+
<g class="sse-stroke"><use href="#chevronLeftIcon" /></g>
|
|
227
|
+
<g class="sse-fill"><use href="#chevronLeftIcon" /></g>
|
|
228
|
+
</svg>
|
|
229
|
+
</button>
|
|
230
|
+
<div class="sse-annotationInfo">
|
|
231
|
+
<span class="sse-annotationNavNumber"></span>
|
|
232
|
+
<span class="sse-annotationNavTitle"></span>
|
|
233
|
+
</div>
|
|
234
|
+
<button class="sse-annotationNext">
|
|
235
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20">
|
|
236
|
+
<g class="sse-stroke"><use href="#chevronRightIcon" /></g>
|
|
237
|
+
<g class="sse-fill"><use href="#chevronRightIcon" /></g>
|
|
238
|
+
</svg>
|
|
239
|
+
</button>
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
<div class="sse-controlsHint sse-hidden">
|
|
244
|
+
<div class="sse-controlsHintHeader">
|
|
245
|
+
<svg class="sse-controlsHintIcon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
246
|
+
<use href="#orbitIcon" />
|
|
247
|
+
</svg>
|
|
248
|
+
<div class="sse-controlsHintTitles">
|
|
249
|
+
<span class="sse-controlsHintTitle"></span>
|
|
250
|
+
<span class="sse-controlsHintSubtitle"></span>
|
|
251
|
+
</div>
|
|
252
|
+
<button class="sse-controlsHintClose sse-panelButton">
|
|
253
|
+
<svg xmlns="http://www.w3.org/2000/svg"><use href="#closeIcon" /></svg>
|
|
254
|
+
</button>
|
|
255
|
+
</div>
|
|
256
|
+
<div class="sse-controlsHintRows"></div>
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
<div class="sse-settingsPanel sse-hidden">
|
|
261
|
+
|
|
262
|
+
<div class="sse-settingsGroup">
|
|
263
|
+
<div class="sse-annotationsRow sse-settingsRow">
|
|
264
|
+
<div class="sse-annotationsOption" data-i18n="settings.show-annotations">Show Annotations</div>
|
|
265
|
+
<div class="sse-annotationsCheck sse-toggleSwitch sse-active">
|
|
266
|
+
<div class="sse-toggleTrack"><div class="sse-toggleThumb"></div></div>
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
<div class="sse-showCollisionRow sse-settingsRow sse-hidden">
|
|
270
|
+
<div class="sse-showCollisionOption" data-i18n="settings.show-collision">Show Collision</div>
|
|
271
|
+
<div class="sse-showCollisionCheck sse-toggleSwitch">
|
|
272
|
+
<div class="sse-toggleTrack"><div class="sse-toggleThumb"></div></div>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
|
|
277
|
+
<div class="sse-settingsGroup">
|
|
278
|
+
<div class="sse-gamingControlsRow sse-settingsRow">
|
|
279
|
+
<div class="sse-gamingControlsOption" data-i18n="settings.gaming-controls">Gaming Controls</div>
|
|
280
|
+
<div class="sse-gamingControlsCheck sse-toggleSwitch">
|
|
281
|
+
<div class="sse-toggleTrack"><div class="sse-toggleThumb"></div></div>
|
|
282
|
+
</div>
|
|
283
|
+
</div>
|
|
284
|
+
<div class="sse-performanceModeRow sse-settingsRow">
|
|
285
|
+
<div class="sse-performanceModeOption" data-i18n="settings.performance-mode">Performance Mode</div>
|
|
286
|
+
<div class="sse-performanceModeCheck sse-toggleSwitch">
|
|
287
|
+
<div class="sse-toggleTrack"><div class="sse-toggleThumb"></div></div>
|
|
288
|
+
</div>
|
|
289
|
+
</div>
|
|
290
|
+
</div>
|
|
291
|
+
|
|
292
|
+
<div class="sse-settingsGroup">
|
|
293
|
+
<div class="sse-settingsRow">
|
|
294
|
+
<button class="sse-frame" data-i18n="settings.frame">Frame</button>
|
|
295
|
+
<button class="sse-reset" data-i18n="settings.reset">Reset</button>
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
<div class="sse-tooltip"></div>
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
<div class="sse-infoPanel sse-hidden">
|
|
305
|
+
<div class="sse-infoPanelContent" onpointerdown="event.stopPropagation()" onwheel="event.stopPropagation()">
|
|
306
|
+
<button class="sse-infoClose sse-panelButton">
|
|
307
|
+
<svg xmlns="http://www.w3.org/2000/svg"><use href="#closeIcon" /></svg>
|
|
308
|
+
</button>
|
|
309
|
+
<a
|
|
310
|
+
class="sse-viewerTitle"
|
|
311
|
+
href="https://github.com/playcanvas/supersplat-viewer"
|
|
312
|
+
target="_blank"
|
|
313
|
+
rel="noopener noreferrer"
|
|
314
|
+
>
|
|
315
|
+
<svg class="sse-viewerLogo" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
316
|
+
<use href="#supersplatIcon" />
|
|
317
|
+
</svg>
|
|
318
|
+
<span class="sse-title-name">SuperSplat Viewer</span>
|
|
319
|
+
<span class="sse-title-version">v<span class="sse-appVersionLabel"></span></span>
|
|
320
|
+
</a>
|
|
321
|
+
<div class="sse-infoGpu">
|
|
322
|
+
<span class="sse-rendererName"></span>
|
|
323
|
+
<span class="sse-gpuName"></span>
|
|
324
|
+
</div>
|
|
325
|
+
<div class="sse-infoLinks">
|
|
326
|
+
<a
|
|
327
|
+
class="sse-infoLink"
|
|
328
|
+
href="https://github.com/playcanvas/engine"
|
|
329
|
+
target="_blank"
|
|
330
|
+
rel="noopener noreferrer"
|
|
331
|
+
>
|
|
332
|
+
<span>PlayCanvas Engine</span>
|
|
333
|
+
<span class="sse-infoLinkVersion">v<span class="sse-engineVersionLabel"></span></span>
|
|
334
|
+
</a>
|
|
335
|
+
</div>
|
|
336
|
+
<div class="sse-infoShortcuts">
|
|
337
|
+
<h1 data-i18n="help.section.modes">Mode Switching</h1>
|
|
338
|
+
<div class="sse-infoShortcut">
|
|
339
|
+
<span data-i18n="help.action.orbit-mode">Orbit Mode</span>
|
|
340
|
+
<kbd class="sse-key">1</kbd>
|
|
341
|
+
</div>
|
|
342
|
+
<div class="sse-infoShortcut">
|
|
343
|
+
<span data-i18n="help.action.fly-mode">Fly Mode</span>
|
|
344
|
+
<kbd class="sse-key">2</kbd>
|
|
345
|
+
</div>
|
|
346
|
+
<div class="sse-walkShortcut sse-infoShortcut">
|
|
347
|
+
<span data-i18n="help.action.toggle-walk">Toggle Walk</span>
|
|
348
|
+
<kbd class="sse-key">3</kbd>
|
|
349
|
+
</div>
|
|
350
|
+
<div class="sse-infoShortcut">
|
|
351
|
+
<span data-i18n="help.action.toggle-gaming">Toggle Gaming Controls</span>
|
|
352
|
+
<kbd class="sse-key">G</kbd>
|
|
353
|
+
</div>
|
|
354
|
+
<div class="sse-showCollisionShortcut sse-infoShortcut sse-hidden">
|
|
355
|
+
<span data-i18n="help.action.show-collision">Show Collision</span>
|
|
356
|
+
<kbd class="sse-key">V</kbd>
|
|
357
|
+
</div>
|
|
358
|
+
<div class="sse-playShortcut sse-infoShortcut">
|
|
359
|
+
<span data-i18n="help.action.play-pause">Play / Pause</span>
|
|
360
|
+
<kbd class="sse-key" data-i18n="help.key.space">Space</kbd>
|
|
361
|
+
</div>
|
|
362
|
+
<div class="sse-infoShortcut">
|
|
363
|
+
<span data-i18n="help.action.toggle-controls">Toggle Controls</span>
|
|
364
|
+
<kbd class="sse-key">H</kbd>
|
|
365
|
+
</div>
|
|
366
|
+
<div class="sse-infoShortcut">
|
|
367
|
+
<span data-i18n="help.action.toggle-help">Toggle Info</span>
|
|
368
|
+
<span class="sse-keys"><kbd class="sse-key">Shift</kbd><kbd class="sse-key">H</kbd></span>
|
|
369
|
+
</div>
|
|
370
|
+
<div class="sse-infoShortcut">
|
|
371
|
+
<span data-i18n="help.action.exit-cancel">Exit / Cancel</span>
|
|
372
|
+
<kbd class="sse-key" data-i18n="help.key.esc">Esc</kbd>
|
|
373
|
+
</div>
|
|
374
|
+
</div>
|
|
375
|
+
</div>
|
|
376
|
+
</div>
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
<div class="sse-xrModal sse-hidden" onwheel="event.stopPropagation()">
|
|
380
|
+
<div class="sse-xrModalContent" onpointerdown="event.stopPropagation()">
|
|
381
|
+
<h2 data-i18n="xr-modal.title">WebGL Required</h2>
|
|
382
|
+
<p data-i18n="xr-modal.message">
|
|
383
|
+
AR and VR are not supported on the WebGPU renderer. Pressing OK will reload the viewer with WebGL.
|
|
384
|
+
</p>
|
|
385
|
+
<div class="sse-xrModalButtons">
|
|
386
|
+
<button class="sse-xrModalCancel" data-i18n="xr-modal.cancel">Cancel</button>
|
|
387
|
+
<button class="sse-xrModalOk" data-i18n="xr-modal.ok">OK</button>
|
|
388
|
+
</div>
|
|
389
|
+
</div>
|
|
390
|
+
</div>
|
|
391
|
+
</div>
|
|
392
|
+
|
|
393
|
+
<svg>
|
|
394
|
+
<symbol id="playIcon" viewBox="-2 0 24 24">
|
|
395
|
+
<path
|
|
396
|
+
d="M1 1.98725C1 1.20022 1.87789 0.730421 2.5332 1.16694L14.5605 9.18061C15.146 9.57066 15.146 10.4302 14.5605 10.8203L2.5332 18.833C1.87788 19.2695 1 18.7997 1 18.0126V1.98725Z"
|
|
397
|
+
transform="translate(2 2)"
|
|
398
|
+
/>
|
|
399
|
+
</symbol>
|
|
400
|
+
<symbol id="pauseIcon" viewBox="0 0 20 20">
|
|
401
|
+
<path d="M5 16V4h3v12H5zm7-12h3v12h-3V4z" />
|
|
402
|
+
</symbol>
|
|
403
|
+
<symbol id="orbitIcon" viewBox="-2 -2 20 20">
|
|
404
|
+
<path
|
|
405
|
+
fill-rule="evenodd"
|
|
406
|
+
clip-rule="evenodd"
|
|
407
|
+
d="M4.44728 1.7561C6.14077 0.763957 8.53159 1.57857 10.4375 3.58357C12.75 3.02099 14.4585 3.40647 14.6491 4.73071C14.7831 5.66256 14.139 6.87994 12.9942 8.09008C13.7427 10.7586 13.2413 13.2551 11.5527 14.2444C9.85867 15.2369 7.46679 14.4208 5.56056 12.4143C3.34914 12.9521 1.691 12.6254 1.38412 11.4371L1.35092 11.2698C1.21678 10.3379 1.85972 9.11931 3.00457 7.90909C2.28693 5.34963 2.72037 2.94871 4.24741 1.88435L4.44728 1.7561ZM11.9889 9.03995C11.002 9.88136 9.79998 10.6808 8.47918 11.3303L8.13673 11.4937C7.70533 11.6942 7.28148 11.8681 6.8698 12.0172C7.32387 12.4287 7.79226 12.7587 8.25326 12.9983C9.39237 13.5903 10.3231 13.574 10.946 13.2092C11.5745 12.841 12.0613 12.0184 12.1263 10.7086C12.1518 10.1934 12.1065 9.62999 11.9889 9.03995ZM3.46225 9.20206C3.15128 9.5802 2.91825 9.93412 2.76238 10.2489C2.54363 10.6909 2.51958 10.967 2.53842 11.0985C2.54902 11.1721 2.56635 11.2146 2.67058 11.2763C2.81832 11.3636 3.12484 11.4626 3.6478 11.467C3.95641 11.4696 4.3074 11.4373 4.69402 11.3707C4.45655 11.0439 4.23084 10.6971 4.02215 10.3303C3.81017 9.95774 3.62416 9.58004 3.46225 9.20206ZM10.0358 4.9423C9.42018 5.13611 8.75205 5.40184 8.05014 5.74698C6.61265 6.45384 5.33887 7.34291 4.37175 8.24373C4.55213 8.73829 4.78227 9.2401 5.06511 9.73722C5.33995 10.2202 5.64271 10.6614 5.96355 11.0575C6.5793 10.8637 7.24782 10.5987 7.94988 10.2535C9.38732 9.54667 10.6605 8.65688 11.6276 7.7561C11.4473 7.26173 11.2176 6.76019 10.9349 6.26326C10.66 5.78001 10.3568 5.33855 10.0358 4.9423ZM7.74675 3.00219C6.60759 2.41015 5.67698 2.42644 5.05405 2.79126C4.42548 3.15953 3.93866 3.98199 3.87371 5.29191C3.84822 5.80668 3.89236 6.36967 4.00978 6.95922C4.91091 6.19088 5.9923 5.45862 7.17905 4.84269L7.52084 4.67016C8.07056 4.39986 8.6096 4.17089 9.12957 3.98266C8.67568 3.57138 8.20757 3.24172 7.74675 3.00219ZM12.3522 4.53344C12.0433 4.53086 11.6918 4.56177 11.3047 4.6285C11.5425 4.95566 11.7689 5.3029 11.9779 5.67016C12.1897 6.04255 12.3753 6.41999 12.5371 6.79777C12.8481 6.41965 13.0818 6.06635 13.2376 5.75154C13.4564 5.30973 13.4804 5.03352 13.4616 4.90193C13.451 4.82831 13.4337 4.7859 13.3294 4.7242C13.1817 4.63684 12.8753 4.53787 12.3522 4.53344Z"
|
|
408
|
+
/>
|
|
409
|
+
</symbol>
|
|
410
|
+
<symbol id="flyIcon" viewBox="-2 -2 20 20">
|
|
411
|
+
<path
|
|
412
|
+
fill-rule="evenodd"
|
|
413
|
+
clip-rule="evenodd"
|
|
414
|
+
d="M12 1.33337C13.4727 1.33337 14.6666 2.52728 14.6666 4.00004C14.6666 5.4728 13.4727 6.66671 12 6.66671C11.4931 6.66671 11.0194 6.52491 10.6159 6.27934L10.2721 6.62374C10.0345 6.86142 9.90102 7.18411 9.90102 7.52022V8.47986C9.90102 8.81597 10.0345 9.13866 10.2721 9.37634L10.6159 9.72009C11.0193 9.47463 11.4932 9.33337 12 9.33337C13.4727 9.33337 14.6666 10.5273 14.6666 12C14.6666 13.4728 13.4727 14.6667 12 14.6667C10.5272 14.6667 9.33331 13.4728 9.33331 12C9.33331 11.4932 9.47457 11.0194 9.72003 10.6159L9.37628 10.2722C9.1386 10.0345 8.81591 9.90108 8.4798 9.90108H7.52016C7.18405 9.90108 6.86136 10.0345 6.62368 10.2722L6.27928 10.6159C6.52485 11.0195 6.66665 11.4931 6.66665 12C6.66665 13.4728 5.47274 14.6667 3.99998 14.6667C2.52722 14.6667 1.33331 13.4728 1.33331 12C1.33331 10.5273 2.52722 9.33337 3.99998 9.33337C4.50658 9.33337 4.98008 9.47481 5.38344 9.72009L5.72784 9.37634C5.9655 9.13866 6.09894 8.81597 6.09894 8.47986V7.52022C6.09894 7.18411 5.9655 6.86142 5.72784 6.62374L5.38344 6.27934C4.98001 6.52473 4.5067 6.66671 3.99998 6.66671C2.52722 6.66671 1.33331 5.4728 1.33331 4.00004C1.33331 2.52728 2.52722 1.33337 3.99998 1.33337C5.47274 1.33337 6.66665 2.52728 6.66665 4.00004C6.66665 4.50676 6.52467 4.98008 6.27928 5.3835L6.62368 5.72791C6.86136 5.96556 7.18405 6.099 7.52016 6.099H8.4798C8.81591 6.099 9.1386 5.96556 9.37628 5.72791L9.72003 5.3835C9.47475 4.98014 9.33331 4.50664 9.33331 4.00004C9.33331 2.52728 10.5272 1.33337 12 1.33337ZM3.99998 10.3998C3.11632 10.3998 2.39972 11.1164 2.39972 12C2.39972 12.8837 3.11632 13.6003 3.99998 13.6003C4.88364 13.6003 5.60024 12.8837 5.60024 12C5.60024 11.7911 5.55917 11.5919 5.48631 11.4089L4.41469 12.4812C4.16723 12.7284 3.76625 12.7286 3.51886 12.4812C3.27147 12.2338 3.27158 11.8328 3.51886 11.5853L4.59047 10.5131C4.40769 10.4404 4.20862 10.3998 3.99998 10.3998ZM12 10.3998C11.7911 10.3998 11.5918 10.4403 11.4088 10.5131L12.4811 11.5853C12.7284 11.8328 12.7285 12.2338 12.4811 12.4812C12.2337 12.7286 11.8327 12.7284 11.5853 12.4812L10.513 11.4089C10.4402 11.5918 10.3997 11.7912 10.3997 12C10.3997 12.8837 11.1163 13.6003 12 13.6003C12.8836 13.6003 13.6002 12.8837 13.6002 12C13.6002 11.1164 12.8836 10.3998 12 10.3998ZM3.99998 2.39978C3.11632 2.39978 2.39972 3.11639 2.39972 4.00004C2.39972 4.8837 3.11632 5.6003 3.99998 5.6003C4.20871 5.6003 4.40763 5.55909 4.59047 5.48637L3.51886 4.41475C3.27158 4.16729 3.27147 3.76631 3.51886 3.51892C3.76625 3.27153 4.16723 3.27164 4.41469 3.51892L5.48631 4.59054C5.55903 4.40769 5.60024 4.20877 5.60024 4.00004C5.60024 3.11639 4.88364 2.39978 3.99998 2.39978ZM12 2.39978C11.1163 2.39978 10.3997 3.11639 10.3997 4.00004C10.3997 4.20868 10.4403 4.40775 10.513 4.59054L11.5853 3.51892C11.8327 3.27164 12.2337 3.27153 12.4811 3.51892C12.7285 3.76631 12.7284 4.16729 12.4811 4.41475L11.4088 5.48637C11.5918 5.55923 11.791 5.6003 12 5.6003C12.8836 5.6003 13.6002 4.8837 13.6002 4.00004C13.6002 3.11639 12.8836 2.39978 12 2.39978Z"
|
|
415
|
+
/>
|
|
416
|
+
</symbol>
|
|
417
|
+
<symbol id="walkIcon" viewBox="-2 -2 20 20">
|
|
418
|
+
<path d="M9.75 2a1.75 1.75 0 1 1-3.5 0 1.75 1.75 0 0 1 3.5 0zM5.25 4.75h5.5v4.5h-.5v5h-2v-5h-.5v5h-2v-5h-.5z" />
|
|
419
|
+
</symbol>
|
|
420
|
+
<symbol id="arIcon" viewBox="0 0 24 24">
|
|
421
|
+
<path
|
|
422
|
+
fill-rule="evenodd"
|
|
423
|
+
clip-rule="evenodd"
|
|
424
|
+
d="M12 15.3994V6H16.9629C18.2109 6 19.1167 6.1089 19.6797 6.32715C20.248 6.53997 20.7021 6.92247 21.042 7.47363C21.3818 8.02476 21.5518 8.65491 21.5518 9.36426C21.5518 10.2645 21.2943 11.0093 20.7793 11.5986C20.2641 12.1825 19.4938 12.5513 18.4688 12.7041C18.9786 13.0097 19.3983 13.3453 19.7275 13.7109C20.0621 14.0766 20.5113 14.7261 21.0742 15.6592L22.5 18H19.6797L17.9746 15.3887C17.3694 14.4559 16.9555 13.8691 16.7324 13.6289C16.5095 13.3835 16.2729 13.2172 16.0234 13.1299C15.7738 13.0371 15.3777 12.9902 14.8359 12.9902H14.3584V18H10.4053L9.35742 15.2744H4.56055L3.57031 18H1L5.67383 6H8.23633L12 15.3994ZM5.30566 13.252H8.58008L6.92676 8.7998L5.30566 13.252ZM14.3584 11.0752H16.1025C17.2336 11.0752 17.9401 11.0259 18.2217 10.9277C18.5032 10.8295 18.7235 10.66 18.8828 10.4199C19.0421 10.1798 19.1221 9.87966 19.1221 9.51953C19.122 9.11597 19.016 8.79138 18.8037 8.5459C18.5966 8.29488 18.3013 8.13677 17.9189 8.07129C17.7276 8.04401 17.1541 8.03027 16.1982 8.03027H14.3584V11.0752Z"
|
|
425
|
+
/>
|
|
426
|
+
</symbol>
|
|
427
|
+
<symbol id="vrIcon" viewBox="0 0 24 24">
|
|
428
|
+
<path
|
|
429
|
+
fill-rule="evenodd"
|
|
430
|
+
clip-rule="evenodd"
|
|
431
|
+
d="M5.52148 10.7021L7.15918 15.1133L8.76465 10.7021L9.50195 8.7002L10.4834 6H16.9541C18.1903 6 19.0879 6.10801 19.6455 6.32422C20.2081 6.53499 20.6576 6.9134 20.9941 7.45898C21.3308 8.00484 21.499 8.62946 21.499 9.33203C21.499 10.2239 21.2446 10.9622 20.7344 11.5459C20.2241 12.1242 19.4606 12.4893 18.4453 12.6406C18.9503 12.9433 19.3662 13.2756 19.6924 13.6377C20.0238 13.9999 20.4688 14.6432 21.0264 15.5674L22.4385 17.8857H19.6455L17.9561 15.2998C17.3565 14.3758 16.9466 13.7946 16.7256 13.5566C16.5047 13.3135 16.2706 13.148 16.0234 13.0615C15.7762 12.9696 15.3841 12.9238 14.8477 12.9238H14.374V17.8857H12.0381V8.54395L8.39941 17.8857H5.86133L1.10254 6H3.71289L5.52148 10.7021ZM14.374 11.0264H16.1025C17.2228 11.0264 17.9224 10.9781 18.2012 10.8809C18.4799 10.7836 17.6986 10.6157 18.8564 10.3779C19.0141 10.1402 19.0928 9.84288 19.0928 9.48633C19.0928 9.08641 18.9877 8.7647 18.7773 8.52148C18.5722 8.27286 18.2801 8.11565 17.9014 8.05078C17.712 8.02376 17.1439 8.01075 16.1973 8.01074H14.374V11.0264Z"
|
|
432
|
+
/>
|
|
433
|
+
</symbol>
|
|
434
|
+
<symbol id="infoIcon" viewBox="-2 -2 24 24">
|
|
435
|
+
<path
|
|
436
|
+
d="M9.98633 7.58301C10.2598 7.58301 10.4854 7.67643 10.6631 7.86328C10.8408 8.05013 10.9297 8.3099 10.9297 8.64258V14.0361C10.9297 14.4098 10.8408 14.6924 10.6631 14.8838C10.4854 15.0752 10.2598 15.1709 9.98633 15.1709C9.71289 15.1709 9.48958 15.0729 9.31641 14.877C9.14779 14.681 9.06348 14.4007 9.06348 14.0361V8.69727C9.06348 8.32812 9.14779 8.05013 9.31641 7.86328C9.48958 7.67643 9.71289 7.58301 9.98633 7.58301Z"
|
|
437
|
+
/>
|
|
438
|
+
<path
|
|
439
|
+
d="M10.0068 4.88965C10.2484 4.88965 10.4626 4.96712 10.6494 5.12207C10.8363 5.27702 10.9297 5.5026 10.9297 5.79883C10.9297 6.08594 10.8385 6.31152 10.6562 6.47559C10.474 6.63509 10.2575 6.71484 10.0068 6.71484C9.74707 6.71484 9.52376 6.63509 9.33691 6.47559C9.15462 6.31608 9.06348 6.09049 9.06348 5.79883C9.06348 5.53451 9.1569 5.31803 9.34375 5.14941C9.53516 4.97624 9.75618 4.88965 10.0068 4.88965Z"
|
|
440
|
+
/>
|
|
441
|
+
<path
|
|
442
|
+
fill-rule="evenodd"
|
|
443
|
+
clip-rule="evenodd"
|
|
444
|
+
d="M10 0C15.5228 0 20 4.47715 20 10C20 15.5228 15.5228 20 10 20C4.47715 20 0 15.5228 0 10C0 4.47715 4.47715 0 10 0ZM10 1.7998C5.47126 1.7998 1.7998 5.47126 1.7998 10C1.7998 14.5287 5.47126 18.2002 10 18.2002C14.5287 18.2002 18.2002 14.5287 18.2002 10C18.2002 5.47126 14.5287 1.7998 10 1.7998Z"
|
|
445
|
+
/>
|
|
446
|
+
</symbol>
|
|
447
|
+
<symbol id="settingsIcon" viewBox="0 0 24 24">
|
|
448
|
+
<path
|
|
449
|
+
fill-rule="evenodd"
|
|
450
|
+
clip-rule="evenodd"
|
|
451
|
+
d="M12 8C14.2091 8 16 9.79086 16 12C16 14.2091 14.2091 16 12 16C9.79086 16 8 14.2091 8 12C8 9.79086 9.79086 8 12 8ZM12 9.7998C10.785 9.7998 9.7998 10.785 9.7998 12C9.7998 13.215 10.785 14.2002 12 14.2002C13.215 14.2002 14.2002 13.215 14.2002 12C14.2002 10.785 13.215 9.7998 12 9.7998Z"
|
|
452
|
+
/>
|
|
453
|
+
<path
|
|
454
|
+
fill-rule="evenodd"
|
|
455
|
+
clip-rule="evenodd"
|
|
456
|
+
d="M12.7119 2.2002C13.1961 2.20028 13.6296 2.49055 13.8164 2.93066L13.8506 3.02051L14.3652 4.56641C14.7875 4.70091 15.1932 4.87075 15.5801 5.07129L17.042 4.3418L17.1299 4.30176C17.5436 4.13502 18.017 4.21245 18.3564 4.50195L18.4268 4.56641L19.4336 5.57324C19.7985 5.93836 19.8889 6.49621 19.6582 6.95801L18.9268 8.41895C19.1274 8.80592 19.2971 9.21159 19.4316 9.63379L20.9795 10.1504C21.4693 10.3138 21.7997 10.7717 21.7998 11.2881V12.7119C21.7997 13.2284 21.4695 13.6873 20.9795 13.8506L19.4316 14.3652C19.2971 14.7875 19.1274 15.1932 18.9268 15.5801L19.6582 17.042C19.8889 17.5038 19.7985 18.0616 19.4336 18.4268L18.4268 19.4336C18.0616 19.7985 17.5038 19.8889 17.042 19.6582L15.5801 18.9268C15.1932 19.1274 14.7875 19.2971 14.3652 19.4316L13.8506 20.9795C13.6873 21.4695 13.2284 21.7997 12.7119 21.7998H11.2881C10.7717 21.7997 10.3138 21.4693 10.1504 20.9795L9.63379 19.4316C9.21159 19.2971 8.80592 19.1274 8.41895 18.9268L6.95801 19.6582C6.49621 19.8889 5.93836 19.7985 5.57324 19.4336L4.56641 18.4268C4.20146 18.0617 4.1112 17.5038 4.3418 17.042L5.07129 15.5801C4.87075 15.1932 4.70091 14.7875 4.56641 14.3652L3.02051 13.8506C2.53057 13.6873 2.20029 13.2283 2.2002 12.7119V11.2881C2.20024 10.7718 2.53076 10.3139 3.02051 10.1504L4.56641 9.63379C4.70094 9.21149 4.86966 8.80498 5.07031 8.41797L4.3418 6.95801C4.11113 6.49617 4.20145 5.93834 4.56641 5.57324L5.57324 4.56641C5.93834 4.20145 6.49617 4.11113 6.95801 4.3418L8.41797 5.07031C8.80498 4.86966 9.21149 4.70094 9.63379 4.56641L10.1504 3.02051L10.1836 2.92969C10.3706 2.49001 10.8042 2.20023 11.2881 2.2002H12.7119ZM11.0186 5.4707L10.8809 5.88477L10.458 5.99316C9.88479 6.13982 9.34317 6.36768 8.84473 6.66309L8.46875 6.88477L8.0791 6.69043L6.50098 5.90137L5.90137 6.50098L6.69043 8.0791L6.88477 8.46875L6.66309 8.84473C6.36768 9.34317 6.13982 9.88479 5.99316 10.458L5.88477 10.8809L5.4707 11.0186L3.7998 11.5762V12.4229L5.4707 12.9805L5.88477 13.1182L5.99316 13.541C6.13969 14.1141 6.36763 14.6556 6.66309 15.1543L6.88477 15.5303L6.69043 15.9199L5.90137 17.498L6.50098 18.0977L8.0791 17.3086L8.46875 17.1133L8.84473 17.3359C9.34314 17.6314 9.88463 17.8591 10.458 18.0059L10.8809 18.1143L11.0186 18.5283L11.5771 20.2002H12.4229L13.1182 18.1143L13.541 18.0059C14.1143 17.8593 14.6556 17.6314 15.1543 17.3359L15.5303 17.1143L15.9199 17.3086L17.498 18.0977L18.0977 17.498L17.3086 15.9199L17.1143 15.5303L17.3359 15.1543C17.6314 14.6556 17.8593 14.1143 18.0059 13.541L18.1143 13.1182L20.2002 12.4229V11.5762L18.1143 10.8809L18.0059 10.458C17.8591 9.88463 17.6314 9.34314 17.3359 8.84473L17.1133 8.46875L17.3086 8.0791L18.0977 6.50098L17.498 5.90137L15.9199 6.69043L15.5303 6.88477L15.1543 6.66309C14.6556 6.36763 14.1141 6.13969 13.541 5.99316L13.1182 5.88477L12.9805 5.4707L12.4229 3.7998H11.5762L11.0186 5.4707Z"
|
|
457
|
+
/>
|
|
458
|
+
</symbol>
|
|
459
|
+
<symbol id="enterFullscreenIcon" viewBox="0 0 24 24">
|
|
460
|
+
<path
|
|
461
|
+
d="M3 14.7002C3.49693 14.7002 3.90018 15.1027 3.90039 15.5996V20C3.90039 20.0552 3.94477 20.0996 4 20.0996H8.40039C8.89727 20.0998 9.2998 20.5031 9.2998 21C9.2998 21.4969 8.89727 21.9002 8.40039 21.9004H4C2.95066 21.9004 2.09961 21.0493 2.09961 20V15.5996C2.09982 15.1027 2.50307 14.7002 3 14.7002Z"
|
|
462
|
+
/>
|
|
463
|
+
<path
|
|
464
|
+
d="M21 14.7002C21.4969 14.7002 21.9002 15.1027 21.9004 15.5996V20C21.9004 21.0493 21.0493 21.9004 20 21.9004H15.5996C15.1027 21.9002 14.7002 21.4969 14.7002 21C14.7002 20.5031 15.1027 20.0998 15.5996 20.0996H20C20.0552 20.0996 20.0996 20.0552 20.0996 20V15.5996C20.0998 15.1027 20.5031 14.7002 21 14.7002Z"
|
|
465
|
+
/>
|
|
466
|
+
<path
|
|
467
|
+
d="M8.40039 2.09961C8.89727 2.09982 9.2998 2.50307 9.2998 3C9.2998 3.49693 8.89727 3.90018 8.40039 3.90039H4C3.94477 3.90039 3.90039 3.94477 3.90039 4V8.40039C3.90018 8.89727 3.49693 9.2998 3 9.2998C2.50307 9.2998 2.09982 8.89727 2.09961 8.40039V4C2.09961 2.95066 2.95066 2.09961 4 2.09961H8.40039Z"
|
|
468
|
+
/>
|
|
469
|
+
<path
|
|
470
|
+
d="M20 2.09961C21.0493 2.09961 21.9004 2.95066 21.9004 4V8.40039C21.9002 8.89727 21.4969 9.2998 21 9.2998C20.5031 9.2998 20.0998 8.89727 20.0996 8.40039V4C20.0996 3.94477 20.0552 3.90039 20 3.90039H15.5996C15.1027 3.90018 14.7002 3.49693 14.7002 3C14.7002 2.50307 15.1027 2.09982 15.5996 2.09961H20Z"
|
|
471
|
+
/>
|
|
472
|
+
</symbol>
|
|
473
|
+
<symbol id="supersplatIcon" viewBox="64 64 384 384">
|
|
474
|
+
<path
|
|
475
|
+
fill="#F26722"
|
|
476
|
+
d="M129.83,217c9.75,0,17.64-7.9,17.64-17.64s-7.9-17.64-17.64-17.64-17.64,7.9-17.64,17.64,7.9,17.64,17.64,17.64Z"
|
|
477
|
+
/>
|
|
478
|
+
<path
|
|
479
|
+
fill="#F26722"
|
|
480
|
+
d="M388.74,253.94c-12.46,0-22.57,10.11-22.57,22.57s10.11,22.57,22.57,22.57,22.57-10.11,22.57-22.57-10.11-22.57-22.57-22.57h0Z"
|
|
481
|
+
/>
|
|
482
|
+
<path
|
|
483
|
+
fill="#F26722"
|
|
484
|
+
d="M345.26,161.1h.02c.37-.05.65-.22.97-.33,16.78-2.32,29.75-16.57,29.75-33.99,0-19.04-15.43-34.46-34.46-34.46s-34.46,15.43-34.46,34.46c0,4.29.88,8.35,2.32,12.14.04.12-.01.25.04.38,10.55,29.18-22.33,23.62-39.07,20.74-.49-.09-.88-.07-1.32-.11-4.27-.56-8.59-.97-13.03-.97-53.93,0-97.64,43.71-97.64,97.64,0,9.84,1.48,19.32,4.2,28.28.04.1,0,.17.05.27,8.97,30.15-14.83,25.49-25.52,25.69-1.04-.13-2.09-.32-3.18-.32-13.51,0-24.45,10.94-24.45,24.45s10.94,24.45,24.45,24.45c11.69,0,21.44-8.22,23.85-19.19h.02c6.31-26.57,25.9-18.02,31.15-12.67.2.2.4.26.59.42,17.44,16.25,40.75,26.26,66.47,26.26.67,0,1.34-.09,2.01-.1.66.01,1.31.04,2.04.04,9.7-.04,17.82,10.91,4.94,29.68h.13c-4.42,6.01-7.1,13.36-7.1,21.41,0,20.09,16.29,36.39,36.39,36.39s36.39-16.29,36.39-36.39c0-15.4-9.61-28.49-23.12-33.81-.32-.15-.61-.29-.98-.45-12.28-4.94-25.43-17.69-6.89-27.1l-.04-.1c31.93-16.06,53.88-49.02,53.88-87.2,0-21.15-6.8-40.68-18.23-56.66-.13-.23-.23-.47-.44-.74-23.15-31.67-2.37-36.07,10.3-38.09h-.01Z"
|
|
485
|
+
/>
|
|
486
|
+
<path
|
|
487
|
+
fill="#fff"
|
|
488
|
+
d="M230.79,284.71c0,7.95-6.44,14.38-14.38,14.38s-14.38-6.44-14.38-14.38v-22.95c0-7.95,6.44-14.38,14.38-14.38s14.38,6.44,14.38,14.38v22.95Z"
|
|
489
|
+
/>
|
|
490
|
+
<path
|
|
491
|
+
fill="#fff"
|
|
492
|
+
d="M309.78,284.71c0,7.95-6.44,14.38-14.38,14.38s-14.38-6.44-14.38-14.38v-22.95c0-7.95,6.44-14.38,14.38-14.38s14.38,6.44,14.38,14.38v22.95Z"
|
|
493
|
+
/>
|
|
494
|
+
</symbol>
|
|
495
|
+
<symbol id="keyboardIcon" viewBox="0 0 24 24">
|
|
496
|
+
<path
|
|
497
|
+
fill-rule="evenodd"
|
|
498
|
+
clip-rule="evenodd"
|
|
499
|
+
d="M4 5.5h16a2.5 2.5 0 0 1 2.5 2.5v8a2.5 2.5 0 0 1-2.5 2.5H4A2.5 2.5 0 0 1 1.5 16V8A2.5 2.5 0 0 1 4 5.5Zm0 1.8a.7.7 0 0 0-.7.7v8c0 .39.31.7.7.7h16a.7.7 0 0 0 .7-.7V8a.7.7 0 0 0-.7-.7H4Z"
|
|
500
|
+
/>
|
|
501
|
+
<path d="M5 9h2v2H5zM8.5 9h2v2h-2zM12 9h2v2h-2zM15.5 9h2v2h-2zM7 13h10v2H7z" />
|
|
502
|
+
</symbol>
|
|
503
|
+
<symbol id="closeIcon" viewBox="0 0 24 24">
|
|
504
|
+
<path d="M6 6l12 12M18 6L6 18" />
|
|
505
|
+
</symbol>
|
|
506
|
+
<symbol id="chevronLeftIcon" viewBox="0 0 24 24">
|
|
507
|
+
<path d="M15 19l-7-7 7-7" />
|
|
508
|
+
</symbol>
|
|
509
|
+
<symbol id="chevronRightIcon" viewBox="0 0 24 24">
|
|
510
|
+
<path d="M9 5l7 7-7 7" />
|
|
511
|
+
</symbol>
|
|
512
|
+
<symbol id="exitFullscreenIcon" viewBox="0 0 24 24">
|
|
513
|
+
<path
|
|
514
|
+
d="M8 15.0996C8.49706 15.0996 8.90039 15.5029 8.90039 16V21C8.90039 21.4971 8.49706 21.9004 8 21.9004C7.50294 21.9004 7.09961 21.4971 7.09961 21V16.9004H3C2.50294 16.9004 2.09961 16.4971 2.09961 16C2.09961 15.5029 2.50294 15.0996 3 15.0996H8Z"
|
|
515
|
+
/>
|
|
516
|
+
<path
|
|
517
|
+
d="M21 15.0996C21.4971 15.0996 21.9004 15.5029 21.9004 16C21.9004 16.4971 21.4971 16.9004 21 16.9004H16.9004V21C16.9004 21.4971 16.4971 21.9004 16 21.9004C15.5029 21.9004 15.0996 21.4971 15.0996 21V16C15.0996 15.5029 15.5029 15.0996 16 15.0996H21Z"
|
|
518
|
+
/>
|
|
519
|
+
<path
|
|
520
|
+
d="M8 2.09961C8.49706 2.09961 8.90039 2.50294 8.90039 3V8C8.90039 8.49706 8.49706 8.90039 8 8.90039H3C2.50294 8.90039 2.09961 8.49706 2.09961 8C2.09961 7.50294 2.50294 7.09961 3 7.09961H7.09961V3C7.09961 2.50294 7.50294 2.09961 8 2.09961Z"
|
|
521
|
+
/>
|
|
522
|
+
<path
|
|
523
|
+
d="M16 2.09961C16.4971 2.09961 16.9004 2.50294 16.9004 3V7.09961H21C21.4971 7.09961 21.9004 7.50294 21.9004 8C21.9004 8.49706 21.4971 8.90039 21 8.90039H16C15.5029 8.90039 15.0996 8.49706 15.0996 8V3C15.0996 2.50294 15.5029 2.09961 16 2.09961Z"
|
|
524
|
+
/>
|
|
525
|
+
</symbol>
|
|
526
|
+
</svg>
|
|
527
|
+
`,Wc=(i,e,t,s=1,n=24,o=20)=>{let r=new Array(n).fill(0).map((y,k)=>k/n*o),a=[],l=[],c=new Array(n).fill(t),h=s*.5,d=i.x-e.x,u=i.z-e.z,p=Math.sqrt(d*d+u*u),g,m,f,v;p>.001?(f=d/p,v=u/p,g=-v,m=f):(f=0,v=1,g=1,m=0);for(let y=0;y<n;++y){let k=y/n*Math.PI*2,w=h*Math.sin(k),P=h*Math.sin(2*k)/2;a.push(i.x+g*w+f*P),a.push(i.y),a.push(i.z+m*w+v*P),l.push(e.x),l.push(e.y),l.push(e.z)}return{name:"figure8",duration:o,frameRate:1,loopMode:"repeat",interpolation:"spline",smoothness:1,keyframes:{times:r,values:{position:a,target:l,fov:c}}}},Gc=(i,e,t,s=12,n=20)=>{let o=new Array(s).fill(0).map((v,y)=>y/s*n),r=[],a=[],l=new Array(s).fill(t),c=i.x-e.x,h=i.y-e.y,d=i.z-e.z,u=Math.sqrt(c*c+d*d),g=Math.sqrt(c*c+h*h+d*d)*.3,m=Math.max(u,g),f=Math.atan2(c,d);for(let v=0;v<s;++v){let y=f-v/s*Math.PI*2;r.push(e.x+m*Math.sin(y)),r.push(e.y+h),r.push(e.z+m*Math.cos(y)),a.push(e.x),a.push(e.y),a.push(e.z)}return{name:"rotate",duration:n,frameRate:1,loopMode:"repeat",interpolation:"spline",smoothness:1,keyframes:{times:o,values:{position:r,target:a,fov:l}}}},Ko=(i,e)=>1-Math.pow(i,e*1e3),Ra=i=>(1-2**(-10*i))/(1-2**-10),Xt=(i,e)=>(i%e+e)%e,Aa=(i,e,t=1e-4)=>!i.some((s,n)=>Math.abs(s-e[n])>=t),Ta=(i,e)=>{let t=180/Math.PI,s=e.x*e.x+e.z*e.z;return i.x=Math.asin(Math.max(-1,Math.min(1,e.y)))*t,i.y=s>1e-8?Math.atan2(-e.x,-e.z)*t:0,i.z=0,i},cs=.95,nt=new Q,Vr=(i,e,t=-90,s=90)=>(i.x-=e[1],i.y-=e[0],i.z=0,i.x=Y.clamp(i.x,t,s),i),Uc=(i,e,t,s)=>{nt.setFromEulerAngles(i),nt.transformVector(x.FORWARD,e),nt.transformVector(x.RIGHT,t),nt.transformVector(x.UP,s)},Xc=(i,e)=>{nt.setFromEulerAngles(i),nt.transformVector(x.FORWARD,e)},Yc=(i,e,t)=>{nt.setFromEulerAngles(0,i,0),nt.transformVector(x.FORWARD,e),nt.transformVector(x.RIGHT,t)},Or=(i,e,t,s,n,o,r)=>(i.set(o.x*e+r.x*t+n.x*s,o.y*e+r.y*t+n.y*s,o.z*e+r.z*t+n.z*s),i),Br=(i,e,t,s)=>{if(s<=0)return i;let n=Ko(t,s);return i.y=Xt(i.y,360),i.z=Xt(i.z,360),e.y=Xt(e.y,360),e.z=Xt(e.z,360),i.x=Y.lerpAngle(i.x,e.x,n),i.y=Y.lerpAngle(i.y,e.y,n),i.z=Y.lerpAngle(i.z,e.z,n),i},Zc=i=>{i.read()},fn=class i{times;knots;dim;constructor(e,t){this.times=e,this.knots=t,this.dim=t.length/e.length/3}evaluate(e,t){let{times:s}=this,n=s.length-1;if(e<=s[0])this.getKnot(0,t);else if(e>=s[n])this.getKnot(n,t);else{let o=0;for(;e>=s[o+1];)o++;this.evaluateSegment(o,(e-s[o])/(s[o+1]-s[o]),t)}}getKnot(e,t){let{knots:s,dim:n}=this,o=e*3*n;for(let r=0;r<n;++r)t[r]=s[o+r*3+1]}evaluateSegment(e,t,s){let{knots:n,dim:o}=this,r=t*t,a=t+t,l=1-t,c=l*l,h=e*o*3;for(let d=0;d<o;++d){let u=n[h+1],p=n[h+2],g=n[h+o*3],m=n[h+o*3+1];h+=3,s[d]=u*((1+a)*c)+p*(t*c)+m*(r*(3-a))+g*(r*(t-1))}}static calcKnots(e,t,s){let n=e.length,o=t.length/n,r=new Array(n*o*3);for(let a=0;a<n;a++){let l=e[a];for(let c=0;c<o;c++){let h=a*o+c,d=t[h],u;a===0?u=(t[h+o]-d)/(e[a+1]-l):a===n-1?u=(d-t[h-o])/(l-e[a-1]):u=(t[h+o]-t[h-o])/(e[a+1]-e[a-1]);let p=a>0?e[a]-e[a-1]:e[1]-e[0],g=a<n-1?e[a+1]-e[a]:e[a]-e[a-1];r[h*3]=u*p*s,r[h*3+1]=d,r[h*3+2]=u*g*s}}return r}static fromPoints(e,t,s=1){return new i(e,i.calcKnots(e,t,s))}static fromPointsLooping(e,t,s,n=1){if(t.length<2)return i.fromPoints(t,s);let o=s.length/t.length,r=t.slice(),a=s.slice();return r.push(e+t[0],e+t[1]),a.push(...s.slice(0,o*2)),r.splice(0,0,t[t.length-2]-e,t[t.length-1]-e),a.splice(0,0,...s.slice(s.length-o*2)),i.fromPoints(r,a,n)}},ho=class{duration=0;loopMode="none";timer=0;cursor=0;constructor(e,t){this.reset(e,t)}update(e){if(this.timer+=e,this.cursor+=e,this.cursor>=this.duration)switch(this.loopMode){case"none":this.cursor=this.duration;break;case"repeat":this.cursor%=this.duration;break;case"pingpong":this.cursor%=this.duration*2;break}}reset(e,t){this.duration=e,this.loopMode=t,this.timer=0,this.cursor=0}get ended(){return this.loopMode==="none"&&this.cursor>=this.duration}set value(e){this.cursor=this.loopMode==="repeat"?Xt(e,this.duration):Math.max(0,Math.min(this.duration,e))}get value(){return this.cursor>this.duration?2*this.duration-this.cursor:this.cursor}},uo=class i{spline;cursor=new ho(0,"none");frameRate;result=[];position=new x;target=new x;fov=90;constructor(e,t,s,n){this.spline=e,this.cursor.reset(t,s),this.frameRate=n}update(e){let{cursor:t,result:s,spline:n,frameRate:o,position:r,target:a}=this;t.update(e),n.evaluate(t.value*o,s),s.every(isFinite)&&(r.set(s[0],s[1],s[2]),a.set(s[3],s[4],s[5]),this.fov=s[6])}static fromTrack(e){let{keyframes:t,duration:s,frameRate:n,loopMode:o,smoothness:r}=e,{times:a,values:l}=t,{position:c,target:h,fov:d}=l,u=[];for(let m=0;m<a.length;m++)u.push(c[m*3],c[m*3+1],c[m*3+2]),u.push(h[m*3],h[m*3+1],h[m*3+2]),u.push(d[m]);let p=s===a[a.length-1]/n?1:0,g=o==="repeat"?fn.fromPointsLooping((s+p)*n,a,u,r):fn.fromPoints(a,u,r);return new i(g,s,o,n)}},po=class{animState;constructor(e){this.animState=uo.fromTrack(e),this.animState.update(0)}onEnter(e){e.look(this.animState.position,this.animState.target),e.fov=this.animState.fov}update(e,t,s){this.animState.update(e),s.look(this.animState.position,this.animState.target),s.fov=this.animState.fov,Zc(t)}onExit(e){}},Kc=new Q,Ht=new x,Da=new x,Tt=class{position=new x;angles=new x;distance=1;fov=65;constructor(e){e&&this.copy(e)}copy(e){this.position.copy(e.position),this.angles.copy(e.angles),this.distance=e.distance,this.fov=e.fov}lerp(e,t,s){e.calcFocusPoint(Ht),t.calcFocusPoint(Da),this.position.lerp(e.position,t.position,s),Ht.lerp(Ht,Da,s).sub(this.position),this.distance=Ht.length(),Ta(this.angles,Ht.mulScalar(1/this.distance)),this.fov=Y.lerp(e.fov,t.fov,s)}lerpAngles(e,t,s,n=s){this.position.lerp(e.position,t.position,s),this.angles.x=Y.lerp(e.angles.x,t.angles.x,n),this.angles.y=e.angles.y+(Xt(t.angles.y-e.angles.y+180,360)-180)*n,this.angles.z=Y.lerp(e.angles.z,t.angles.z,n),this.distance=Y.lerp(e.distance,t.distance,s),this.fov=Y.lerp(e.fov,t.fov,s)}look(e,t){this.position.copy(e),this.distance=e.distance(t);let s=Ht.sub2(t,e).normalize();Ta(this.angles,s)}calcFocusPoint(e){Kc.setFromEulerAngles(this.angles).transformVector(x.FORWARD,e).mulScalar(this.distance).add(this.position)}},mn=class{_position=new x;_angles=new x;_distance=1;_has=!1;get has(){return this._has}store(e,t,s){this._position.copy(e),this._angles.copy(t),this._distance=s,this._has=!0}clear(){this._has=!1}restore(e,t){return e.copy(this._position),t.copy(this._angles),this._distance}},za=.001,Qc=3,Fa=1e-10,dt=1/Math.sqrt(2),Ia=[[0,0,!0],[1,0,!1],[-1,0,!1],[0,1,!1],[0,-1,!1],[dt,dt,!1],[-dt,dt,!1],[-dt,-dt,!1],[dt,-dt,!1]],js=new x,Ws=new x,Jc=new x,Gs=new x,Va=new x,Us=new x,Oa=new x,tt=new x,eh=new x(0,1,0),th=new x(1,0,0),ut={x:0,y:0,z:0},jt={x:0,y:0,z:0,nx:0,ny:1,nz:0,travel:0},fo=class{collision=null;radius;_lastClearPosition=new x;_hasLastClearPosition=!1;constructor(e){this.radius=e}reset(e){this._setLastClearPosition(e)}move(e,t){if(!this.collision){e.add(t),this._setLastClearPosition(e);return}if(Ws.copy(t),this._isMoveComplete(Ws)){this.resolve(e);return}for(let s=0;s<Qc&&!(this._isMoveComplete(Ws)||!this._moveAndSlide(e,Ws));s++);}resolve(e){if(!this.collision){this._setLastClearPosition(e);return}this._resolveSphere(e,Jc),!this._isSphereClear(e)&&this._hasLastClearPosition&&e.copy(this._lastClearPosition),this._isSphereClear(e)&&this._setLastClearPosition(e)}_moveAndSlide(e,t){let s=t.x*t.x+t.y*t.y+t.z*t.z,n=Math.sqrt(s);return Gs.copy(t).mulScalar(1/n),this._querySweep(e,Gs,n,jt)?(e.add(js.copy(Gs).mulScalar(jt.travel)),this.resolve(e),Va.set(jt.nx,jt.ny,jt.nz),t.add(js.copy(Gs).mulScalar(-jt.travel)),this._clipMove(t,Va),!0):(e.add(t),this.resolve(e),!1)}_querySweep(e,t,s,n){Math.abs(t.y)<.99?Us.cross(t,eh).normalize():Us.cross(t,th).normalize(),Oa.cross(t,Us).normalize();let o=!1,r=1/0;for(let a=0;a<Ia.length;a++){let[l,c,h]=Ia[a],d=h?0:this.radius,u=h?this.radius:0;tt.copy(e),h||(tt.add(js.copy(Us).mulScalar(l*d)),tt.add(js.copy(Oa).mulScalar(c*d)));let p=this.collision.queryRay(tt.x,tt.y,tt.z,t.x,t.y,t.z,s+u+za);if(!p)continue;let g=p.x-tt.x,m=p.y-tt.y,f=p.z-tt.z,v=Math.max(0,g*t.x+m*t.y+f*t.z),y=h?this.radius:0,k=Y.clamp(v-y-za,0,s);if(k>=r)continue;let w=this.collision.querySurfaceNormal(p.x,p.y,p.z,t.x,t.y,t.z);o=!0,r=k,n.x=p.x,n.y=p.y,n.z=p.z,n.nx=w.nx,n.ny=w.ny,n.nz=w.nz,n.travel=k}return o}_resolveSphere(e,t){return this.collision.querySphere(e.x,e.y,e.z,this.radius,ut)?(e.x+=ut.x,e.y+=ut.y,e.z+=ut.z,t.x=ut.x,t.y=ut.y,t.z=ut.z,!0):(t.set(0,0,0),!1)}_clipMove(e,t){let s=t.x*t.x+t.y*t.y+t.z*t.z;if(s<=Fa)return;let n=1/Math.sqrt(s),o=t.x*n,r=t.y*n,a=t.z*n,l=e.x*o+e.y*r+e.z*a;l<0&&(e.x-=l*o,e.y-=l*r,e.z-=l*a)}_isMoveComplete(e){return e.x*e.x+e.y*e.y+e.z*e.z<=Fa}_isSphereClear(e){return!this.collision.querySphere(e.x,e.y,e.z,this.radius,ut)}_setLastClearPosition(e){this._lastClearPosition.copy(e),this._hasLastClearPosition=!this.collision||this._isSphereClear(e)}},mo=5,sh=mo*mo,nh=3,Ba=1e3,oh={x:0,y:0,z:0},ih=(i,e,t,s,n,o)=>{let r=i.voxelResolution,a=n*nh,l=a*a,c=Math.ceil(a/r),h=1/0,d=!1;for(let u=0;u<=c&&!(u*r*(u*r)>=h);u++)for(let g=-u;g<=u;g++){let m=g<0?-g:g;for(let f=-u;f<=u;f++){let v=f<0?-f:f,k=m===u||v===u?1:2*u;for(let w=-u;w<=u;w+=k){let P=(w*w+g*g+f*f)*r*r;if(P>=h||P>l)continue;let S=e+w*r,b=t+g*r,z=s+f*r;i.querySphere(S,b,z,n,oh)||(h=P,o.x=S,o.y=b,o.z=z,d=!0)}}}return d},ah=(i,e,t,s,n,o,r)=>{let a=i.voxelResolution,l=Math.ceil(mo/a),c=Math.ceil(o/a),h=o*o,d=1/0,u=!1;for(let p=0;p<=l&&!(p*a*(p*a)>=d);p++)for(let m=-p;m<=p;m++){let f=m<0?-m:m;for(let v=-p;v<=p;v++){let y=v<0?-v:v,w=f===p||y===p?1:2*p;for(let P=-p;P<=p;P+=w){let S=(P*P+m*m+v*v)*a*a;if(S>=d||S>sh)continue;let b=e+P*a,z=t+m*a,_=s+v*a;if(!i.isFreeAt(b,z,_))continue;let R=-1/0,L=1/0,M=!0;for(let T=-c;T<=c&&M;T++){let V=T*a,B=V*V;for(let I=-c;I<=c;I++){let C=I*a;if(B+C*C>h)continue;let A=b+V,D=_+C,O=i.queryRay(A,z,D,0,-1,0,Ba);if(!O){M=!1;break}O.y>R&&(R=O.y);let H=i.queryRay(A,z,D,0,1,0,Ba);H&&H.y<L&&(L=H.y)}}M&&(R+2*n>L||(d=S,r.x=b,r.y=R,r.z=_,u=!0))}}}return u},go=.2,Na=new x,qa=new x,$a=new x,Ha=new x,ja=new x,rh={x:0,y:0,z:0},Wa=(i,e)=>!i.querySphere(e.x,e.y,e.z,go,rh),vo=class{fov=90;rotateDamping=cs;_position=new x;_angles=new x;_targetAngles=new x;_distance=1;_spawn=new mn;_mover=new fo(go);_pendingCollision=null;_hasPosition=!1;set collision(e){if(e&&this._hasPosition&&!Wa(e,this._position)){this._pendingCollision=e,this._mover.collision=null,this._mover.reset(this._position);return}this._pendingCollision=null,this._mover.collision=e,this._mover.reset(this._position)}get collision(){return this._mover.collision}_reengage(){this.collision=this._mover.collision??this._pendingCollision}onEnter(e){this.goto(e);let t=this._mover.collision??this._pendingCollision;this._pendingCollision=null,t&&(ih(t,this._position.x,this._position.y,this._position.z,go,ja)&&this._position.copy(ja),this.collision=t),this._storeSpawn()}update(e,t,s){this._pendingCollision&&Wa(this._pendingCollision,this._position)&&this._reengage();let{move:n,rotate:o}=t.read();Vr(this._targetAngles,o),Br(this._angles,this._targetAngles,this.rotateDamping,e),this._step(n),s.position.copy(this._position),s.angles.set(this._angles.x,this._angles.y,0),s.distance=this._distance,s.fov=this.fov}onExit(e){}goto(e){this._hasPosition=!0,this._position.copy(e.position),this._angles.set(e.angles.x,e.angles.y,0),this._targetAngles.copy(this._angles),this._distance=e.distance,this._mover.reset(this._position)}resetToSpawn(e){return this._spawn.has?(this._distance=this._spawn.restore(this._position,this._angles),this._targetAngles.copy(this._angles),this._reengage(),e.position.copy(this._position),e.angles.copy(this._angles),e.distance=this._distance,e.fov=this.fov,!0):!1}_storeSpawn(){this._spawn.store(this._position,this._angles,this._distance)}_step(e){Uc(this._angles,Na,qa,$a),Or(Ha,e[0],e[1],e[2],Na,qa,$a),this._mover.move(this._position,Ha)}},Nr=180/Math.PI,lh=i=>(i%360+540)%360-180,ch=(i,e,t)=>{let s=Y.clamp((t-i)/(e-i),0,1);return s*s*(3-2*s)},Ga=(i,e,t)=>i<e?Math.min(e,i+t):Math.max(e,i-t),yo=(i,e,t,s,n)=>{if(n<=0)return i;let o=Y.clamp(e*s,-t,t),r=1-Math.exp(-4*s*n);return i+(o-i)*r},xo=(i,e,t)=>{let s=i*t;return Math.abs(e)<1e-4?0:Math.sign(s)===Math.sign(e)&&Math.abs(s)>Math.abs(e)?e:s},hh=(i,e)=>Math.atan2(-i,-e)*Nr,qr=(i,e,t)=>lh(hh(i,e)-t),dh=i=>Math.asin(Y.clamp(i,-1,1))*Nr,gn=class{_blockedTime=0;_prevDist=1/0;reset(){this._blockedTime=0,this._prevDist=1/0}update(e,t,s,n,o=!0){if(o&&this._prevDist!==1/0&&t>0)if((this._prevDist-e)/t<s){if(this._blockedTime+=t,this._blockedTime>=n)return this._prevDist=e,!0}else this._blockedTime=0;return this._prevDist=e,!1}},uh=Math.PI/180,ph=.75,fh=.75,mh=4,gh=.03,vh=.05,Ua=1.75,Xa=.25,yh=.5,Ct=new x,Xs=new x,Ya=new x,xh=i=>{let e=Y.clamp(i,15,120)*uh*.5;return Y.clamp(ph/Math.tan(e),fh,mh)},wo=class{flySpeed=4;maxTurnRate=180;turnGain=4;moveAcceleration=6;moveDeceleration=8;onComplete=null;_target=null;_yawRate=0;_pitchRate=0;_speed=0;_progress=new gn;_speedMul=1;get isActive(){return this._target!==null}navigateTo(e,t=1){let s=this._target!==null;this._target||(this._target=new x),this._target.copy(e),this._speedMul=t,s||(this._yawRate=0,this._pitchRate=0,this._speed=0),this._progress.reset()}cancel(){this._target&&(this._target=null,this._yawRate=0,this._pitchRate=0,this._speed=0,this._progress.reset(),this.onComplete?.())}update(e,t,s){if(!this._target)return;let n=this._target,o=t.position,r=t.angles;Ct.sub2(n,o);let a=Ct.length(),l=xh(t.fov),c=a-l,h=Math.max(0,c);if(h<=gh&&this._speed<=vh){this.cancel();return}if(e<=0)return;let d=1/a,u=Ct.x*d,p=Ct.y*d,g=Ct.z*d,m=qr(Ct.x,Ct.z,r.y),f=dh(p)-r.x;this._yawRate=yo(this._yawRate,m,this.maxTurnRate,this.turnGain,e),this._pitchRate=yo(this._pitchRate,f,this.maxTurnRate,this.turnGain,e);let v=xo(this._yawRate,m,e),y=xo(this._pitchRate,f,e);this._yawRate=v/e,this._pitchRate=y/e,s.deltas.rotate.append([-v,-y,0]),Ya.set(r.x+y,r.y+v,0),Xc(Ya,Xs);let k=Y.clamp(Xs.x*u+Xs.y*p+Xs.z*g,0,1),w=ch(.05,.95,k),P=Math.sqrt(2*this.moveDeceleration*h),S=h*Ua,b=Math.min(this.flySpeed*this._speedMul,P,S);b>this._speed?this._speed=Ga(this._speed,b,this.moveAcceleration*w*e):this._speed=Ga(this._speed,b,this.moveDeceleration*e);let z=h*(1-Math.exp(-Ua*e)),_=Math.min(this._speed*e,z);_>0&&s.deltas.move.append([0,0,_]),this._progress.update(a,e,Xa,yh,k>.5&&this._speed>Xa)&&this.cancel()}},Ys=new fa,bo=class{controller;fov=90;constructor(){this.controller=new ya,this.controller.zoomRange=new yt(.01,1/0),this.controller.pitchRange=new yt(-90,90),this.controller.rotateDamping=cs,this.controller.moveDamping=cs,this.controller.zoomDamping=cs}onEnter(e){this._attach(e)}update(e,t,s){let n=this.controller.update(t,e);s.position.copy(n.position),s.angles.copy(n.angles),s.distance=n.distance,s.fov=this.fov}onExit(e){}goto(e){this.fov=e.fov,this._attach(e)}_attach(e){Ys.position.copy(e.position),Ys.angles.copy(e.angles),Ys.distance=Math.max(e.distance,this.controller.zoomRange.x),this.controller.attach(Ys,!1)}},rs=1/60,wh=10,ls={x:0,y:0,z:0},bh=new x,_h=new x,Za=new x,Ka=new x,Zs=[0,0,0],Qa=new x,Ks=new x,_o=class{fov=90;capsuleHeight=1.5;capsuleRadius=.2;eyeHeight=1.3;gravity=9.8;jumpSpeed=4;moveGroundSpeed=7;moveAirSpeed=1;rotateDamping=cs;velocityDampingGround=.99;velocityDampingAir=.998;hoverHeight=.2;springStiffness=800;springDamping=57;groundProbeRange=1;_position=new x;_prevPosition=new x;_angles=new x;_targetAngles=new x;_distance=1;_spawn=new mn;_spawnGrounded=!1;_velocity=new x;_pendingMove=[0,0,0];_accumulator=0;_grounded=!1;_jumping=!1;_jumpHeld=!1;_collision=null;_pendingCollision=null;_heldFloorY=null;_hasPosition=!1;set collision(e){if(e&&this._hasPosition&&!this._capsuleClear(e,this._position)){this._pendingCollision=e,this._collision=null,this._heldFloorY=this._position.y-this.eyeHeight-this.hoverHeight;return}this._pendingCollision=null,this._heldFloorY=null,this._collision=e}get collision(){return this._collision}onEnter(e){this.goto(e),this._spawn.clear();let t=this._collision??this._pendingCollision;this._pendingCollision=null,this._collision=null,this._heldFloorY=this._position.y-this.eyeHeight-this.hoverHeight,this._grounded=!0,t&&(ah(t,e.position.x,e.position.y,e.position.z,(this.capsuleHeight+this.hoverHeight)*.5,this.capsuleRadius,Ks)?(this._position.set(Ks.x,Ks.y+this.hoverHeight+this.eyeHeight,Ks.z),this._storeSpawn(),this._heldFloorY=null,this._collision=t):this.collision=t),this._prevPosition.copy(this._position)}update(e,t,s){let{move:n,rotate:o}=t.read();Vr(this._targetAngles,o),Br(this._angles,this._targetAngles,this.rotateDamping,e),this._pendingMove[0]+=n[0],this._pendingMove[1]=this._pendingMove[1]||n[1],this._pendingMove[2]+=n[2],this._accumulator=Math.min(this._accumulator+e,wh*rs);let r=Math.floor(this._accumulator/rs);if(r>0){let l=1/r;Zs[0]=this._pendingMove[0]*l,Zs[1]=this._pendingMove[1],Zs[2]=this._pendingMove[2]*l;for(let c=0;c<r;c++)this._prevPosition.copy(this._position),this._step(rs,Zs),this._accumulator-=rs;this._pendingMove[0]=0,this._pendingMove[1]=0,this._pendingMove[2]=0}let a=this._accumulator/rs;s.position.lerp(this._prevPosition,this._position,a),s.angles.set(this._angles.x,this._angles.y,0),s.distance=this._distance,s.fov=this.fov}_step(e,t){this._pendingCollision&&this._capsuleClear(this._pendingCollision,this._position)&&(this._collision=this._pendingCollision,this._pendingCollision=null,this._heldFloorY=null);let s=this._probeGround(this._position),n=s!==null;if(this._velocity.y<0&&(this._jumping=!1),t[1]&&!this._jumping&&this._grounded&&!this._jumpHeld&&(this._jumping=!0,this._velocity.y=this.jumpSpeed,this._grounded=!1),this._jumpHeld=!!t[1],n&&!this._jumping){let a=s+this.hoverHeight+this.eyeHeight,l=this._position.y-a;if(l>.1)this._velocity.y-=this.gravity*e,this._position.y+this._velocity.y*e<=a&&(this._position.y=a,this._velocity.y=0),this._grounded=!1;else{let c=-this.springStiffness*l-this.springDamping*this._velocity.y;this._velocity.y+=c*e,this._grounded=!0}}else this._velocity.y-=this.gravity*e,this._grounded=!1;Yc(this._angles.y,Za,Ka),Or(Qa,t[0],0,t[2],Za,Ka,x.UP),this._velocity.add(Qa.mulScalar(this._grounded?this.moveGroundSpeed:this.moveAirSpeed));let o=this._grounded?this.velocityDampingGround:this.velocityDampingAir,r=Ko(o,e);this._velocity.x=Y.lerp(this._velocity.x,0,r),this._velocity.z=Y.lerp(this._velocity.z,0,r),this._position.add(bh.copy(this._velocity).mulScalar(e)),this._checkCollision(this._position,_h)}onExit(e){}goto(e){this._hasPosition=!0,this._position.copy(e.position),this._prevPosition.copy(this._position),this._angles.set(e.angles.x,e.angles.y,0),this._targetAngles.copy(this._angles),this._distance=e.distance,this._resetMotion()}resetToSpawn(e){return this._spawn.has?(this._distance=this._spawn.restore(this._position,this._angles),this._prevPosition.copy(this._position),this._targetAngles.copy(this._angles),this._resetMotion(),this._grounded=this._spawnGrounded,e.position.copy(this._position),e.angles.copy(this._angles),e.distance=this._distance,e.fov=this.fov,!0):!1}_storeSpawn(){this._spawn.store(this._position,this._angles,this._distance),this._spawnGrounded=this._grounded}_resetMotion(){this._velocity.set(0,0,0),this._grounded=!1,this._jumping=!1,this._jumpHeld=!1,this._pendingMove[0]=0,this._pendingMove[1]=0,this._pendingMove[2]=0,this._accumulator=0}_capsuleClear(e,t){let s=t.y-this.eyeHeight+this.capsuleHeight*.5,n=this.capsuleHeight*.5-this.capsuleRadius;return!e.queryCapsule(t.x,s,t.z,n,this.capsuleRadius,ls)}_probeGround(e){if(this._heldFloorY!==null)return this._heldFloorY;if(!this.collision)return null;let t=e.y-this.eyeHeight,s=this.capsuleRadius,n=this.groundProbeRange,o=0,r=0;for(let a=0;a<5;a++){let l=e.x,c=e.z;a===1?l-=s:a===2?l+=s:a===3?c+=s:a===4&&(c-=s);let h=this.collision.queryRay(l,t,c,0,-1,0,n);h&&(o+=h.y,r++)}return r>0?o/r:null}_checkCollision(e,t){if(!this.collision)return;let s=e.y-this.eyeHeight+this.capsuleHeight*.5,n=this.capsuleHeight*.5-this.capsuleRadius;this.collision.queryCapsule(e.x,s,e.z,n,this.capsuleRadius,ls)&&(t.set(ls.x,ls.y,ls.z),e.add(t),t.y<0&&this._velocity.y>0&&(this._velocity.y=0),!this._grounded&&t.y>0&&this._velocity.y<0&&(this._velocity.y=0,this._grounded=!0))}},kh=.5,Mh=.6,Sh=.2,ko=class{walkSpeed=4;maxTurnRate=192;turnGain=5;onComplete=null;_target=null;_yawRate=0;_progress=new gn;_speedMul=1;get isActive(){return this._target!==null}navigateTo(e,t=1){this._target||(this._target=new x),this._target.copy(e),this._speedMul=t,this._progress.reset()}cancel(){this._target&&(this._target=null,this._yawRate=0,this._progress.reset(),this.onComplete?.())}update(e,t,s){if(!this._target)return;let n=this._target,o=t.position,r=t.angles,a=n.x-o.x,l=n.z-o.z,c=Math.sqrt(a*a+l*l);if(c<kh){this.cancel();return}if(this._progress.update(c,e,Mh,Sh)){this.cancel();return}let h=qr(a,l,r.y);this._yawRate=yo(this._yawRate,h,this.maxTurnRate,this.turnGain,e);let d=xo(this._yawRate,h,e);this._yawRate=e>0?d/e:this._yawRate,s.deltas.rotate.append([-d,0,0]);let u=Math.max(0,Math.cos(h*Math.PI/180));s.deltas.move.append([0,0,this.walkSpeed*this._speedMul*e*u])}},me=new Tt,Be=new x,Qs=new x,Ja=1.2,Ch=2.4,er=5,Ph=3,tr=.8,sr=i=>e=>1-Math.pow(1-e,er)*(1+(er-i)*e),nr=5,Mo=(i,e)=>{let{x:t,z:s}=i.halfExtents;return!!e&&t*2>=nr&&s*2>=nr},$r=(i,e,t)=>{let s=new Tt;return s.look(i,e),s.fov=t,s},Lh=(i,e)=>{let s=i.halfExtents.length()/Math.sin(e/180*Math.PI*.5);return $r(new x(2,1,2).normalize().mulScalar(s).add(i.center),i.center,e)},So=class{update;seek;selectAnnotation;snap;transitionProgress;setCollision;camera=new Tt;constructor(e,t,s=null){let{events:n,settings:o,state:r}=e,a=Mo(t,s),l=o.cameras[0]?.initial,c=l?.fov??io,h=Lh(t,c),d=l?$r(new x(l.position),new x(l.target),l.fov):h,u=(C,A)=>{let{animTracks:D}=o;return D?.length>0&&o.startMode==="animTrack"?D[0]:A?(C.calcFocusPoint(Be),Gc(C.position,Be,C.fov)):(C.calcFocusPoint(Be),Wc(C.position,Be,C.fov))},p=!t.containsPoint(d.position),g=u(d,p),m={orbit:new bo,fly:new vo,walk:new _o,anim:g?new po(g):null};m.orbit.fov=d.fov,m.fly.fov=d.fov,m.fly.collision=s,m.walk.collision=s;let f=new ko,v=new wo,y={walk:f,fly:v};f.onComplete=v.onComplete=()=>{n.fire("navigateComplete")};let k=C=>m[C];r.hasAnimation=!!m.anim,r.animationDuration=m.anim?m.anim.animState.cursor.duration:0,r.cameraMode=r.hasAnimation?"anim":p?"orbit":a?"walk":"fly",this.camera.copy(d);let w=new Tt(this.camera),P=new Tt(this.camera),S=()=>p?"orbit":a?"walk":"fly",b=null,z=p?"orbit":"fly";k(r.cameraMode).onEnter(this.camera);let _=1,R=1,L=Ra,M=null,T=!1,V=0,B=this.camera.position.clone(),I=(C=1,A=Ra,D=null)=>{P.copy(this.camera),_=0,R=C,L=A,M=D};this.transitionProgress=()=>_<1?L(_):1,this.setCollision=C=>{m.fly.collision=C,m.walk.collision=C,a=Mo(t,C)},this.snap=()=>{k(r.cameraMode).onEnter(this.camera),w.copy(this.camera),_=1,e.app.renderNextFrame=!0},this.update=(C,A)=>{let D=r.cameraMode==="anim"&&r.animationPaused?0:C,O=_;_=Math.min(1,_+C/R);let H=k(r.cameraMode);if(y[r.cameraMode]?.update(D,this.camera,A),H.update(D,A,w),_<1){let N=L(_);M?this.camera.lerpAngles(P,w,N,M(_)):this.camera.lerp(P,w,N)}else this.camera.copy(w);C>0&&(V=this.camera.position.distance(B)/C),B.copy(this.camera.position),r.cameraMode==="anim"&&(r.animationTime=m.anim.animState.cursor.value,!r.animationPaused&&m.anim.animState.cursor.ended&&(r.animationPaused=!0)),T&&O<1&&_===1&&(T=!1,n.fire("orbitTarget:clear"))},n.on("inputEvent",C=>{switch(C){case"frame":n.fire("orbitTarget:clear"),r.cameraMode="orbit",m.orbit.goto(h),I();break;case"reset":r.cameraMode==="walk"?(f.cancel(),n.fire("navTarget:clear"),I(),m.walk.resetToSpawn(w)):r.cameraMode==="fly"?(v.cancel(),I(),m.fly.resetToSpawn(w)):(n.fire("orbitTarget:clear"),r.cameraMode="orbit",m.orbit.goto(d),I());break;case"playPause":r.hasAnimation&&(r.cameraMode==="anim"?r.animationPaused=!r.animationPaused:(r.cameraMode="anim",r.animationPaused=!1));break;case"requestFirstPerson":r.cameraMode=a?"walk":"fly";break;case"toggleWalk":a&&(r.cameraMode==="walk"?r.cameraMode=z:r.cameraMode="walk");break;case"exitWalk":r.cameraMode==="walk"&&(r.cameraMode=z);break;case"cancel":r.cameraMode==="anim"&&(r.cameraMode=b??S());break;case"interrupt":r.cameraMode==="anim"&&(r.cameraMode=b??S());break}}),n.on("cameraMode:changed",(C,A)=>{C==="walk"&&(z=A),y[A]?.cancel(),I(),w.copy(this.camera),b=A,k(A).onExit(this.camera),k(C).onEnter(this.camera)}),n.on("animationPaused:changed",C=>{let A=m.anim?.animState;if(!C&&A){let{cursor:D}=A;D.loopMode==="none"&&D.duration-D.value<1/A.frameRate&&(D.value=0)}}),this.seek=C=>{r.cameraMode="anim",m.anim.animState.cursor.value=C,r.animationTime=m.anim.animState.cursor.value},n.on("pick",C=>{r.cameraMode="orbit",me.copy(this.camera),me.look(this.camera.position,C),m.orbit.goto(me),I(),T=!0}),this.selectAnnotation=C=>{n.fire("orbitTarget:clear"),r.cameraMode="orbit";let{initial:A}=C.camera;me.fov=A.fov,me.look(new x(A.position),new x(A.target)),me.calcFocusPoint(Be),Be.sub(me.position).normalize();let D=Qs.set(...C.position).sub(me.position).dot(Be);D>.001&&(me.distance=D);let O=this.camera.position.distance(me.position),H=Math.max(t.halfExtents.length(),.001);this.camera.calcFocusPoint(Be),Be.sub(this.camera.position).normalize(),me.calcFocusPoint(Qs),Qs.sub(me.position).normalize();let N=Math.acos(Math.max(-1,Math.min(1,Be.dot(Qs))))/Math.PI,E=Math.max(Ja,Math.min(Ch,Ja+.8*O/H+.6*N)),j=O>1e-6?Math.min(Ph,V*E/O):0,$=sr(j),F=sr(j*tr);m.orbit.goto(me),I(E,$,W=>F(Math.min(1,W/tr)))},n.on("navigateTo",(C,A,D=1)=>{let O=y[r.cameraMode];O&&(O.navigateTo(C,D),n.fire("navTarget:set",C,A))}),n.on("navigateCancel",()=>{y[r.cameraMode]?.cancel(),n.fire("navTarget:clear")}),n.on("navigateComplete",()=>{n.fire("navTarget:clear")})}},Eh=`
|
|
528
|
+
varying vec2 vUv0;
|
|
529
|
+
|
|
530
|
+
uniform sampler2D source;
|
|
531
|
+
uniform vec2 uSrcSize;
|
|
532
|
+
uniform float uRatio;
|
|
533
|
+
uniform float uFlipY;
|
|
534
|
+
|
|
535
|
+
void main(void) {
|
|
536
|
+
int r = int(uRatio);
|
|
537
|
+
vec2 outSize = uSrcSize / uRatio;
|
|
538
|
+
vec2 uv = vUv0;
|
|
539
|
+
if (uFlipY > 0.5) {
|
|
540
|
+
uv.y = 1.0 - uv.y;
|
|
541
|
+
}
|
|
542
|
+
vec2 base = floor(uv * outSize) * uRatio;
|
|
543
|
+
vec4 sum = vec4(0.0);
|
|
544
|
+
for (int y = 0; y < 8; y++) {
|
|
545
|
+
if (y >= r) break;
|
|
546
|
+
for (int x = 0; x < 8; x++) {
|
|
547
|
+
if (x >= r) break;
|
|
548
|
+
vec2 texel = base + vec2(float(x) + 0.5, float(y) + 0.5);
|
|
549
|
+
sum += texture2D(source, texel / uSrcSize);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
gl_FragColor = sum / (uRatio * uRatio);
|
|
553
|
+
}
|
|
554
|
+
`,Rh=`
|
|
555
|
+
varying vUv0: vec2f;
|
|
556
|
+
var source: texture_2d<f32>;
|
|
557
|
+
var sourceSampler: sampler;
|
|
558
|
+
uniform uSrcSize: vec2f;
|
|
559
|
+
uniform uRatio: f32;
|
|
560
|
+
uniform uFlipY: f32;
|
|
561
|
+
|
|
562
|
+
@fragment
|
|
563
|
+
fn fragmentMain(input: FragmentInput) -> FragmentOutput {
|
|
564
|
+
var output: FragmentOutput;
|
|
565
|
+
let r: i32 = i32(uniform.uRatio);
|
|
566
|
+
let outSize: vec2f = uniform.uSrcSize / uniform.uRatio;
|
|
567
|
+
var uv: vec2f = input.vUv0;
|
|
568
|
+
if (uniform.uFlipY > 0.5) {
|
|
569
|
+
uv.y = 1.0 - uv.y;
|
|
570
|
+
}
|
|
571
|
+
let base: vec2f = floor(uv * outSize) * uniform.uRatio;
|
|
572
|
+
var sum: vec4f = vec4f(0.0);
|
|
573
|
+
for (var y: i32 = 0; y < 8; y = y + 1) {
|
|
574
|
+
if (y >= r) { break; }
|
|
575
|
+
for (var x: i32 = 0; x < 8; x = x + 1) {
|
|
576
|
+
if (x >= r) { break; }
|
|
577
|
+
let texel: vec2f = base + vec2f(f32(x) + 0.5, f32(y) + 0.5);
|
|
578
|
+
sum = sum + textureSample(source, sourceSampler, texel / uniform.uSrcSize);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
output.color = sum / (uniform.uRatio * uniform.uRatio);
|
|
582
|
+
return output;
|
|
583
|
+
}
|
|
584
|
+
`,Co=class{app;device;camera;getCameraFrame;shader;srcRT=null;dstRT=null;destroyed=!1;constructor(e,t,s){this.app=e,this.device=e.graphicsDevice,this.camera=t,this.getCameraFrame=s,this.shader=Ss.createShader(this.device,{uniqueName:"captureBoxDownsample",attributes:{vertex_position:pe},vertexChunk:"fullscreenQuadVS",fragmentGLSL:Eh,fragmentWGSL:Rh})}makeRT(e,t,s,n){let r=this.device.backBuffer?.isColorBufferSrgb?.(0)??!1,a=new ze(this.device,{name:e,width:t,height:s,format:ss,mipmaps:!1,minFilter:1,magFilter:1,addressU:1,addressV:1}),l=new Xe({name:e,colorBuffer:a,depth:n});return l.isColorBufferSrgb=()=>r,l}destroy(){this.destroyed=!0;for(let e of["srcRT","dstRT"])this[e]?.colorBuffer.destroy(),this[e]?.destroy(),this[e]=null;this.shader.destroy()}ensure(e,t,s,n){let o=this[e];if(o&&o.width===t&&o.height===s)return o;o?.colorBuffer.destroy(),o?.destroy();let r=this.makeRT(e,t,s,n);return this[e]=r,r}setCameraTarget(e){this.camera.renderTarget=e;let t=this.getCameraFrame();if(t){let s=t.renderPassCamera;s&&(s.layersDirty=!0),t.update()}}async grab({time:e,width:t,height:s,supersample:n=2,scrub:o}){let r=this.device,a=Math.min(8,Math.max(1,Math.round(n))),l=Math.max(1,Math.round(t)),c=Math.max(1,Math.round(s)),h=l*a,d=c*a,u=this.ensure("srcRT",h,d,!0),p=this.camera,g={renderTarget:p.renderTarget,aspectRatioMode:p.aspectRatioMode,aspectRatio:p.aspectRatio,horizontalFov:p.horizontalFov};try{p.aspectRatioMode=Ui,p.horizontalFov=h>=d,this.setCameraTarget(u),e!==void 0&&o&&o(e),this.app.renderNextFrame=!0,await new Promise(P=>{this.app.once("frameend",()=>P())});let m=this.ensure("dstRT",l,c,!1),{scope:f}=r;f.resolve("source").setValue(u.colorBuffer),f.resolve("uSrcSize").setValue([h,d]),f.resolve("uRatio").setValue(a),f.resolve("uFlipY").setValue(u.flipY?0:1),r.setBlendState(is.NOBLEND),Cs(r,m,this.shader);let v=await m.colorBuffer.read(0,0,l,c,{renderTarget:m,immediate:!0}),y=v instanceof Uint8Array?v:new Uint8Array(v.buffer),k=[],w=32768;for(let P=0;P<y.length;P+=w)k.push(String.fromCharCode.apply(null,y.subarray(P,P+w)));return{width:l,height:c,data:btoa(k.join(""))}}finally{this.destroyed||(p.aspectRatioMode=g.aspectRatioMode,p.aspectRatio=g.aspectRatio,p.horizontalFov=g.horizontalFov,this.setCameraTarget(g.renderTarget),this.app.renderNextFrame=!0)}}},or=(i,e)=>({position:[i.camera.position.x,i.camera.position.y,i.camera.position.z],angles:[i.camera.angles.x,i.camera.angles.y,i.camera.angles.z],distance:i.camera.distance,fov:i.camera.fov,mode:e.cameraMode}),ir=(i,e,t)=>{i.camera.position.set(t.position[0],t.position[1],t.position[2]),i.camera.angles.set(t.angles[0],t.angles[1],t.angles[2]),i.camera.distance=t.distance,i.camera.fov=t.fov,e.cameraMode!==t.mode&&(e.cameraMode=t.mode),i.snap()},Ah=`
|
|
585
|
+
vec4 encodePickOutput(uint id) {
|
|
586
|
+
const vec4 inv = vec4(1.0 / 255.0);
|
|
587
|
+
const uvec4 shifts = uvec4(16, 8, 0, 24);
|
|
588
|
+
uvec4 col = (uvec4(id) >> shifts) & uvec4(0xff);
|
|
589
|
+
return vec4(col) * inv;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
#ifdef GSPLAT_PICK_DEPTH
|
|
593
|
+
#ifndef CAMERAPLANES
|
|
594
|
+
#define CAMERAPLANES
|
|
595
|
+
uniform vec4 camera_params; // x: 1/far, y: far, z: near, w: isOrtho
|
|
596
|
+
#endif
|
|
597
|
+
|
|
598
|
+
uint pickHash(uint v) {
|
|
599
|
+
v ^= v >> 16u;
|
|
600
|
+
v *= 0x7feb352du;
|
|
601
|
+
v ^= v >> 15u;
|
|
602
|
+
v *= 0x846ca68bu;
|
|
603
|
+
v ^= v >> 16u;
|
|
604
|
+
return v;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Four independent trials, one per channel: each keeps the fragment with probability alpha,
|
|
608
|
+
// the splat's opacity at this fragment with its falloff included, writing its depth there,
|
|
609
|
+
// and the far plane where it does not. The splat's seed (see pickSeedVS) and depth, both
|
|
610
|
+
// the same across its quad, hashed with the pixel decorrelate the splats covering one pixel.
|
|
611
|
+
vec4 getSplatPickOutput(float alpha) {
|
|
612
|
+
float normalizedDepth;
|
|
613
|
+
if (camera_params.w > 0.5) {
|
|
614
|
+
normalizedDepth = gl_FragCoord.z;
|
|
615
|
+
} else {
|
|
616
|
+
float linearDepth = 1.0 / gl_FragCoord.w;
|
|
617
|
+
normalizedDepth = (linearDepth - camera_params.z) / (camera_params.y - camera_params.z);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
uvec2 p = uvec2(gl_FragCoord.xy);
|
|
621
|
+
uint h = pickHash(p.x ^ pickHash(p.y ^ pickHash(vPickSeed ^ floatBitsToUint(normalizedDepth))));
|
|
622
|
+
uvec4 trials = uvec4(pickHash(h), pickHash(h ^ 0x9e3779b9u), pickHash(h ^ 0x7f4a7c15u), pickHash(h ^ 0x2545f491u));
|
|
623
|
+
bvec4 keep = lessThan(vec4(trials >> 8u) * (1.0 / 16777216.0), vec4(alpha));
|
|
624
|
+
if (!any(keep)) {
|
|
625
|
+
discard;
|
|
626
|
+
}
|
|
627
|
+
return mix(vec4(1.0), vec4(normalizedDepth), vec4(keep));
|
|
628
|
+
}
|
|
629
|
+
#else
|
|
630
|
+
#ifndef PICK_CUSTOM_ID
|
|
631
|
+
uniform uint meshInstanceId;
|
|
632
|
+
|
|
633
|
+
vec4 getPickOutput() {
|
|
634
|
+
return encodePickOutput(meshInstanceId);
|
|
635
|
+
}
|
|
636
|
+
#endif
|
|
637
|
+
#endif
|
|
638
|
+
|
|
639
|
+
#ifdef DEPTH_PICK_PASS
|
|
640
|
+
#include "floatAsUintPS"
|
|
641
|
+
#ifndef CAMERAPLANES
|
|
642
|
+
#define CAMERAPLANES
|
|
643
|
+
uniform vec4 camera_params; // x: 1/far, y: far, z: near, w: isOrtho
|
|
644
|
+
#endif
|
|
645
|
+
|
|
646
|
+
vec4 getPickDepth() {
|
|
647
|
+
float linearDepth;
|
|
648
|
+
if (camera_params.w > 0.5) {
|
|
649
|
+
linearDepth = gl_FragCoord.z;
|
|
650
|
+
} else {
|
|
651
|
+
float viewDist = 1.0 / gl_FragCoord.w;
|
|
652
|
+
linearDepth = (viewDist - camera_params.z) / (camera_params.y - camera_params.z);
|
|
653
|
+
}
|
|
654
|
+
return float2uint(linearDepth);
|
|
655
|
+
}
|
|
656
|
+
#endif
|
|
657
|
+
`,Th=`
|
|
658
|
+
fn encodePickOutput(id: u32) -> vec4f {
|
|
659
|
+
let inv: vec4f = vec4f(1.0 / 255.0);
|
|
660
|
+
let shifts: vec4u = vec4u(16u, 8u, 0u, 24u);
|
|
661
|
+
let col: vec4u = (vec4u(id) >> shifts) & vec4u(0xffu);
|
|
662
|
+
return vec4f(col) * inv;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
#ifdef GSPLAT_PICK_DEPTH
|
|
666
|
+
#ifndef CAMERAPLANES
|
|
667
|
+
#define CAMERAPLANES
|
|
668
|
+
uniform camera_params: vec4f; // x: 1/far, y: far, z: near, w: isOrtho
|
|
669
|
+
#endif
|
|
670
|
+
|
|
671
|
+
fn pickHash(value: u32) -> u32 {
|
|
672
|
+
var v = value;
|
|
673
|
+
v ^= v >> 16u;
|
|
674
|
+
v *= 0x7feb352du;
|
|
675
|
+
v ^= v >> 15u;
|
|
676
|
+
v *= 0x846ca68bu;
|
|
677
|
+
v ^= v >> 16u;
|
|
678
|
+
return v;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// Four independent trials, one per channel: each keeps the fragment with probability alpha,
|
|
682
|
+
// the splat's opacity at this fragment with its falloff included, writing its depth there,
|
|
683
|
+
// and the far plane where it does not. The splat's seed (see pickSeedVS) and depth, both
|
|
684
|
+
// the same across its quad, hashed with the pixel decorrelate the splats covering one pixel.
|
|
685
|
+
fn getSplatPickOutput(alpha: f32) -> vec4f {
|
|
686
|
+
var normalizedDepth: f32;
|
|
687
|
+
if (uniform.camera_params.w > 0.5) {
|
|
688
|
+
normalizedDepth = pcPosition.z;
|
|
689
|
+
} else {
|
|
690
|
+
let linearDepth = 1.0 / pcPosition.w;
|
|
691
|
+
normalizedDepth = (linearDepth - uniform.camera_params.z) / (uniform.camera_params.y - uniform.camera_params.z);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
let p = vec2u(pcPosition.xy);
|
|
695
|
+
let h = pickHash(p.x ^ pickHash(p.y ^ pickHash(vPickSeed ^ bitcast<u32>(normalizedDepth))));
|
|
696
|
+
let trials = vec4u(pickHash(h), pickHash(h ^ 0x9e3779b9u), pickHash(h ^ 0x7f4a7c15u), pickHash(h ^ 0x2545f491u));
|
|
697
|
+
let keep = vec4f(trials >> vec4u(8u)) * (1.0 / 16777216.0) < vec4f(alpha);
|
|
698
|
+
if (!any(keep)) {
|
|
699
|
+
discard;
|
|
700
|
+
}
|
|
701
|
+
return select(vec4f(1.0), vec4f(normalizedDepth), keep);
|
|
702
|
+
}
|
|
703
|
+
#else
|
|
704
|
+
#ifndef PICK_CUSTOM_ID
|
|
705
|
+
uniform meshInstanceId: u32;
|
|
706
|
+
|
|
707
|
+
fn getPickOutput() -> vec4f {
|
|
708
|
+
return encodePickOutput(uniform.meshInstanceId);
|
|
709
|
+
}
|
|
710
|
+
#endif
|
|
711
|
+
#endif
|
|
712
|
+
|
|
713
|
+
#ifdef DEPTH_PICK_PASS
|
|
714
|
+
#include "floatAsUintPS"
|
|
715
|
+
#ifndef CAMERAPLANES
|
|
716
|
+
#define CAMERAPLANES
|
|
717
|
+
uniform camera_params: vec4f; // x: 1/far, y: far, z: near, w: isOrtho
|
|
718
|
+
#endif
|
|
719
|
+
|
|
720
|
+
fn getPickDepth() -> vec4f {
|
|
721
|
+
var linearDepth: f32;
|
|
722
|
+
if (uniform.camera_params.w > 0.5) {
|
|
723
|
+
linearDepth = pcPosition.z;
|
|
724
|
+
} else {
|
|
725
|
+
let viewDist = 1.0 / pcPosition.w;
|
|
726
|
+
linearDepth = (viewDist - uniform.camera_params.z) / (uniform.camera_params.y - uniform.camera_params.z);
|
|
727
|
+
}
|
|
728
|
+
return float2uint(linearDepth);
|
|
729
|
+
}
|
|
730
|
+
#endif
|
|
731
|
+
`,We=(i,e,t)=>{let s=i.replace(e,t);if(s===i)throw new Error("picker: engine gsplat/pick chunk patch failed (engine version mismatch?)");return s},Hr=i=>`#ifdef PICK_PASS
|
|
732
|
+
${i}
|
|
733
|
+
#define GSPLAT_PICK_DEPTH
|
|
734
|
+
#include "pickPS"
|
|
735
|
+
#endif`,jr="flat varying uint vPickSeed;",Qo="varying @interpolate(flat) vPickSeed: u32;",Dh=i=>`vPickSeed = (floatBitsToUint(${i}.x) * 0x9e3779b9u) ^ (floatBitsToUint(${i}.y) * 0x85ebca6bu) ^ (floatBitsToUint(${i}.w) * 0xc2b2ae35u);`,Wr=i=>`output.vPickSeed = (bitcast<u32>(${i}.x) * 0x9e3779b9u) ^ (bitcast<u32>(${i}.y) * 0x85ebca6bu) ^ (bitcast<u32>(${i}.w) * 0xc2b2ae35u);`,Yt=i=>`
|
|
736
|
+
#ifdef PICK_PASS
|
|
737
|
+
${i}
|
|
738
|
+
#endif
|
|
739
|
+
`,zh=i=>We(We(i,/(varying mediump vec2 gaussianUV;)/,`$1${Yt(jr)}`),/(gaussianUV = corner\.uv;)/,`$1${Yt(Dh("center.proj"))}`),Fh=i=>We(We(i,/(varying gaussianUV: half2;)/,`$1${Yt(Qo)}`),/(output\.gaussianUV = corner\.uv;)/,`$1${Yt(Wr("center.proj"))}`),Ih=i=>We(We(i,/(varying gaussianUV: half2;)/,`$1${Yt(Qo)}`),/(output\.gaussianUV = half2\(cornerClipped\);)/,`$1${Yt(Wr("proj"))}`),Vh=i=>We(We(i,/#ifdef PICK_PASS\s*#include "pickPS"\s*#endif/,Hr(jr)),/pcFragColor0 = (encodePickOutput\(vPickId\)|getPickOutput\(\));/g,"pcFragColor0 = getSplatPickOutput(alpha);"),Oh=i=>We(We(i,/#ifdef PICK_PASS\s*#include "pickPS"\s*#endif/,Hr(Qo)),/output\.color = (encodePickOutput\(vPickId\)|getPickOutput\(\));/g,"output.color = getSplatPickOutput(f32(alpha));"),vn=new WeakMap,ge=new yi,ar=new U,Bh=new K(1,1,1,1),ps=4,te=2,Jo=.3,Nh=1e-12,qh=1e-20,$h=.2,Hh=6,ei=48,jh=[.3,.55,.8,1],Wh=2.5,Gh=[[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1],[0,-1],[1,-1]],Gr=new Float32Array(1),Js=new Uint32Array(Gr.buffer),Ur=i=>{let e=(i&32768)<<16,t=(i&31744)>>10,s=i&1023;if(t===0)if(s===0)Js[0]=e;else{let n=-1,o=s;do n++,o<<=1;while(!(o&1024));Js[0]=e|112-n<<23|(o&1023)<<13}else t===31?Js[0]=e|2139095040|s<<13:Js[0]=e|t+127-15<<23|s<<13;return Gr[0]},Uh=i=>{let e=i.graphicsDevice,t=vn.get(e);if(t){t.refCount++;return}let s=ie.get(e,"glsl"),n=ie.get(e,"wgsl"),o=s.get("pickPS"),r=s.get("gsplatPS"),a=n.get("pickPS"),l=n.get("gsplatPS"),c=s.get("gsplatVS"),h=n.get("gsplatVS"),d=n.get("gsplatHybridVS"),u=Vh(r),p=Oh(l),g=zh(c),m=Fh(h),f=Ih(d),v={glslPickPS:o,glslGsplatPS:r,wgslPickPS:a,wgslGsplatPS:l,glslGsplatVS:c,wgslGsplatVS:h,wgslGsplatHybridVS:d,refCount:1};vn.set(e,v),s.set("pickPS",Ah),n.set("pickPS",Th),s.set("gsplatPS",u),n.set("gsplatPS",p),s.set("gsplatVS",g),n.set("gsplatVS",m),n.set("gsplatHybridVS",f)},Xh=i=>{let e=i.graphicsDevice,t=vn.get(e);if(!t||(t.refCount--,t.refCount>0))return;let s=ie.get(e,"glsl"),n=ie.get(e,"wgsl");s.set("pickPS",t.glslPickPS),s.set("gsplatPS",t.glslGsplatPS),n.set("pickPS",t.wgslPickPS),n.set("gsplatPS",t.wgslGsplatPS),s.set("gsplatVS",t.glslGsplatVS),n.set("gsplatVS",t.wgslGsplatVS),n.set("gsplatHybridVS",t.wgslGsplatHybridVS),vn.delete(e)},ds=()=>({position:new x,viewMatrix:new U,projectionMatrix:new U,nearClip:0,farClip:0,projection:0}),Xr=(i,e)=>{let t=i.camera;e.position.copy(i.getPosition()),e.viewMatrix.copy(t.viewMatrix),e.projectionMatrix.copy(t.projectionMatrix),e.nearClip=t.nearClip,e.farClip=t.farClip,e.projection=t.projection},ti=(i,e,t,s,n,o,r)=>{if(!Number.isFinite(o)||o<0||o>1)return null;let{farClip:a,nearClip:l}=i,c=i.projection===qt?o:a*o/(o*(a-l)+l);return ar.mul2(i.projectionMatrix,i.viewMatrix).invert(),ge.set(e/s*2-1,(1-t/n)*2-1,c*2-1,1),ar.transformVec4(ge,ge),!Number.isFinite(ge.w)||Math.abs(ge.w)<1e-8||(ge.mulScalar(1/ge.w),!Number.isFinite(ge.x)||!Number.isFinite(ge.y)||!Number.isFinite(ge.z))?null:(r??new x).set(ge.x,ge.y,ge.z)},Yh=(i,e,t)=>{t.sub2(i,e);let s=t.length();return s>1e-6?t.mulScalar(1/s):t.set(0,1,0),t},Zh=(i,e,t,s)=>{let n=i.projectionMatrix.data[5];if(i.projection===qt)return s*n*t/2;let o=e.x-i.position.x,r=e.y-i.position.y,a=e.z-i.position.z,l=Math.sqrt(o*o+r*r+a*a);return l<1e-6?1/0:s*n*t/(2*l)},rr=i=>{let e=i.length;if(e<3)return null;let t=0,s=0,n=0;for(let ee=0;ee<e;ee++)t+=i[ee].x,s+=i[ee].y,n+=i[ee].z;t/=e,s/=e,n/=e;let o=0,r=0,a=0,l=0,c=0,h=0;for(let ee=0;ee<e;ee++){let Te=i[ee].x-t,De=i[ee].y-s,Ae=i[ee].z-n;o+=Te*Te,r+=Te*De,a+=Te*Ae,l+=De*De,c+=De*Ae,h+=Ae*Ae}let d=(o+l+h)/3,u=o-d,p=l-d,g=h-d,m=u*u+p*p+g*g+2*(r*r+a*a+c*c);if(m<qh)return null;let f=Math.sqrt(m/6),v=1/f,y=u*v,k=r*v,w=a*v,P=p*v,S=c*v,b=g*v,z=y*(P*b-S*S)-k*(k*b-S*w)+w*(k*S-P*w),_=Math.max(-1,Math.min(1,z/2)),R=Math.acos(_)/3,L=d+2*f*Math.cos(R+2*Math.PI/3),M=o-L,T=l-L,V=h-L,B=r*c-a*T,I=a*r-M*c,C=M*T-r*r,A=r*V-a*c,D=a*a-M*V,O=M*c-r*a,H=T*V-c*c,N=c*a-r*V,E=r*c-T*a,j=B*B+I*I+C*C,$=A*A+D*D+O*O,F=H*H+N*N+E*E,W,_e,J,ne;if(j>=$&&j>=F?(W=B,_e=I,J=C,ne=j):$>=F?(W=A,_e=D,J=O,ne=$):(W=H,_e=N,J=E,ne=F),ne<Nh)return null;let ue=1/Math.sqrt(ne);return{cx:t,cy:s,cz:n,vx:W*ue,vy:_e*ue,vz:J*ue}},Kh=(i,e,t)=>{let s=rr(i);if(!s)return!1;let n=0;for(let d=0;d<i.length;d++){let u=i[d].x-s.cx,p=i[d].y-s.cy,g=i[d].z-s.cz;n+=Math.abs(u*s.vx+p*s.vy+g*s.vz)}let o=n/i.length*Wh,r=[];for(let d=0;d<i.length;d++){let u=i[d].x-s.cx,p=i[d].y-s.cy,g=i[d].z-s.cz;Math.abs(u*s.vx+p*s.vy+g*s.vz)<=o&&r.push(i[d])}let a=s;if(r.length>=3&&r.length<i.length){let d=rr(r);d&&(a=d)}let{vx:l,vy:c,vz:h}=a;return l*e.x+c*e.y+h*e.z<0&&(l=-l,c=-c,h=-h),t.set(l,c,h),!0},si=(i,e,t)=>{let s=[];for(let o=-te;o<=te;o++)for(let r=-te;r<=te;r++)if(!(r*r+o*o>te*te))for(let a=0;a<ps;a++){let l=i(e+r,t+o,a);l!==null&&s.push(l)}if(s.length===0)return null;s.sort((o,r)=>o-r);let n=s[Math.max(0,Math.ceil(s.length*Jo)-1)];return Number.isFinite(n)&&n<1?n:null},Yr=(i,e,t,s,n,o)=>{let r=(m,f)=>{let v=si(i,m,f);return v===null?null:ti(e,m,f,t,s,v)},a=r(n,o);if(!a)return null;let l=(m,f)=>m<0||m>=t||f<0||f>=s?null:r(m,f),c=Math.max(Hh,Math.min(ei,Zh(e,a,s,$h))),d=jh.map(m=>Math.max(1,Math.round(m*c))).map(m=>Gh.map(([f,v])=>l(n+f*m,o+v*m))),u=Yh(e.position,a,new x),p=[a];for(let m=0;m<d.length;m++){let f=d[m];for(let v=0;v<f.length;v++){let y=f[v];y&&p.push(y)}}let g=new x;return Kh(p,u,g)||g.copy(u),{position:a,normal:g}},Zr=(i,e,t,s,n,o)=>{let r=(o-e.nearClip)/(e.farClip-e.nearClip),a=0,l=0;for(let c=-n;c<=n;c++)for(let h=-n;h<=n;h++)if(!(h*h+c*c>n*n))for(let d=0;d<ps;d++){let u=i(t+h,s+c,d);u!==null&&(a++,u<r&&l++)}return a>0?l/a:null},Kr=i=>{let e=i.canvas,t=Math.min(Math.floor(i.width),Math.round(e.clientWidth)),s=Math.min(Math.floor(i.height),Math.round(e.clientHeight));return t>0&&s>0?{width:t,height:s}:null},ft=(i,e)=>Math.min(e-1,Math.max(0,Math.floor(i*e))),yn=class{released=!1;queue=Promise.resolve();run(e,t){let s=()=>this.released?Promise.resolve(t):e().catch(o=>{if(this.released)return t;throw o}),n=this.queue.then(s,s);return this.queue=n.then(()=>{},()=>{}),n}},Po=class{pick;pickSurface;pickVisibility;renderView;release;constructor(e,t){let{graphicsDevice:s}=e,n,o,r,a=!1,l=new yn,c=!1,h=0,d=0,u=0,p=ds(),g=(L,M)=>{n=new ze(s,{format:ks,width:L,height:M,mipmaps:!1,minFilter:0,magFilter:0,addressU:1,addressV:1,name:"picker-stochastic"}),o=new Xe({colorBuffer:n,depth:!1}),r=new ua(s,e.renderer),r.blendState=new is(!0,3,1,1,3,1,1)},m=(L,M)=>{Xr(t,p),d=L,u=M},f=(L,M)=>{let T=t.camera;return c&&d===L&&u===M&&p.viewMatrix.equals(T.viewMatrix)&&p.projectionMatrix.equals(T.projectionMatrix)&&p.nearClip===T.nearClip&&p.farClip===T.farClip&&p.projection===T.projection},v=()=>{let L=ds();return L.position.copy(p.position),L.viewMatrix.copy(p.viewMatrix),L.projectionMatrix.copy(p.projectionMatrix),L.nearClip=p.nearClip,L.farClip=p.farClip,L.projection=p.projection,L},y=async(L,M,T,V,B)=>{let I=Math.max(0,L-T),C=Math.max(0,M-T),A=Math.min(V-1,L+T)-I+1,D=Math.min(B-1,M+T)-C+1,O=s.isWebGL2?o.height-C-D:C,H=await n.read(I,O,A,D,{renderTarget:o,immediate:!0});return(N,E,j)=>{let $=N-I,F=E-C;if($<0||$>=A||F<0||F>=D)return null;let W=s.isWebGL2?D-F-1:F;return Ur(H[(W*A+$)*4+j])}},k=(L,M,T)=>{f(L,M)||(a||(Uh(e),a=!0),r?(d!==L||u!==M)&&(c=!1,o.resize(L,M)):g(L,M),r.init(o),r.setClearColor(Bh),r.update(t.camera,e.scene,[T],new Map,!1),r.render(),m(L,M),c=!0,h++)},w=(L,M)=>{if(l.released)return null;let T=Kr(s);if(!T)return null;let{width:V,height:B}=T,I=e.scene.layers.getLayerByName("World");if(!I)return null;let C=ft(L,V),A=ft(M,B);k(V,B,I);let D=v();return{width:V,height:B,screenX:C,screenY:A,pickCamera:D}},P=async(L,M)=>{let T=w(L,M);if(!T)return null;let{width:V,height:B,screenX:I,screenY:C,pickCamera:A}=T,D=await y(I,C,te,V,B),O=si(D,I,C);return O===null?null:ti(A,I,C,V,B,O)},S=async(L,M)=>{let T=w(L,M);if(!T)return null;let{width:V,height:B,screenX:I,screenY:C,pickCamera:A}=T,D=await y(I,C,ei+te,V,B);return Yr(D,A,V,B,I,C)},b=async(L,M)=>L.length===0?[]:Promise.all(L.map(async({x:T,y:V,depth:B})=>{let I=w(T,V);if(!I)return null;let{width:C,height:A,screenX:D,screenY:O,pickCamera:H}=I,N=Math.max(1,Math.round(M*A)),E=await y(D,O,N,C,A);return Zr(E,H,D,O,N,B)}));this.pick=(L,M)=>l.run(()=>P(L,M),null),this.pickSurface=(L,M)=>l.run(()=>S(L,M),null),this.pickVisibility=(L,M)=>l.run(()=>b(L,M),L.map(()=>null)),this.renderView=()=>w(0,0)?{texture:n,renders:h}:null;let z=e.systems.gsplat,_=!1,R=(L,M,T)=>{L===t.camera&&((!T||!_)&&(c=!1),_=T)};z.on("frame:ready",R),this.release=()=>{l.released=!0,z.off("frame:ready",R),a&&(Xh(e),a=!1),r?.destroy(),o?.destroy(),n?.destroy(),c=!1}}},Qr=0;for(let i=-te;i<=te;i++)for(let e=-te;e<=te;e++)e*e+i*i<=te*te&&Qr++;var He=Math.ceil(Qr*ps*Jo),Le=te,Qh=`
|
|
740
|
+
uniform highp sampler2D pickDepth;
|
|
741
|
+
uniform vec2 pickRange;
|
|
742
|
+
uniform float surfaceOpacity;
|
|
743
|
+
void main(void) {
|
|
744
|
+
ivec2 size = textureSize(pickDepth, 0);
|
|
745
|
+
ivec2 p = ivec2(gl_FragCoord.xy);
|
|
746
|
+
float nearest[${He}];
|
|
747
|
+
for (int i = 0; i < ${He}; i++) nearest[i] = 2.0;
|
|
748
|
+
int n = 0;
|
|
749
|
+
for (int dy = -${Le}; dy <= ${Le}; dy++) {
|
|
750
|
+
for (int dx = -${Le}; dx <= ${Le}; dx++) {
|
|
751
|
+
ivec2 q = p + ivec2(dx, dy);
|
|
752
|
+
if (dx * dx + dy * dy > ${Le*Le} || any(lessThan(q, ivec2(0))) || any(greaterThanEqual(q, size))) continue;
|
|
753
|
+
vec4 trials = texelFetch(pickDepth, q, 0);
|
|
754
|
+
for (int c = 0; c < ${ps}; c++) {
|
|
755
|
+
float v = trials[c];
|
|
756
|
+
n++;
|
|
757
|
+
if (v < nearest[${He-1}]) {
|
|
758
|
+
int i = ${He-1};
|
|
759
|
+
while (i > 0 && nearest[i - 1] > v) {
|
|
760
|
+
nearest[i] = nearest[i - 1];
|
|
761
|
+
i--;
|
|
762
|
+
}
|
|
763
|
+
nearest[i] = v;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
float depth = nearest[clamp(int(ceil(float(n) * surfaceOpacity)) - 1, 0, ${He-1})];
|
|
769
|
+
float z = depth < 1.0 ? pickRange.x + depth * (pickRange.y - pickRange.x) : 0.0;
|
|
770
|
+
gl_FragColor = vec4(z, 0.0, 0.0, 1.0);
|
|
771
|
+
}
|
|
772
|
+
`,Jh=`
|
|
773
|
+
var pickDepth: texture_2d<uff>;
|
|
774
|
+
uniform pickRange: vec2f;
|
|
775
|
+
uniform surfaceOpacity: f32;
|
|
776
|
+
@fragment fn fragmentMain(input: FragmentInput) -> FragmentOutput {
|
|
777
|
+
var output: FragmentOutput;
|
|
778
|
+
let size = vec2i(textureDimensions(pickDepth, 0));
|
|
779
|
+
let p = vec2i(pcPosition.xy);
|
|
780
|
+
var nearest: array<f32, ${He}>;
|
|
781
|
+
for (var i = 0; i < ${He}; i++) {
|
|
782
|
+
nearest[i] = 2.0;
|
|
783
|
+
}
|
|
784
|
+
var n = 0;
|
|
785
|
+
for (var dy = -${Le}; dy <= ${Le}; dy++) {
|
|
786
|
+
for (var dx = -${Le}; dx <= ${Le}; dx++) {
|
|
787
|
+
let q = p + vec2i(dx, dy);
|
|
788
|
+
if (dx * dx + dy * dy > ${Le*Le} || any(q < vec2i(0)) || any(q >= size)) {
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
let trials = textureLoad(pickDepth, q, 0);
|
|
792
|
+
for (var c = 0; c < ${ps}; c++) {
|
|
793
|
+
let v = trials[c];
|
|
794
|
+
n++;
|
|
795
|
+
if (v < nearest[${He-1}]) {
|
|
796
|
+
var i = ${He-1};
|
|
797
|
+
loop {
|
|
798
|
+
if (i == 0 || nearest[i - 1] <= v) {
|
|
799
|
+
break;
|
|
800
|
+
}
|
|
801
|
+
nearest[i] = nearest[i - 1];
|
|
802
|
+
i--;
|
|
803
|
+
}
|
|
804
|
+
nearest[i] = v;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
let depth = nearest[clamp(i32(ceil(f32(n) * uniform.surfaceOpacity)) - 1, 0, ${He-1})];
|
|
810
|
+
let z = select(0.0, uniform.pickRange.x + depth * (uniform.pickRange.y - uniform.pickRange.x), depth < 1.0);
|
|
811
|
+
output.color = vec4f(z, 0.0, 0.0, 1.0);
|
|
812
|
+
return output;
|
|
813
|
+
}
|
|
814
|
+
`,ed=`
|
|
815
|
+
vec3 turbo(float x) {
|
|
816
|
+
const vec4 r4 = vec4(0.13572138, 4.61539260, -42.66032258, 132.13108234);
|
|
817
|
+
const vec4 g4 = vec4(0.09140261, 2.19418839, 4.84296658, -14.18503333);
|
|
818
|
+
const vec4 b4 = vec4(0.10667330, 12.64194608, -60.58204836, 110.36276771);
|
|
819
|
+
const vec2 r2 = vec2(-152.94239396, 59.28637943);
|
|
820
|
+
const vec2 g2 = vec2(4.27729857, 2.82956604);
|
|
821
|
+
const vec2 b2 = vec2(-89.90310912, 27.34824973);
|
|
822
|
+
vec4 v4 = vec4(1.0, x, x * x, x * x * x);
|
|
823
|
+
vec2 v2 = v4.zw * v4.z;
|
|
824
|
+
return vec3(dot(v4, r4) + dot(v2, r2), dot(v4, g4) + dot(v2, g2), dot(v4, b4) + dot(v2, b2));
|
|
825
|
+
}
|
|
826
|
+
`,td=`
|
|
827
|
+
fn turbo(x: f32) -> vec3f {
|
|
828
|
+
let r4 = vec4f(0.13572138, 4.61539260, -42.66032258, 132.13108234);
|
|
829
|
+
let g4 = vec4f(0.09140261, 2.19418839, 4.84296658, -14.18503333);
|
|
830
|
+
let b4 = vec4f(0.10667330, 12.64194608, -60.58204836, 110.36276771);
|
|
831
|
+
let r2 = vec2f(-152.94239396, 59.28637943);
|
|
832
|
+
let g2 = vec2f(4.27729857, 2.82956604);
|
|
833
|
+
let b2 = vec2f(-89.90310912, 27.34824973);
|
|
834
|
+
let v4 = vec4f(1.0, x, x * x, x * x * x);
|
|
835
|
+
let v2 = v4.zw * v4.z;
|
|
836
|
+
return vec3f(dot(v4, r4) + dot(v2, r2), dot(v4, g4) + dot(v2, g2), dot(v4, b4) + dot(v2, b2));
|
|
837
|
+
}
|
|
838
|
+
`,sd=`
|
|
839
|
+
uniform highp sampler2D surfaceDepth;
|
|
840
|
+
uniform vec4 viewport_size;
|
|
841
|
+
${ed}
|
|
842
|
+
void main(void) {
|
|
843
|
+
ivec2 size = textureSize(surfaceDepth, 0);
|
|
844
|
+
float z = texelFetch(surfaceDepth, ivec2(gl_FragCoord.xy * vec2(size) * viewport_size.zw), 0).r;
|
|
845
|
+
gl_FragColor = z > 0.0 ? vec4(turbo(fract(log2(z))), 1.0) : vec4(0.6, 0.0, 0.6, 1.0);
|
|
846
|
+
}
|
|
847
|
+
`,nd=`
|
|
848
|
+
var surfaceDepth: texture_2d<uff>;
|
|
849
|
+
uniform viewport_size: vec4f;
|
|
850
|
+
${td}
|
|
851
|
+
@fragment fn fragmentMain(input: FragmentInput) -> FragmentOutput {
|
|
852
|
+
var output: FragmentOutput;
|
|
853
|
+
let size = vec2f(textureDimensions(surfaceDepth, 0));
|
|
854
|
+
let z = textureLoad(surfaceDepth, vec2i(pcPosition.xy * size * uniform.viewport_size.zw), 0).r;
|
|
855
|
+
output.color = select(vec4f(0.6, 0.0, 0.6, 1.0), vec4f(turbo(fract(log2(z))), 1.0), z > 0.0);
|
|
856
|
+
return output;
|
|
857
|
+
}
|
|
858
|
+
`,Lo=class{app;camera;picker;resolveShader;material=new Ze;quad;depthTarget=null;onFrameEnd=()=>this.resolve();onPrerender=()=>this.draw();resolvedRenders=-1;_enabled=!1;constructor(e,t,s){this.app=e,this.camera=t,this.picker=s,this.resolveShader=Ss.createShader(e.graphicsDevice,{uniqueName:"PickDepthResolve",attributes:{vertex_position:pe},vertexChunk:"fullscreenQuadVS",fragmentGLSL:Qh,fragmentWGSL:Jh});let n=this.material;n.cull=0,n.blendType=Ms,n.depthTest=!1,n.depthWrite=!1,n.shaderDesc={uniqueName:"PickDepthColour",vertexGLSL:`
|
|
859
|
+
attribute vec2 vertex_position;
|
|
860
|
+
uniform mat4 matrix_model;
|
|
861
|
+
void main(void) {
|
|
862
|
+
gl_Position = matrix_model * vec4(vertex_position, 0, 1);
|
|
863
|
+
}
|
|
864
|
+
`,vertexWGSL:`
|
|
865
|
+
attribute vertex_position: vec2f;
|
|
866
|
+
uniform matrix_model: mat4x4f;
|
|
867
|
+
@vertex fn vertexMain(input: VertexInput) -> VertexOutput {
|
|
868
|
+
var output: VertexOutput;
|
|
869
|
+
output.position = uniform.matrix_model * vec4f(input.vertex_position, 0.0, 1.0);
|
|
870
|
+
return output;
|
|
871
|
+
}
|
|
872
|
+
`,fragmentGLSL:sd,fragmentWGSL:nd,attributes:{vertex_position:pe}},n.update();let o=new Ye(e.graphicsDevice);o.setPositions([-.5,-.5,0,.5,-.5,0,-.5,.5,0,.5,.5,0]),o.update(Fn);let r=new $t("PickDepthColour");r.setLocalScale(2,-2,1),r.getWorldTransform(),this.quad=new ke(o,n,r),this.quad.cull=!1,this.quad.castShadow=!1,this.quad.pick=!1,this.quad.visible=!1,e.scene.defaultDrawLayer.addMeshInstances([this.quad],!0)}get enabled(){return this._enabled}set enabled(e){e!==this._enabled&&(this._enabled=e,e?(this.app.on("frameend",this.onFrameEnd),this.app.on("prerender",this.onPrerender)):(this.app.off("frameend",this.onFrameEnd),this.app.off("prerender",this.onPrerender),this.resolvedRenders=-1,this.quad.visible=!1),this.app.renderNextFrame=!0)}ensureDepthTarget(e,t){let s=this.depthTarget;if(s&&s.width===e&&s.height===t)return s;s?.colorBuffer.destroy(),s?.destroy();let n=this.app.graphicsDevice,o=new ze(n,{name:"pick-depth-resolved",width:e,height:t,format:n.textureFloatRenderable?_i:Ci,mipmaps:!1,minFilter:0,magFilter:0,addressU:1,addressV:1});return this.depthTarget=new Xe({name:"pick-depth-resolved",colorBuffer:o,depth:!1}),this.depthTarget}resolve(){if(this.camera.camera.renderTarget)return;let e=this.picker.renderView();if(!e||e.renders===this.resolvedRenders)return;this.resolvedRenders=e.renders;let t=this.app.graphicsDevice,s=this.camera.camera,n=this.ensureDepthTarget(e.texture.width,e.texture.height);t.scope.resolve("pickDepth").setValue(e.texture),t.scope.resolve("pickRange").setValue([s.nearClip,s.farClip]),t.scope.resolve("surfaceOpacity").setValue(Jo),t.setBlendState(is.NOBLEND),Cs(t,n,this.resolveShader),this.app.renderNextFrame=!0}draw(){this.quad.visible=!1,!(!this.depthTarget||this.resolvedRenders<0||this.camera.camera.renderTarget)&&(this.material.setParameter("surfaceDepth",this.depthTarget.colorBuffer),this.quad.visible=!0)}destroy(){this.enabled=!1,this.app.scene.defaultDrawLayer.removeMeshInstances([this.quad],!0),this.quad.destroy(),this.material.destroy(),this.resolveShader.destroy(),this.depthTarget?.colorBuffer.destroy(),this.depthTarget?.destroy(),this.depthTarget=null}},od="sse-debug-panel-style",ae="sse-debug-panel",id=`
|
|
873
|
+
.${ae} {
|
|
874
|
+
position: absolute;
|
|
875
|
+
top: max(8px, env(safe-area-inset-top));
|
|
876
|
+
left: max(8px, env(safe-area-inset-left));
|
|
877
|
+
padding: 8px 10px;
|
|
878
|
+
background: rgba(0, 0, 0, 0.7);
|
|
879
|
+
color: #eee;
|
|
880
|
+
font: 11px/1.4 ui-monospace, Menlo, Consolas, monospace;
|
|
881
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
882
|
+
border-radius: 4px;
|
|
883
|
+
pointer-events: auto;
|
|
884
|
+
user-select: none;
|
|
885
|
+
min-width: 220px;
|
|
886
|
+
}
|
|
887
|
+
.${ae} .sse-debug-row {
|
|
888
|
+
display: flex;
|
|
889
|
+
justify-content: space-between;
|
|
890
|
+
gap: 12px;
|
|
891
|
+
white-space: nowrap;
|
|
892
|
+
}
|
|
893
|
+
.${ae} .sse-debug-row .sse-debug-label {
|
|
894
|
+
color: #888;
|
|
895
|
+
}
|
|
896
|
+
.${ae} .sse-debug-row .sse-debug-value {
|
|
897
|
+
color: #eee;
|
|
898
|
+
font-variant-numeric: tabular-nums;
|
|
899
|
+
cursor: text;
|
|
900
|
+
padding: 0 4px;
|
|
901
|
+
margin: 0 -4px;
|
|
902
|
+
border-radius: 2px;
|
|
903
|
+
transition: background-color 0.15s ease;
|
|
904
|
+
outline: none;
|
|
905
|
+
}
|
|
906
|
+
.${ae} .sse-debug-row .sse-debug-value:hover {
|
|
907
|
+
background: rgba(255, 255, 255, 0.08);
|
|
908
|
+
}
|
|
909
|
+
.${ae} .sse-debug-row .sse-debug-value:focus {
|
|
910
|
+
background: rgba(255, 255, 255, 0.12);
|
|
911
|
+
box-shadow: inset 0 0 0 1px rgba(120, 180, 255, 0.45);
|
|
912
|
+
}
|
|
913
|
+
.${ae} .sse-debug-row .sse-debug-value.sse-flash-ok {
|
|
914
|
+
background: rgba(120, 220, 140, 0.35);
|
|
915
|
+
}
|
|
916
|
+
.${ae} .sse-debug-row .sse-debug-value.sse-flash-bad {
|
|
917
|
+
background: rgba(220, 100, 100, 0.45);
|
|
918
|
+
}
|
|
919
|
+
.${ae} .sse-debug-buttons {
|
|
920
|
+
display: flex;
|
|
921
|
+
gap: 6px;
|
|
922
|
+
margin-top: 6px;
|
|
923
|
+
}
|
|
924
|
+
.${ae} button {
|
|
925
|
+
flex: 1;
|
|
926
|
+
background: rgba(255, 255, 255, 0.08);
|
|
927
|
+
color: #eee;
|
|
928
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
929
|
+
border-radius: 3px;
|
|
930
|
+
padding: 4px 8px;
|
|
931
|
+
font: inherit;
|
|
932
|
+
cursor: pointer;
|
|
933
|
+
transition: background-color 0.15s ease;
|
|
934
|
+
}
|
|
935
|
+
.${ae} button:hover {
|
|
936
|
+
background: rgba(255, 255, 255, 0.16);
|
|
937
|
+
}
|
|
938
|
+
.${ae} button.sse-flash {
|
|
939
|
+
background: rgba(120, 220, 140, 0.35);
|
|
940
|
+
}
|
|
941
|
+
.${ae} button.sse-flash-bad {
|
|
942
|
+
background: rgba(220, 100, 100, 0.45);
|
|
943
|
+
}
|
|
944
|
+
.${ae} input.sse-debug-paste {
|
|
945
|
+
width: 100%;
|
|
946
|
+
box-sizing: border-box;
|
|
947
|
+
margin-top: 6px;
|
|
948
|
+
background: rgba(255, 255, 255, 0.08);
|
|
949
|
+
color: #eee;
|
|
950
|
+
border: 1px solid rgba(120, 180, 255, 0.6);
|
|
951
|
+
border-radius: 3px;
|
|
952
|
+
padding: 4px 8px;
|
|
953
|
+
font: inherit;
|
|
954
|
+
}
|
|
955
|
+
.${ae} button.sse-debug-on {
|
|
956
|
+
background: rgba(120, 180, 255, 0.35);
|
|
957
|
+
}
|
|
958
|
+
`,lr=i=>`${i.x.toFixed(3)}, ${i.y.toFixed(3)}, ${i.z.toFixed(3)}`,ad=i=>{let e=i.trim().split(/[\s,]+/).filter(s=>s.length>0);if(e.length!==3)return null;let t=e.map(Number);return t.some(s=>!Number.isFinite(s))?null:[t[0],t[1],t[2]]},Eo=class{_global;_cameraManager;_picker;_splatRenderer;_cullButton=null;_taaButton=null;_poplessButton=null;_culledValue=null;_cullStatsFrames=0;_cullStatsPending=!1;_pickDepthOverlay=null;_focusTmp=new x;_root=null;_style=null;_positionValue=null;_focusValue=null;_copyButton=null;_pasteButton=null;_pasteField=null;_miniStatsButton=null;_miniStats;_screenshotButton=null;_pickDepthButton=null;_editing=null;_editCanceled=!1;_visible=!1;_onPrerender=()=>this._render();_onFrameEnd=()=>this._renderCull();_onKeyDown=e=>{this._global.state.inputEnabled&&e.code==="KeyD"&&e.shiftKey&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),this.toggle())};constructor(e,t,s,n=null,o=null){this._global=e,this._cameraManager=t,this._picker=s,this._splatRenderer=n,this._miniStats=o,window.addEventListener("keydown",this._onKeyDown),e.config.debug&&this.show()}show(){this._visible||(this._visible=!0,this._root||this._build(),this._root.style.display="",this._global.app.on("prerender",this._onPrerender),this._global.app.on("frameend",this._onFrameEnd),this._global.config.exposeGlobals&&(window.getCameraState=()=>or(this._cameraManager,this._global.state),window.setCameraState=e=>ir(this._cameraManager,this._global.state,e)),this._render())}hide(){this._visible&&(this._visible=!1,this._root&&(this._root.style.display="none"),this._setPickDepth(!1),this._global.app.off("prerender",this._onPrerender),this._global.app.off("frameend",this._onFrameEnd),this._global.config.exposeGlobals&&(delete window.getCameraState,delete window.setCameraState))}toggle(){this._visible?this.hide():this.show()}destroy(){this.hide(),this._pickDepthOverlay?.destroy(),this._pickDepthOverlay=null,window.removeEventListener("keydown",this._onKeyDown),this._root&&(this._root.remove(),this._root=null),this._style?.remove(),this._style=null}_build(){let e=this._global.root;if(!this._style){let s=document.createElement("style");s.id=od,s.textContent=id,e.appendChild(s),this._style=s}let t=document.createElement("div");t.className=ae,t.innerHTML=`
|
|
959
|
+
<div class="sse-debug-row"><span class="sse-debug-label">camera</span><span class="sse-debug-value" data-id="position" contenteditable="plaintext-only" spellcheck="false" title="Edit to set camera position">\u2014</span></div>
|
|
960
|
+
<div class="sse-debug-row"><span class="sse-debug-label">focus</span><span class="sse-debug-value" data-id="focus" contenteditable="plaintext-only" spellcheck="false" title="Edit to look at this point">\u2014</span></div>
|
|
961
|
+
<div class="sse-debug-buttons">
|
|
962
|
+
<button data-id="copy">Copy</button>
|
|
963
|
+
<button data-id="paste">Paste</button>
|
|
964
|
+
</div>
|
|
965
|
+
<div class="sse-debug-buttons">
|
|
966
|
+
<button data-id="screenshot">Screenshot</button>
|
|
967
|
+
<button data-id="pick-depth" title="Show the depth a navigation pick finds at every pixel">Show depth</button>
|
|
968
|
+
<button data-id="ministats" title="Show the engine's MiniStats graphs (the viewer then renders every frame)">MiniStats</button>
|
|
969
|
+
</div>
|
|
970
|
+
<input class="sse-debug-paste" data-id="paste-field" placeholder="Clipboard blocked here: paste with Ctrl/Cmd+V" spellcheck="false" style="display: none">
|
|
971
|
+
${this._splatRenderer?`
|
|
972
|
+
<div class="sse-debug-buttons">
|
|
973
|
+
<button data-id="depth-cull" title="Stochastic renderer: cull splats hidden in the previous frame's depth (auto suspends the test when it removes too little)">Depth cull</button>
|
|
974
|
+
<button data-id="taa" title="Stochastic renderer: accumulate the samples over frames (reprojected while the camera moves)">TAA</button>
|
|
975
|
+
<button data-id="popless" title="Stochastic renderer: give each fragment the depth of the Gaussian's peak along its ray, rather than the splat centre's">Popless</button>
|
|
976
|
+
</div>
|
|
977
|
+
<div class="sse-debug-row"><span class="sse-debug-label">culled</span><span class="sse-debug-value" data-id="culled">\u2014</span></div>`:""}
|
|
978
|
+
`,e.appendChild(t),this._root=t,this._positionValue=t.querySelector('[data-id="position"]'),this._focusValue=t.querySelector('[data-id="focus"]'),this._copyButton=t.querySelector('[data-id="copy"]'),this._pasteButton=t.querySelector('[data-id="paste"]'),this._screenshotButton=t.querySelector('[data-id="screenshot"]'),this._pickDepthButton=t.querySelector('[data-id="pick-depth"]'),this._miniStatsButton=t.querySelector('[data-id="ministats"]'),this._pasteField=t.querySelector('[data-id="paste-field"]'),this._wirePasteField(this._pasteField),this._miniStats?(this._miniStatsButton.addEventListener("click",()=>{this._miniStats.show(!this._miniStats.shown()),this._renderMiniStats()}),this._renderMiniStats()):this._miniStatsButton.remove(),this._copyButton.addEventListener("click",()=>this._copy()),this._pasteButton.addEventListener("click",()=>this._paste()),this._screenshotButton.addEventListener("click",()=>this._screenshot()),this._pickDepthButton.addEventListener("click",()=>{this._setPickDepth(!this._pickDepthOverlay?.enabled)}),this._splatRenderer&&(this._cullButton=t.querySelector('[data-id="depth-cull"]'),this._taaButton=t.querySelector('[data-id="taa"]'),this._poplessButton=t.querySelector('[data-id="popless"]'),this._poplessButton.addEventListener("click",()=>{let s=this._splatRenderer;s.variant.popless=s.variant.popless==="on"?"off":"on",s.applyVariant(),this._renderCull()}),this._culledValue=t.querySelector('[data-id="culled"]'),this._taaButton.addEventListener("click",()=>{let s=this._splatRenderer;s.variant.taa=s.variant.taa==="on"?"off":"on",s.applyVariant(),this._renderCull()}),this._cullButton.addEventListener("click",()=>{let s=this._splatRenderer,n={auto:"l2",l2:"off",l1:"off",off:"auto"};s.variant.cull=n[s.variant.cull],s.applyVariant(),this._renderCull()}),this._renderCull()),this._wireEditable(this._positionValue,"position"),this._wireEditable(this._focusValue,"focus")}_wireEditable(e,t){e.addEventListener("focus",()=>{this._editing=e,this._editCanceled=!1;let n=document.createRange();n.selectNodeContents(e);let o=window.getSelection();o?.removeAllRanges(),o?.addRange(n)});let s=n=>n.stopPropagation();e.addEventListener("keydown",n=>{s(n),n.key==="Enter"?(n.preventDefault(),e.blur()):n.key==="Escape"&&(n.preventDefault(),this._editCanceled=!0,e.blur())}),e.addEventListener("keyup",s),e.addEventListener("keypress",s),e.addEventListener("blur",()=>{let n=this._editCanceled;if(this._editing=null,this._editCanceled=!1,window.getSelection()?.removeAllRanges(),n){this._render();return}let o=ad(e.textContent??"");if(!o){this._flashBad(e),this._render();return}t==="position"?this._applyPosition(o):this._applyFocus(o),this._flashOk(e)})}_setPickDepth(e){e&&!this._pickDepthOverlay&&(this._pickDepthOverlay=new Lo(this._global.app,this._global.camera,this._picker)),this._pickDepthOverlay&&(this._pickDepthOverlay.enabled=e),this._pickDepthButton?.classList.toggle("sse-debug-on",e)}_applyPosition(e){this._cameraManager.camera.position.set(e[0],e[1],e[2]),this._cameraManager.snap()}_applyFocus(e){let t=this._cameraManager.camera,s=this._focusTmp.copy(t.position),n=new x(e[0],e[1],e[2]);t.look(s,n),this._cameraManager.snap()}_render(){if(!this._visible||!this._positionValue||!this._focusValue)return;let e=this._cameraManager.camera;e.calcFocusPoint(this._focusTmp),this._editing!==this._positionValue&&(this._positionValue.textContent=lr(e.position)),this._editing!==this._focusValue&&(this._focusValue.textContent=lr(this._focusTmp))}_renderCull(){let e=this._splatRenderer;if(!e||!this._cullButton||!this._culledValue)return;let t=e.variant.cull;if(this._cullButton.textContent=`Depth cull: ${t==="off"?"off":t==="auto"?"auto":"on"}`,this._cullButton.classList.toggle("sse-debug-on",t!=="off"),this._taaButton&&(this._taaButton.textContent=`TAA: ${e.variant.taa}`,this._taaButton.classList.toggle("sse-debug-on",e.variant.taa==="on")),this._poplessButton&&(this._poplessButton.textContent=`Popless: ${e.variant.popless}`,this._poplessButton.classList.toggle("sse-debug-on",e.variant.popless==="on")),t==="off"){this._culledValue.textContent="off";return}if(!e.culling){this._culledValue.textContent=e.cullSuspended?"suspended":"no previous frame";return}this._cullStatsPending||this._cullStatsFrames++%15!==0||(this._cullStatsPending=!0,e.readStats().then(({survivors:s,occluded:n})=>{let o=s+n,r=o>0?(100*n/o).toFixed(0):"0";this._culledValue&&e.culling&&(this._culledValue.textContent=`${r}% (${n.toLocaleString()} of ${o.toLocaleString()})`)}).catch(()=>{}).finally(()=>{this._cullStatsPending=!1}))}async _copy(){let e=JSON.stringify(or(this._cameraManager,this._global.state)),t=!1;try{await navigator.clipboard.writeText(e),t=!0}catch{if(!this._root)return;let s=document.createElement("textarea");s.value=e,s.style.cssText="position: fixed; opacity: 0; pointer-events: none",this._root.appendChild(s),s.select();try{t=document.execCommand("copy")}catch{t=!1}s.remove()}t?this._flash(this._copyButton):(console.warn("[debug-panel] copy failed"),this._flashBad(this._copyButton))}async _paste(){let e;try{e=await navigator.clipboard.readText()}catch{this._showPasteField(!0);return}this._root&&this._applyPasted(e)}_applyPasted(e){try{let t=JSON.parse(e);ir(this._cameraManager,this._global.state,t),this._flash(this._pasteButton)}catch(t){console.warn("[debug-panel] paste failed",t),this._flashBad(this._pasteButton)}}_showPasteField(e){let t=this._pasteField;t&&(t.value="",t.style.display=e?"":"none",e&&t.focus())}_wirePasteField(e){let t=s=>s.stopPropagation();e.addEventListener("keydown",s=>{if(t(s),s.key==="Enter"){s.preventDefault();let n=e.value;this._showPasteField(!1),this._applyPasted(n)}else s.key==="Escape"&&(s.preventDefault(),this._showPasteField(!1))}),e.addEventListener("keyup",t),e.addEventListener("keypress",t),e.addEventListener("paste",s=>{s.preventDefault();let n=s.clipboardData?.getData("text/plain")??"";this._showPasteField(!1),this._applyPasted(n)}),e.addEventListener("blur",()=>this._showPasteField(!1))}_renderMiniStats(){if(!this._miniStats||!this._miniStatsButton)return;let e=this._miniStats.shown();this._miniStatsButton.textContent=`MiniStats: ${e?"on":"off"}`,this._miniStatsButton.classList.toggle("sse-debug-on",e)}_screenshot(){let e=this._global.app,t=e.graphicsDevice.canvas;e.renderNextFrame=!0,e.once("frameend",()=>{t.toBlob(s=>{if(!s){console.warn("[debug-panel] screenshot toBlob returned null");return}let n=s.type==="image/webp"?"webp":"png",o=URL.createObjectURL(s),r=document.createElement("a");r.href=o,r.download=`supersplat-viewer.${n}`,r.click(),URL.revokeObjectURL(o),this._flash(this._screenshotButton)},"image/webp",1)})}_flash(e){e&&(e.classList.add("sse-flash"),setTimeout(()=>e.classList.remove("sse-flash"),250))}_flashOk(e){e.classList.add("sse-flash-ok"),setTimeout(()=>e.classList.remove("sse-flash-ok"),250)}_flashBad(e){e.classList.add("sse-flash-bad"),setTimeout(()=>e.classList.remove("sse-flash-bad"),400)}},rd=({root:i,state:e,events:t})=>{let s=!!(i.requestFullscreen&&document.exitFullscreen),n=!1,o=()=>{n||(e.isFullscreen=document.fullscreenElement===i)};s&&(document.addEventListener("fullscreenchange",o),o());let r=async()=>{s?(document.fullscreenElement===i&&await document.exitFullscreen(),o()):e.isFullscreen&&(window.parent.postMessage("exitFullscreen","*"),e.isFullscreen=!1)},a=async()=>{if(s)await i.requestFullscreen(),n?await r():o();else if(window.parent!==window)window.parent.postMessage("requestFullscreen","*"),e.isFullscreen=!0;else throw new Error("requestFullscreen: fullscreen is not supported")},l=t.on("inputEvent",c=>{c==="cancel"&&r().catch(()=>{})});return{request:a,exit:r,destroy:()=>{n=!0,document.removeEventListener("fullscreenchange",o),l.off(),r().catch(()=>{}),e.isFullscreen=!1}}},Ro=class{_global=null;_onPointer=e=>{this._global&&(this._global.state.inputMode=e.pointerType==="touch"?"touch":"desktop")};attach(e){this._global=e,e.root.addEventListener("pointerdown",this._onPointer),e.root.addEventListener("pointermove",this._onPointer)}detach(){this._global?.root.removeEventListener("pointerdown",this._onPointer),this._global?.root.removeEventListener("pointermove",this._onPointer),this._global=null}},cr=i=>i==="walk"||i==="fly",ld=i=>i.code==="KeyW"||i.code==="KeyA"||i.code==="KeyS"||i.code==="KeyD",cd=i=>{if(/^[\x20-\x7e]$/.test(i.key))return i.key;let e=/^(?:Key([A-Z])|Digit(\d))$/.exec(i.code);return e?e[1]?.toLowerCase()??e[2]:i.key},Ao=class{_global=null;_pointerLock=null;_onKeyDown=e=>{let t=this._global;if(!t)return;let{state:s,events:n}=t;if(!s.inputEnabled)return;if(e.key==="Escape"){this._pointerLock?.recentlyExitedCapture||(cr(s.cameraMode)&&s.gamingControls&&s.inputMode==="desktop"?s.gamingControls=!1:s.cameraMode==="walk"?n.fire("inputEvent","exitWalk",e):n.fire("inputEvent","cancel",e));return}if(e.ctrlKey||e.altKey||e.metaKey)return;let o=cd(e);switch(o){case"1":s.cameraMode="orbit";break;case"2":s.cameraMode="fly";break;case"3":n.fire("inputEvent","toggleWalk");break;case"v":s.hasCollisionOverlay&&(s.collisionOverlayEnabled=!s.collisionOverlayEnabled);break;case"g":s.gamingControls=!s.gamingControls;break;case"h":case"H":n.fire("inputEvent",e.shiftKey?"toggleHelp":"toggleControls");break;case"r":n.fire("inputEvent","reset",e);break;default:ld(e)&&s.inputMode==="desktop"&&(cr(s.cameraMode)||n.fire("inputEvent","requestFirstPerson"),s.gamingControls||(s.gamingControls=!0));break}if(s.cameraMode!=="walk")switch(o){case"f":n.fire("inputEvent","frame",e);break;case" ":n.fire("inputEvent","playPause",e);break}};attach(e,t){this._global=e,this._pointerLock=t,window.addEventListener("keydown",this._onKeyDown)}detach(){window.removeEventListener("keydown",this._onKeyDown),this._global=null,this._pointerLock=null}},pt=new x,en=i=>i.state.cameraMode==="fly"&&!(i.state.inputMode==="desktop"&&i.state.gamingControls),hr=(i,e)=>{if(!i)return 1;if(e==="walk"){if(i.shiftKey)return 2;if(i.ctrlKey)return .5}else if(e==="fly"){if(i.shiftKey)return 4;if(i.ctrlKey)return .25}return 1},To=class{collision=null;_picker;_global=null;_canvas=null;_lastPointerOffsetX=0;_lastPointerOffsetY=0;_mouseClickTracking=!1;_mouseClickDelta=0;_suppressClick=!1;_targetPickRequest=0;_lastTap={time:0,x:0,y:0};constructor(e){this._picker=e}_updateCursor=()=>{let e=this._global,t=this._canvas;if(!e||!t)return;let{state:s}=e;s.inputMode==="desktop"&&(s.cameraMode==="walk"&&!s.gamingControls||en(e)||s.cameraMode==="orbit")?t.style.cursor=this._mouseClickTracking?"default":"pointer":t.style.cursor=""};_onCameraModeChanged=()=>{this._targetPickRequest++,this._updateCursor()};_pickCollision(e,t){if(!this.collision||!this._global)return null;let s=this._global.camera,n=s.getPosition();s.camera.screenToWorld(e,t,1,pt),pt.sub(n).normalize();let o=this.collision.queryRay(n.x,n.y,n.z,pt.x,pt.y,pt.z,s.camera.farClip);if(!o)return null;let r=this.collision.querySurfaceNormal(o.x,o.y,o.z,pt.x,pt.y,pt.z);return{position:new x(o.x,o.y,o.z),normal:new x(r.nx,r.ny,r.nz)}}async _pickSceneTarget(e,t){let s=this._global,n=this._canvas;if(!s||!n)return null;let o=this._pickCollision(e,t);if(o)return o;let r=await this._picker.pickSurface(e/n.clientWidth,t/n.clientHeight);return r||null}async _flyToPickedPosition(e,t,s){let n=this._global;if(!n||!en(n))return;let o=++this._targetPickRequest,r=await this._pickSceneTarget(e,t);if(r&&o===this._targetPickRequest&&this._global&&en(this._global)){let a=hr(s,this._global.state.cameraMode);this._global.events.fire("navigateTo",r.position,r.normal,a)}}async _focusPickedPosition(e,t){let s=this._global;if(!s||s.state.cameraMode!=="orbit")return;let n=++this._targetPickRequest,o=await this._pickSceneTarget(e,t);if(o&&n===this._targetPickRequest&&this._global?.state.cameraMode==="orbit"){let{events:r}=this._global;r.fire("orbitTarget:set",o.position,o.normal),r.fire("pick",o.position)}}_onPointerDown=e=>{let t=this._global;if(!t)return;let{events:s}=t;this._lastPointerOffsetX=e.offsetX,this._lastPointerOffsetY=e.offsetY,e.pointerType!=="touch"&&e.button===0&&(this._mouseClickTracking=!0,this._mouseClickDelta=0,this._updateCursor());let n=Date.now();Math.max(0,n-this._lastTap.time)<300&&Math.abs(e.clientX-this._lastTap.x)<8&&Math.abs(e.clientY-this._lastTap.y)<8?(this._suppressClick=!0,s.fire("inputEvent","dblclick",e),this._lastTap.time=0):(this._lastTap.time=n,this._lastTap.x=e.clientX,this._lastTap.y=e.clientY)};_onPointerMove=e=>{let t=this._global;if(!t)return;let{state:s,events:n}=t;if(this._mouseClickTracking&&e.pointerType!=="touch"){let o=this._mouseClickDelta;this._mouseClickDelta+=Math.abs(e.movementX)+Math.abs(e.movementY),o<At&&this._mouseClickDelta>=At&&(s.cameraMode==="walk"&&!s.gamingControls||en(t))&&n.fire("navigateCancel")}};_onPointerUp=e=>{let t=this._global;if(!t)return;let{state:s,events:n}=t;if(this._mouseClickTracking&&e.pointerType!=="touch"&&e.button===0){if(this._mouseClickTracking=!1,this._updateCursor(),this._suppressClick){this._suppressClick=!1;return}if(this._mouseClickDelta<At)if(s.cameraMode==="walk"&&!s.gamingControls){let o=this._pickCollision(this._lastPointerOffsetX,this._lastPointerOffsetY);if(o){let r=hr(e,s.cameraMode);n.fire("navigateTo",o.position,o.normal,r)}}else s.cameraMode==="fly"?this._flyToPickedPosition(this._lastPointerOffsetX,this._lastPointerOffsetY,e):s.cameraMode==="orbit"&&this._focusPickedPosition(this._lastPointerOffsetX,this._lastPointerOffsetY)}};_onInputEvent=async(e,t)=>{let s=this._global,n=this._canvas;if(!s||!n||e!=="dblclick"||!(t instanceof MouseEvent))return;let{events:o,state:r}=s;if((r.cameraMode==="walk"||r.cameraMode==="fly")&&r.gamingControls)return;let a=++this._targetPickRequest,l=await this._pickSceneTarget(t.offsetX,t.offsetY);if(!l||a!==this._targetPickRequest)return;let c=this._global?.state;c&&(c.cameraMode==="walk"?c.gamingControls||o.fire("navigateTo",l.position,l.normal,2):(o.fire("pick",l.position),o.fire("orbitTarget:set",l.position,l.normal)))};_onMobileTap=()=>{let e=this._global;if(!e)return;let{state:t,events:s}=e;if(this._suppressClick){this._suppressClick=!1;return}if(t.cameraMode==="walk"&&!t.gamingControls){let n=this._pickCollision(this._lastPointerOffsetX,this._lastPointerOffsetY);n&&s.fire("navigateTo",n.position,n.normal)}else t.cameraMode==="fly"?this._flyToPickedPosition(this._lastPointerOffsetX,this._lastPointerOffsetY):t.cameraMode==="orbit"&&this._focusPickedPosition(this._lastPointerOffsetX,this._lastPointerOffsetY)};attach(e,t){this._canvas=e,this._global=t;let{events:s}=t;e.addEventListener("pointerdown",this._onPointerDown),e.addEventListener("pointermove",this._onPointerMove),e.addEventListener("pointerup",this._onPointerUp),s.on("inputEvent",this._onInputEvent),s.on("mobileTap",this._onMobileTap),s.on("cameraMode:changed",this._onCameraModeChanged),s.on("inputMode:changed",this._updateCursor),s.on("gamingControls:changed",this._updateCursor)}detach(){if(this._canvas&&(this._canvas.removeEventListener("pointerdown",this._onPointerDown),this._canvas.removeEventListener("pointermove",this._onPointerMove),this._canvas.removeEventListener("pointerup",this._onPointerUp)),this._global){let{events:e}=this._global;e.off("inputEvent",this._onInputEvent),e.off("mobileTap",this._onMobileTap),e.off("cameraMode:changed",this._onCameraModeChanged),e.off("inputMode:changed",this._updateCursor),e.off("gamingControls:changed",this._updateCursor)}this._canvas=null,this._global=null}},Pt=i=>i==="walk"||i==="fly",hd=()=>navigator.userActivation?.isActive===!0,Do=class{_global=null;_canvas=null;_keyboardMouse=null;_recentlyExitedCapture=!1;_onPointerLockChange=()=>{let e=this._global;if(!e)return;let{state:t,events:s}=e;!document.pointerLockElement&&Pt(t.cameraMode)&&t.gamingControls&&(this._recentlyExitedCapture=!0,requestAnimationFrame(()=>{this._recentlyExitedCapture=!1}),t.inputMode==="desktop"?t.gamingControls=!1:t.cameraMode==="walk"&&s.fire("inputEvent","exitWalk"))};_onPointerLockError=()=>{this._keyboardMouse&&(this._keyboardMouse.source._pointerLock=!1);let e=this._global;if(!e)return;let{state:t,events:s}=e;t.inputMode==="desktop"&&Pt(t.cameraMode)?t.gamingControls=!1:t.cameraMode==="walk"&&s.fire("inputEvent","exitWalk")};_onCameraModeChanged=(e,t)=>{let s=this._global?.state;s&&(Pt(e)&&s.inputMode==="desktop"&&s.gamingControls?this._activate():Pt(t)&&this._deactivate())};_onGamingControlsChanged=e=>{let t=this._global?.state;t&&Pt(t.cameraMode)&&t.inputMode==="desktop"&&(e?this._activate():this._deactivate())};_onInputModeChanged=e=>{let t=this._global?.state;!t||!Pt(t.cameraMode)||!t.gamingControls||(e==="desktop"&&hd()?this._activate():e!=="desktop"&&this._deactivate())};_onPointerDown=()=>{let e=this._global?.state;e&&e.inputMode==="desktop"&&Pt(e.cameraMode)&&e.gamingControls&&this._activate()};_activate(){this._keyboardMouse&&(this._keyboardMouse.source._pointerLock=!0),document.pointerLockElement!==this._canvas&&this._canvas?.requestPointerLock()}_deactivate(){this._keyboardMouse&&(this._keyboardMouse.source._pointerLock=!1),document.pointerLockElement===this._canvas&&document.exitPointerLock()}get recentlyExitedCapture(){return this._recentlyExitedCapture}attach(e,t,s){this._canvas=e,this._global=t,this._keyboardMouse=s;let{events:n}=t;n.on("cameraMode:changed",this._onCameraModeChanged),n.on("gamingControls:changed",this._onGamingControlsChanged),n.on("inputMode:changed",this._onInputModeChanged),e.addEventListener("pointerdown",this._onPointerDown),document.addEventListener("pointerlockchange",this._onPointerLockChange),document.addEventListener("pointerlockerror",this._onPointerLockError)}detach(){if(this._global){let{events:e}=this._global;e.off("cameraMode:changed",this._onCameraModeChanged),e.off("gamingControls:changed",this._onGamingControlsChanged),e.off("inputMode:changed",this._onInputModeChanged)}this._canvas?.removeEventListener("pointerdown",this._onPointerDown),document.removeEventListener("pointerlockchange",this._onPointerLockChange),document.removeEventListener("pointerlockerror",this._onPointerLockError),this._canvas=null,this._global=null,this._keyboardMouse=null}},dd=new x,dr=new x,ur=new x,zo=class{moveSpeed=4;orbitSpeed=18;gamepadRotateSensitivity=1;_source=new va;_global=null;attach(e,t){this._global=t}detach(){this._global=null}update(e,t){if(!this._global?.state.inputEnabled)return;let{dt:s,cameraComponent:n,isFly:o,isFirstPerson:r}=e,{leftStick:a,rightStick:l}=this._source.read(),c=r?n.fov/120:1,{deltas:h}=t;o&&(a[0]!==0||a[1]!==0||l[0]!==0||l[1]!==0)&&this._global?.events.fire("navigateCancel");let d=dd.set(0,0,0);dr.set(a[0],0,-a[1]),d.add(dr.mulScalar(this.moveSpeed*s)),h.move.append([d.x,d.y,d.z]),d.set(0,0,0),ur.set(l[0],l[1],0),d.add(ur.mulScalar(this.orbitSpeed*c*this.gamepadRotateSensitivity*s)),h.rotate.append([d.x,d.y,d.z])}},pr=new x,ud=new x,Lt=new x,tn=new x,fr=new x,mr=new x,gr=new x,pd=(i,e)=>{let t=i._onKeyDown;i._onKeyDown=n=>{e()&&(n.key==="Meta"?i._keyNow.fill(0):n.metaKey||t(n))};let s=i._onKeyUp;i._onKeyUp=n=>{n.key==="Meta"?i._keyNow.fill(0):n.metaKey||s(n)}},Fo=class{moveSpeed=4;orbitSpeed=18;wheelSpeed=.06;mouseRotateSensitivity=.5;flyMoveAccelerationDamping=.992;flyMoveDecelerationDamping=.993;_source=new Hn;_global=null;_axis=new x;_buttons=[0,0,0];_shift=0;_ctrl=0;_jump=0;_flyKeyVelocity=new x;_onInputEnabled=e=>{e||this._source._keyNow.fill(0)};get source(){return this._source}attach(e,t){this._global=t,pd(this._source,()=>t.state.inputEnabled),this._source.attach(e),t.events.on("inputEnabled:changed",this._onInputEnabled)}detach(){this._global?.events.off("inputEnabled:changed",this._onInputEnabled),this._source.detach(),this._global=null}update(e,t){let{keyCode:s}=Hn,{key:n,button:o,mouse:r,wheel:a}=this._source.read(),{events:l}=this._global;this._axis.add(pr.set(n[s.D]-n[s.A]+(n[s.RIGHT]-n[s.LEFT]),n[s.E]-n[s.Q],n[s.W]-n[s.S]+(n[s.UP]-n[s.DOWN]))),this._jump+=n[s.SPACE],this._shift+=n[s.SHIFT],this._ctrl+=n[s.CTRL];let c=Math.min(o.length,this._buttons.length);for(let R=0;R<c;R++)this._buttons[R]+=o[R];let{isFly:h,isWalk:d,isFirstPerson:u,gamingControls:p,dt:g,distance:m,cameraComponent:f,mode:v,touchCount:y}=e,k=this._buttons[2]||+(o[2]===-1)||+(y>1);d&&(this._axis.x!==0||this._axis.z!==0)&&l.fire("navigateCancel"),h&&(this._axis.x!==0||this._axis.y!==0||this._axis.z!==0)&&l.fire("navigateCancel"),h&&a[0]!==0&&l.fire("navigateCancel"),h&&(p||k)&&(r[0]!==0||r[1]!==0)&&l.fire("navigateCancel"),!u&&this._axis.length()>0&&l.fire("inputEvent","requestFirstPerson");let w=u?f.fov/120:1,{deltas:P}=t,S=pr.set(0,0,0);Lt.copy(this._axis),d&&(Lt.y=0),Lt.normalize();let b=d?2:4,z=d?.5:.25,_=this.moveSpeed*(this._shift?b:this._ctrl?z:1);if(Lt.mulScalar(_),h){tn.copy(Lt);let R=tn.lengthSq()>this._flyKeyVelocity.lengthSq()?this.flyMoveAccelerationDamping:this.flyMoveDecelerationDamping;this._flyKeyVelocity.lerp(this._flyKeyVelocity,tn,Ko(R,g)),tn.lengthSq()===0&&this._flyKeyVelocity.lengthSq()<1e-4&&this._flyKeyVelocity.set(0,0,0),Lt.copy(this._flyKeyVelocity)}else this._flyKeyVelocity.set(0,0,0);S.add(ud.copy(Lt).mulScalar((u?1:0)*g)),d&&(S.y=this._jump>0?1:0),pn(f,r[0],r[1],m,fr),S.add(fr.mulScalar(k)),gr.set(0,0,-a[0]),S.add(gr.mulScalar(this.wheelSpeed*st)),P.move.append([S.x,S.y,xc(v,S.z)]),S.set(0,0,0),mr.set(r[0],r[1],0),S.add(mr.mulScalar((1-k)*this.orbitSpeed*w*this.mouseRotateSensitivity*st)),P.rotate.append([S.x,S.y,S.z])}},fd=new x,Zn=new x,vr=new x,yr=new x,xr=new x,Io=class{orbitSpeed=18;pinchSpeed=.4;touchRotateSensitivity=1.5;_source=new ga;_global=null;_touchCount=0;_tapTouches=0;_tapMaxTouches=0;_tapDelta=0;_tapJump=!1;get touchCount(){return this._touchCount}attach(e,t){this._global=t,this._source.attach(e)}detach(){this._source.detach(),this._global=null}update(e,t){let{touch:s,pinch:n,count:o}=this._source.read(),{isFly:r,isWalk:a,isFirstPerson:l,isOrbit:c,gamingControls:h,distance:d,cameraComponent:u}=e;if(this._touchCount+=o[0],a||r||c){let P=this._tapTouches;if(this._tapTouches=Math.max(0,this._tapTouches+o[0]),P===0&&this._tapTouches>0&&(this._tapDelta=0),this._tapTouches>0&&(this._tapMaxTouches=Math.max(this._tapMaxTouches,this._tapTouches)),this._tapTouches>0){let S=this._tapDelta;this._tapDelta+=Math.abs(s[0])+Math.abs(s[1])+Math.abs(n[0]),S<At&&this._tapDelta>=At&&(a&&!h||r)&&this._global.events.fire("navigateCancel")}P>0&&this._tapTouches===0&&(this._tapDelta<At&&this._tapMaxTouches===1&&(a&&!h?this._global.events.fire("mobileTap"):a?this._tapJump=!0:r&&!h?this._global.events.fire("mobileTap"):c&&this._global.events.fire("mobileTap")),this._tapMaxTouches=0)}else this._tapTouches=0,this._tapMaxTouches=0;let p=c?1:0,g=l?1:0,m=this._touchCount>1?1:0,f=l?u.fov/120:1,v=l&&!h?-1:1,y=g*(h?0:1),{deltas:k}=t,w=fd.set(0,0,0);pn(u,s[0],s[1],d,Zn),a&&(Zn.y=0),w.add(Zn.mulScalar((p+y)*m)),vr.set(0,0,(p-y)*n[0]),w.add(vr.mulScalar(m*this.pinchSpeed*st)),a&&this._tapJump&&(w.y=1,this._tapJump=!1),k.move.append([w.x,w.y,w.z]),w.set(0,0,0),yr.set(s[0],s[1],0),w.add(yr.mulScalar(p*(1-m)*this.orbitSpeed*this.touchRotateSensitivity*st)),xr.set(s[0]*v,s[1]*v,0),w.add(xr.mulScalar(g*(1-m)*this.orbitSpeed*f*this.touchRotateSensitivity*st)),k.rotate.append([w.x,w.y,w.z])}},Ne=new x,Vo=class{orbitSpeed=18;wheelSpeed=.06;trackpadOrbitSensitivity=.75;trackpadPanSensitivity=1;trackpadZoomSensitivity=2;_global=null;_orbit=[0,0];_pan=[0,0];_zoom=0;_ctrlDown=!1;_onKeyDown=e=>{e.key==="Control"&&this._global?.state.inputEnabled&&(this._ctrlDown=!0)};_onKeyUp=e=>{e.key==="Control"&&(this._ctrlDown=!1)};_onBlur=()=>{this._ctrlDown=!1};_onWheel=e=>{let t=this._global.state.cameraMode,s=e.ctrlKey&&!this._ctrlDown,n=e.ctrlKey&&this._ctrlDown,o=e.shiftKey;if(!s&&!n&&!o)return;e.preventDefault(),e.stopImmediatePropagation(),this._global.events.fire("inputEvent","interrupt",e),t==="fly"&&this._global.events.fire("navigateCancel");let{deltaX:r,deltaY:a}=e;s?this._zoom+=a:n?(this._orbit[0]+=r,this._orbit[1]+=a):(this._pan[0]+=r,this._pan[1]+=a)};_canvas=null;attach(e,t){this._canvas=e,this._global=t,e.addEventListener("wheel",this._onWheel,{passive:!1}),window.addEventListener("keydown",this._onKeyDown),window.addEventListener("keyup",this._onKeyUp),window.addEventListener("blur",this._onBlur)}detach(){this._canvas&&(this._canvas.removeEventListener("wheel",this._onWheel),this._canvas=null),window.removeEventListener("keydown",this._onKeyDown),window.removeEventListener("keyup",this._onKeyUp),window.removeEventListener("blur",this._onBlur),this._global=null,this._ctrlDown=!1,this._orbit[0]=this._orbit[1]=0,this._pan[0]=this._pan[1]=0,this._zoom=0}update(e,t){let{isOrbit:s,isFirstPerson:n,distance:o,cameraComponent:r}=e,a=n?r.fov/120:1,{deltas:l}=t;if(s){let c=Ne.set(this._orbit[0],this._orbit[1],0);c.mulScalar(this.orbitSpeed*this.trackpadOrbitSensitivity*st),l.rotate.append([c.x,c.y,0]),pn(r,this._pan[0],this._pan[1],o,Ne),Ne.mulScalar(this.trackpadPanSensitivity),l.move.append([Ne.x,Ne.y,0]);let h=this._zoom*this.wheelSpeed*this.trackpadZoomSensitivity*st;l.move.append([0,0,h])}else if(n){let c=Ne.set(this._orbit[0],this._orbit[1],0);c.mulScalar(this.orbitSpeed*a*this.trackpadOrbitSensitivity*st),l.rotate.append([c.x,c.y,0]),pn(r,this._pan[0],this._pan[1],o,Ne),Ne.mulScalar(this.trackpadPanSensitivity),l.move.append([Ne.x,Ne.y,0]);let h=-this._zoom*this.wheelSpeed*this.trackpadZoomSensitivity*st;l.move.append([0,0,h])}this._orbit[0]=this._orbit[1]=0,this._pan[0]=this._pan[1]=0,this._zoom=0}},Oo=class{moveSpeed=4;frame=new ma({move:[0,0,0],rotate:[0,0,0]});_global;_trackpad=new Vo;_keyboardMouse=new Fo;_touch=new Io;_gamepad=new zo;_navInteraction;_pointerLock=new Do;_modeShortcuts=new Ao;_inputModeTracker=new Ro;_canvas;_canvasListeners=[];_moveInput=[0,0];_clearMoveInput=()=>this._moveInput.fill(0);setMoveInput(e,t){let{state:s}=this._global;(s.cameraMode==="fly"||s.cameraMode==="walk")&&s.xrMode===null&&(this._moveInput[0]=e,this._moveInput[1]=t)}set collision(e){this._navInteraction.collision=e}get collision(){return this._navInteraction.collision}constructor(e,t){this._global=e,this._navInteraction=new To(t);let{app:s,events:n}=e,o=s.graphicsDevice.canvas;this._canvas=o,this._trackpad.attach(o,e),this._keyboardMouse.attach(o,e),this._touch.attach(o,e),this._gamepad.attach(o,e),this._navInteraction.attach(o,e),this._pointerLock.attach(o,e,this._keyboardMouse),this._modeShortcuts.attach(e,this._pointerLock),this._inputModeTracker.attach(e),n.on("cameraMode:changed",this._clearMoveInput),n.on("xrMode:changed",this._clearMoveInput),window.addEventListener("blur",this._clearMoveInput),document.addEventListener("visibilitychange",this._clearMoveInput);let r=(a,l)=>{o.addEventListener(a,l),this._canvasListeners.push([a,l])};["wheel","pointerdown","contextmenu","keydown"].forEach(a=>{r(a,l=>{n.fire("inputEvent","interrupt",l)})}),r("pointermove",a=>{n.fire("inputEvent","interact",a)})}destroy(){this._clearMoveInput(),this._global.events.off("cameraMode:changed",this._clearMoveInput),this._global.events.off("xrMode:changed",this._clearMoveInput),window.removeEventListener("blur",this._clearMoveInput),document.removeEventListener("visibilitychange",this._clearMoveInput);for(let[e,t]of this._canvasListeners)this._canvas.removeEventListener(e,t);this._canvasListeners.length=0,this._inputModeTracker.detach(),this._modeShortcuts.detach(),this._pointerLock.detach(),this._navInteraction.detach(),this._gamepad.detach(),this._touch.detach(),this._keyboardMouse.detach(),this._trackpad.detach()}update(e,t){let{state:s}=this._global,n=this._global.camera.camera,o=s.cameraMode==="orbit",r=s.cameraMode==="fly",a=s.cameraMode==="walk",l=r||a,c={dt:e,distance:t,cameraComponent:n,mode:s.cameraMode,isOrbit:o,isFly:r,isWalk:a,isFirstPerson:l,gamingControls:s.gamingControls,touchCount:this._touch.touchCount};if(this._touch.update(c,this.frame),c.touchCount=this._touch.touchCount,this._keyboardMouse.update(c,this.frame),this._trackpad.update(c,this.frame),this._gamepad.update(c,this.frame),l&&(this._moveInput[0]!==0||this._moveInput[1]!==0)){this._global.events.fire("navigateCancel");let h=this.moveSpeed*e;this.frame.deltas.move.append([this._moveInput[0]*h,0,this._moveInput[1]*h])}}},md=.85,gd=.55,vd=.3,yd=.3,Jr=(i,e,t)=>{let s=h=>Math.round((e?h:Math.pow(h,2.2))*255),n=s(md),o=s(gd),r=s(vd),a=Math.round(yd*255),l=i.count,c=t??new Uint8Array(l*12);for(let h=0;h<l;h++){let d=Math.abs(i.nx[h]),u=Math.abs(i.ny[h]),p=Math.abs(i.nz[h]),g;d>u&&d>p?g=n:u>p?g=o:g=r;let m=h*12;for(let f=0;f<3;f++){let v=m+f*4;c[v]=g,c[v+1]=g,c[v+2]=g,c[v+3]=a}}return c},xd=(i,e)=>{let t=i.count,s=new Float32Array(t*9),n=Jr(i,e),o=new Uint32Array(t*3);for(let r=0;r<t;r++){let a=r*9;s[a]=i.v0x[r],s[a+1]=i.v0y[r],s[a+2]=i.v0z[r],s[a+3]=i.v1x[r],s[a+4]=i.v1y[r],s[a+5]=i.v1z[r],s[a+6]=i.v2x[r],s[a+7]=i.v2y[r],s[a+8]=i.v2z[r];let l=r*3;o[l]=l,o[l+1]=l+1,o[l+2]=l+2}return{flatPositions:s,flatColors:n,flatIndices:o}},wr=()=>{let i=new qn;return i.useLighting=!1,i.useSkybox=!1,i.useFog=!1,i.useTonemap=!1,i.ambient=new K(0,0,0),i.diffuse=new K(0,0,0),i.specular=new K(0,0,0),i.emissive=new K(1,1,1),i.emissiveVertexColor=!0,i.emissiveVertexColorChannel="rgb",i.opacityVertexColor=!0,i.opacityVertexColorChannel="a",i.opacity=1,i},Bo=class{app;camera;layer;entity;mesh;triangles;flatColors;materials;_enabled=!1;constructor(e,t,s,n){this.app=e,this.camera=s,this.triangles=t.triangles;let o=e.graphicsDevice,{flatPositions:r,flatColors:a,flatIndices:l}=xd(this.triangles,n);this.flatColors=a;let c=new Ye(o);c.setPositions(r),c.setColors32(a),c.setIndices(l),c.update(rt),c.generateWireframe(),this.mesh=c,this.layer=new Ji({name:"CollisionOverlay",enabled:!1,clearColorBuffer:!1,clearDepthBuffer:!0,opaqueSortMode:Nn,transparentSortMode:Nn}),e.scene.layers.push(this.layer),s.camera.layers=[...s.camera.layers,this.layer.id];let h=wr();h.cull=1,h.blendType=wt,h.depthTest=!0,h.depthWrite=!0,h.depthBias=1,h.slopeDepthBias=1,h.redWrite=!1,h.greenWrite=!1,h.blueWrite=!1,h.alphaWrite=!1,h.update();let d=new ke(c,h);d.drawOrder=0;let u=new ce("CollisionDepthPrepass",this.app);u.addComponent("render",{meshInstances:[d],layers:[this.layer.id]});let p=wr();p.cull=1,p.blendType=wt,p.depthTest=!0,p.depthFunc=2,p.depthWrite=!1,p.depthBias=1,p.slopeDepthBias=1,p.update();let g=new ke(c,p);g.drawOrder=1;let m=new ce("CollisionSurface",this.app);m.addComponent("render",{meshInstances:[g],layers:[this.layer.id]});let f=new qn;f.useLighting=!1,f.useSkybox=!1,f.useFog=!1,f.useTonemap=!1,f.ambient=new K(0,0,0),f.diffuse=new K(0,0,0),f.specular=new K(0,0,0),f.emissive=new K(0,0,0),f.opacity=1,f.blendType=wt,f.depthTest=!0,f.depthFunc=3,f.depthWrite=!1,f.cull=0,f.update();let v=new ke(c,f);v.drawOrder=2;let y=new ce("CollisionWireframe",this.app);y.addComponent("render",{meshInstances:[v],layers:[this.layer.id]}),y.render.renderStyle=Oi,this.materials=[h,p,f],this.entity=new ce("MeshCollisionDebug",this.app),this.entity.addChild(u),this.entity.addChild(m),this.entity.addChild(y),this.entity.enabled=!1,e.root.addChild(this.entity)}set enabled(e){this._enabled=e,this.entity.enabled=e,this.layer.enabled=e}get enabled(){return this._enabled}setCameraFrameEnabled(e){Jr(this.triangles,e,this.flatColors),this.mesh.setColors32(this.flatColors),this.mesh.update(rt)}destroy(){this.entity?.destroy();for(let e of this.materials)e.destroy();this.materials.length=0,this.app.scene.layers.remove(this.layer),this.camera.camera.layers=this.camera.camera.layers.filter(e=>e!==this.layer.id)}},No="http://www.w3.org/2000/svg",Wt=12,wd=.2,bd=.17/.2,_d=48,sn=1/6,kd=.25,br=Math.PI/4,el=1e-6,Md=()=>{let i=[];for(let e=-2;e<=2;e++){let t=e*br,s=Math.cos(t),n=Math.sin(t);if(Math.abs(s)<=el){i.push(new x(0,n>0?1:-1,0));continue}for(let o=0;o<8;o++){let r=o*br;i.push(new x(Math.cos(r)*s,n,Math.sin(r)*s))}}return i},Kn=Md(),Sd=(i,e,t,s)=>{let n=Math.sqrt(i*i+e*e+t*t);if(n<=el)return s.set(0,1,0);let o=1/n,r=i*o,a=e*o,l=t*o,c=Kn[0],h=-1/0;for(let d=0;d<Kn.length;d++){let u=Kn[d],p=u.x*r+u.y*a+u.z*l;p>h&&(h=p,c=u)}return s.copy(c)},ve=new x,Qn=new x,Gt=new x,nn=new x,_r=new x,Cd=new x(0,1,0),Pd=new x(1,0,0),Ld=(i,e,t,s)=>{let n=i.camera;if(n.projection===qt)return s*n.orthoHeight/e;let o=i.getPosition(),r=t.x-o.x,a=t.y-o.y,l=t.z-o.z,c=Math.sqrt(r*r+a*a+l*l),h=Math.tan(n.fov*Math.PI/360);return s*c*h/e},kr=(i,e)=>{let t=i.length,s=`M${i[0].toFixed(1)},${e[0].toFixed(1)}`;for(let n=0;n<t;n++){let o=(n-1+t)%t,r=n,a=(n+1)%t,l=(n+2)%t,c=i[r]+(i[a]-i[o])*sn,h=e[r]+(e[a]-e[o])*sn,d=i[a]-(i[l]-i[r])*sn,u=e[a]-(e[l]-e[r])*sn;s+=` C${c.toFixed(1)},${h.toFixed(1)} ${d.toFixed(1)},${u.toFixed(1)} ${i[a].toFixed(1)},${e[a].toFixed(1)}`}return`${s} Z`},xn=class{path;svg;canvas;camera;smoothing;smoothNx=0;smoothNy=1;smoothNz=0;hasSmoothedNormal=!1;outerX=new Float64Array(Wt);outerY=new Float64Array(Wt);innerX=new Float64Array(Wt);innerY=new Float64Array(Wt);constructor(e,t,s,n){this.svg=e,this.canvas=t,this.camera=s,this.smoothing=n,this.path=document.createElementNS(No,"path"),this.path.setAttribute("fill","white"),this.path.setAttribute("fill-opacity","0.6"),this.path.setAttribute("fill-rule","evenodd"),this.path.setAttribute("stroke","none"),this.path.style.display="none",e.appendChild(this.path)}projectCircle(e,t,s,n,o,r,a,l,c){let h=ve.set(n,o,r);Math.abs(h.y)<.99?Gt.cross(h,Cd).normalize():Gt.cross(h,Pd).normalize(),nn.cross(h,Gt);let d=this.camera.camera,u=2*Math.PI/Wt;for(let p=0;p<Wt;p++){let g=p*u,m=Math.cos(g),f=Math.sin(g),v=m*Gt.x+f*nn.x,y=m*Gt.y+f*nn.y,k=m*Gt.z+f*nn.z;_r.set(e+v*a,t+y*a,s+k*a),d.worldToScreen(_r,Qn),l[p]=Qn.x,c[p]=Qn.y}}render(e,t,s){Sd(t.x,t.y,t.z,ve);let n=ve.x,o=ve.y,r=ve.z;if(this.smoothing){if(this.hasSmoothedNormal){let c=kd;n=this.smoothNx+(n-this.smoothNx)*c,o=this.smoothNy+(o-this.smoothNy)*c,r=this.smoothNz+(r-this.smoothNz)*c;let h=Math.sqrt(n*n+o*o+r*r);if(h>1e-6){let d=1/h;n*=d,o*=d,r*=d}}this.smoothNx=n,this.smoothNy=o,this.smoothNz=r,this.hasSmoothedNormal=!0}let a=s!==null?Ld(this.camera,this.canvas.clientHeight||1,e,s):wd,l=a*bd;this.projectCircle(e.x,e.y,e.z,n,o,r,a,this.outerX,this.outerY),this.projectCircle(e.x,e.y,e.z,n,o,r,l,this.innerX,this.innerY),this.path.setAttribute("d",`${kr(this.outerX,this.outerY)} ${kr(this.innerX,this.innerY)}`),this.path.style.display="",this.svg.style.display=""}hide(){this.path.style.display="none",this.hasSmoothedNormal=!1}},qo=class{svg;captureMarker;hoverRing;targetRing;camera;collision;canvas;state;app;onPrerender;hoverActive=!1;navigating=!1;targetPos=null;targetNormal=null;targetMode=null;onPointerMove;onPointerLeave;collisionTarget={position:new x,normal:new x};constructor(e,t,s,n,o,r){this.camera=t,this.collision=s,this.canvas=e.graphicsDevice.canvas,this.state=o,this.app=e,this.svg=document.createElementNS(No,"svg"),this.svg.style.cssText="position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;overflow:visible";let a=this.canvas.parentElement;(a.querySelector(":scope > .sse-sceneLayer")??a).prepend(this.svg),this.captureMarker=document.createElementNS(No,"circle"),this.captureMarker.setAttribute("cx","50%"),this.captureMarker.setAttribute("cy","50%"),this.captureMarker.setAttribute("r","2"),this.captureMarker.setAttribute("fill","white"),this.captureMarker.setAttribute("stroke","black"),this.captureMarker.setAttribute("stroke-opacity","0.5"),this.captureMarker.setAttribute("stroke-width","2"),this.captureMarker.style.display="none",this.svg.appendChild(this.captureMarker),this.hoverRing=new xn(this.svg,this.canvas,t,!0),this.targetRing=new xn(this.svg,this.canvas,t,!1),this.svg.style.display="none",this.onPointerMove=c=>{if(c.pointerType==="touch"||c.buttons){this.hoverRing.hide();return}this.updateCursor(c.offsetX,c.offsetY)},this.onPointerLeave=()=>{this.hoverRing.hide()},this.canvas.addEventListener("pointermove",this.onPointerMove),this.canvas.addEventListener("pointerleave",this.onPointerLeave);let l=()=>{let c=r&&o.inputMode==="desktop"&&o.gamingControls&&(o.cameraMode==="walk"||o.cameraMode==="fly");this.captureMarker.style.display=c?"":"none",c&&(this.svg.style.display=""),this.hoverActive=o.cameraMode==="walk"&&!o.gamingControls,this.hoverRing.hide(),this.targetMode&&this.targetMode!==o.cameraMode&&(this.navigating=!1,this.clearTarget())};n.on("cameraMode:changed",l),n.on("inputMode:changed",l),n.on("gamingControls:changed",l),n.on("navigateTo",()=>{this.navigating=!0,this.hoverRing.hide()}),n.on("navigateCancel",()=>{this.navigating=!1,this.clearTarget()}),n.on("navigateComplete",()=>{this.navigating=!1,this.clearTarget()}),n.on("navTarget:set",(c,h)=>{let d=o.cameraMode==="walk"||o.cameraMode==="fly"?o.cameraMode:"walk";this.setTarget(c,h,d)}),n.on("navTarget:clear",()=>{this.clearTarget()}),n.on("orbitTarget:set",(c,h)=>{this.navigating=!1,this.setTarget(c,h,"orbit")}),n.on("orbitTarget:clear",()=>{this.targetMode==="orbit"&&this.clearTarget()}),this.onPrerender=()=>{this.updateTarget()},e.on("prerender",this.onPrerender),l()}screenPixelsForRing(){return this.state.walkAllowed?null:_d}setTarget(e,t,s){this.targetPos=e.clone(),this.targetNormal=t.clone(),this.targetMode=s,this.hoverRing.hide(),this.targetRing.hide()}clearTarget(){this.targetPos=null,this.targetNormal=null,this.targetMode=null,this.targetRing.hide()}pickCollision(e,t){if(!this.collision)return null;let{camera:s,collision:n}=this,o=s.getPosition();s.camera.screenToWorld(e,t,1,ve),ve.sub(o).normalize();let r=n.queryRay(o.x,o.y,o.z,ve.x,ve.y,ve.z,s.camera.farClip);if(!r)return null;let a=n.querySurfaceNormal(r.x,r.y,r.z,ve.x,ve.y,ve.z);return this.collisionTarget.position.set(r.x,r.y,r.z),this.collisionTarget.normal.set(a.nx,a.ny,a.nz),this.collisionTarget}updateCursor(e,t){if(!this.hoverActive||this.navigating){this.hoverRing.hide();return}let s=this.pickCollision(e,t);if(!s){this.hoverRing.hide();return}this.hoverRing.render(s.position,s.normal,this.screenPixelsForRing())}updateTarget(){if(!this.targetPos||!this.targetNormal||!this.targetMode)return;let t=this.camera.getPosition().distance(this.targetPos);if(this.targetMode!=="orbit"&&t<2){this.targetRing.hide();return}this.targetRing.render(this.targetPos,this.targetNormal,this.screenPixelsForRing())}destroy(){this.app.off("prerender",this.onPrerender),this.canvas.removeEventListener("pointermove",this.onPointerMove),this.canvas.removeEventListener("pointerleave",this.onPointerLeave),this.svg.remove()}},Ed=`
|
|
979
|
+
var splatDepth: texture_depth_2d;
|
|
980
|
+
// clip z = a * viewDepth + b over w = viewDepth (x, y), near and far (z, w)
|
|
981
|
+
uniform depthViewParams: vec4f;
|
|
982
|
+
// device pixels per snapshot pixel (x, y), 1 for an orthographic camera (z)
|
|
983
|
+
uniform snapshotParams: vec4f;
|
|
984
|
+
|
|
985
|
+
@fragment fn fragmentMain(input: FragmentInput) -> FragmentOutput {
|
|
986
|
+
var output: FragmentOutput;
|
|
987
|
+
let dims = vec2i(textureDimensions(splatDepth));
|
|
988
|
+
let p = uniform.depthViewParams;
|
|
989
|
+
let ortho = uniform.snapshotParams.z > 0.5;
|
|
990
|
+
let origin = floor(pcPosition.xy);
|
|
991
|
+
var trials = vec4f(1.0);
|
|
992
|
+
for (var i = 0; i < 4; i++) {
|
|
993
|
+
let corner = vec2f(0.25 + 0.5 * f32(i & 1), 0.25 + 0.5 * f32(i >> 1));
|
|
994
|
+
let q = clamp(vec2i(floor((origin + corner) * uniform.snapshotParams.xy)), vec2i(0), dims - vec2i(1));
|
|
995
|
+
let depth = textureLoad(splatDepth, q, 0);
|
|
996
|
+
if (depth < 1.0) {
|
|
997
|
+
let dz = depth - p.x;
|
|
998
|
+
let safeDz = select(dz, sign(dz) * 1e-9 + 1e-12, abs(dz) < 1e-9);
|
|
999
|
+
let viewDepth = select(p.y / safeDz, (depth - p.y) / p.x, ortho);
|
|
1000
|
+
trials[i] = clamp((viewDepth - p.z) / (p.w - p.z), 0.0, 0.99951171875);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
output.color = trials;
|
|
1004
|
+
return output;
|
|
1005
|
+
}
|
|
1006
|
+
`,Rd=(i,e)=>{let t=e.camera;return i.viewMatrix.equals(t.viewMatrix)&&i.projectionMatrix.equals(t.projectionMatrix)&&i.nearClip===t.nearClip&&i.farClip===t.farClip&&i.projection===t.projection},Ad=(i,e)=>i.viewMatrix.equals(e.viewMatrix)&&i.projectionMatrix.equals(e.projectionMatrix)&&i.nearClip===e.nearClip&&i.farClip===e.farClip&&i.projection===e.projection,Mr=(i,e)=>{e.position.copy(i.position),e.viewMatrix.copy(i.viewMatrix),e.projectionMatrix.copy(i.projectionMatrix),e.nearClip=i.nearClip,e.farClip=i.farClip,e.projection=i.projection},$o=class{app;camera;renderer;shader;texture=null;target=null;snapshotFrame=-1;snapshotCamera=ds();renders=0;queue=new yn;waiters=new Set;constructor(e,t,s){this.app=e,this.camera=t,this.renderer=s,this.shader=Ss.createShader(e.graphicsDevice,{uniqueName:"sse-splat-depth-snapshot",attributes:{vertex_position:pe},vertexChunk:"fullscreenQuadVS",fragmentWGSL:Ed})}nextFrame(){return new Promise(e=>{let t=()=>{s.off(),this.waiters.delete(t),e()},s=this.app.once("frameend",t);this.waiters.add(t)})}snapshot(e,t){let s=this.app.graphicsDevice,n=Kr(s);if(!n)return null;let{width:o,height:r}=n;if(this.texture?(this.texture.width!==o||this.texture.height!==r)&&(this.target.resize(o,r),this.snapshotFrame=-1):(this.texture=new ze(s,{name:"sse-splat-depth-snapshot",format:ks,width:o,height:r,mipmaps:!1,minFilter:0,magFilter:0,addressU:1,addressV:1}),this.target=new Xe({name:"sse-splat-depth-snapshot",colorBuffer:this.texture,depth:!1}),this.snapshotFrame=-1),!(this.snapshotFrame===e.id||!t&&this.snapshotFrame>=0&&Ad(this.snapshotCamera,e.camera))){let{scope:c}=s,{camera:h}=e;c.resolve("splatDepth").setValue(this.renderer.frameDepthTexture),c.resolve("depthViewParams").setValue([e.clipZ[0],e.clipZ[1],h.nearClip,h.farClip]),c.resolve("snapshotParams").setValue([e.width/o,e.height/r,h.projection===qt?1:0,0]),s.setBlendState(is.NOBLEND),Cs(s,this.target,this.shader),this.snapshotFrame=e.id,Mr(e.camera,this.snapshotCamera),this.renders++}let l=ds();return Mr(e.camera,l),{width:o,height:r,pickCamera:l}}async prepare(){if(this.queue.released)return null;let e=this.renderer.depthFrame;return(!e||!Rd(e.camera,this.camera))&&(this.app.renderNextFrame=!0,await this.nextFrame(),e=this.renderer.depthFrame,!e||this.queue.released)?null:this.snapshot(e,!0)}async readAround(e,t,s,n,o){let r=Math.max(0,e-s),a=Math.max(0,t-s),l=Math.min(n-1,e+s)-r+1,c=Math.min(o-1,t+s)-a+1,h=await this.texture.read(r,a,l,c,{renderTarget:this.target,immediate:!0});return(d,u,p)=>{let g=d-r,m=u-a;return g<0||g>=l||m<0||m>=c?null:Ur(h[(m*l+g)*4+p])}}pick(e,t){return this.queue.run(async()=>{let s=await this.prepare();if(!s)return null;let{width:n,height:o,pickCamera:r}=s,a=ft(e,n),l=ft(t,o),c=await this.readAround(a,l,te,n,o),h=si(c,a,l);return h===null?null:ti(r,a,l,n,o,h)},null)}pickSurface(e,t){return this.queue.run(async()=>{let s=await this.prepare();if(!s)return null;let{width:n,height:o,pickCamera:r}=s,a=ft(e,n),l=ft(t,o),c=await this.readAround(a,l,ei+te,n,o);return Yr(c,r,n,o,a,l)},null)}pickVisibility(e,t){return e.length===0?Promise.resolve([]):this.queue.run(async()=>{let s=await this.prepare();if(!s)return e.map(()=>null);let{width:n,height:o,pickCamera:r}=s,a=Math.max(1,Math.round(t*o));return Promise.all(e.map(async({x:l,y:c,depth:h})=>{let d=ft(l,n),u=ft(c,o),p=await this.readAround(d,u,a,n,o);return Zr(p,r,d,u,a,h)}))},e.map(()=>null))}renderView(){if(this.queue.released)return null;let e=this.renderer.depthFrame;return!e||!this.snapshot(e,!1)?null:{texture:this.texture,renders:this.renders}}release(){this.queue.released=!0;for(let e of[...this.waiters])e();this.target?.destroy(),this.texture?.destroy(),this.target=null,this.texture=null,this.shader.destroy()}},qe=new x,Td=new x,Ho=class{app;camera;layer;handles=[];callbacks=new Set;set=null;takenOver=new Map;active=!0;constructor(e,t,s){this.app=e,this.camera=t,this.layer=s;let n=e.systems.gsplat;this.handles.push(n.on("material:created",(o,r,a)=>{if(r===this.camera&&a===this.layer&&this.active){let l=this.manager();l&&this.takeOver(l)}})),this.handles.push(n.on("frame:ready",(o,r)=>{if(o!==this.camera||r!==this.layer||!this.active)return;let a=this.manager();if(!a)return;this.takeOver(a);let l=a.world.currentState;if(!l)return;let c=!this.set||this.set.version!==l.version;c&&(this.set=Dd(l));for(let h of this.callbacks)h(this.set,c,a)}))}manager(){return this.app.renderer.gsplatDirector?.camerasMap.get(this.camera.camera)?.layersMap.get(this.layer)?.gsplatManager??null}setActive(e){if(this.active!==e)if(this.active=e,e){let t=this.manager();t&&this.takeOver(t)}else this.restore()}onFrame(e){return this.callbacks.add(e),()=>this.callbacks.delete(e)}takeOver(e){let t=e.renderer;this.takenOver.has(t)||(this.takenOver.set(t,t.prepareRenderView),t.prepareRenderView=()=>!1),t.meshInstance&&(t.meshInstance.visible=!1);for(let s of["gpuSorter","projector","intervalCompaction"]){let n=t[s];n&&(n.destroy(),t[s]=null)}}restore(){for(let[e,t]of this.takenOver)e.prepareRenderView=t,e.meshInstance&&(e.meshInstance.visible=!0);this.takenOver.clear()}destroy(){this.restore();for(let e of this.handles)e.off();this.handles.length=0,this.callbacks.clear(),this.set=null}},Dd=i=>{let e=[],t=[],s=[],n=(o,r,a,l,c)=>{t.push(o),s.push(r,a,l,c)};return i.splats.forEach((o,r)=>{let a=o.node.getWorldTransform().clone(),l=a.getScale(Td),c=Math.max(Math.abs(l.x),Math.abs(l.y),Math.abs(l.z)),h={fileIndex:r,lodIndex:o.lodIndex,nodes:[],modelMatrix:a,resource:o.resource},d=o.intervals.length/2;if(d===0){let u={fileIndex:r,nodeIndex:t.length,lodIndex:o.lodIndex,sourceBase:0,count:o.activeSplats,slotBase:o.intervalOffsets[0]??0,allocId:o.allocId};a.transformPoint(o.aabb.center,qe);let p=o.aabb.halfExtents;n(u,qe.x,qe.y,qe.z,p.length()*c),h.nodes.push(u)}else for(let u=0;u<d;u++){let p=o.intervals[u*2],g=o.intervals[u*2+1],m=o.intervalNodeIndices[u],f={fileIndex:r,nodeIndex:t.length,lodIndex:o.lodIndex,sourceBase:p,count:g-p,slotBase:o.intervalOffsets[u]??0,allocId:o.intervalAllocIds[u]},v=o.octreeNodes?.[m]?.boundingSphere;v?(qe.set(v.x,v.y,v.z),a.transformPoint(qe,qe),n(f,qe.x,qe.y,qe.z,v.w*c)):n(f,0,0,0,1/0),h.nodes.push(f)}e.push(h)}),{version:i.version,activeSplats:i.totalActiveSplats,slotCapacity:i.textureSize*i.textureSize,fullRebuild:i.fullRebuild,changedAllocIds:i.needsUploadIds,files:e,nodes:t,nodeSpheres:new Float32Array(s)}},zd=`
|
|
1007
|
+
struct DrawIndexedIndirectArgs {
|
|
1008
|
+
indexCount: u32,
|
|
1009
|
+
instanceCount: u32,
|
|
1010
|
+
firstIndex: u32,
|
|
1011
|
+
baseVertex: i32,
|
|
1012
|
+
firstInstance: u32
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
struct ArgsUniforms {
|
|
1016
|
+
drawSlot: u32,
|
|
1017
|
+
indexCount: u32,
|
|
1018
|
+
quadsPerInstance: u32,
|
|
1019
|
+
dispatchSlot: u32,
|
|
1020
|
+
scatterWorkgroupSize: u32
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
@group(0) @binding(0) var<storage, read> counter: array<u32>;
|
|
1024
|
+
@group(0) @binding(1) var<storage, read_write> indirectDrawArgs: array<DrawIndexedIndirectArgs>;
|
|
1025
|
+
@group(0) @binding(2) var<storage, read_write> indirectDispatchArgs: array<u32>;
|
|
1026
|
+
@group(0) @binding(3) var<uniform> uniforms: ArgsUniforms;
|
|
1027
|
+
|
|
1028
|
+
@compute @workgroup_size(1)
|
|
1029
|
+
fn main() {
|
|
1030
|
+
let count = counter[0];
|
|
1031
|
+
indirectDrawArgs[uniforms.drawSlot] = DrawIndexedIndirectArgs(
|
|
1032
|
+
uniforms.indexCount,
|
|
1033
|
+
(count + uniforms.quadsPerInstance - 1u) / uniforms.quadsPerInstance,
|
|
1034
|
+
0u,
|
|
1035
|
+
0,
|
|
1036
|
+
0u
|
|
1037
|
+
);
|
|
1038
|
+
// the scatter: one workgroup per scatterWorkgroupSize survivors, 2D past the 65535 limit
|
|
1039
|
+
let groups = (count + uniforms.scatterWorkgroupSize - 1u) / uniforms.scatterWorkgroupSize;
|
|
1040
|
+
let x = max(min(groups, 65535u), 1u);
|
|
1041
|
+
let base = uniforms.dispatchSlot * 3u;
|
|
1042
|
+
indirectDispatchArgs[base] = x;
|
|
1043
|
+
indirectDispatchArgs[base + 1u] = (groups + x - 1u) / x;
|
|
1044
|
+
indirectDispatchArgs[base + 2u] = 1u;
|
|
1045
|
+
}
|
|
1046
|
+
`,jo=511,Fd=`
|
|
1047
|
+
attribute vertex_position: vec2f;
|
|
1048
|
+
|
|
1049
|
+
@vertex
|
|
1050
|
+
fn vertexMain(input: VertexInput) -> VertexOutput {
|
|
1051
|
+
var output: VertexOutput;
|
|
1052
|
+
output.position = vec4f(vertex_position, 0.0, 1.0);
|
|
1053
|
+
return output;
|
|
1054
|
+
}
|
|
1055
|
+
`,tl=`
|
|
1056
|
+
const TAA_COLOR_SCALE = 65535.0;
|
|
1057
|
+
|
|
1058
|
+
fn decodeColor(v: vec4u) -> vec4f {
|
|
1059
|
+
return vec4f(v) / TAA_COLOR_SCALE;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// premultiplied colour and coverage: coverage in [0, 1] and colour within it (the Catmull-Rom
|
|
1063
|
+
// fetch over- and undershoots both), rounded to the nearest step
|
|
1064
|
+
fn encodeColor(c: vec4f) -> vec4u {
|
|
1065
|
+
let a = clamp(c.a, 0.0, 1.0);
|
|
1066
|
+
return vec4u(round(vec4f(clamp(c.rgb, vec3f(0.0), vec3f(a)), a) * TAA_COLOR_SCALE));
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
struct HistoryInfo {
|
|
1070
|
+
depth: f32,
|
|
1071
|
+
count: f32
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// The mean view depth keeps the float's exponent and top 15 mantissa bits, rounded: a relative
|
|
1075
|
+
// step of 2^-15, which stalls a 256-sample mean only within 0.4 % of the depth (the depth steers
|
|
1076
|
+
// the reprojection, where that is far below a pixel). The count takes the low 9 bits
|
|
1077
|
+
fn decodeInfo(v: u32) -> HistoryInfo {
|
|
1078
|
+
var info: HistoryInfo;
|
|
1079
|
+
info.depth = bitcast<f32>((v >> 9u) << 8u);
|
|
1080
|
+
info.count = f32(v & ${jo}u);
|
|
1081
|
+
return info;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
fn encodeInfo(depth: f32, count: f32) -> u32 {
|
|
1085
|
+
let bits = bitcast<u32>(max(depth, 0.0));
|
|
1086
|
+
return (((bits + 0x80u) >> 8u) << 9u) | min(u32(count), ${jo}u);
|
|
1087
|
+
}
|
|
1088
|
+
`,Id=`
|
|
1089
|
+
var curColor: texture_2d<f32>;
|
|
1090
|
+
var curColorSampler: sampler;
|
|
1091
|
+
var curDepth: texture_depth_2d;
|
|
1092
|
+
// premultiplied colour and coverage, unorm16 (decodeColor)
|
|
1093
|
+
var histColor: texture_2d<u32>;
|
|
1094
|
+
// the mean view depth of the pixel's samples and their count, in the previous frame's view
|
|
1095
|
+
// depths (decodeInfo)
|
|
1096
|
+
var histInfo: texture_2d<u32>;
|
|
1097
|
+
|
|
1098
|
+
// the camera's world transform (the inverse view), and its projection's x and y terms: x, y the
|
|
1099
|
+
// scales, z, w the offsets (the column that multiplies view z for a perspective camera, the
|
|
1100
|
+
// translation for an orthographic one)
|
|
1101
|
+
uniform cameraWorld: mat4x4f;
|
|
1102
|
+
uniform unproject: vec4f;
|
|
1103
|
+
uniform prevViewProj: mat4x4f;
|
|
1104
|
+
uniform prevView: mat4x4f;
|
|
1105
|
+
// clip z = a * viewDepth + b over w = viewDepth (x, y); z: 1 for an orthographic camera
|
|
1106
|
+
uniform clipZParams: vec4f;
|
|
1107
|
+
// x: sample cap, y: 1 when the history holds the previous frame, z: 1 when the camera moved
|
|
1108
|
+
// since that frame, w: the frames the camera has rested, this one included (0 on a frame whose
|
|
1109
|
+
// resident set changed)
|
|
1110
|
+
uniform taaParams: vec4f;
|
|
1111
|
+
// width, height, 1 / width, 1 / height
|
|
1112
|
+
uniform taaViewport: vec4f;
|
|
1113
|
+
// x: -1 when the target's rows run bottom-up (an offscreen target), else 1; y: the colour
|
|
1114
|
+
// clamp width in neighbourhood standard deviations (0: no clamp); z: 1 when the raster's
|
|
1115
|
+
// thresholds are quad-stratified
|
|
1116
|
+
uniform taaControl: vec4f;
|
|
1117
|
+
// x: 1 for a Catmull-Rom history fetch while moving (0: bilinear); y: 1 to reproject through
|
|
1118
|
+
// the pixel's accumulated mean depth rather than this frame's sample depth; z: image motion in
|
|
1119
|
+
// pixels a frame that halves the moving sample cap (0: fixed cap)
|
|
1120
|
+
uniform taaFilter: vec4f;
|
|
1121
|
+
// 1: write the accumulation state instead of colour (r: count / cap, g: history accepted,
|
|
1122
|
+
// b: the camera moved since the previous frame)
|
|
1123
|
+
uniform taaDebug: f32;
|
|
1124
|
+
|
|
1125
|
+
${tl}
|
|
1126
|
+
|
|
1127
|
+
fn viewDepthOf(z: f32) -> f32 {
|
|
1128
|
+
let p = uniform.clipZParams;
|
|
1129
|
+
let dz = z - p.x;
|
|
1130
|
+
let safeDz = select(dz, sign(dz) * 1e-9 + 1e-12, abs(dz) < 1e-9);
|
|
1131
|
+
return select(p.y / safeDz, (z - p.y) / p.x, p.z != 0.0);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
struct Reprojected {
|
|
1135
|
+
valid: bool,
|
|
1136
|
+
uv: vec2f,
|
|
1137
|
+
prevDepth: f32
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// The previous frame's view depth and texture coordinate of this pixel's point at a view
|
|
1141
|
+
// depth. The point is built in view space from the depth and the projection's x and y terms,
|
|
1142
|
+
// then moved by the camera's rigid transform: through the inverse view-projection instead, a
|
|
1143
|
+
// near plane at 1e-4 of the depth (the viewer's floor once the camera is inside the scene
|
|
1144
|
+
// bound) leaves a 32-bit float about three digits of the point, a pixel of error that changes
|
|
1145
|
+
// with every frame's sample depths and shakes the history.
|
|
1146
|
+
fn reproject(pix: vec2i, viewDepth: f32) -> Reprojected {
|
|
1147
|
+
var r: Reprojected;
|
|
1148
|
+
let flip = uniform.taaControl.x;
|
|
1149
|
+
let uv = (vec2f(pix) + vec2f(0.5)) * uniform.taaViewport.zw;
|
|
1150
|
+
let ndc = vec2f(uv.x * 2.0 - 1.0, flip * (1.0 - 2.0 * uv.y));
|
|
1151
|
+
let u = uniform.unproject;
|
|
1152
|
+
let viewXY = select((ndc + u.zw) * viewDepth, ndc - u.zw, uniform.clipZParams.z != 0.0) / u.xy;
|
|
1153
|
+
let world = (uniform.cameraWorld * vec4f(viewXY, -viewDepth, 1.0)).xyz;
|
|
1154
|
+
let prevClip = uniform.prevViewProj * vec4f(world, 1.0);
|
|
1155
|
+
r.prevDepth = -(uniform.prevView * vec4f(world, 1.0)).z;
|
|
1156
|
+
r.valid = prevClip.w > 0.0;
|
|
1157
|
+
let prevNdc = prevClip.xy / max(prevClip.w, 1e-9);
|
|
1158
|
+
r.uv = vec2f(prevNdc.x * 0.5 + 0.5, 0.5 - prevNdc.y * flip * 0.5);
|
|
1159
|
+
r.valid = r.valid && all(r.uv >= vec2f(0.0)) && all(r.uv <= vec2f(1.0));
|
|
1160
|
+
return r;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// the premultiplied sample at a texel: its colour where a fragment landed, nothing otherwise
|
|
1164
|
+
fn sampleAt(pix: vec2i, dims: vec2i) -> vec4f {
|
|
1165
|
+
let p = clamp(pix, vec2i(0), dims - vec2i(1));
|
|
1166
|
+
let c = textureLoad(curColor, p, 0);
|
|
1167
|
+
let z = textureLoad(curDepth, p, 0);
|
|
1168
|
+
return select(vec4f(0.0), vec4f(c.rgb, 1.0), c.a > 0.0 && z < 1.0);
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
// The mean of the 2x2 quad's samples, bilinear between quad centres (the compose's resolve of
|
|
1172
|
+
// the raster's quad-stratified thresholds): four samples a pixel, at a little sharpness, for
|
|
1173
|
+
// the frames where the history is too short to carry the noise on its own
|
|
1174
|
+
fn quadSample(pix: vec2i, dims: vec2i) -> vec4f {
|
|
1175
|
+
let size = vec2f(dims);
|
|
1176
|
+
let u = (vec2f(pix) - vec2f(0.5)) * 0.5;
|
|
1177
|
+
let q0 = floor(u);
|
|
1178
|
+
let f = u - q0;
|
|
1179
|
+
let uv = (q0 * 2.0 + vec2f(1.0)) / size;
|
|
1180
|
+
let step = vec2f(2.0) / size;
|
|
1181
|
+
let a = textureSampleLevel(curColor, curColorSampler, uv, 0.0);
|
|
1182
|
+
let b = textureSampleLevel(curColor, curColorSampler, uv + vec2f(step.x, 0.0), 0.0);
|
|
1183
|
+
let c = textureSampleLevel(curColor, curColorSampler, uv + vec2f(0.0, step.y), 0.0);
|
|
1184
|
+
let d = textureSampleLevel(curColor, curColorSampler, uv + step, 0.0);
|
|
1185
|
+
let m = mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
|
|
1186
|
+
// the raster leaves empty texels transparent black, so the mean is premultiplied coverage
|
|
1187
|
+
return vec4f(m.rgb, m.a);
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
fn historyTexel(p: vec2i, dims: vec2i) -> vec4f {
|
|
1191
|
+
return decodeColor(textureLoad(histColor, clamp(p, vec2i(0), dims - vec2i(1)), 0));
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
// Catmull-Rom fetch of the history: sharper than bilinear, whose blur compounds over the
|
|
1195
|
+
// frames a moving history lives. Its lobes over- and undershoot; the encoding clamps both
|
|
1196
|
+
fn historyCubicAt(uv: vec2f, dims: vec2i) -> vec4f {
|
|
1197
|
+
let p = uv * vec2f(dims) - vec2f(0.5);
|
|
1198
|
+
let i0 = vec2i(floor(p));
|
|
1199
|
+
let f = p - floor(p);
|
|
1200
|
+
let f2 = f * f;
|
|
1201
|
+
let f3 = f2 * f;
|
|
1202
|
+
// Catmull-Rom weights for taps at -1, 0, 1, 2
|
|
1203
|
+
let w0 = -0.5 * f3 + f2 - 0.5 * f;
|
|
1204
|
+
let w1 = 1.5 * f3 - 2.5 * f2 + vec2f(1.0);
|
|
1205
|
+
let w2 = -1.5 * f3 + 2.0 * f2 + 0.5 * f;
|
|
1206
|
+
let w3 = 0.5 * f3 - 0.5 * f2;
|
|
1207
|
+
let wx = array<f32, 4>(w0.x, w1.x, w2.x, w3.x);
|
|
1208
|
+
let wy = array<f32, 4>(w0.y, w1.y, w2.y, w3.y);
|
|
1209
|
+
var sum = vec4f(0.0);
|
|
1210
|
+
for (var y = 0; y < 4; y++) {
|
|
1211
|
+
for (var x = 0; x < 4; x++) {
|
|
1212
|
+
sum += historyTexel(i0 + vec2i(x - 1, y - 1), dims) * (wx[x] * wy[y]);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
return sum;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
// bilinear fetch of the history
|
|
1219
|
+
fn historyAt(uv: vec2f, dims: vec2i) -> vec4f {
|
|
1220
|
+
let p = uv * vec2f(dims) - vec2f(0.5);
|
|
1221
|
+
let i0 = vec2i(floor(p));
|
|
1222
|
+
let f = p - floor(p);
|
|
1223
|
+
let a = historyTexel(i0, dims);
|
|
1224
|
+
let b = historyTexel(i0 + vec2i(1, 0), dims);
|
|
1225
|
+
let c = historyTexel(i0 + vec2i(0, 1), dims);
|
|
1226
|
+
let d = historyTexel(i0 + vec2i(1, 1), dims);
|
|
1227
|
+
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
@fragment
|
|
1231
|
+
fn fragmentMain(input: FragmentInput) -> FragmentOutput {
|
|
1232
|
+
var output: FragmentOutput;
|
|
1233
|
+
let pix = vec2i(pcPosition.xy);
|
|
1234
|
+
let dims = vec2i(textureDimensions(curColor));
|
|
1235
|
+
let cur = textureLoad(curColor, pix, 0);
|
|
1236
|
+
let z = textureLoad(curDepth, pix, 0);
|
|
1237
|
+
let hit = cur.a > 0.0 && z < 1.0;
|
|
1238
|
+
let d = viewDepthOf(z);
|
|
1239
|
+
|
|
1240
|
+
let own = decodeInfo(textureLoad(histInfo, pix, 0).x);
|
|
1241
|
+
let maxCount = uniform.taaParams.x;
|
|
1242
|
+
let historyValid = uniform.taaParams.y > 0.5;
|
|
1243
|
+
let moving = uniform.taaParams.z > 0.5;
|
|
1244
|
+
// still: this pixel's own sample, so the history converges to full resolution; moving:
|
|
1245
|
+
// the quad's four
|
|
1246
|
+
var sample = select(vec4f(0.0), vec4f(cur.rgb, 1.0), hit);
|
|
1247
|
+
if (moving && uniform.taaControl.z > 0.5) {
|
|
1248
|
+
sample = quadSample(pix, dims);
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
// Where to look in the history: through this frame's sample, or, with no sample, through
|
|
1252
|
+
// the depth this pixel's own history remembers, so an emptied pixel fades rather than
|
|
1253
|
+
// holding a stale colour
|
|
1254
|
+
var r: Reprojected;
|
|
1255
|
+
r.valid = false;
|
|
1256
|
+
var carryDepth = 0.0;
|
|
1257
|
+
if (!moving) {
|
|
1258
|
+
// a resting camera maps every pixel onto itself: the exact texel, not the round trip
|
|
1259
|
+
// through the matrices, whose rounding would blur the history a little every frame
|
|
1260
|
+
r.valid = true;
|
|
1261
|
+
r.uv = (vec2f(pix) + vec2f(0.5)) * uniform.taaViewport.zw;
|
|
1262
|
+
r.prevDepth = select(own.depth, d, hit);
|
|
1263
|
+
carryDepth = r.prevDepth;
|
|
1264
|
+
} else if (uniform.taaFilter.y > 0.5 && own.count > 0.5) {
|
|
1265
|
+
// through the depth the pixel's history has settled on: one stochastic sample's depth
|
|
1266
|
+
// is one layer of the mixture the history holds, and reprojecting the mixture through
|
|
1267
|
+
// a different layer every frame smears it by their parallax
|
|
1268
|
+
r = reproject(pix, own.depth);
|
|
1269
|
+
carryDepth = own.depth;
|
|
1270
|
+
} else if (hit) {
|
|
1271
|
+
r = reproject(pix, d);
|
|
1272
|
+
carryDepth = d;
|
|
1273
|
+
} else if (own.count > 0.5) {
|
|
1274
|
+
r = reproject(pix, own.depth);
|
|
1275
|
+
carryDepth = own.depth;
|
|
1276
|
+
}
|
|
1277
|
+
r.valid = r.valid && historyValid;
|
|
1278
|
+
|
|
1279
|
+
var hist = vec4f(0.0);
|
|
1280
|
+
var info: HistoryInfo;
|
|
1281
|
+
var accepted = false;
|
|
1282
|
+
if (r.valid) {
|
|
1283
|
+
// at rest the exact texel; moving, a filtered fetch of the reprojected position
|
|
1284
|
+
if (!moving) {
|
|
1285
|
+
hist = decodeColor(textureLoad(histColor, pix, 0));
|
|
1286
|
+
info = own;
|
|
1287
|
+
} else {
|
|
1288
|
+
if (uniform.taaFilter.x > 0.5) {
|
|
1289
|
+
hist = historyCubicAt(r.uv, dims);
|
|
1290
|
+
} else {
|
|
1291
|
+
hist = historyAt(r.uv, dims);
|
|
1292
|
+
}
|
|
1293
|
+
info = decodeInfo(textureLoad(histInfo, clamp(vec2i(r.uv * vec2f(dims)), vec2i(0), dims - vec2i(1)), 0).x);
|
|
1294
|
+
}
|
|
1295
|
+
// a resting camera cannot disocclude anything, so every sample is accepted and the
|
|
1296
|
+
// pixel converges; moving, the colour clamp below keeps a disoccluded history out
|
|
1297
|
+
accepted = info.count > 0.5;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
var color: vec4f;
|
|
1301
|
+
var mean: f32;
|
|
1302
|
+
var count: f32;
|
|
1303
|
+
if (!accepted) {
|
|
1304
|
+
color = sample;
|
|
1305
|
+
mean = d;
|
|
1306
|
+
count = select(0.0, 1.0, hit);
|
|
1307
|
+
// at rest a pixel with no hit yet has still been sampled on every rest frame, so its
|
|
1308
|
+
// first hit is one of that many samples: at full weight a rare hit would flash to full
|
|
1309
|
+
// coverage, and freeze there once the viewer stops rendering. Its depth is the hit's
|
|
1310
|
+
if (hit && !moving && historyValid) {
|
|
1311
|
+
count = clamp(uniform.taaParams.w, 1.0, maxCount);
|
|
1312
|
+
color = sample / count;
|
|
1313
|
+
}
|
|
1314
|
+
} else {
|
|
1315
|
+
// moving, the history is clamped to the colours this frame's neighbourhood holds, so a
|
|
1316
|
+
// stale colour cannot survive where nothing like it is drawn any more
|
|
1317
|
+
if (moving && uniform.taaControl.y > 0.0) {
|
|
1318
|
+
var m1 = vec4f(0.0);
|
|
1319
|
+
var m2 = vec4f(0.0);
|
|
1320
|
+
for (var dy = -1; dy <= 1; dy++) {
|
|
1321
|
+
for (var dx = -1; dx <= 1; dx++) {
|
|
1322
|
+
let n = sampleAt(pix + vec2i(dx, dy), dims);
|
|
1323
|
+
m1 += n;
|
|
1324
|
+
m2 += n * n;
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
let mu = m1 / 9.0;
|
|
1328
|
+
let sd = sqrt(max(m2 / 9.0 - mu * mu, vec4f(0.0)));
|
|
1329
|
+
let halfWidth = sd * uniform.taaControl.y;
|
|
1330
|
+
hist = clamp(hist, mu - halfWidth, mu + halfWidth);
|
|
1331
|
+
}
|
|
1332
|
+
// the faster the image moves, the shorter the history: a stochastic pixel's layers
|
|
1333
|
+
// reproject through one layer's depth, so their parallax smears the history by an
|
|
1334
|
+
// amount that grows with the motion, and a raw frame is the better estimate past it
|
|
1335
|
+
var cap = maxCount;
|
|
1336
|
+
if (moving && uniform.taaFilter.z > 0.0) {
|
|
1337
|
+
let speed = length(r.uv * vec2f(dims) - vec2f(pix) - vec2f(0.5));
|
|
1338
|
+
cap = max(2.0, maxCount / (1.0 + speed / uniform.taaFilter.z));
|
|
1339
|
+
}
|
|
1340
|
+
count = min(info.count + 1.0, cap);
|
|
1341
|
+
let w = 1.0 / count;
|
|
1342
|
+
color = mix(hist, sample, w);
|
|
1343
|
+
// an empty sample takes at least a step off the coverage: rounded to the nearest, a
|
|
1344
|
+
// decay of less than half a step holds (below cap / 2 steps, about 0.002 at the default
|
|
1345
|
+
// cap), so a faint pixel would keep its coverage, and the depth the compose writes for
|
|
1346
|
+
// it, forever. The colour follows, since the encoding keeps it within the coverage
|
|
1347
|
+
if (sample.a == 0.0) {
|
|
1348
|
+
color.a = min(color.a, max(hist.a - 1.0 / TAA_COLOR_SCALE, 0.0));
|
|
1349
|
+
}
|
|
1350
|
+
// carry the mean depth into this view: the point moved along the ray by the change in
|
|
1351
|
+
// its depth, then the sample joins it
|
|
1352
|
+
let shifted = info.depth + (carryDepth - r.prevDepth);
|
|
1353
|
+
mean = select(shifted, shifted + w * (d - shifted), hit);
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
if (uniform.taaDebug > 1.5) {
|
|
1357
|
+
// the reprojection offset in pixels, 8 px full scale, centred on 0.5
|
|
1358
|
+
let mv = select(vec2f(0.0), r.uv * vec2f(dims) - vec2f(pix) - vec2f(0.5), r.valid);
|
|
1359
|
+
color = vec4f(mv / 16.0 + vec2f(0.5), select(0.0, 1.0, r.valid), 1.0);
|
|
1360
|
+
} else if (uniform.taaDebug > 0.5) {
|
|
1361
|
+
color = vec4f(count / maxCount, select(0.0, 1.0, accepted), select(0.0, 1.0, moving), 1.0);
|
|
1362
|
+
}
|
|
1363
|
+
output.color = encodeColor(color);
|
|
1364
|
+
output.color1 = encodeInfo(mean, count);
|
|
1365
|
+
return output;
|
|
1366
|
+
}
|
|
1367
|
+
`,Vd=`
|
|
1368
|
+
attribute vertex_position: vec2f;
|
|
1369
|
+
|
|
1370
|
+
@vertex
|
|
1371
|
+
fn vertexMain(input: VertexInput) -> VertexOutput {
|
|
1372
|
+
var output: VertexOutput;
|
|
1373
|
+
output.position = vec4f(vertex_position, 0.0, 1.0);
|
|
1374
|
+
return output;
|
|
1375
|
+
}
|
|
1376
|
+
`,Od=`
|
|
1377
|
+
#include "gsplatOutputVS"
|
|
1378
|
+
|
|
1379
|
+
var splatColor: texture_2d<f32>;
|
|
1380
|
+
var splatColorSampler: sampler;
|
|
1381
|
+
var splatDepth: texture_depth_2d;
|
|
1382
|
+
// the temporally accumulated frame (premultiplied colour, coverage), when taa ran this frame
|
|
1383
|
+
var taaColor: texture_2d<u32>;
|
|
1384
|
+
// its depth record: the mean view depth of the pixel's samples and their count
|
|
1385
|
+
var taaInfo: texture_2d<u32>;
|
|
1386
|
+
// x: 1 when the splat target's rows run the other way to the camera target's; y: 1 for an
|
|
1387
|
+
// orthographic camera; z: 1 to read the taa history instead of the raw frame
|
|
1388
|
+
uniform composeParams: vec4f;
|
|
1389
|
+
// the splat target's depth: clip z = a * viewDepth + b over w = viewDepth (x, y), and the
|
|
1390
|
+
// camera's near and far (z, w) for the depth view
|
|
1391
|
+
uniform depthViewParams: vec4f;
|
|
1392
|
+
// the camera's own depth, the one written for the rest of the scene: clip z = a * viewDepth + b
|
|
1393
|
+
// (x, y). The splat target's far plane is at infinity where the camera's is fitted to the scene
|
|
1394
|
+
uniform cameraClipZ: vec4f;
|
|
1395
|
+
|
|
1396
|
+
${tl}
|
|
1397
|
+
|
|
1398
|
+
const FAR_DEPTH = 0.999999;
|
|
1399
|
+
|
|
1400
|
+
// the splat target's depth texel of a view depth, just inside its far plane at most: the
|
|
1401
|
+
// raster clamps there too, since a depth of 1 reads as no sample
|
|
1402
|
+
fn clipZOf(viewDepth: f32) -> f32 {
|
|
1403
|
+
let p = uniform.depthViewParams;
|
|
1404
|
+
let w = select(viewDepth, 1.0, uniform.composeParams.y > 0.5);
|
|
1405
|
+
return min(clamp(p.x * viewDepth + p.y, 0.0, w) / w, FAR_DEPTH);
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
// the view depth of a splat target depth texel
|
|
1409
|
+
fn viewDepthOf(z: f32) -> f32 {
|
|
1410
|
+
let p = uniform.depthViewParams;
|
|
1411
|
+
let dz = z - p.x;
|
|
1412
|
+
let safeDz = select(dz, sign(dz) * 1e-9 + 1e-12, abs(dz) < 1e-9);
|
|
1413
|
+
return select(p.y / safeDz, (z - p.y) / p.x, uniform.composeParams.y > 0.5);
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
// A splat target depth in the camera's depth, clamped just inside the camera's far plane as the
|
|
1417
|
+
// engine clamps its splats: splats beyond it stay behind everything the scene draws
|
|
1418
|
+
fn cameraDepthOf(z: f32) -> f32 {
|
|
1419
|
+
let viewDepth = viewDepthOf(z);
|
|
1420
|
+
let c = uniform.cameraClipZ;
|
|
1421
|
+
let w = select(viewDepth, 1.0, uniform.composeParams.y > 0.5);
|
|
1422
|
+
return min(clamp(c.x * viewDepth + c.y, 0.0, w) / w, FAR_DEPTH);
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
@fragment
|
|
1426
|
+
fn fragmentMain(input: FragmentInput) -> FragmentOutput {
|
|
1427
|
+
var output: FragmentOutput;
|
|
1428
|
+
let dims = vec2i(textureDimensions(splatColor));
|
|
1429
|
+
var pix = vec2i(pcPosition.xy);
|
|
1430
|
+
if (uniform.composeParams.x > 0.5) {
|
|
1431
|
+
pix.y = dims.y - 1 - pix.y;
|
|
1432
|
+
}
|
|
1433
|
+
pix = clamp(pix, vec2i(0), dims - vec2i(1));
|
|
1434
|
+
|
|
1435
|
+
var depth = textureLoad(splatDepth, pix, 0);
|
|
1436
|
+
var color: vec4f;
|
|
1437
|
+
if (uniform.composeParams.z > 0.5) {
|
|
1438
|
+
// the accumulated history: coverage in alpha, colour premultiplied by it
|
|
1439
|
+
color = decodeColor(textureLoad(taaColor, pix, 0));
|
|
1440
|
+
if (depth >= 1.0 && color.a > 0.001) {
|
|
1441
|
+
// covered by the history but no sample this frame, which a faint pixel sees on
|
|
1442
|
+
// most frames: the mean depth of the pixel's own samples, or, for a history this
|
|
1443
|
+
// frame started (moving, from the quad's samples), the nearest sampled neighbour's,
|
|
1444
|
+
// so the pixel still occludes what is behind the splats
|
|
1445
|
+
let info = decodeInfo(textureLoad(taaInfo, pix, 0).x);
|
|
1446
|
+
if (info.count > 0.5) {
|
|
1447
|
+
depth = clipZOf(info.depth);
|
|
1448
|
+
} else {
|
|
1449
|
+
for (var dy = -1; dy <= 1; dy++) {
|
|
1450
|
+
for (var dx = -1; dx <= 1; dx++) {
|
|
1451
|
+
depth = min(depth, textureLoad(splatDepth, clamp(pix + vec2i(dx, dy), vec2i(0), dims - vec2i(1)), 0));
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
} else {
|
|
1457
|
+
#ifdef SSE_SPP_QUAD
|
|
1458
|
+
// the mean of each 2x2 quad, bilinearly interpolated between quad centres: the four
|
|
1459
|
+
// strata of the raster's thresholds average out to the splat's coverage. Empty texels
|
|
1460
|
+
// are transparent black, so the result is premultiplied coverage
|
|
1461
|
+
let size = vec2f(dims);
|
|
1462
|
+
let u = (vec2f(pix) - vec2f(0.5)) * 0.5;
|
|
1463
|
+
let q0 = floor(u);
|
|
1464
|
+
let f = u - q0;
|
|
1465
|
+
let uv = (q0 * 2.0 + vec2f(1.0)) / size;
|
|
1466
|
+
let step = vec2f(2.0) / size;
|
|
1467
|
+
let a = textureSampleLevel(splatColor, splatColorSampler, uv, 0.0);
|
|
1468
|
+
let b = textureSampleLevel(splatColor, splatColorSampler, uv + vec2f(step.x, 0.0), 0.0);
|
|
1469
|
+
let c = textureSampleLevel(splatColor, splatColorSampler, uv + vec2f(0.0, step.y), 0.0);
|
|
1470
|
+
let d = textureSampleLevel(splatColor, splatColorSampler, uv + step, 0.0);
|
|
1471
|
+
color = mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
|
|
1472
|
+
if (depth >= 1.0) {
|
|
1473
|
+
// no sample at this pixel but coverage from its quad: the quad's nearest depth
|
|
1474
|
+
let quad = (pix >> vec2u(1u)) << vec2u(1u);
|
|
1475
|
+
depth = min(
|
|
1476
|
+
min(textureLoad(splatDepth, quad, 0), textureLoad(splatDepth, min(quad + vec2i(1, 0), dims - 1), 0)),
|
|
1477
|
+
min(textureLoad(splatDepth, min(quad + vec2i(0, 1), dims - 1), 0), textureLoad(splatDepth, min(quad + vec2i(1, 1), dims - 1), 0))
|
|
1478
|
+
);
|
|
1479
|
+
}
|
|
1480
|
+
#else
|
|
1481
|
+
color = textureLoad(splatColor, pix, 0);
|
|
1482
|
+
#endif
|
|
1483
|
+
}
|
|
1484
|
+
if (color.a <= 0.001) {
|
|
1485
|
+
discard;
|
|
1486
|
+
}
|
|
1487
|
+
// coverage with no depth anywhere near: shown, behind everything the scene drew
|
|
1488
|
+
depth = min(depth, FAR_DEPTH);
|
|
1489
|
+
|
|
1490
|
+
#ifdef SSE_SHOW_DEPTH
|
|
1491
|
+
// the nearest sample's view depth, log-spaced between near and far, as grey
|
|
1492
|
+
let p = uniform.depthViewParams;
|
|
1493
|
+
let viewDepth = viewDepthOf(depth);
|
|
1494
|
+
let t = clamp(log(max(viewDepth, p.z) / p.z) / log(p.w / p.z), 0.0, 1.0);
|
|
1495
|
+
// 24-bit fixed point across rgb (high byte in red), so a capture can read it precisely
|
|
1496
|
+
let fixed = floor(t * 16777215.0);
|
|
1497
|
+
let hi = floor(fixed / 65536.0);
|
|
1498
|
+
let mid = floor((fixed - hi * 65536.0) / 256.0);
|
|
1499
|
+
let lo = fixed - hi * 65536.0 - mid * 256.0;
|
|
1500
|
+
output.color = vec4f(vec3f(hi, mid, lo) / 255.0, 1.0);
|
|
1501
|
+
output.fragDepth = cameraDepthOf(depth);
|
|
1502
|
+
return output;
|
|
1503
|
+
#endif
|
|
1504
|
+
|
|
1505
|
+
// un-premultiply for the output transform, then premultiply for the blend
|
|
1506
|
+
let gamma = color.rgb / color.a;
|
|
1507
|
+
let rgb = prepareOutputFromGamma(gamma, 0.0);
|
|
1508
|
+
output.color = vec4f(rgb * color.a, color.a);
|
|
1509
|
+
output.fragDepth = cameraDepthOf(depth);
|
|
1510
|
+
return output;
|
|
1511
|
+
}
|
|
1512
|
+
`,mt=7,ni=256,le=ni,Jn=ni,Bd=(i,e,t)=>`
|
|
1513
|
+
struct ProjectorUniforms {
|
|
1514
|
+
view: mat4x4f,
|
|
1515
|
+
viewProj: mat4x4f,
|
|
1516
|
+
// the previous frame, for the occlusion cull: its view and view-projection, its clip-z
|
|
1517
|
+
// mapping (a, b, isOrtho, 0), viewport and focal length
|
|
1518
|
+
prevViewProj: mat4x4f,
|
|
1519
|
+
prevView: mat4x4f,
|
|
1520
|
+
prevClipZ: vec4f,
|
|
1521
|
+
viewport: vec2f,
|
|
1522
|
+
focal: vec2f,
|
|
1523
|
+
prevViewport: vec2f,
|
|
1524
|
+
prevFocal: vec2f,
|
|
1525
|
+
numChunks: u32,
|
|
1526
|
+
splatTextureSize: u32,
|
|
1527
|
+
isOrtho: u32,
|
|
1528
|
+
minPixelSize: f32,
|
|
1529
|
+
alphaClip: f32,
|
|
1530
|
+
minContribution: f32,
|
|
1531
|
+
// the occlusion grid: level 1 (8 px) and level 2 (32 px) block counts
|
|
1532
|
+
occBlocksX1: u32,
|
|
1533
|
+
occBlocksY1: u32,
|
|
1534
|
+
occBlocksX2: u32,
|
|
1535
|
+
occBlocksY2: u32,
|
|
1536
|
+
// 0 off, 1 level 1 only, 2 both levels
|
|
1537
|
+
occlusionMode: u32,
|
|
1538
|
+
// -1 when the previous depth texture's rows run bottom-up (an offscreen target), else 1
|
|
1539
|
+
prevFlip: f32,
|
|
1540
|
+
// the order key: bucket = (log(depth) - keyLogNear) * keyInvLogRange, over [0, 1)
|
|
1541
|
+
keyLogNear: f32,
|
|
1542
|
+
keyInvLogRange: f32,
|
|
1543
|
+
// first chunk-table entry of this dispatch (per-file sources dispatch one run each)
|
|
1544
|
+
chunkBase: u32,
|
|
1545
|
+
// the file's model transform, for sources that read splats in the file's own space
|
|
1546
|
+
model: mat4x4f,
|
|
1547
|
+
modelRotation: vec4f,
|
|
1548
|
+
modelScale: vec4f,
|
|
1549
|
+
cameraPosition: vec4f,
|
|
1550
|
+
// colour ceiling per channel: 8 keeps the cache's range, 1 clamps like the engine's 8-bit cache
|
|
1551
|
+
colorMax: f32,
|
|
1552
|
+
// 1: covariance in true pixels. 2: the engine's convention (its focal is viewport * proj[0][0],
|
|
1553
|
+
// twice the pixel focal), which makes its 0.3 dilation 0.075 px^2 and scales its culls
|
|
1554
|
+
unitScale: f32,
|
|
1555
|
+
// the view depths of the near plane and of the raster's far clamp
|
|
1556
|
+
depthNear: f32,
|
|
1557
|
+
depthFar: f32
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
// chunk table entry: slotBase, count, node, lod | file << 16
|
|
1561
|
+
@group(0) @binding(0) var<storage, read> chunks: array<vec4u>;
|
|
1562
|
+
// one bit per node: 1 = inside the frustum this frame
|
|
1563
|
+
@group(0) @binding(1) var<storage, read> nodeVisible: array<u32>;
|
|
1564
|
+
@group(0) @binding(2) var<storage, read_write> cache: array<u32>;
|
|
1565
|
+
// [0] survivors this frame, [1] splats the occlusion cull removed
|
|
1566
|
+
@group(0) @binding(3) var<storage, read_write> counter: array<atomic<u32>>;
|
|
1567
|
+
@group(0) @binding(4) var<uniform> uniforms: ProjectorUniforms;
|
|
1568
|
+
// the previous frame's farthest depth per block, as f32 bits (see shaders/reduce.ts)
|
|
1569
|
+
@group(0) @binding(5) var<storage, read> occL1: array<u32>;
|
|
1570
|
+
@group(0) @binding(6) var<storage, read> occL2: array<u32>;
|
|
1571
|
+
// order:bucket: survivors per depth bucket (see shaders/order.ts)
|
|
1572
|
+
@group(0) @binding(7) var<storage, read_write> buckets: array<atomic<u32>>;
|
|
1573
|
+
|
|
1574
|
+
struct Splat {
|
|
1575
|
+
index: u32,
|
|
1576
|
+
uv: vec2i
|
|
1577
|
+
}
|
|
1578
|
+
var<private> splat: Splat;
|
|
1579
|
+
fn setSplat(idx: u32) {
|
|
1580
|
+
splat.index = idx;
|
|
1581
|
+
splat.uv = vec2i(i32(idx % uniforms.splatTextureSize), i32(idx / uniforms.splatTextureSize));
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
${i}
|
|
1585
|
+
|
|
1586
|
+
// quaternion (x, y, z, w) to a rotation matrix (columns)
|
|
1587
|
+
fn rotationMatrix(qIn: vec4f) -> mat3x3f {
|
|
1588
|
+
let q = normalize(qIn);
|
|
1589
|
+
let x = q.x;
|
|
1590
|
+
let y = q.y;
|
|
1591
|
+
let z = q.z;
|
|
1592
|
+
let w = q.w;
|
|
1593
|
+
return mat3x3f(
|
|
1594
|
+
vec3f(1.0 - 2.0 * (y * y + z * z), 2.0 * (x * y + w * z), 2.0 * (x * z - w * y)),
|
|
1595
|
+
vec3f(2.0 * (x * y - w * z), 1.0 - 2.0 * (x * x + z * z), 2.0 * (y * z + w * x)),
|
|
1596
|
+
vec3f(2.0 * (x * z + w * y), 2.0 * (y * z - w * x), 1.0 - 2.0 * (x * x + y * y))
|
|
1597
|
+
);
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
struct Projected {
|
|
1601
|
+
valid: bool,
|
|
1602
|
+
occluded: bool,
|
|
1603
|
+
words: array<u32, ${mt}>
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
// The farthest depth over the (2g+1)^2 blocks around a block, on one grid level.
|
|
1607
|
+
fn farthestL1(block: vec2i, gather: i32, blocks: vec2i) -> u32 {
|
|
1608
|
+
var farthest = 0u;
|
|
1609
|
+
for (var dy = -gather; dy <= gather; dy++) {
|
|
1610
|
+
for (var dx = -gather; dx <= gather; dx++) {
|
|
1611
|
+
farthest = max(farthest, occL1[u32((block.y + dy) * blocks.x + block.x + dx)]);
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
return farthest;
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
fn farthestL2(block: vec2i, gather: i32, blocks: vec2i) -> u32 {
|
|
1618
|
+
var farthest = 0u;
|
|
1619
|
+
for (var dy = -gather; dy <= gather; dy++) {
|
|
1620
|
+
for (var dx = -gather; dx <= gather; dx++) {
|
|
1621
|
+
farthest = max(farthest, occL2[u32((block.y + dy) * blocks.x + block.x + dx)]);
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
return farthest;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
fn project(slot: u32, file: u32) -> Projected {
|
|
1628
|
+
var result: Projected;
|
|
1629
|
+
result.valid = false;
|
|
1630
|
+
result.occluded = false;
|
|
1631
|
+
|
|
1632
|
+
setSplat(slot);
|
|
1633
|
+
let center = srcCenter();
|
|
1634
|
+
let opacity = srcOpacity();
|
|
1635
|
+
if (opacity < uniforms.alphaClip) {
|
|
1636
|
+
return result;
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
let viewCenter = uniforms.view * vec4f(center, 1.0);
|
|
1640
|
+
let depth = -viewCenter.z;
|
|
1641
|
+
if (uniforms.isOrtho == 0u && depth <= 0.0) {
|
|
1642
|
+
return result;
|
|
1643
|
+
}
|
|
1644
|
+
let clip = uniforms.viewProj * vec4f(center, 1.0);
|
|
1645
|
+
if (clip.w == 0.0) {
|
|
1646
|
+
return result;
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
let viewport = uniforms.viewport;
|
|
1650
|
+
let focal = uniforms.focal;
|
|
1651
|
+
|
|
1652
|
+
// the work buffer holds world-space splats, so the view matrix is the model-view
|
|
1653
|
+
let rot = srcRotation();
|
|
1654
|
+
let scale = srcScale();
|
|
1655
|
+
let linear = mat3x3f(uniforms.view[0].xyz, uniforms.view[1].xyz, uniforms.view[2].xyz);
|
|
1656
|
+
let gaussian = linear * rotationMatrix(vec4f(rot.yzw, rot.x)) * mat3x3f(
|
|
1657
|
+
vec3f(scale.x, 0.0, 0.0),
|
|
1658
|
+
vec3f(0.0, scale.y, 0.0),
|
|
1659
|
+
vec3f(0.0, 0.0, scale.z)
|
|
1660
|
+
);
|
|
1661
|
+
let row0 = vec3f(gaussian[0].x, gaussian[1].x, gaussian[2].x);
|
|
1662
|
+
let row1 = vec3f(gaussian[0].y, gaussian[1].y, gaussian[2].y);
|
|
1663
|
+
let row2 = vec3f(gaussian[0].z, gaussian[1].z, gaussian[2].z);
|
|
1664
|
+
let c00 = dot(row0, row0);
|
|
1665
|
+
let c01 = dot(row0, row1);
|
|
1666
|
+
let c02 = dot(row0, row2);
|
|
1667
|
+
let c11 = dot(row1, row1);
|
|
1668
|
+
let c12 = dot(row1, row2);
|
|
1669
|
+
let c22 = dot(row2, row2);
|
|
1670
|
+
|
|
1671
|
+
var cov00: f32;
|
|
1672
|
+
var cov01: f32;
|
|
1673
|
+
var cov11: f32;
|
|
1674
|
+
if (uniforms.isOrtho != 0u) {
|
|
1675
|
+
cov00 = focal.x * focal.x * c00;
|
|
1676
|
+
cov01 = focal.x * focal.y * c01;
|
|
1677
|
+
cov11 = focal.y * focal.y * c11;
|
|
1678
|
+
} else {
|
|
1679
|
+
let safeDepth = max(depth, 0.001);
|
|
1680
|
+
let invDepth = 1.0 / safeDepth;
|
|
1681
|
+
let jx0 = focal.x * invDepth;
|
|
1682
|
+
let jx2 = focal.x * viewCenter.x * invDepth * invDepth;
|
|
1683
|
+
let jy1 = focal.y * invDepth;
|
|
1684
|
+
let jy2 = focal.y * viewCenter.y * invDepth * invDepth;
|
|
1685
|
+
let u00 = jx0 * c00 + jx2 * c02;
|
|
1686
|
+
let u01 = jx0 * c01 + jx2 * c12;
|
|
1687
|
+
let u02 = jx0 * c02 + jx2 * c22;
|
|
1688
|
+
let u11 = jy1 * c11 + jy2 * c12;
|
|
1689
|
+
let u12 = jy1 * c12 + jy2 * c22;
|
|
1690
|
+
cov00 = u00 * jx0 + u02 * jx2;
|
|
1691
|
+
cov01 = u01 * jy1 + u02 * jy2;
|
|
1692
|
+
cov11 = u11 * jy1 + u12 * jy2;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
// the low-pass dilation every splat renderer applies, 0.3 in the chosen units
|
|
1696
|
+
let us2 = uniforms.unitScale * uniforms.unitScale;
|
|
1697
|
+
cov00 += 0.3 / us2;
|
|
1698
|
+
cov11 += 0.3 / us2;
|
|
1699
|
+
let determinant = cov00 * cov11 - cov01 * cov01;
|
|
1700
|
+
if (determinant <= 0.0) {
|
|
1701
|
+
return result;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
let mid = 0.5 * (cov00 + cov11);
|
|
1705
|
+
let radius = length(vec2f(0.5 * (cov00 - cov11), cov01));
|
|
1706
|
+
let lambda1 = mid + radius;
|
|
1707
|
+
let lambda2 = max(mid - radius, 0.1 / us2);
|
|
1708
|
+
|
|
1709
|
+
// size cull on the footprint's radius in pixels. minPixelSize is a diameter: the engine's
|
|
1710
|
+
// projector is dispatched with half of it, and its quad path tests the full extent
|
|
1711
|
+
// (2 sqrt(2 lambda)) against the whole value, so both cut at the same place
|
|
1712
|
+
if (uniforms.unitScale * sqrt(2.0 * lambda1) < 0.5 * uniforms.minPixelSize) {
|
|
1713
|
+
return result;
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
// contribution cull: the splat's alpha mass in pixels (the engine's minContribution rule)
|
|
1717
|
+
if (opacity * 6.283185 * sqrt(determinant) * us2 < uniforms.minContribution) {
|
|
1718
|
+
return result;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
// principal axis; a circular footprint has no defined eigenvector, any axis is right
|
|
1722
|
+
let eigenVec = vec2f(cov01, lambda1 - cov00);
|
|
1723
|
+
let eigenLen = length(eigenVec);
|
|
1724
|
+
let direction = select(vec2f(1.0, 0.0), eigenVec / eigenLen, eigenLen > 1e-9);
|
|
1725
|
+
let maxRadius = min(1024.0, min(viewport.x, viewport.y));
|
|
1726
|
+
let len1 = 2.0 * sqrt(2.0 * lambda1);
|
|
1727
|
+
let radiusScale = min(1.0, maxRadius / len1);
|
|
1728
|
+
let axis1 = len1 * radiusScale * direction;
|
|
1729
|
+
let len2 = 2.0 * sqrt(2.0 * lambda2) * radiusScale;
|
|
1730
|
+
let axis2 = len2 * vec2f(direction.y, -direction.x);
|
|
1731
|
+
|
|
1732
|
+
let ndc = clip.xy / clip.w;
|
|
1733
|
+
let extent = abs(axis1) + abs(axis2);
|
|
1734
|
+
let centerPixels = (ndc * 0.5 + 0.5) * viewport;
|
|
1735
|
+
if (centerPixels.x + extent.x < 0.0 || centerPixels.x - extent.x > viewport.x
|
|
1736
|
+
|| centerPixels.y + extent.y < 0.0 || centerPixels.y - extent.y > viewport.y) {
|
|
1737
|
+
return result;
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
${e?`
|
|
1741
|
+
// Occlusion cull against the previous frame. Each stored depth is the nearest sample that
|
|
1742
|
+
// survived there, so the farthest depth over the blocks a splat covered bounds what could
|
|
1743
|
+
// still have shown behind it; a splat whose front (its centre less 2 sqrt(2) sigma along the
|
|
1744
|
+
// view, the same cut-off as the quad's edge) lies beyond that bound was invisible last
|
|
1745
|
+
// frame, up to sampling. Static splats reproject exactly through the previous view, so only
|
|
1746
|
+
// true disocclusions arrive a frame late. The footprint the splat had last frame bounds the
|
|
1747
|
+
// gather; footprints up to 16 px use the 8 px grid, up to 64 px the 32 px grid, larger ones
|
|
1748
|
+
// skip the test.
|
|
1749
|
+
if (uniforms.occlusionMode != 0u) {
|
|
1750
|
+
let prevClip = uniforms.prevViewProj * vec4f(center, 1.0);
|
|
1751
|
+
let prevDepth = -(uniforms.prevView * vec4f(center, 1.0)).z;
|
|
1752
|
+
let prevOrtho = uniforms.prevClipZ.z != 0.0;
|
|
1753
|
+
if (prevClip.w > 0.0 && (prevOrtho || prevDepth > 0.0)) {
|
|
1754
|
+
let prevNdc = prevClip.xy / prevClip.w;
|
|
1755
|
+
let halfExtent = len1 * radiusScale;
|
|
1756
|
+
let prevHalfExtent = halfExtent * (uniforms.prevFocal.x / focal.x)
|
|
1757
|
+
* select(depth / max(prevDepth, 0.001), 1.0, prevOrtho);
|
|
1758
|
+
let footprint = max(halfExtent, prevHalfExtent);
|
|
1759
|
+
var level = 1u;
|
|
1760
|
+
var blockSize = 8.0;
|
|
1761
|
+
var blocks = vec2i(i32(uniforms.occBlocksX1), i32(uniforms.occBlocksY1));
|
|
1762
|
+
if (footprint > 16.0) {
|
|
1763
|
+
level = 2u;
|
|
1764
|
+
blockSize = 32.0;
|
|
1765
|
+
blocks = vec2i(i32(uniforms.occBlocksX2), i32(uniforms.occBlocksY2));
|
|
1766
|
+
}
|
|
1767
|
+
if (footprint <= 64.0 && level <= uniforms.occlusionMode) {
|
|
1768
|
+
let gather = max(i32(ceil(footprint / blockSize)), 1);
|
|
1769
|
+
let prevPixel = vec2f(prevNdc.x * 0.5 + 0.5, 0.5 - prevNdc.y * uniforms.prevFlip * 0.5) * uniforms.prevViewport;
|
|
1770
|
+
let block = vec2i(floor(prevPixel / blockSize));
|
|
1771
|
+
if (block.x >= gather && block.y >= gather && block.x < blocks.x - gather && block.y < blocks.y - gather) {
|
|
1772
|
+
let front = prevDepth - 2.8284 * sqrt(c22);
|
|
1773
|
+
let w = select(front, 1.0, prevOrtho);
|
|
1774
|
+
if (w > 0.0) {
|
|
1775
|
+
let frontZ = clamp(uniforms.prevClipZ.x * front + uniforms.prevClipZ.y, 0.0, w) / w;
|
|
1776
|
+
// the block under the centre first: the neighbourhood's maximum is at
|
|
1777
|
+
// least its maximum, so a splat that block alone cannot cull is visible,
|
|
1778
|
+
// and most splats leave here after one load
|
|
1779
|
+
let centreIndex = u32(block.y * blocks.x + block.x);
|
|
1780
|
+
var farthest = select(occL1[centreIndex], occL2[centreIndex], level == 2u);
|
|
1781
|
+
if (frontZ > bitcast<f32>(farthest)) {
|
|
1782
|
+
if (level == 1u) {
|
|
1783
|
+
farthest = farthestL1(block, gather, blocks);
|
|
1784
|
+
} else {
|
|
1785
|
+
farthest = farthestL2(block, gather, blocks);
|
|
1786
|
+
}
|
|
1787
|
+
if (frontZ > bitcast<f32>(farthest)) {
|
|
1788
|
+
result.occluded = true;
|
|
1789
|
+
return result;
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
`:""}
|
|
1799
|
+
let color = clamp(srcColor(), vec3f(0.0), vec3f(uniforms.colorMax));
|
|
1800
|
+
|
|
1801
|
+
// rgb: 10/10/10 unorm with a 2-bit shared exponent (scale 1/2/4/8, range [0, 8])
|
|
1802
|
+
let maxChannel = max(color.r, max(color.g, color.b));
|
|
1803
|
+
var exponent = 0u;
|
|
1804
|
+
if (maxChannel > 4.0) {
|
|
1805
|
+
exponent = 3u;
|
|
1806
|
+
} else if (maxChannel > 2.0) {
|
|
1807
|
+
exponent = 2u;
|
|
1808
|
+
} else if (maxChannel > 1.0) {
|
|
1809
|
+
exponent = 1u;
|
|
1810
|
+
}
|
|
1811
|
+
let rgb = vec3u(clamp(color / f32(1u << exponent), vec3f(0.0), vec3f(1.0)) * 1023.0 + 0.5);
|
|
1812
|
+
|
|
1813
|
+
// centre as snorm16 over the range the offscreen cull bounds visible centres to; the
|
|
1814
|
+
// raster shader derives the same range from its viewport
|
|
1815
|
+
let ndcRange = vec2f(1.0) + vec2f(4.0 * maxRadius) / viewport;
|
|
1816
|
+
|
|
1817
|
+
result.valid = true;
|
|
1818
|
+
result.words[0] = pack2x16snorm(ndc / ndcRange);
|
|
1819
|
+
result.words[1] = bitcast<u32>(depth);
|
|
1820
|
+
result.words[2] = pack2x16float(axis1);
|
|
1821
|
+
result.words[3] = pack2x16float(vec2f(len2, 0.0)) | (u32(clamp(opacity, 0.0, 1.0) * 255.0 + 0.5) << 16u);
|
|
1822
|
+
${t?`
|
|
1823
|
+
// the depth bucket, log-spaced from the near plane, in the flags byte
|
|
1824
|
+
let key = clamp((log(max(depth, 1e-6)) - uniforms.keyLogNear) * uniforms.keyInvLogRange, 0.0, 0.999) * ${le}.0;
|
|
1825
|
+
result.words[3] |= u32(key) << 24u;
|
|
1826
|
+
`:""}
|
|
1827
|
+
result.words[4] = rgb.r | (rgb.g << 10u) | (rgb.b << 20u) | (exponent << 30u);
|
|
1828
|
+
// Popless depth (StochasticSplats 3.4): the quad is tilted onto the plane through the
|
|
1829
|
+
// centre with normal adj(Sigma) mu (view space; adj rather than the inverse, so a flat
|
|
1830
|
+
// splat needs no division by its tiny determinant), and the raster gives each corner the
|
|
1831
|
+
// depth where its own view ray meets that plane, so hardware interpolation hands every
|
|
1832
|
+
// fragment the depth of the Gaussian's peak along its ray instead of the centre's. Stored
|
|
1833
|
+
// as g = n.xy / (n . mu): a corner at view offset d (in the plane z = -depth) lands at
|
|
1834
|
+
// depth / (1 + g . d). Orthographic rays are parallel, so there n = adj(Sigma) e_z and
|
|
1835
|
+
// the corner depth is depth + g . d with g = n.xy / n.z. Bounded so every corner stays
|
|
1836
|
+
// within 2 sqrt(2) sigma_z of the centre (the occlusion cull's front margin) and the
|
|
1837
|
+
// perspective scale stays positive, and so no corner crosses the near plane or the
|
|
1838
|
+
// raster's far clamp: a corner clamped there while the others are not bends the quad, whose
|
|
1839
|
+
// two triangles then take different depths and show as polygons (large far splats, the
|
|
1840
|
+
// sky, sit right at a far plane fitted to the scene). A centre beyond the clamp stays flat.
|
|
1841
|
+
let ortho = uniforms.isOrtho != 0u;
|
|
1842
|
+
let a00 = c11 * c22 - c12 * c12;
|
|
1843
|
+
let a01 = c02 * c12 - c01 * c22;
|
|
1844
|
+
let a02 = c01 * c12 - c02 * c11;
|
|
1845
|
+
let a11 = c00 * c22 - c02 * c02;
|
|
1846
|
+
let a12 = c01 * c02 - c00 * c12;
|
|
1847
|
+
let a22 = c00 * c11 - c01 * c01;
|
|
1848
|
+
let toward = select(viewCenter.xyz, vec3f(0.0, 0.0, -1.0), ortho);
|
|
1849
|
+
let n = vec3f(
|
|
1850
|
+
a00 * toward.x + a01 * toward.y + a02 * toward.z,
|
|
1851
|
+
a01 * toward.x + a11 * toward.y + a12 * toward.z,
|
|
1852
|
+
a02 * toward.x + a12 * toward.y + a22 * toward.z
|
|
1853
|
+
);
|
|
1854
|
+
let denom = select(dot(n, viewCenter.xyz), n.z, ortho);
|
|
1855
|
+
var g = vec2f(0.0);
|
|
1856
|
+
if (abs(denom) > 1e-20) {
|
|
1857
|
+
g = n.xy / denom;
|
|
1858
|
+
// view units per pixel at the centre's depth, and the quad's half extents in them
|
|
1859
|
+
let viewScale = select(depth, 1.0, ortho) / focal;
|
|
1860
|
+
let ext = (abs(axis1) + abs(axis2)) * viewScale;
|
|
1861
|
+
let bound = abs(g.x) * ext.x + abs(g.y) * ext.y;
|
|
1862
|
+
let margin = 2.8284 * sqrt(max(c22, 0.0));
|
|
1863
|
+
let inRange = select(
|
|
1864
|
+
min(1.0 - depth / uniforms.depthFar, depth / max(uniforms.depthNear, 1e-6) - 1.0),
|
|
1865
|
+
min(uniforms.depthFar - depth, depth - uniforms.depthNear),
|
|
1866
|
+
ortho
|
|
1867
|
+
);
|
|
1868
|
+
let limit = max(0.0, min(select(min(0.25, margin / max(depth, 1e-6)), margin, ortho), 0.999 * inRange));
|
|
1869
|
+
if (bound > limit) {
|
|
1870
|
+
g *= limit / max(bound, 1e-20);
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
result.words[5] = pack2x16float(g);
|
|
1874
|
+
// the stable id the coverage hash seeds from: the slot, salted by the file for sources
|
|
1875
|
+
// whose indices restart per file
|
|
1876
|
+
result.words[6] = slot ^ (file * 2654435761u);
|
|
1877
|
+
return result;
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
var<workgroup> wgCount: atomic<u32>;
|
|
1881
|
+
var<workgroup> wgOccluded: atomic<u32>;
|
|
1882
|
+
var<workgroup> wgBase: u32;
|
|
1883
|
+
${t?`var<workgroup> wgBuckets: array<atomic<u32>, ${le}>;`:""}
|
|
1884
|
+
|
|
1885
|
+
@compute @workgroup_size(${ni})
|
|
1886
|
+
fn main(
|
|
1887
|
+
@builtin(workgroup_id) wg: vec3u,
|
|
1888
|
+
@builtin(num_workgroups) numWorkgroups: vec3u,
|
|
1889
|
+
@builtin(local_invocation_index) local: u32
|
|
1890
|
+
) {
|
|
1891
|
+
// no early returns: the barriers below need every thread of the workgroup
|
|
1892
|
+
let chunkIndex = wg.x + wg.y * numWorkgroups.x;
|
|
1893
|
+
${t?" atomicStore(&wgBuckets[local], 0u);":""}
|
|
1894
|
+
var projected: Projected;
|
|
1895
|
+
projected.valid = false;
|
|
1896
|
+
projected.occluded = false;
|
|
1897
|
+
if (chunkIndex < uniforms.numChunks) {
|
|
1898
|
+
let chunk = chunks[uniforms.chunkBase + chunkIndex];
|
|
1899
|
+
let visible = (nodeVisible[chunk.z >> 5u] >> (chunk.z & 31u)) & 1u;
|
|
1900
|
+
if (visible != 0u && local < chunk.y) {
|
|
1901
|
+
projected = project(chunk.x + local, chunk.w >> 16u);
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
// survivors claim consecutive slots: one workgroup-local count, one global atomic
|
|
1906
|
+
var localSlot = 0u;
|
|
1907
|
+
if (projected.valid) {
|
|
1908
|
+
localSlot = atomicAdd(&wgCount, 1u);
|
|
1909
|
+
}
|
|
1910
|
+
if (projected.occluded) {
|
|
1911
|
+
atomicAdd(&wgOccluded, 1u);
|
|
1912
|
+
}
|
|
1913
|
+
workgroupBarrier();
|
|
1914
|
+
if (local == 0u) {
|
|
1915
|
+
let n = atomicLoad(&wgCount);
|
|
1916
|
+
wgBase = select(0u, atomicAdd(&counter[0], n), n > 0u);
|
|
1917
|
+
let o = atomicLoad(&wgOccluded);
|
|
1918
|
+
if (o > 0u) {
|
|
1919
|
+
atomicAdd(&counter[1], o);
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
workgroupBarrier();
|
|
1923
|
+
if (projected.valid) {
|
|
1924
|
+
let base = (wgBase + localSlot) * ${mt}u;
|
|
1925
|
+
for (var i = 0u; i < ${mt}u; i++) {
|
|
1926
|
+
cache[base + i] = projected.words[i];
|
|
1927
|
+
}
|
|
1928
|
+
${t?" atomicAdd(&wgBuckets[projected.words[3] >> 24u], 1u);":""}
|
|
1929
|
+
}
|
|
1930
|
+
${t?`
|
|
1931
|
+
// a chunk's splats are spatially coherent, so few of its buckets are non-empty: one
|
|
1932
|
+
// workgroup count each, then one global atomic per non-empty bucket
|
|
1933
|
+
workgroupBarrier();
|
|
1934
|
+
let bucketCount = atomicLoad(&wgBuckets[local]);
|
|
1935
|
+
if (bucketCount > 0u) {
|
|
1936
|
+
atomicAdd(&buckets[local], bucketCount);
|
|
1937
|
+
}
|
|
1938
|
+
`:""}
|
|
1939
|
+
}
|
|
1940
|
+
`,Nd=`
|
|
1941
|
+
// [0, ${le}) counts, [${le}, ${2*le}) offsets
|
|
1942
|
+
@group(0) @binding(0) var<storage, read_write> buckets: array<u32>;
|
|
1943
|
+
|
|
1944
|
+
var<workgroup> partial: array<u32, ${le}>;
|
|
1945
|
+
|
|
1946
|
+
@compute @workgroup_size(${le})
|
|
1947
|
+
fn main(@builtin(local_invocation_index) local: u32) {
|
|
1948
|
+
partial[local] = buckets[local];
|
|
1949
|
+
for (var stride = 1u; stride < ${le}u; stride <<= 1u) {
|
|
1950
|
+
workgroupBarrier();
|
|
1951
|
+
let value = select(0u, partial[local - stride], local >= stride);
|
|
1952
|
+
workgroupBarrier();
|
|
1953
|
+
partial[local] += value;
|
|
1954
|
+
}
|
|
1955
|
+
// inclusive to exclusive
|
|
1956
|
+
buckets[${le}u + local] = partial[local] - buckets[local];
|
|
1957
|
+
}
|
|
1958
|
+
`,qd=`
|
|
1959
|
+
@group(0) @binding(0) var<storage, read> counter: array<u32>;
|
|
1960
|
+
@group(0) @binding(1) var<storage, read> cache: array<u32>;
|
|
1961
|
+
@group(0) @binding(2) var<storage, read_write> buckets: array<atomic<u32>>;
|
|
1962
|
+
@group(0) @binding(3) var<storage, read_write> ordered: array<u32>;
|
|
1963
|
+
|
|
1964
|
+
var<workgroup> wgCounts: array<atomic<u32>, ${le}>;
|
|
1965
|
+
var<workgroup> wgBase: array<u32, ${le}>;
|
|
1966
|
+
|
|
1967
|
+
@compute @workgroup_size(${le})
|
|
1968
|
+
fn main(
|
|
1969
|
+
@builtin(workgroup_id) wg: vec3u,
|
|
1970
|
+
@builtin(num_workgroups) numWorkgroups: vec3u,
|
|
1971
|
+
@builtin(local_invocation_index) local: u32
|
|
1972
|
+
) {
|
|
1973
|
+
// no early returns: the barriers need every thread
|
|
1974
|
+
atomicStore(&wgCounts[local], 0u);
|
|
1975
|
+
let slot = (wg.x + wg.y * numWorkgroups.x) * ${le}u + local;
|
|
1976
|
+
let live = slot < counter[0];
|
|
1977
|
+
var key = 0u;
|
|
1978
|
+
var rank = 0u;
|
|
1979
|
+
workgroupBarrier();
|
|
1980
|
+
if (live) {
|
|
1981
|
+
key = cache[slot * ${mt}u + 3u] >> 24u;
|
|
1982
|
+
rank = atomicAdd(&wgCounts[key], 1u);
|
|
1983
|
+
}
|
|
1984
|
+
workgroupBarrier();
|
|
1985
|
+
let count = atomicLoad(&wgCounts[local]);
|
|
1986
|
+
if (count > 0u) {
|
|
1987
|
+
wgBase[local] = atomicAdd(&buckets[${le}u + local], count);
|
|
1988
|
+
}
|
|
1989
|
+
workgroupBarrier();
|
|
1990
|
+
if (live) {
|
|
1991
|
+
ordered[wgBase[key] + rank] = slot;
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
`,ln=128,Wo=.999999,$d=`
|
|
1995
|
+
attribute vertex_position: vec3f;
|
|
1996
|
+
|
|
1997
|
+
// width, height, 2 / width, 2 / height
|
|
1998
|
+
uniform viewportSize: vec4f;
|
|
1999
|
+
// clip z = a * viewDepth + b (x, y); z = 1 for an orthographic camera
|
|
2000
|
+
uniform clipZParams: vec4f;
|
|
2001
|
+
// set by the engine's forward renderer for the target being rendered
|
|
2002
|
+
uniform projectionFlipY: f32;
|
|
2003
|
+
// focal length in pixels (x, y), for the popless corner depths
|
|
2004
|
+
uniform focalParams: vec4f;
|
|
2005
|
+
|
|
2006
|
+
var<storage, read> splatCache: array<u32>;
|
|
2007
|
+
var<storage, read> splatCount: array<u32>;
|
|
2008
|
+
#ifdef SSE_ORDERED
|
|
2009
|
+
// cache slots in draw order (variant order:bucket)
|
|
2010
|
+
var<storage, read> orderedSlots: array<u32>;
|
|
2011
|
+
#endif
|
|
2012
|
+
|
|
2013
|
+
// screen-linear: the corners share one screen footprint whatever depth they carry
|
|
2014
|
+
varying @interpolate(linear) gaussianUV: vec2f;
|
|
2015
|
+
varying @interpolate(flat, either) packedColor: u32;
|
|
2016
|
+
varying @interpolate(flat, either) packedAlpha: u32;
|
|
2017
|
+
varying @interpolate(flat, either) splatId: u32;
|
|
2018
|
+
|
|
2019
|
+
const discardPosition = vec4f(0.0, 0.0, 2.0, 1.0);
|
|
2020
|
+
|
|
2021
|
+
@vertex
|
|
2022
|
+
fn vertexMain(input: VertexInput) -> VertexOutput {
|
|
2023
|
+
var output: VertexOutput;
|
|
2024
|
+
let order = pcInstanceIndex * ${ln}u + u32(vertex_position.z);
|
|
2025
|
+
// the indirect instance count rounds up to whole instances; the tail is discarded here
|
|
2026
|
+
if (order >= splatCount[0]) {
|
|
2027
|
+
output.position = discardPosition;
|
|
2028
|
+
return output;
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
#ifdef SSE_ORDERED
|
|
2032
|
+
let base = orderedSlots[order] * ${mt}u;
|
|
2033
|
+
#else
|
|
2034
|
+
let base = order * ${mt}u;
|
|
2035
|
+
#endif
|
|
2036
|
+
let maxRadius = min(1024.0, min(uniform.viewportSize.x, uniform.viewportSize.y));
|
|
2037
|
+
let ndcRange = vec2f(1.0) + vec2f(4.0 * maxRadius) / uniform.viewportSize.xy;
|
|
2038
|
+
let ndc = unpack2x16snorm(splatCache[base]) * ndcRange;
|
|
2039
|
+
let depth = bitcast<f32>(splatCache[base + 1u]);
|
|
2040
|
+
let ortho = uniform.clipZParams.z != 0.0;
|
|
2041
|
+
|
|
2042
|
+
let axis1 = unpack2x16float(splatCache[base + 2u]);
|
|
2043
|
+
let word3 = splatCache[base + 3u];
|
|
2044
|
+
let len2 = unpack2x16float(word3).x;
|
|
2045
|
+
let axis2 = len2 * normalize(vec2f(axis1.y, -axis1.x));
|
|
2046
|
+
|
|
2047
|
+
let corner = vertex_position.xy;
|
|
2048
|
+
let pixelOffset = corner.x * axis1 + corner.y * axis2;
|
|
2049
|
+
#ifdef SSE_POPLESS
|
|
2050
|
+
// the corner's depth on the splat's plane (see the projector): its view-space offset
|
|
2051
|
+
// at the centre's depth, times the stored gradient
|
|
2052
|
+
let g = unpack2x16float(splatCache[base + 5u]);
|
|
2053
|
+
let shift = dot(g, pixelOffset * select(depth, 1.0, ortho) / uniform.focalParams.xy);
|
|
2054
|
+
let cornerDepth = select(depth / (1.0 + shift), depth + shift, ortho);
|
|
2055
|
+
#else
|
|
2056
|
+
let cornerDepth = depth;
|
|
2057
|
+
#endif
|
|
2058
|
+
let w = select(cornerDepth, 1.0, ortho);
|
|
2059
|
+
let ndcCorner = ndc + pixelOffset * uniform.viewportSize.zw;
|
|
2060
|
+
// clip z clamped into range like the engine's splat shader, so splats beyond the far plane
|
|
2061
|
+
// still draw; just inside it, since the compose and the accumulation read depth 1 as empty
|
|
2062
|
+
let pos = vec4f(ndcCorner * w, clamp(uniform.clipZParams.x * cornerDepth + uniform.clipZParams.y, 0.0, w * ${Wo}), w);
|
|
2063
|
+
output.position = vec4f(pos.x, pos.y * uniform.projectionFlipY, pos.z, pos.w);
|
|
2064
|
+
output.gaussianUV = corner;
|
|
2065
|
+
output.packedColor = splatCache[base + 4u];
|
|
2066
|
+
output.packedAlpha = word3 >> 16u;
|
|
2067
|
+
output.splatId = splatCache[base + 6u];
|
|
2068
|
+
return output;
|
|
2069
|
+
}
|
|
2070
|
+
`,Hd=`
|
|
2071
|
+
varying @interpolate(linear) gaussianUV: vec2f;
|
|
2072
|
+
varying @interpolate(flat, either) packedColor: u32;
|
|
2073
|
+
varying @interpolate(flat, either) packedAlpha: u32;
|
|
2074
|
+
varying @interpolate(flat, either) splatId: u32;
|
|
2075
|
+
|
|
2076
|
+
uniform sseAlphaClip: f32;
|
|
2077
|
+
// 0 while the camera rests, so a still frame reproduces itself; the frame index once TAA
|
|
2078
|
+
// accumulates
|
|
2079
|
+
uniform frameSeed: u32;
|
|
2080
|
+
|
|
2081
|
+
const EXP4 = exp(-4.0);
|
|
2082
|
+
const INV_EXP4 = 1.0 / (1.0 - EXP4);
|
|
2083
|
+
|
|
2084
|
+
// the engine's falloff: zero at the quad edge, which sits at 2 sqrt(2) sigma
|
|
2085
|
+
fn normExp(x: f32) -> f32 {
|
|
2086
|
+
return (exp(x * -4.0) - EXP4) * INV_EXP4;
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
// integer hash (Wellons' prospector mix)
|
|
2090
|
+
fn hashU32(x: u32) -> u32 {
|
|
2091
|
+
var v = x;
|
|
2092
|
+
v ^= v >> 16u;
|
|
2093
|
+
v *= 0x7feb352du;
|
|
2094
|
+
v ^= v >> 15u;
|
|
2095
|
+
v *= 0x846ca68bu;
|
|
2096
|
+
v ^= v >> 16u;
|
|
2097
|
+
return v;
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
@fragment
|
|
2101
|
+
fn fragmentMain(input: FragmentInput) -> FragmentOutput {
|
|
2102
|
+
var output: FragmentOutput;
|
|
2103
|
+
let radius = dot(gaussianUV, gaussianUV);
|
|
2104
|
+
if (radius > 1.0) {
|
|
2105
|
+
discard;
|
|
2106
|
+
}
|
|
2107
|
+
let opacity = f32(packedAlpha & 0xffu) / 255.0;
|
|
2108
|
+
let alpha = normExp(radius) * opacity;
|
|
2109
|
+
if (alpha < uniform.sseAlphaClip) {
|
|
2110
|
+
discard;
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
let pix = vec2u(pcPosition.xy);
|
|
2114
|
+
#ifdef SSE_SPP_QUAD
|
|
2115
|
+
// one sample per pixel, thresholds stratified over each 2x2 pixel quad: the quad's four
|
|
2116
|
+
// pixels take the four strata of [0, 1) in a per-(quad, splat) scrambled order, so a
|
|
2117
|
+
// splat with alpha a covers 4a +- 1 of its quad and the compose's quad mean is nearly
|
|
2118
|
+
// exact. Hashing quad and splat keeps overlapping splats decorrelated
|
|
2119
|
+
let quad = pix >> vec2u(1u);
|
|
2120
|
+
let h = hashU32((quad.x * 1973u) ^ (quad.y * 9277u) ^ ((splatId + 1u) * 26699u) ^ uniform.frameSeed);
|
|
2121
|
+
let stratum = ((pix.y & 1u) * 2u + (pix.x & 1u)) ^ (h & 3u);
|
|
2122
|
+
let rnd = (f32(stratum) + f32(h >> 8u) * (1.0 / 16777216.0)) * 0.25;
|
|
2123
|
+
#else
|
|
2124
|
+
let h = hashU32((pix.x * 1973u) ^ (pix.y * 9277u) ^ ((splatId + 1u) * 26699u) ^ uniform.frameSeed);
|
|
2125
|
+
let rnd = f32(h >> 8u) * (1.0 / 16777216.0);
|
|
2126
|
+
#endif
|
|
2127
|
+
if (rnd >= alpha) {
|
|
2128
|
+
discard;
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
let bits = packedColor;
|
|
2132
|
+
let color = vec3f(vec3u(bits, bits >> 10u, bits >> 20u) & vec3u(1023u)) * (f32(1u << (bits >> 30u)) / 1023.0);
|
|
2133
|
+
// opaque coverage: the compose reads alpha 1 as "a sample landed here"
|
|
2134
|
+
output.color = vec4f(color, 1.0);
|
|
2135
|
+
return output;
|
|
2136
|
+
}
|
|
2137
|
+
`,jd=`
|
|
2138
|
+
struct ReduceUniforms {
|
|
2139
|
+
width: u32,
|
|
2140
|
+
height: u32,
|
|
2141
|
+
blocksX: u32,
|
|
2142
|
+
blocksY: u32
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
@group(0) @binding(0) var prevDepth: texture_depth_2d;
|
|
2146
|
+
@group(0) @binding(1) var<storage, read_write> blockMax: array<u32>;
|
|
2147
|
+
@group(0) @binding(2) var<uniform> uniforms: ReduceUniforms;
|
|
2148
|
+
|
|
2149
|
+
var<workgroup> wgMax: atomic<u32>;
|
|
2150
|
+
|
|
2151
|
+
@compute @workgroup_size(8, 8)
|
|
2152
|
+
fn main(
|
|
2153
|
+
@builtin(workgroup_id) wg: vec3u,
|
|
2154
|
+
@builtin(local_invocation_id) lid: vec3u,
|
|
2155
|
+
@builtin(local_invocation_index) li: u32
|
|
2156
|
+
) {
|
|
2157
|
+
let pixel = wg.xy * 8u + lid.xy;
|
|
2158
|
+
// pixels off the texture count as far, so an edge block never culls
|
|
2159
|
+
var depth = 1.0;
|
|
2160
|
+
if (pixel.x < uniforms.width && pixel.y < uniforms.height) {
|
|
2161
|
+
depth = textureLoad(prevDepth, vec2i(pixel), 0);
|
|
2162
|
+
}
|
|
2163
|
+
atomicMax(&wgMax, bitcast<u32>(depth));
|
|
2164
|
+
workgroupBarrier();
|
|
2165
|
+
if (li == 0u) {
|
|
2166
|
+
blockMax[wg.y * uniforms.blocksX + wg.x] = atomicLoad(&wgMax);
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
`,Wd=`
|
|
2170
|
+
struct ReduceUniforms {
|
|
2171
|
+
blocksX1: u32,
|
|
2172
|
+
blocksY1: u32,
|
|
2173
|
+
blocksX2: u32,
|
|
2174
|
+
blocksY2: u32
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
@group(0) @binding(0) var<storage, read> level1: array<u32>;
|
|
2178
|
+
@group(0) @binding(1) var<storage, read_write> level2: array<u32>;
|
|
2179
|
+
@group(0) @binding(2) var<uniform> uniforms: ReduceUniforms;
|
|
2180
|
+
|
|
2181
|
+
@compute @workgroup_size(64)
|
|
2182
|
+
fn main(@builtin(global_invocation_id) gid: vec3u) {
|
|
2183
|
+
let index = gid.x;
|
|
2184
|
+
if (index >= uniforms.blocksX2 * uniforms.blocksY2) {
|
|
2185
|
+
return;
|
|
2186
|
+
}
|
|
2187
|
+
let bx = index % uniforms.blocksX2;
|
|
2188
|
+
let by = index / uniforms.blocksX2;
|
|
2189
|
+
var farthest = 0u;
|
|
2190
|
+
for (var dy = 0u; dy < 4u; dy++) {
|
|
2191
|
+
for (var dx = 0u; dx < 4u; dx++) {
|
|
2192
|
+
let x = bx * 4u + dx;
|
|
2193
|
+
let y = by * 4u + dy;
|
|
2194
|
+
if (x < uniforms.blocksX1 && y < uniforms.blocksY1) {
|
|
2195
|
+
farthest = max(farthest, level1[y * uniforms.blocksX1 + x]);
|
|
2196
|
+
} else {
|
|
2197
|
+
farthest = max(farthest, bitcast<u32>(1.0));
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
level2[index] = farthest;
|
|
2202
|
+
}
|
|
2203
|
+
`,on=new x,Et=new Q,Go=class{kind="direct";device;resource=null;set=null;constants=new Map;patched=null;constructor(e){this.device=e}update(e,t){this.resource=e.files[0]?.resource??this.resource,this.set=e,this.constants.clear();for(let s of e.files){let n=s.modelMatrix;n.getScale(on),Et.setFromMat4(n),Et.w<0&&Et.mulScalar(-1),this.constants.set(s.fileIndex,{rotation:[Et.x,Et.y,Et.z,Et.w],scale:[on.x,on.y,on.z,0]})}this.patchWorkBuffer(t.world.workBuffer)}patchWorkBuffer(e){if(this.patched?.workBuffer===e)return;this.restoreWorkBuffer(),this.patched={workBuffer:e,render:e.render,renderColor:e.renderColor};let t=()=>{};e.render=t,e.renderColor=t}restoreWorkBuffer(){if(!this.patched)return;let{workBuffer:e,render:t,renderColor:s}=this.patched;e.render=t,e.renderColor=s,this.patched=null}readChunk(e){let t=this.require().format;return`
|
|
2204
|
+
#include "halfTypesCS"
|
|
2205
|
+
#include "gsplatEvalSHVS"
|
|
2206
|
+
${t.getComputeInputDeclarations(e).replace(/texture_2d<uff>/g,"texture_2d<f32>")}
|
|
2207
|
+
${t.getReadCode()}
|
|
2208
|
+
|
|
2209
|
+
var<private> srcWorldCenter: vec3f;
|
|
2210
|
+
var<private> srcColorCache: vec4f;
|
|
2211
|
+
|
|
2212
|
+
fn quatMulF(a: vec4f, b: vec4f) -> vec4f {
|
|
2213
|
+
return vec4f(a.w * b.xyz + b.w * a.xyz + cross(a.xyz, b.xyz), a.w * b.w - dot(a.xyz, b.xyz));
|
|
2214
|
+
}
|
|
2215
|
+
|
|
2216
|
+
// v rotated by the inverse of q (x, y, z, w)
|
|
2217
|
+
fn quatRotateInvF(q: vec4f, v: vec3f) -> vec3f {
|
|
2218
|
+
let u = -q.xyz;
|
|
2219
|
+
return v + 2.0 * cross(u, cross(u, v) + q.w * v);
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2222
|
+
fn srcCenter() -> vec3f {
|
|
2223
|
+
srcWorldCenter = (uniforms.model * vec4f(getCenter(), 1.0)).xyz;
|
|
2224
|
+
return srcWorldCenter;
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
fn srcOpacity() -> f32 {
|
|
2228
|
+
srcColorCache = getColor();
|
|
2229
|
+
return srcColorCache.a;
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
// the format returns (w, x, y, z); world = model * source
|
|
2233
|
+
fn srcRotation() -> vec4f {
|
|
2234
|
+
let q = quatMulF(uniforms.modelRotation, getRotation().yzwx);
|
|
2235
|
+
return q.wxyz;
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
fn srcScale() -> vec3f {
|
|
2239
|
+
return uniforms.modelScale.xyz * getScale();
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2242
|
+
fn srcColor() -> vec3f {
|
|
2243
|
+
var color = srcColorCache.rgb;
|
|
2244
|
+
#if SH_BANDS > 0
|
|
2245
|
+
// the view direction in the file's space; orthographic rays all run along the camera forward
|
|
2246
|
+
let view = uniforms.view;
|
|
2247
|
+
let orthoDir = vec3f(0.0, 0.0, -1.0) * mat3x3f(view[0].xyz, view[1].xyz, view[2].xyz);
|
|
2248
|
+
let viewDir = select(srcWorldCenter - uniforms.cameraPosition.xyz, orthoDir, uniforms.isOrtho != 0u);
|
|
2249
|
+
let dir = normalize(quatRotateInvF(uniforms.modelRotation, viewDir));
|
|
2250
|
+
var sh: array<half3, SH_COEFFS>;
|
|
2251
|
+
var shScale: f32;
|
|
2252
|
+
readSHData(&sh, &shScale);
|
|
2253
|
+
color += vec3f(evalSH(&sh, dir) * half(shScale));
|
|
2254
|
+
#endif
|
|
2255
|
+
return color;
|
|
2256
|
+
}
|
|
2257
|
+
`}bindFormats(){let e=this.require().format,t=e.getComputeBindFormats();for(let s of t)e.streams.find(o=>o.name===s.name)?.format===zn&&(s.sampleType=Pi);return t}shaderIncludes(){return new Map(ie.get(this.device,Ue))}shaderDefines(){let e=new Map;return this.require().configureMaterialDefines(e),e}chunkBase(e){return e.sourceBase}dispatchPlan(e){let t=[],s=0,n=this.resource?.format.hash;for(let o of e.files){let r=0;for(let a of o.nodes)r+=Math.ceil(a.count/256);r>0&&o.resource.format.hash===n&&t.push({fileIndex:o.fileIndex,chunkBase:s,chunkCount:r}),s+=r}return t}bind(e,t,s){let n=s.files[t.fileIndex],{resource:o}=n;for(let[a,l]of o.streams.textures)e.setParameter(a,l);for(let[a,l]of o.parameters)e.setParameter(a,l);let r=this.constants.get(n.fileIndex);e.setParameter("model",n.modelMatrix.data),e.setParameter("modelRotation",r.rotation),e.setParameter("modelScale",r.scale)}textureSize(e){return this.set?.files[e.fileIndex].resource.textureDimensions.x??0}gpuBytes(){return 0}shaderKey(){let e=this.resource;if(!e)return"direct:";let t=this.shaderDefines();return`direct:${e.format.hash}:${[...t].map(([s,n])=>`${s}=${n}`).join(",")}`}destroy(){this.restoreWorkBuffer(),this.resource=null,this.set=null,this.constants.clear()}require(){if(!this.resource)throw new Error("DirectSplatSource: update() has not run");return this.resource}},Uo=class{kind="workbuffer";workBuffer=null;update(e,t){this.workBuffer=t.world.workBuffer}readChunk(e){let t=this.require().format;return`
|
|
2258
|
+
${t.getComputeInputDeclarations(e)}
|
|
2259
|
+
${t.getReadCode()}
|
|
2260
|
+
fn srcCenter() -> vec3f { return getCenter(); }
|
|
2261
|
+
fn srcOpacity() -> f32 { return getOpacity(); }
|
|
2262
|
+
fn srcRotation() -> vec4f { return getRotation(); }
|
|
2263
|
+
fn srcScale() -> vec3f { return getScale(); }
|
|
2264
|
+
fn srcColor() -> vec3f { return getColor(); }
|
|
2265
|
+
`}bindFormats(){return this.require().format.getComputeBindFormats()}shaderIncludes(){}shaderDefines(){}chunkBase(e){return e.slotBase}dispatchPlan(e,t){return[{fileIndex:-1,chunkBase:0,chunkCount:t}]}bind(e){let t=this.require();for(let s of t.format.streams)e.setParameter(s.name,t.getTexture(s.name))}textureSize(){return this.require().textureSize}gpuBytes(){return 0}shaderKey(){let e=this.workBuffer;return e?`wb:${e.format.streams.map(t=>t.name).join(",")}`:"wb:"}destroy(){this.workBuffer=null}require(){if(!this.workBuffer)throw new Error("WorkBufferSplatSource: update() has not run");return this.workBuffer}},Gd=()=>({spp:"quad",compose:"blend",cull:"auto",order:"bucket",contribution:0,popless:"on",taa:"on",taaMax:256,taaMoveMax:16,taaClip:1.25,taaFilter:"cubic",taaReproj:"sample",taaMotion:4,taaDebug:0,taaFlip:0,colorMax:8,units:"engine"}),Sr=i=>Math.min(jo,Math.max(1,Math.round(i))),Cr=i=>{let e=Gd();for(let t of(i??"").split(",")){let[s,n]=t.split(":").map(o=>o.trim());!s||n===void 0||(s==="spp"&&(n==="1"||n==="quad")&&(e.spp=n),s==="compose"&&(n==="blend"||n==="none"||n==="depth")&&(e.compose=n),s==="popless"&&(n==="on"||n==="off")&&(e.popless=n),s==="taa"&&(n==="on"||n==="off")&&(e.taa=n),s==="taaMax"&&Number.isFinite(Number(n))&&(e.taaMax=Sr(Number(n))),s==="taaMoveMax"&&Number.isFinite(Number(n))&&(e.taaMoveMax=Sr(Number(n))),s==="taaDebug"&&Number.isFinite(Number(n))&&(e.taaDebug=Number(n)),s==="taaClip"&&Number.isFinite(Number(n))&&(e.taaClip=Math.max(0,Number(n))),s==="taaFilter"&&(n==="linear"||n==="cubic")&&(e.taaFilter=n),s==="taaReproj"&&(n==="sample"||n==="history")&&(e.taaReproj=n),s==="taaMotion"&&Number.isFinite(Number(n))&&(e.taaMotion=Math.max(0,Number(n))),s==="taaFlip"&&Number.isFinite(Number(n))&&(e.taaFlip=Number(n)),s==="colorMax"&&Number.isFinite(Number(n))&&(e.colorMax=Math.max(0,Number(n))),s==="units"&&(n==="px"||n==="engine")&&(e.units=n),s==="cull"&&(n==="off"||n==="l1"||n==="l2"||n==="auto")&&(e.cull=n),s==="order"&&(n==="append"||n==="bucket")&&(e.order=n),s==="contribution"&&Number.isFinite(Number(n))&&(e.contribution=Math.max(0,Number(n))))}return e},wn=class extends Ai{forward;camera;instances;constructor(e,t,s,n,o="sse-splat-raster"){super(e),this.forward=t,this.camera=s,this.instances=n,this.name=o}execute(){this.forward.renderForwardLayer(this.camera.camera,this.renderTarget,null,void 0,Gi,{meshInstances:this.instances})}},Ud=(i,e)=>{let t=new Float32Array(e*4*3),s=new Uint32Array(e*6);for(let o=0;o<e;++o){t.set([-1,-1,o,1,-1,o,1,1,o,-1,1,o],o*12);let r=o*4;s.set([r,r+1,r+2,r,r+2,r+3],o*6)}let n=new Ye(i);return n.setPositions(t,3),n.setIndices(s),n.update(rt),n},Pr=i=>{let e=new Ye(i);return e.setPositions(new Float32Array([-1,-1,1,-1,1,1,-1,1]),2),e.setIndices(new Uint16Array([0,1,2,0,2,3])),e.update(rt),e},Ut=new yt,bn=class i{variant;source;app;device;camera;worldLayer;provider;unsubscribe;enabled=!0;set=null;numChunks=0;chunkData=new Uint32Array(0);chunkBuffer=null;nodeVisibleData=new Uint32Array(0);nodeVisibleBuffer=null;cacheBuffer=null;cacheSlots=0;counter;projectorShaders=new Map;projectorComputes=new Map;projectorBindGroupFormat=null;projectorSourceKey="";args;argsBindGroupFormat;orderBuckets;orderedBuffer=null;orderScan;orderScatter;orderFormats=[];occL1;occL2;occBlocks={x1:0,y1:0,x2:0,y2:0};reduceL1;reduceL2;reduceFormats=[];prevValid=!1;prevViewProjection=new U;prevView=new U;prevProjection=new U;prevClipZ=[0,0,0,0];prevViewport=[0,0];prevFocal=[0,0];prevFlip=1;prevWidth=0;prevHeight=0;prevVersion=-1;residentAllocIds=new Set;nodesRemoved=!1;prevOrtho=!1;culling=!1;get cullSuspended(){return this.variant.cull==="auto"&&(!this.cullActive||this.cullSuspendedFrames>0)}cullActive=!0;cullSuspendedFrames=0;cullStatsPending=!1;static cacheFits(e,t){let s=e.wgpu?.limits?.maxStorageBufferBindingSize;return!s||Math.ceil(t*1.25)*mt*4<=s}static CULL_SUSPEND_FRAMES=60;static CULL_MIN_FRACTION=.08;allowQueryCull=!1;colorTexture;depthTexture;target;rasterMesh;rasterMaterial;rasterInstance;rasterPass;taaColor=[];taaInfo=[];taaTargets=[];taaWrite=0;taaMesh;taaMaterial;taaInstance;taaPass;taaHistoryValid=!1;taaPrevViewProjection=new U;taaPrevView=new U;taaLastViewProjection=new U;taaLastView=new U;taaWidth=0;taaHeight=0;cameraWorld=new U;restFrames=0;depthFrame=null;depthFrameCount=0;depthFrameCamera=ds();frameWanted=!1;onFrameEnd=()=>{this.frameWanted&&(this.frameWanted=!1,this.app.renderNextFrame=!0)};frameIndex=0;userSeed=0;taaActive=!1;allowQueryTaa=!1;composeMesh;composeMaterial;composeInstance;frustum=new bi;sphere=new wi;shaderProjection=new U;viewProjection=new U;frameSeed=0;ready=!1;get frameDepthTexture(){return this.depthTexture}get gpuBytes(){let e=[this.chunkBuffer,this.nodeVisibleBuffer,this.cacheBuffer,this.orderedBuffer,this.counter,this.orderBuckets,this.occL1,this.occL2],t=0;for(let s of e)t+=s?.byteSize??0;t+=this.colorTexture.gpuSize+this.depthTexture.gpuSize;for(let s of[...this.taaColor,...this.taaInfo])t+=s.gpuSize;return t+this.source.gpuBytes()}get seed(){return this.frameSeed}set seed(e){this.userSeed=e>>>0,this.frameSeed=this.userSeed,this.app.renderNextFrame=!0}async readStats(){let e=new Uint32Array(2);return await this.counter.read(0,8,e,!0),{survivors:e[0],occluded:e[1]}}reduceDepth(e,t){let{device:s}=this,n=Math.ceil(e/8),o=Math.ceil(t/8),r=Math.ceil(n/4),a=Math.ceil(o/4);(this.occBlocks.x1!==n||this.occBlocks.y1!==o)&&(this.occL1.destroy(),this.occL2.destroy(),this.occL1=new fe(s,n*o*4,8),this.occL2=new fe(s,r*a*4,8),this.occBlocks={x1:n,y1:o,x2:r,y2:a});let l=this.reduceL1;l.setParameter("prevDepth",this.depthTexture),l.setParameter("blockMax",this.occL1),l.setParameter("width",e),l.setParameter("height",t),l.setParameter("blocksX",n),l.setParameter("blocksY",o),l.setupDispatch(n,o,1);let c=this.reduceL2;c.setParameter("level1",this.occL1),c.setParameter("level2",this.occL2),c.setParameter("blocksX1",n),c.setParameter("blocksY1",o),c.setParameter("blocksX2",r),c.setParameter("blocksY2",a),Fe.calcDispatchSize(Math.ceil(r*a/64),Ut),c.setupDispatch(Ut.x,Ut.y,1),s.computeDispatch([l],"sse-splat-reduce1"),s.computeDispatch([c],"sse-splat-reduce2")}activeSplats=0;constructor(e,t,s,n={}){this.app=e,this.device=e.graphicsDevice,this.camera=t,this.worldLayer=s,this.variant=Cr(n.variant);let{device:o}=this;n.source==="direct"?this.source=new Go(o):(n.source&&n.source!=="workbuffer"&&console.warn(`StochasticSplatRenderer: splat source '${n.source}' is not implemented yet, using the work buffer`),this.source=new Uo),this.counter=new fe(o,16,12),this.argsBindGroupFormat=new lt(o,[new Z("counter",X,!0),new Z("indirectDrawArgs",X),new Z("indirectDispatchArgs",X),new Bt("uniforms",X)]),this.args=new Fe(o,new ct(o,{name:"sse-splat-args",shaderLanguage:Ue,cshader:zd,computeUniformBufferFormats:{uniforms:new Nt(o,[new q("drawSlot",G),new q("indexCount",G),new q("quadsPerInstance",G),new q("dispatchSlot",G),new q("scatterWorkgroupSize",G)])},computeBindGroupFormat:this.argsBindGroupFormat}),"sse-splat-args"),this.orderBuckets=new fe(o,2*le*4,8);let r=new lt(o,[new Z("buckets",X)]),a=new lt(o,[new Z("counter",X,!0),new Z("cache",X,!0),new Z("buckets",X),new Z("ordered",X)]);this.orderFormats=[r,a],this.orderScan=new Fe(o,new ct(o,{name:"sse-splat-order-scan",shaderLanguage:Ue,cshader:Nd,computeBindGroupFormat:r}),"sse-splat-order-scan"),this.orderScatter=new Fe(o,new ct(o,{name:"sse-splat-order-scatter",shaderLanguage:Ue,cshader:qd,computeBindGroupFormat:a}),"sse-splat-order-scatter"),this.occL1=new fe(o,4,8),this.occL2=new fe(o,4,8);let l=new lt(o,[new Ei("prevDepth",X,Vn,Li,!1),new Z("blockMax",X),new Bt("uniforms",X)]),c=new lt(o,[new Z("level1",X,!0),new Z("level2",X),new Bt("uniforms",X)]);this.reduceFormats=[l,c],this.reduceL1=new Fe(o,new ct(o,{name:"sse-splat-reduce1",shaderLanguage:Ue,cshader:jd,computeUniformBufferFormats:{uniforms:new Nt(o,[new q("width",G),new q("height",G),new q("blocksX",G),new q("blocksY",G)])},computeBindGroupFormat:l}),"sse-splat-reduce1"),this.reduceL2=new Fe(o,new ct(o,{name:"sse-splat-reduce2",shaderLanguage:Ue,cshader:Wd,computeUniformBufferFormats:{uniforms:new Nt(o,[new q("blocksX1",G),new q("blocksY1",G),new q("blocksX2",G),new q("blocksY2",G)])},computeBindGroupFormat:c}),"sse-splat-reduce2"),this.colorTexture=new ze(o,{name:"sse-splat-color",width:4,height:4,format:ss,mipmaps:!1,minFilter:1,magFilter:1,addressU:1,addressV:1}),this.depthTexture=new ze(o,{name:"sse-splat-depth",width:4,height:4,format:ki,mipmaps:!1,addressU:1,addressV:1}),this.target=new Xe({name:"sse-splat-target",colorBuffer:this.colorTexture,depthBuffer:this.depthTexture,samples:1}),this.rasterMesh=Ud(o,ln),this.rasterMaterial=new Ze({uniqueName:"sse-splat-raster",vertexWGSL:$d,fragmentWGSL:Hd,attributes:{vertex_position:pe}}),this.rasterMaterial.blendType=Ms,this.rasterMaterial.depthWrite=!0,this.rasterMaterial.depthTest=!0,this.rasterMaterial.cull=0,this.rasterInstance=new ke(this.rasterMesh,this.rasterMaterial,new $t("sse-splat-raster")),this.rasterInstance.cull=!1,this.rasterInstance.castShadow=!1,this.rasterInstance.receiveShadow=!1,this.rasterPass=new wn(o,e.renderer,t,[this.rasterInstance]),this.rasterPass.init(this.target),this.rasterPass.setClearColor(new K(0,0,0,0)),this.rasterPass.setClearDepth(1),this.rasterPass.enabled=!1;for(let h=0;h<2;h++)this.taaColor.push(new ze(o,{name:`sse-splat-taa-color-${h}`,width:4,height:4,format:Si,mipmaps:!1,minFilter:0,magFilter:0,addressU:1,addressV:1})),this.taaInfo.push(new ze(o,{name:`sse-splat-taa-info-${h}`,width:4,height:4,format:Mi,mipmaps:!1,minFilter:0,magFilter:0,addressU:1,addressV:1})),this.taaTargets.push(new Xe({name:`sse-splat-taa-${h}`,colorBuffers:[this.taaColor[h],this.taaInfo[h]],depth:!1,samples:1}));this.taaMesh=Pr(o),this.taaMaterial=new Ze({uniqueName:"sse-splat-taa",vertexWGSL:Fd,fragmentWGSL:Id,attributes:{vertex_position:pe},fragmentOutputTypes:["uvec4","uint"]}),this.taaMaterial.blendType=Ms,this.taaMaterial.depthWrite=!1,this.taaMaterial.depthTest=!1,this.taaMaterial.cull=0,this.taaInstance=new ke(this.taaMesh,this.taaMaterial,new $t("sse-splat-taa")),this.taaInstance.cull=!1,this.taaInstance.castShadow=!1,this.taaInstance.receiveShadow=!1,this.taaPass=new wn(o,e.renderer,t,[this.taaInstance],"sse-splat-taa"),this.taaPass.init(this.taaTargets[0]),this.taaPass.enabled=!1,this.composeMesh=Pr(o),this.composeMaterial=new Ze({uniqueName:"sse-splat-compose",vertexWGSL:Vd,fragmentWGSL:Od,attributes:{vertex_position:pe}}),this.composeMaterial.blendType=Bn,this.composeMaterial.depthWrite=!0,this.composeMaterial.depthTest=!0,this.composeMaterial.cull=0,this.composeMaterial.setParameter("splatColor",this.colorTexture),this.composeMaterial.setParameter("splatDepth",this.depthTexture),this.composeMaterial.setParameter("taaColor",this.taaColor[0]),this.composeMaterial.setParameter("taaInfo",this.taaInfo[0]),this.composeInstance=new ke(this.composeMesh,this.composeMaterial,new $t("sse-splat-compose")),this.composeInstance.cull=!1,this.composeInstance.castShadow=!1,this.composeInstance.receiveShadow=!1,this.composeInstance.pick=!1,this.composeInstance.drawOrder=-1e3,this.composeInstance.calculateSortDistance=()=>Number.MAX_VALUE,this.applyVariant(),this.provider=new Ho(e,t,s),this.unsubscribe=this.provider.onFrame((h,d,u)=>this.frame(h,d,u)),e.on("frameend",this.onFrameEnd),this.attach()}setEnabled(e){this.enabled!==e&&(this.enabled=e,e?this.attach():this.detach(),this.app.renderNextFrame=!0)}setVariant(e){Object.assign(this.variant,Cr(e)),this.cullActive=!0,this.cullSuspendedFrames=0,this.applyVariant()}resetHistory(){this.taaHistoryValid=!1,this.restFrames=0,this.frameIndex=0}applyVariant(){this.resetHistory();let e=this.variant.spp==="quad";this.rasterMaterial.setDefine("SSE_SPP_QUAD",e?"":void 0),this.rasterMaterial.setDefine("SSE_ORDERED",this.variant.order==="bucket"?"":void 0),this.rasterMaterial.setDefine("SSE_POPLESS",this.variant.popless==="on"?"":void 0),this.composeMaterial.setDefine("SSE_SHOW_DEPTH",this.variant.compose==="depth"?"":void 0),this.composeMaterial.setDefine("SSE_SPP_QUAD",e?"":void 0),this.rasterMaterial.update(),this.composeMaterial.update(),this.setReady(this.ready),this.app.renderNextFrame=!0}setReady(e){this.ready=e,this.rasterPass.enabled=e,this.composeInstance.visible=e&&this.variant.compose!=="none"}attach(){let e=this.camera.camera.beforePasses;e.includes(this.rasterPass)||e.push(this.rasterPass),e.includes(this.taaPass)||e.push(this.taaPass),this.worldLayer.addMeshInstances([this.composeInstance]),this.provider.setActive(!0)}detach(){let e=this.camera.camera.beforePasses;for(let t of[this.rasterPass,this.taaPass]){let s=e.indexOf(t);s>=0&&e.splice(s,1)}this.resetHistory(),this.depthFrame=null,this.worldLayer.removeMeshInstances([this.composeInstance]),this.provider.setActive(!1)}frame(e,t,s){if(!this.enabled)return;let{device:n,camera:o}=this,r=o.camera;if(this.nodesRemoved=!1,t||this.set!==e){let $=new Set(e.nodes.map(F=>F.allocId));for(let F of this.residentAllocIds)if(!$.has(F)){this.nodesRemoved=!0;break}this.residentAllocIds=$,this.source.update(e,s),this.rebuildChunkTable(e),this.ensureCache(e.activeSplats),this.activeSplats=e.activeSplats,this.pruneProjectorComputes(e),this.set=e}if(this.numChunks===0){this.setReady(!1);return}let a=o.renderTarget,l=a?a.width:n.width,c=a?a.height:n.height;(this.target.width!==l||this.target.height!==c)&&this.target.resize(l,c);let h=r.viewMatrix,d=r.projectionMatrix,u=r.projection===qt,p=Qi.applyShaderProjectionTransform(d,this.shaderProjection,!1,!0);this.viewProjection.mul2(p,h);let g=[-p.data[10],p.data[14]],m=u?g:[1,g[1]/g[0]],f=[m[0],m[1],u?1:0,0];this.cullNodes(e,this.viewProjection);let v=[Math.abs(d.data[0])*l*.5,Math.abs(d.data[5])*c*.5],y=this.app.scene.gsplat,k=this.viewMoved(h,d),w=k&&this.variant.contribution>0,P=w?Math.max(y.minContribution,this.variant.contribution):y.minContribution;w&&(this.frameWanted=!0);let S=!!a,b=this.variant.taa==="on"&&(!S||this.allowQueryTaa&&l===this.taaWidth&&c===this.taaHeight);b?((this.taaWidth!==l||this.taaHeight!==c)&&(this.resizeHistory(l,c),this.taaWidth=l,this.taaHeight=c,this.taaHistoryValid=!1,this.restFrames=0),this.frameSeed=this.frameIndex++>>>0,k||t?this.restFrames=0:this.restFrames++,this.restFrames<this.variant.taaMax+2&&(this.frameWanted=!0)):(this.frameSeed=this.userSeed,this.taaHistoryValid=!1,this.restFrames=0,this.variant.taa==="off"&&this.taaWidth!==0&&(this.resizeHistory(4,4),this.taaWidth=0,this.taaHeight=0)),this.taaActive=b,this.prevVersion!==e.version&&(this.cullSuspendedFrames=0,this.cullActive=!0);let z=this.variant.cull,_=z!=="off",R=!1;if(z==="auto"&&!S)if(this.cullSuspendedFrames>0){let $=this.viewMoved(h,d);this.cullSuspendedFrames=Math.max(0,this.cullSuspendedFrames-($?4:1)),_=!1}else this.cullActive||(R=!this.cullStatsPending,_=R);let L=_&&(!S||this.allowQueryCull)&&this.prevValid&&this.prevWidth===l&&this.prevHeight===c&&!this.nodesRemoved&&this.prevOrtho===u;this.culling=L,L&&this.reduceDepth(l,c),this.counter.clear();let M=this.variant.order==="bucket";M&&this.orderBuckets.clear();let T=Math.log(Math.max(r.nearClip,1e-6)),V=1/Math.max(Math.log(Math.max(r.farClip,1e-6))-T,1e-6),[B,I]=m,C=-I/B,A=u?(Wo-I)/B:I/(Wo-B),D=o.entity.getPosition(),O=this.source.dispatchPlan(e,this.numChunks);for(let $ of O){if($.fileIndex>=0&&!this.anyNodeVisible(e.files[$.fileIndex].nodes))continue;let F=this.ensureProjector(L,M,$);this.source.bind(F,$,e),F.setParameter("chunks",this.chunkBuffer),F.setParameter("nodeVisible",this.nodeVisibleBuffer),F.setParameter("cache",this.cacheBuffer),F.setParameter("counter",this.counter),F.setParameter("occL1",this.occL1),F.setParameter("occL2",this.occL2),F.setParameter("buckets",this.orderBuckets),F.setParameter("view",h.data),F.setParameter("viewProj",this.viewProjection.data),F.setParameter("prevViewProj",this.prevViewProjection.data),F.setParameter("prevView",this.prevView.data),F.setParameter("prevClipZ",this.prevClipZ),F.setParameter("viewport",[l,c]),F.setParameter("focal",v),F.setParameter("prevViewport",this.prevViewport),F.setParameter("prevFocal",this.prevFocal),F.setParameter("numChunks",$.chunkCount),F.setParameter("chunkBase",$.chunkBase),F.setParameter("cameraPosition",[D.x,D.y,D.z,0]),F.setParameter("splatTextureSize",this.source.textureSize($)),F.setParameter("isOrtho",u?1:0),F.setParameter("minPixelSize",y.minPixelSize),F.setParameter("alphaClip",y.alphaClipForward),F.setParameter("minContribution",P),F.setParameter("colorMax",this.variant.colorMax),F.setParameter("unitScale",this.variant.units==="engine"?2:1),F.setParameter("occBlocksX1",this.occBlocks.x1),F.setParameter("occBlocksY1",this.occBlocks.y1),F.setParameter("occBlocksX2",this.occBlocks.x2),F.setParameter("occBlocksY2",this.occBlocks.y2),F.setParameter("occlusionMode",L?z==="l1"?1:2:0),F.setParameter("prevFlip",this.prevFlip),F.setParameter("keyLogNear",T),F.setParameter("keyInvLogRange",V),F.setParameter("depthNear",C),F.setParameter("depthFar",A),Fe.calcDispatchSize($.chunkCount,Ut),F.setupDispatch(Ut.x,Ut.y,1),n.computeDispatch([F],"sse-splat-project")}let H=n.getIndirectDrawSlot(1),N=n.getIndirectDispatchSlot(1);if(this.args.setParameter("counter",this.counter),this.args.setParameter("indirectDrawArgs",n.indirectDrawBuffer),this.args.setParameter("indirectDispatchArgs",n.indirectDispatchBuffer),this.args.setParameter("drawSlot",H),this.args.setParameter("indexCount",ln*6),this.args.setParameter("quadsPerInstance",ln),this.args.setParameter("dispatchSlot",N),this.args.setParameter("scatterWorkgroupSize",le),this.args.setupDispatch(1,1,1),n.computeDispatch([this.args],"sse-splat-args"),this.rasterInstance.setIndirect(null,H,1),M){this.orderScan.setParameter("buckets",this.orderBuckets),this.orderScan.setupDispatch(1,1,1),n.computeDispatch([this.orderScan],"sse-splat-order-scan");let $=this.orderScatter;$.setParameter("counter",this.counter),$.setParameter("cache",this.cacheBuffer),$.setParameter("buckets",this.orderBuckets),$.setParameter("ordered",this.orderedBuffer),$.setupIndirectDispatch(N),n.computeDispatch([$],"sse-splat-order-scatter")}let E=this.rasterMaterial;if(E.setParameter("splatCache",this.cacheBuffer),E.setParameter("splatCount",this.counter),M&&E.setParameter("orderedSlots",this.orderedBuffer),E.setParameter("viewportSize",[l,c,2/l,2/c]),E.setParameter("clipZParams",f),E.setParameter("focalParams",[v[0],v[1],0,0]),E.setParameter("sseAlphaClip",y.alphaClipForward),E.setParameter("frameSeed",this.frameSeed),this.taaPass.enabled=b,b){let $=this.taaWrite^1,F=this.taaMaterial;this.taaPrevViewProjection.copy(this.taaLastViewProjection),this.taaPrevView.copy(this.taaLastView),this.cameraWorld.copy(h).invert(),F.setParameter("curColor",this.colorTexture),F.setParameter("curDepth",this.depthTexture),F.setParameter("histColor",this.taaColor[$]),F.setParameter("histInfo",this.taaInfo[$]),F.setParameter("cameraWorld",this.cameraWorld.data);let W=p.data;F.setParameter("unproject",u?[W[0],W[5],W[12],W[13]]:[W[0],W[5],W[8],W[9]]),F.setParameter("prevViewProj",this.taaPrevViewProjection.data),F.setParameter("prevView",this.taaPrevView.data),F.setParameter("clipZParams",f),F.setParameter("taaParams",[k||t?this.variant.taaMoveMax:this.variant.taaMax,this.taaHistoryValid?1:0,k?1:0,this.restFrames]),F.setParameter("taaViewport",[l,c,1/l,1/c]),F.setParameter("taaControl",[this.variant.taaFlip||(this.target.flipY?-1:1),this.variant.taaClip,this.variant.spp==="quad"?1:0,0]),F.setParameter("taaDebug",this.variant.taaDebug),F.setParameter("taaFilter",[this.variant.taaFilter==="cubic"?1:0,this.variant.taaReproj==="history"?1:0,this.variant.taaMotion,0]),this.taaPass.renderTarget=this.taaTargets[this.taaWrite],this.composeMaterial.setParameter("taaColor",this.taaColor[this.taaWrite]),this.composeMaterial.setParameter("taaInfo",this.taaInfo[this.taaWrite]),this.taaLastViewProjection.copy(this.viewProjection),this.taaLastView.copy(h),this.taaHistoryValid=!0,this.taaWrite^=1}let j=a?a.flipY:n.backBuffer.flipY;if(this.composeMaterial.setParameter("composeParams",[this.target.flipY!==j?1:0,u?1:0,b?1:0,0]),this.composeMaterial.setParameter("depthViewParams",[m[0],m[1],Math.max(r.nearClip,1e-4),Math.max(r.farClip,r.nearClip*2)]),this.composeMaterial.setParameter("cameraClipZ",[g[0],g[1],u?1:0,0]),L&&z==="auto"&&!S&&!this.cullStatsPending){this.cullStatsPending=!0;let $=new Uint32Array(2);this.counter.read(0,8,$,!1).then(()=>{let F=$[0]+$[1],W=F>0&&$[1]/F>=i.CULL_MIN_FRACTION;this.cullActive=W,W||(this.cullSuspendedFrames=i.CULL_SUSPEND_FRAMES)}).catch(()=>{}).finally(()=>{this.cullStatsPending=!1})}if(S){this.prevValid=!1,this.depthFrame=null,this.setReady(!0);return}Xr(o.entity,this.depthFrameCamera),this.depthFrame={id:++this.depthFrameCount,camera:this.depthFrameCamera,width:l,height:c,clipZ:[m[0],m[1]]},this.prevViewProjection.copy(this.viewProjection),this.prevView.copy(h),this.prevProjection.copy(d),this.prevClipZ=f,this.prevViewport=[l,c],this.prevFocal=v,this.prevFlip=this.target.flipY?-1:1,this.prevWidth=l,this.prevHeight=c,this.prevVersion=e.version,this.prevOrtho=u,this.prevValid=!S,this.setReady(!0)}resizeHistory(e,t){for(let s of[...this.taaColor,...this.taaInfo])s.resize(e,t);for(let s of this.taaTargets)s.resize(e,t)}viewMoved(e,t){for(let s=0;s<16;s++)if(Math.abs(e.data[s]-this.prevView.data[s])>1e-6||Math.abs(t.data[s]-this.prevProjection.data[s])>1e-6)return!0;return!1}rebuildChunkTable(e){let t=0;for(let r of e.nodes)t+=Math.ceil(r.count/Jn);this.chunkData.length<t*4&&(this.chunkData=new Uint32Array(Math.ceil(t*1.25)*4),this.chunkBuffer?.destroy(),this.chunkBuffer=new fe(this.device,this.chunkData.byteLength,8));let s=this.chunkData,n=0;e.nodes.forEach((r,a)=>{let l=this.source.chunkBase(r);for(let c=0;c<r.count;c+=Jn)s[n++]=l+c,s[n++]=Math.min(Jn,r.count-c),s[n++]=a,s[n++]=r.lodIndex&65535|r.fileIndex<<16}),this.numChunks=t,t>0&&this.chunkBuffer.write(0,s,0,t*4);let o=Math.max(1,Math.ceil(e.nodes.length/32));this.nodeVisibleData.length<o&&(this.nodeVisibleData=new Uint32Array(o),this.nodeVisibleBuffer?.destroy(),this.nodeVisibleBuffer=new fe(this.device,o*4,8))}ensureCache(e){e<=this.cacheSlots||(this.cacheBuffer?.destroy(),this.orderedBuffer?.destroy(),this.cacheSlots=Math.max(1,Math.ceil(e*1.25)),this.cacheBuffer=new fe(this.device,this.cacheSlots*mt*4,4),this.orderedBuffer=new fe(this.device,this.cacheSlots*4))}cullNodes(e,t){let s=this.nodeVisibleData;s.fill(0),this.frustum.setFromMat4(t);let n=e.nodeSpheres,{sphere:o}=this;for(let a=0;a<e.nodes.length;a++)o.center.set(n[a*4],n[a*4+1],n[a*4+2]),o.radius=n[a*4+3],this.frustum.containsSphere(o)!==0&&(s[a>>5]|=1<<(a&31));let r=Math.max(1,Math.ceil(e.nodes.length/32));this.nodeVisibleBuffer.write(0,s,0,r)}anyNodeVisible(e){let t=this.nodeVisibleData;return e.some(s=>t[s.nodeIndex>>5]>>>(s.nodeIndex&31)&1)}ensureProjector(e,t,s){let n=this.source.shaderKey();this.projectorSourceKey!==n&&(this.destroyProjector(),this.projectorSourceKey=n);let o=`${e?"occlusion":"plain"}-${t?"bucket":"append"}`,r=`${o}:${s.fileIndex}`,a=this.projectorComputes.get(r);if(a)return a;let l=this.ensureProjectorShader(o,e,t),c=new Fe(this.device,l,"sse-splat-project");return this.projectorComputes.set(r,c),c}pruneProjectorComputes(e){for(let[t,s]of this.projectorComputes){let n=Number(t.slice(t.lastIndexOf(":")+1));n>=0&&!e.files[n]&&(s.destroy(),this.projectorComputes.delete(t))}}ensureProjectorShader(e,t,s){let n=this.projectorShaders.get(e);if(n)return n;let{device:o}=this,r=[new Z("chunks",X,!0),new Z("nodeVisible",X,!0),new Z("cache",X),new Z("counter",X),new Bt("uniforms",X),new Z("occL1",X,!0),new Z("occL2",X,!0),new Z("buckets",X)],a=[...r,...this.source.bindFormats()];this.projectorBindGroupFormat??=new lt(o,a);let l=new ct(o,{name:`sse-splat-project-${e}`,shaderLanguage:Ue,cshader:Bd(this.source.readChunk(r.length),t,s),cincludes:this.source.shaderIncludes(),cdefines:this.source.shaderDefines(),computeUniformBufferFormats:{uniforms:new Nt(o,[new q("view",xt),new q("viewProj",xt),new q("prevViewProj",xt),new q("prevView",xt),new q("prevClipZ",os),new q("viewport",ns),new q("focal",ns),new q("prevViewport",ns),new q("prevFocal",ns),new q("numChunks",G),new q("splatTextureSize",G),new q("isOrtho",G),new q("minPixelSize",oe),new q("alphaClip",oe),new q("minContribution",oe),new q("occBlocksX1",G),new q("occBlocksY1",G),new q("occBlocksX2",G),new q("occBlocksY2",G),new q("occlusionMode",G),new q("prevFlip",oe),new q("keyLogNear",oe),new q("keyInvLogRange",oe),new q("chunkBase",G),new q("model",xt),new q("modelRotation",os),new q("modelScale",os),new q("cameraPosition",os),new q("colorMax",oe),new q("unitScale",oe),new q("depthNear",oe),new q("depthFar",oe)])},computeBindGroupFormat:this.projectorBindGroupFormat});return this.projectorShaders.set(e,l),l}destroyProjector(){for(let e of this.projectorComputes.values())e.destroy();this.projectorComputes.clear();for(let e of this.projectorShaders.values())e.destroy();this.projectorShaders.clear(),this.projectorBindGroupFormat?.destroy(),this.projectorBindGroupFormat=null,this.projectorSourceKey=""}destroy(){this.unsubscribe(),this.app.off("frameend",this.onFrameEnd),this.detach(),this.provider.destroy(),this.source.destroy(),this.destroyProjector(),this.args.shader.destroy(),this.args.destroy(),this.argsBindGroupFormat.destroy();for(let e of[this.reduceL1,this.reduceL2])e.shader.destroy(),e.destroy();for(let e of this.reduceFormats)e.destroy();this.occL1.destroy(),this.occL2.destroy();for(let e of[this.orderScan,this.orderScatter])e.shader.destroy(),e.destroy();for(let e of this.orderFormats)e.destroy();this.orderBuckets.destroy(),this.orderedBuffer?.destroy(),this.orderedBuffer=null,this.chunkBuffer?.destroy(),this.nodeVisibleBuffer?.destroy(),this.cacheBuffer?.destroy(),this.counter.destroy(),this.chunkBuffer=this.nodeVisibleBuffer=this.cacheBuffer=null,this.rasterPass.destroy(),this.rasterInstance.destroy(),this.rasterMaterial.destroy(),this.rasterMesh.destroy(),this.taaPass.destroy(),this.taaInstance.destroy(),this.taaMaterial.destroy(),this.taaMesh.destroy();for(let e of this.taaTargets)e.destroy();for(let e of[...this.taaColor,...this.taaInfo])e.destroy();this.taaTargets.length=0,this.taaColor.length=0,this.taaInfo.length=0,this.target.destroy(),this.colorTexture.destroy(),this.depthTexture.destroy(),this.composeInstance.destroy(),this.composeMaterial.destroy(),this.composeMesh.destroy()}},Xd=`
|
|
2266
|
+
|
|
2267
|
+
// Solid leaf sentinel: childMask=0xFF, baseOffset=0
|
|
2268
|
+
const SOLID_LEAF_MARKER: u32 = 0xFF000000u;
|
|
2269
|
+
|
|
2270
|
+
// Maximum DDA steps to prevent infinite loops
|
|
2271
|
+
const MAX_STEPS: u32 = 512u;
|
|
2272
|
+
|
|
2273
|
+
// Target wireframe edge width in pixels
|
|
2274
|
+
const EDGE_PIXELS: f32 = 1.5;
|
|
2275
|
+
|
|
2276
|
+
// Keep edges tinted so dense grids retain the solid/free-space distinction.
|
|
2277
|
+
const EDGE_ALPHA: f32 = 0.8;
|
|
2278
|
+
|
|
2279
|
+
const FILL_ALPHA: f32 = 0.4;
|
|
2280
|
+
|
|
2281
|
+
struct Uniforms {
|
|
2282
|
+
invVP: mat4x4<f32>,
|
|
2283
|
+
screenWidth: u32,
|
|
2284
|
+
screenHeight: u32,
|
|
2285
|
+
gridMinX: f32,
|
|
2286
|
+
gridMinY: f32,
|
|
2287
|
+
gridMinZ: f32,
|
|
2288
|
+
voxelRes: f32,
|
|
2289
|
+
numVoxelsX: u32,
|
|
2290
|
+
numVoxelsY: u32,
|
|
2291
|
+
numVoxelsZ: u32,
|
|
2292
|
+
leafSize: u32,
|
|
2293
|
+
treeDepth: u32,
|
|
2294
|
+
projScaleY: f32,
|
|
2295
|
+
displayMode: u32,
|
|
2296
|
+
inverted: u32
|
|
2297
|
+
};
|
|
2298
|
+
|
|
2299
|
+
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
2300
|
+
@group(0) @binding(1) var<storage, read> nodes: array<u32>;
|
|
2301
|
+
@group(0) @binding(2) var<storage, read> leafData: array<u32>;
|
|
2302
|
+
@group(0) @binding(3) var outputTexture: texture_storage_2d<rgba8unorm, write>;
|
|
2303
|
+
|
|
2304
|
+
// ---- helpers ----
|
|
2305
|
+
|
|
2306
|
+
// Traverse the octree for block (bx, by, bz). Returns vec2u(result, skipLevel):
|
|
2307
|
+
// result: 0 = empty, 1 = solid, 2+ = mixed leaf (2 + leafDataIndex)
|
|
2308
|
+
// skipLevel: octree level for multi-block skipping (meaningful when result == 0 or 1)
|
|
2309
|
+
fn queryBlock(bx: i32, by: i32, bz: i32) -> vec2u {
|
|
2310
|
+
let depth = uniforms.treeDepth;
|
|
2311
|
+
var nodeIndex: u32 = 0u;
|
|
2312
|
+
|
|
2313
|
+
for (var level: u32 = depth - 1u; ; ) {
|
|
2314
|
+
let node = nodes[nodeIndex];
|
|
2315
|
+
|
|
2316
|
+
// Solid leaf sentinel -- return level + 1 so the caller can skip 2^(level+1) blocks
|
|
2317
|
+
if (node == SOLID_LEAF_MARKER) {
|
|
2318
|
+
return vec2u(1u, level + 1u);
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
let childMask = (node >> 24u) & 0xFFu;
|
|
2322
|
+
|
|
2323
|
+
// childMask == 0 means this is a mixed leaf node
|
|
2324
|
+
if (childMask == 0u) {
|
|
2325
|
+
let leafIdx = node & 0x00FFFFFFu;
|
|
2326
|
+
return vec2u(2u + leafIdx, 0u);
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2329
|
+
// Determine octant at this level
|
|
2330
|
+
let bitX = (u32(bx) >> level) & 1u;
|
|
2331
|
+
let bitY = (u32(by) >> level) & 1u;
|
|
2332
|
+
let bitZ = (u32(bz) >> level) & 1u;
|
|
2333
|
+
let octant = (bitZ << 2u) | (bitY << 1u) | bitX;
|
|
2334
|
+
|
|
2335
|
+
// Check if child exists
|
|
2336
|
+
if ((childMask & (1u << octant)) == 0u) {
|
|
2337
|
+
return vec2u(0u, level);
|
|
2338
|
+
}
|
|
2339
|
+
|
|
2340
|
+
// Compute child index
|
|
2341
|
+
let baseOffset = node & 0x00FFFFFFu;
|
|
2342
|
+
let prefix = (1u << octant) - 1u;
|
|
2343
|
+
let childOffset = countOneBits(childMask & prefix);
|
|
2344
|
+
nodeIndex = baseOffset + childOffset;
|
|
2345
|
+
|
|
2346
|
+
if (level == 0u) { break; }
|
|
2347
|
+
level -= 1u;
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
// Reached leaf level
|
|
2351
|
+
let node = nodes[nodeIndex];
|
|
2352
|
+
if (node == SOLID_LEAF_MARKER) {
|
|
2353
|
+
return vec2u(1u, 0u);
|
|
2354
|
+
}
|
|
2355
|
+
let leafIdx = node & 0x00FFFFFFu;
|
|
2356
|
+
return vec2u(2u + leafIdx, 0u);
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
// Ray-AABB intersection returning (tNear, tFar). If tNear > tFar \u2192 miss.
|
|
2360
|
+
fn intersectAABB(ro: vec3f, invDir: vec3f, bmin: vec3f, bmax: vec3f) -> vec2f {
|
|
2361
|
+
let t1 = (bmin - ro) * invDir;
|
|
2362
|
+
let t2 = (bmax - ro) * invDir;
|
|
2363
|
+
let tmin = min(t1, t2);
|
|
2364
|
+
let tmax = max(t1, t2);
|
|
2365
|
+
let tNear = max(max(tmin.x, tmin.y), tmin.z);
|
|
2366
|
+
let tFar = min(min(tmax.x, tmax.y), tmax.z);
|
|
2367
|
+
return vec2f(tNear, tFar);
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
// Compute wireframe edge factor (0 = interior, 1 = on edge) for a hit point on a voxel cube.
|
|
2371
|
+
// Uses the median of the three per-axis face distances so it works on ANY face.
|
|
2372
|
+
fn edgeFactor(hitPos: vec3f, voxMin: vec3f, voxSize: f32, edgeWidth: f32) -> f32 {
|
|
2373
|
+
let local = (hitPos - voxMin) / voxSize;
|
|
2374
|
+
|
|
2375
|
+
// Distance to nearest face boundary for each axis
|
|
2376
|
+
let fx = min(local.x, 1.0 - local.x);
|
|
2377
|
+
let fy = min(local.y, 1.0 - local.y);
|
|
2378
|
+
let fz = min(local.z, 1.0 - local.z);
|
|
2379
|
+
|
|
2380
|
+
// Median of three values = second smallest = edge distance.
|
|
2381
|
+
// On a face, one of fx/fy/fz is ~0 (the face normal axis).
|
|
2382
|
+
// The median gives the smaller of the other two = distance to nearest edge.
|
|
2383
|
+
let edgeDist = max(min(fx, fy), min(max(fx, fy), fz));
|
|
2384
|
+
|
|
2385
|
+
return 1.0 - smoothstep(0.0, edgeWidth, edgeDist);
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
// Shade by the camera's occupancy: gray in free space, orange inside solid
|
|
2389
|
+
// voxels (where the ray-march is inverted to find the exit into free space).
|
|
2390
|
+
// Face-axis brightness and darker edges keep the grid shape readable.
|
|
2391
|
+
fn shadeVoxelHit(hitPos: vec3f, voxMin: vec3f, voxelRes: f32, ro: vec3f) -> vec4f {
|
|
2392
|
+
let dist = length(hitPos - ro);
|
|
2393
|
+
let pixelWorld = 2.0 * dist / (f32(uniforms.screenHeight) * uniforms.projScaleY);
|
|
2394
|
+
let ew = clamp(EDGE_PIXELS * pixelWorld / voxelRes, 0.01, 0.5);
|
|
2395
|
+
|
|
2396
|
+
let ef = edgeFactor(hitPos, voxMin, voxelRes, ew);
|
|
2397
|
+
|
|
2398
|
+
let local = (hitPos - voxMin) / voxelRes;
|
|
2399
|
+
let fx = min(local.x, 1.0 - local.x);
|
|
2400
|
+
let fy = min(local.y, 1.0 - local.y);
|
|
2401
|
+
let fz = min(local.z, 1.0 - local.z);
|
|
2402
|
+
|
|
2403
|
+
var faceAxis: u32 = 0u;
|
|
2404
|
+
if (fy <= fx && fy <= fz) {
|
|
2405
|
+
faceAxis = 1u;
|
|
2406
|
+
} else if (fz <= fx) {
|
|
2407
|
+
faceAxis = 2u;
|
|
2408
|
+
}
|
|
2409
|
+
|
|
2410
|
+
var brightness = 0.85;
|
|
2411
|
+
if (faceAxis == 1u) { brightness = 0.65; }
|
|
2412
|
+
else if (faceAxis == 2u) { brightness = 0.45; }
|
|
2413
|
+
|
|
2414
|
+
let tint = select(vec3f(1.0), vec3f(1.0, 0.35, 0.08), uniforms.inverted != 0u);
|
|
2415
|
+
let color = tint * mix(brightness, 0.2, ef);
|
|
2416
|
+
let alpha = mix(FILL_ALPHA, EDGE_ALPHA, ef);
|
|
2417
|
+
|
|
2418
|
+
return vec4f(color * alpha, alpha);
|
|
2419
|
+
}
|
|
2420
|
+
|
|
2421
|
+
// Blue (0) -> Cyan (0.25) -> Green (0.5) -> Yellow (0.75) -> Red (1.0)
|
|
2422
|
+
fn heatmap(t: f32) -> vec3f {
|
|
2423
|
+
let c = clamp(t, 0.0, 1.0);
|
|
2424
|
+
let r = clamp(min(c - 0.5, 1.0) * 2.0, 0.0, 1.0);
|
|
2425
|
+
let g = select(clamp(c * 4.0, 0.0, 1.0), clamp((1.0 - c) * 4.0, 0.0, 1.0), c > 0.5);
|
|
2426
|
+
let b = clamp(1.0 - c * 2.0, 0.0, 1.0);
|
|
2427
|
+
return vec3f(r, g, b);
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
// ---- main ----
|
|
2431
|
+
|
|
2432
|
+
@compute @workgroup_size(8, 8, 1)
|
|
2433
|
+
fn main(@builtin(global_invocation_id) gid: vec3u) {
|
|
2434
|
+
let px = i32(gid.x);
|
|
2435
|
+
let py = i32(gid.y);
|
|
2436
|
+
let sw = i32(uniforms.screenWidth);
|
|
2437
|
+
let sh = i32(uniforms.screenHeight);
|
|
2438
|
+
|
|
2439
|
+
if (px >= sw || py >= sh) {
|
|
2440
|
+
return;
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
// Reconstruct world-space ray from pixel coordinates
|
|
2444
|
+
let ndcX = (f32(px) + 0.5) / f32(sw) * 2.0 - 1.0;
|
|
2445
|
+
let ndcY = -((f32(py) + 0.5) / f32(sh) * 2.0 - 1.0);
|
|
2446
|
+
|
|
2447
|
+
let clipNear = vec4f(ndcX, ndcY, 0.0, 1.0);
|
|
2448
|
+
let clipFar = vec4f(ndcX, ndcY, 1.0, 1.0);
|
|
2449
|
+
|
|
2450
|
+
var worldNear = uniforms.invVP * clipNear;
|
|
2451
|
+
worldNear = worldNear / worldNear.w;
|
|
2452
|
+
var worldFar = uniforms.invVP * clipFar;
|
|
2453
|
+
worldFar = worldFar / worldFar.w;
|
|
2454
|
+
|
|
2455
|
+
let ro = worldNear.xyz;
|
|
2456
|
+
let rd = normalize(worldFar.xyz - worldNear.xyz);
|
|
2457
|
+
|
|
2458
|
+
// Grid AABB
|
|
2459
|
+
let gridMin = vec3f(uniforms.gridMinX, uniforms.gridMinY, uniforms.gridMinZ);
|
|
2460
|
+
let gridMax = gridMin + vec3f(
|
|
2461
|
+
f32(uniforms.numVoxelsX),
|
|
2462
|
+
f32(uniforms.numVoxelsY),
|
|
2463
|
+
f32(uniforms.numVoxelsZ)
|
|
2464
|
+
) * uniforms.voxelRes;
|
|
2465
|
+
|
|
2466
|
+
let invDir = 1.0 / rd;
|
|
2467
|
+
let gridHit = intersectAABB(ro, invDir, gridMin, gridMax);
|
|
2468
|
+
|
|
2469
|
+
if (max(gridHit.x, 0.0) > gridHit.y) {
|
|
2470
|
+
textureStore(outputTexture, vec2i(px, py), vec4f(0.0));
|
|
2471
|
+
return;
|
|
2472
|
+
}
|
|
2473
|
+
|
|
2474
|
+
let tEntry = max(gridHit.x, 0.0) + 0.0001;
|
|
2475
|
+
|
|
2476
|
+
// Entry point in voxel-index space
|
|
2477
|
+
let entryWorld = ro + rd * tEntry;
|
|
2478
|
+
let voxelRes = uniforms.voxelRes;
|
|
2479
|
+
let lsf = f32(uniforms.leafSize);
|
|
2480
|
+
let blockRes = voxelRes * lsf;
|
|
2481
|
+
let leafSz = i32(uniforms.leafSize);
|
|
2482
|
+
|
|
2483
|
+
// Block-level DDA setup
|
|
2484
|
+
let entryBlock = (entryWorld - gridMin) / blockRes;
|
|
2485
|
+
let numBlocksX = i32(uniforms.numVoxelsX / uniforms.leafSize);
|
|
2486
|
+
let numBlocksY = i32(uniforms.numVoxelsY / uniforms.leafSize);
|
|
2487
|
+
let numBlocksZ = i32(uniforms.numVoxelsZ / uniforms.leafSize);
|
|
2488
|
+
|
|
2489
|
+
var bx = clamp(i32(floor(entryBlock.x)), 0, numBlocksX - 1);
|
|
2490
|
+
var by = clamp(i32(floor(entryBlock.y)), 0, numBlocksY - 1);
|
|
2491
|
+
var bz = clamp(i32(floor(entryBlock.z)), 0, numBlocksZ - 1);
|
|
2492
|
+
|
|
2493
|
+
let stepX = select(-1, 1, rd.x >= 0.0);
|
|
2494
|
+
let stepY = select(-1, 1, rd.y >= 0.0);
|
|
2495
|
+
let stepZ = select(-1, 1, rd.z >= 0.0);
|
|
2496
|
+
|
|
2497
|
+
let tDeltaX = abs(blockRes / rd.x);
|
|
2498
|
+
let tDeltaY = abs(blockRes / rd.y);
|
|
2499
|
+
let tDeltaZ = abs(blockRes / rd.z);
|
|
2500
|
+
|
|
2501
|
+
// tMax: t value to reach next block boundary along each axis
|
|
2502
|
+
let blockMinWorld = gridMin + vec3f(f32(bx), f32(by), f32(bz)) * blockRes;
|
|
2503
|
+
let nextBoundX = select(blockMinWorld.x, blockMinWorld.x + blockRes, rd.x >= 0.0);
|
|
2504
|
+
let nextBoundY = select(blockMinWorld.y, blockMinWorld.y + blockRes, rd.y >= 0.0);
|
|
2505
|
+
let nextBoundZ = select(blockMinWorld.z, blockMinWorld.z + blockRes, rd.z >= 0.0);
|
|
2506
|
+
|
|
2507
|
+
var tMaxX = (nextBoundX - ro.x) / rd.x;
|
|
2508
|
+
var tMaxY = (nextBoundY - ro.y) / rd.y;
|
|
2509
|
+
var tMaxZ = (nextBoundZ - ro.z) / rd.z;
|
|
2510
|
+
|
|
2511
|
+
let inv = uniforms.inverted != 0u;
|
|
2512
|
+
var totalWork: u32 = 0u;
|
|
2513
|
+
|
|
2514
|
+
for (var step: u32 = 0u; step < MAX_STEPS; step++) {
|
|
2515
|
+
totalWork += 1u;
|
|
2516
|
+
|
|
2517
|
+
let qResult = queryBlock(bx, by, bz);
|
|
2518
|
+
let blockResult = qResult.x;
|
|
2519
|
+
let skipLevel = qResult.y;
|
|
2520
|
+
|
|
2521
|
+
// Normal: skip large empty regions. Inverted: skip large solid regions.
|
|
2522
|
+
let shouldSkip = select(
|
|
2523
|
+
blockResult == 0u && skipLevel >= 1u,
|
|
2524
|
+
blockResult == 1u && skipLevel >= 1u,
|
|
2525
|
+
inv
|
|
2526
|
+
);
|
|
2527
|
+
|
|
2528
|
+
if (shouldSkip) {
|
|
2529
|
+
let cellBlocks = i32(1u << skipLevel);
|
|
2530
|
+
let cellMask = ~(cellBlocks - 1);
|
|
2531
|
+
let cellXMin = bx & cellMask;
|
|
2532
|
+
let cellYMin = by & cellMask;
|
|
2533
|
+
let cellZMin = bz & cellMask;
|
|
2534
|
+
|
|
2535
|
+
for (var skip: u32 = 0u; skip < 128u; skip++) {
|
|
2536
|
+
totalWork += 1u;
|
|
2537
|
+
|
|
2538
|
+
if (tMaxX < tMaxY && tMaxX < tMaxZ) {
|
|
2539
|
+
bx += stepX;
|
|
2540
|
+
tMaxX += tDeltaX;
|
|
2541
|
+
if (bx < cellXMin || bx >= cellXMin + cellBlocks) { break; }
|
|
2542
|
+
} else if (tMaxY < tMaxZ) {
|
|
2543
|
+
by += stepY;
|
|
2544
|
+
tMaxY += tDeltaY;
|
|
2545
|
+
if (by < cellYMin || by >= cellYMin + cellBlocks) { break; }
|
|
2546
|
+
} else {
|
|
2547
|
+
bz += stepZ;
|
|
2548
|
+
tMaxZ += tDeltaZ;
|
|
2549
|
+
if (bz < cellZMin || bz >= cellZMin + cellBlocks) { break; }
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
} else {
|
|
2553
|
+
// Normal: enter voxel DDA for non-empty blocks.
|
|
2554
|
+
// Inverted: enter voxel DDA for non-solid blocks (empty or mixed).
|
|
2555
|
+
let enterDDA = select(blockResult != 0u, blockResult != 1u, inv);
|
|
2556
|
+
|
|
2557
|
+
if (enterDDA) {
|
|
2558
|
+
let blockOrigin = gridMin + vec3f(f32(bx), f32(by), f32(bz)) * blockRes;
|
|
2559
|
+
|
|
2560
|
+
let blockMax = blockOrigin + vec3f(blockRes);
|
|
2561
|
+
let bHit = intersectAABB(ro, invDir, blockOrigin, blockMax);
|
|
2562
|
+
let tBlockEntry = max(bHit.x, 0.0);
|
|
2563
|
+
|
|
2564
|
+
// Voxel-level DDA within the block
|
|
2565
|
+
let entryVoxWorld = ro + rd * (tBlockEntry + 0.0001);
|
|
2566
|
+
let entryLocal = (entryVoxWorld - blockOrigin) / voxelRes;
|
|
2567
|
+
var vx = clamp(i32(floor(entryLocal.x)), 0, leafSz - 1);
|
|
2568
|
+
var vy = clamp(i32(floor(entryLocal.y)), 0, leafSz - 1);
|
|
2569
|
+
var vz = clamp(i32(floor(entryLocal.z)), 0, leafSz - 1);
|
|
2570
|
+
|
|
2571
|
+
let vTDeltaX = abs(voxelRes / rd.x);
|
|
2572
|
+
let vTDeltaY = abs(voxelRes / rd.y);
|
|
2573
|
+
let vTDeltaZ = abs(voxelRes / rd.z);
|
|
2574
|
+
|
|
2575
|
+
let voxOrigin = blockOrigin + vec3f(f32(vx), f32(vy), f32(vz)) * voxelRes;
|
|
2576
|
+
let vNextX = select(voxOrigin.x, voxOrigin.x + voxelRes, rd.x >= 0.0);
|
|
2577
|
+
let vNextY = select(voxOrigin.y, voxOrigin.y + voxelRes, rd.y >= 0.0);
|
|
2578
|
+
let vNextZ = select(voxOrigin.z, voxOrigin.z + voxelRes, rd.z >= 0.0);
|
|
2579
|
+
|
|
2580
|
+
var vTMaxX = (vNextX - ro.x) / rd.x;
|
|
2581
|
+
var vTMaxY = (vNextY - ro.y) / rd.y;
|
|
2582
|
+
var vTMaxZ = (vNextZ - ro.z) / rd.z;
|
|
2583
|
+
|
|
2584
|
+
var maskLo: u32 = 0u;
|
|
2585
|
+
var maskHi: u32 = 0u;
|
|
2586
|
+
if (blockResult > 1u) {
|
|
2587
|
+
let leafIdx = blockResult - 2u;
|
|
2588
|
+
maskLo = leafData[leafIdx * 2u];
|
|
2589
|
+
maskHi = leafData[leafIdx * 2u + 1u];
|
|
2590
|
+
}
|
|
2591
|
+
|
|
2592
|
+
for (var vStep: u32 = 0u; vStep < 12u; vStep++) {
|
|
2593
|
+
totalWork += 1u;
|
|
2594
|
+
|
|
2595
|
+
var isSolid = false;
|
|
2596
|
+
|
|
2597
|
+
if (blockResult == 1u) {
|
|
2598
|
+
isSolid = true;
|
|
2599
|
+
} else {
|
|
2600
|
+
let bitIndex = u32(vz) * 16u + u32(vy) * 4u + u32(vx);
|
|
2601
|
+
isSolid = select(
|
|
2602
|
+
(maskHi & (1u << (bitIndex - 32u))) != 0u,
|
|
2603
|
+
(maskLo & (1u << bitIndex)) != 0u,
|
|
2604
|
+
bitIndex < 32u
|
|
2605
|
+
);
|
|
2606
|
+
}
|
|
2607
|
+
|
|
2608
|
+
let isHit = select(isSolid, !isSolid, inv);
|
|
2609
|
+
|
|
2610
|
+
if (isHit) {
|
|
2611
|
+
if (uniforms.displayMode == 0u) {
|
|
2612
|
+
let voxMin = blockOrigin + vec3f(f32(vx), f32(vy), f32(vz)) * voxelRes;
|
|
2613
|
+
let vHit = intersectAABB(ro, invDir, voxMin, voxMin + vec3f(voxelRes));
|
|
2614
|
+
let hitPos = ro + rd * max(vHit.x, 0.0);
|
|
2615
|
+
let result = shadeVoxelHit(hitPos, voxMin, voxelRes, ro);
|
|
2616
|
+
textureStore(outputTexture, vec2i(px, py), result);
|
|
2617
|
+
} else {
|
|
2618
|
+
let effort = f32(totalWork) / 256.0;
|
|
2619
|
+
let color = heatmap(effort);
|
|
2620
|
+
textureStore(outputTexture, vec2i(px, py), vec4f(color, 1.0));
|
|
2621
|
+
}
|
|
2622
|
+
return;
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2625
|
+
// Advance voxel DDA
|
|
2626
|
+
if (vTMaxX < vTMaxY && vTMaxX < vTMaxZ) {
|
|
2627
|
+
vx += stepX;
|
|
2628
|
+
vTMaxX += vTDeltaX;
|
|
2629
|
+
if (vx < 0 || vx >= leafSz) { break; }
|
|
2630
|
+
} else if (vTMaxY < vTMaxZ) {
|
|
2631
|
+
vy += stepY;
|
|
2632
|
+
vTMaxY += vTDeltaY;
|
|
2633
|
+
if (vy < 0 || vy >= leafSz) { break; }
|
|
2634
|
+
} else {
|
|
2635
|
+
vz += stepZ;
|
|
2636
|
+
vTMaxZ += vTDeltaZ;
|
|
2637
|
+
if (vz < 0 || vz >= leafSz) { break; }
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
|
|
2642
|
+
// Advance block DDA
|
|
2643
|
+
if (tMaxX < tMaxY && tMaxX < tMaxZ) {
|
|
2644
|
+
bx += stepX;
|
|
2645
|
+
tMaxX += tDeltaX;
|
|
2646
|
+
} else if (tMaxY < tMaxZ) {
|
|
2647
|
+
by += stepY;
|
|
2648
|
+
tMaxY += tDeltaY;
|
|
2649
|
+
} else {
|
|
2650
|
+
bz += stepZ;
|
|
2651
|
+
tMaxZ += tDeltaZ;
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2655
|
+
if (bx < 0 || by < 0 || bz < 0 ||
|
|
2656
|
+
bx >= numBlocksX || by >= numBlocksY || bz >= numBlocksZ) {
|
|
2657
|
+
break;
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
|
|
2661
|
+
textureStore(outputTexture, vec2i(px, py), vec4f(0.0));
|
|
2662
|
+
}
|
|
2663
|
+
`,Xo=class{app;camera;compute;storageTexture;overlayMaterial;quad;nodesBuffer;leafDataBuffer;collision;currentWidth=0;currentHeight=0;invVP=new U;vpTemp=new U;enabled=!1;mode="overlay";constructor(e,t,s){this.app=e,this.camera=s,this.collision=t;let n=e.graphicsDevice,o=t.nodes,r=Math.max(o.byteLength,4);this.nodesBuffer=new fe(n,r,8),o.byteLength>0&&this.nodesBuffer.write(0,o,0,o.length);let a=t.leafData,l=Math.max(a.byteLength,4);this.leafDataBuffer=new fe(n,l,8),a.byteLength>0&&this.leafDataBuffer.write(0,a,0,a.length),this.currentWidth=Math.max(n.width,1),this.currentHeight=Math.max(n.height,1),this.storageTexture=this.createStorageTexture(this.currentWidth,this.currentHeight);let c={name:"VoxelDebugOverlay",shaderLanguage:Ue,cshader:Xd,computeUniformBufferFormats:{uniforms:new Nt(n,[new q("invVP",xt),new q("screenWidth",G),new q("screenHeight",G),new q("gridMinX",oe),new q("gridMinY",oe),new q("gridMinZ",oe),new q("voxelRes",oe),new q("numVoxelsX",G),new q("numVoxelsY",G),new q("numVoxelsZ",G),new q("leafSize",G),new q("treeDepth",G),new q("projScaleY",oe),new q("displayMode",G),new q("inverted",G)])},computeBindGroupFormat:new lt(n,[new Bt("uniforms",X),new Z("nodes",X,!0),new Z("leafData",X,!0),new Ri("outputTexture",ss,Vn)])},h=new ct(n,c);this.compute=new Fe(n,h,"VoxelDebugOverlay"),this.overlayMaterial=new Ze,this.overlayMaterial.cull=0,this.overlayMaterial.blendType=Bn,this.overlayMaterial.depthTest=!1,this.overlayMaterial.depthWrite=!1,this.overlayMaterial.setParameter("colorMap",this.storageTexture),this.overlayMaterial.shaderDesc={uniqueName:"VoxelOverlayComposite",vertexGLSL:`
|
|
2664
|
+
attribute vec2 vertex_position;
|
|
2665
|
+
uniform mat4 matrix_model;
|
|
2666
|
+
varying vec2 uv0;
|
|
2667
|
+
void main(void) {
|
|
2668
|
+
gl_Position = matrix_model * vec4(vertex_position, 0, 1);
|
|
2669
|
+
uv0 = vertex_position.xy + 0.5;
|
|
2670
|
+
}
|
|
2671
|
+
`,vertexWGSL:`
|
|
2672
|
+
attribute vertex_position: vec2f;
|
|
2673
|
+
uniform matrix_model: mat4x4f;
|
|
2674
|
+
varying uv0: vec2f;
|
|
2675
|
+
@vertex fn vertexMain(input: VertexInput) -> VertexOutput {
|
|
2676
|
+
var output: VertexOutput;
|
|
2677
|
+
output.position = uniform.matrix_model * vec4f(input.vertex_position, 0.0, 1.0);
|
|
2678
|
+
output.uv0 = input.vertex_position.xy + vec2f(0.5);
|
|
2679
|
+
return output;
|
|
2680
|
+
}
|
|
2681
|
+
`,fragmentGLSL:`
|
|
2682
|
+
varying vec2 uv0;
|
|
2683
|
+
uniform sampler2D colorMap;
|
|
2684
|
+
void main(void) {
|
|
2685
|
+
gl_FragColor = texture2D(colorMap, uv0);
|
|
2686
|
+
}
|
|
2687
|
+
`,fragmentWGSL:`
|
|
2688
|
+
varying uv0: vec2f;
|
|
2689
|
+
var colorMap: texture_2d<f32>;
|
|
2690
|
+
var colorMapSampler: sampler;
|
|
2691
|
+
@fragment fn fragmentMain(input: FragmentInput) -> FragmentOutput {
|
|
2692
|
+
var output: FragmentOutput;
|
|
2693
|
+
output.color = textureSample(colorMap, colorMapSampler, input.uv0);
|
|
2694
|
+
return output;
|
|
2695
|
+
}
|
|
2696
|
+
`,attributes:{vertex_position:pe}},this.overlayMaterial.update();let d=new Ye(n);d.setPositions([-.5,-.5,0,.5,-.5,0,-.5,.5,0,.5,.5,0]),d.update(Fn);let u=new $t("VoxelOverlayComposite");u.setLocalScale(2,-2,1),u.getWorldTransform(),this.quad=new ke(d,this.overlayMaterial,u),this.quad.cull=!1,this.quad.castShadow=!1,this.quad.pick=!1,this.quad.visible=!1,e.scene.defaultDrawLayer.addMeshInstances([this.quad],!0)}createStorageTexture(e,t){return new ze(this.app.graphicsDevice,{name:"VoxelOverlay-Storage",width:e,height:t,format:ss,mipmaps:!1,addressU:3,addressV:3,storage:!0})}update(){if(this.quad.visible=!1,!this.enabled||this.collision.nodes.length===0)return;let{app:e,camera:t,compute:s,collision:n}=this,o=e.graphicsDevice,r=o.width,a=o.height;if(r<=0||a<=0)return;(r!==this.currentWidth||a!==this.currentHeight)&&(this.storageTexture.destroy(),this.currentWidth=r,this.currentHeight=a,this.storageTexture=this.createStorageTexture(r,a),this.overlayMaterial.setParameter("colorMap",this.storageTexture),this.overlayMaterial.update());let l=t.camera;if(this.vpTemp.mul2(l.projectionMatrix,l.viewMatrix),this.invVP.copy(this.vpTemp).invert(),n.flipXY){let y=this.invVP.data;y[0]=-y[0],y[1]=-y[1],y[4]=-y[4],y[5]=-y[5],y[8]=-y[8],y[9]=-y[9],y[12]=-y[12],y[13]=-y[13]}s.setParameter("invVP",this.invVP.data),s.setParameter("screenWidth",r),s.setParameter("screenHeight",a),s.setParameter("gridMinX",n.gridMinX),s.setParameter("gridMinY",n.gridMinY),s.setParameter("gridMinZ",n.gridMinZ),s.setParameter("voxelRes",n.voxelResolution),s.setParameter("numVoxelsX",n.numVoxelsX),s.setParameter("numVoxelsY",n.numVoxelsY),s.setParameter("numVoxelsZ",n.numVoxelsZ),s.setParameter("leafSize",n.leafSize),s.setParameter("treeDepth",n.treeDepth),s.setParameter("projScaleY",l.projectionMatrix.data[5]),s.setParameter("displayMode",this.mode==="heatmap"?1:0);let c=t.getPosition(),h=c.x,d=c.y,u=c.z;n.flipXY&&(h=-h,d=-d);let p=Math.floor((h-n.gridMinX)/n.voxelResolution),g=Math.floor((d-n.gridMinY)/n.voxelResolution),m=Math.floor((u-n.gridMinZ)/n.voxelResolution);s.setParameter("inverted",n.isVoxelSolid(p,g,m)?1:0),s.setParameter("nodes",this.nodesBuffer),s.setParameter("leafData",this.leafDataBuffer),s.setParameter("outputTexture",this.storageTexture);let f=Math.ceil(r/8),v=Math.ceil(a/8);s.setupDispatch(f,v,1),o.computeDispatch([s],"VoxelDebugOverlay"),this.quad.visible=!0}destroy(){this.app.scene.defaultDrawLayer.removeMeshInstances([this.quad],!0),this.quad.destroy(),this.compute.destroy(),this.compute.shader.destroy(),this.overlayMaterial.destroy(),this.nodesBuffer?.destroy(),this.leafDataBuffer?.destroy(),this.storageTexture?.destroy()}},Yo=/OculusBrowser|PicoBrowser/.test(globalThis.navigator?.userAgent??""),Yd=i=>{let{app:e,events:t,state:s,camera:n,renderer:o,root:r}=i,{xr:a}=e,l=!1,c=null,h=null,d=null,u=()=>new Error("the viewer has been destroyed"),p=!1,g=!1,m=()=>{l||(s.canStartAR=a.isAvailable("immersive-ar"),s.canStartVR=a.isAvailable("immersive-vr"),s.hasAR=s.canStartAR||p,s.hasVR=s.canStartVR||g)};m();let f=a.on("available",m);o==="webgpu"&&Promise.all([Ls.isDeviceSupported(On,"immersive-ar"),Ls.isDeviceSupported(On,"immersive-vr")]).then(([M,T])=>{l||(p=M,g=T,m())});let v=n.parent,y=new x,k=new Q,w=new x,P=new Q;v.addComponent("script"),v.script.create(Es),v.script.create(Vs);let S=null,b=a.on("start",()=>{if(l)return;e.autoRender=!0,y.copy(v.getPosition()),k.copy(v.getRotation()),w.copy(n.getPosition()),P.copy(n.getRotation());let{forward:M}=n,T=M.x,V=M.z;if(T*T+V*V<1e-6){let{up:B}=n,I=M.y<0?1:-1;T=B.x*I,V=B.z*I}v.setPosition(w.x,0,w.z),v.setEulerAngles(0,Math.atan2(-T,-V)*Y.RAD_TO_DEG,0),Yo&&(a.fixedFoveation=1,a.supportedFrameRates?.includes(72)&&a.updateTargetFrameRate(72)),S&&(S.scalePivot=a.type==="immersive-ar"?"hands":"feet"),s.xrMode=a.type==="immersive-ar"?"ar":"vr"}),z=a.on("end",()=>{l||(e.autoRender=!1,v.setPosition(y),v.setRotation(k),n.setPosition(w),n.setRotation(P),S?.reset(),s.xrMode=null,c!==null&&cancelAnimationFrame(c),c=requestAnimationFrame(()=>{c=null,!l&&(r.prepend(e.graphicsDevice.canvas),e.renderNextFrame=!0)}))}),_=async M=>{if(l)throw u();if(M!=="ar"&&M!=="vr")throw new Error("startXR: mode must be ar or vr");if(h||d||a.active)throw new Error("startXR: a session is active or pending");let T=M==="ar"?"immersive-ar":"immersive-vr";if(!a.isAvailable(T)){let I=M==="ar"?s.hasAR:s.hasVR;throw new Error(I?"startXR: reload with WebGL to start this session":"startXR: XR is not available")}let{nearClip:V,farClip:B}=n.camera;n.camera.nearClip=.01,n.camera.farClip=1e3;try{if(await new Promise((I,C)=>{h=C,a.start(n.camera,T,"local-floor",{framebufferScaleFactor:Yo&&s.performanceMode?.8:1,callback:A=>{l?(a.session?.end().catch(()=>{}),C(u())):A?C(A):I()}})}),l)throw u()}catch(I){throw l||(n.camera.nearClip=V,n.camera.farClip=B),I}finally{h=null}},R=async()=>{if(l)throw u();if(h)throw new Error("endXR: a session is still starting");if(d)throw new Error("endXR: a session is already ending");let{session:M}=a;if(!M)return;let T;try{if(await new Promise((V,B)=>{d=B,T=V,a.once("end",T),M.end().catch(B)}),l)throw u()}finally{a.off("end",T),d=null}},L=t.on("inputEvent",M=>{M==="cancel"&&a.active&&R().catch(()=>{})});return{start:_,end:R,setManipulationTarget:M=>{S=v.script.create(Fs,{properties:{target:M}})},destroy:()=>{l=!0,f.off(),b.off(),z.off(),L.off(),c!==null&&cancelAnimationFrame(c),h?.(u()),d?.(u()),s.xrMode=null}}},an=(i,e,t,s)=>(i.includes(e)||console.warn(`patchChunk: substring not found in '${s}', shader chunk patch may be out of sync with the engine.`),i.replace(e,t)),Zd=`
|
|
2697
|
+
vec3 prepareOutputFromGamma(vec3 gammaColor, float depth) {
|
|
2698
|
+
return gammaColor;
|
|
2699
|
+
}
|
|
2700
|
+
`,Kd=`
|
|
2701
|
+
fn prepareOutputFromGamma(gammaColor: vec3f, depth: f32) -> vec3f {
|
|
2702
|
+
return gammaColor;
|
|
2703
|
+
}
|
|
2704
|
+
`,Qd={webgl:Xi,webgpu:Yi},rn={mobile:{low:1,high:2},desktop:{low:2,high:4}},Jd=1,Lr=5,Er=90,Rr={none:Wi,linear:Bi,filmic:Ni,hejl:qi,aces:$i,aces2:Hi,neutral:ji},eu=(i,e)=>{e.sharpness.enabled?i.rendering.sharpness=e.sharpness.amount:i.rendering.sharpness=0;let{bloom:t}=i;e.bloom.enabled?(t.intensity=e.bloom.intensity,t.blurLevel=e.bloom.blurLevel):t.intensity=0;let{grading:s}=i;e.grading.enabled?(s.enabled=!0,s.brightness=e.grading.brightness,s.contrast=e.grading.contrast,s.saturation=e.grading.saturation,s.tint=new K().fromArray(e.grading.tint)):s.enabled=!1;let{vignette:n}=i;e.vignette.enabled?(n.intensity=e.vignette.intensity,n.inner=e.vignette.inner,n.outer=e.vignette.outer,n.curvature=e.vignette.curvature):n.intensity=0;let{fringing:o}=i;e.fringing.enabled?o.intensity=e.fringing.intensity:o.intensity=0},tu=i=>i.sharpness.enabled&&i.sharpness.amount>0||i.bloom.enabled&&i.bloom.intensity>0||i.grading.enabled||i.vignette.enabled&&i.vignette.intensity>0||i.fringing.enabled&&i.fringing.intensity>0,Ar=new x,sl=Xe.prototype.isColorBufferSrgb,cn=new Set,su=function(i){return cn.has(this.device)&&this===this.device.backBuffer?!0:sl.call(this,i)},eo=(i,e)=>{e?cn.add(i):cn.delete(i),Xe.prototype.isColorBufferSrgb=cn.size?su:sl},Zo=class{global;cameraFrame;inputController;cameraManager;picker;voxelOverlay=null;meshOverlay=null;navCursor=null;debugPanel=null;splatRenderer=null;miniStats=null;destroyed=!1;disposers=[];fullscreen;xr;capture=null;captureQueue=Promise.resolve();ready;failReady;abortHandlers=new Set;origChunks;constructor(e,t,s,n){this.global=e;let{app:o,settings:r,config:a,events:l,state:c,camera:h,renderer:d}=e,{graphicsDevice:u}=o;this.fullscreen=rd(e),this.xr=Yd(e),this.ready=new Promise((_,R)=>{l.once("firstFrame",()=>_()),this.failReady=R}),this.ready.catch(()=>{});let p=ie.get(u,"glsl");p.set("skyboxPS",an(p.get("skyboxPS"),"mapRoughnessUv(uv, mipLevel)","uv","glsl skyboxPS"));let g=ie.get(u,"wgsl");if(g.set("skyboxPS",an(g.get("skyboxPS"),"mapRoughnessUv(uv, uniform.mipLevel)","uv","wgsl skyboxPS")),this.origChunks={glsl:{gsplatOutputVS:p.get("gsplatOutputVS"),skyboxPS:p.get("skyboxPS")},wgsl:{gsplatOutputVS:g.get("gsplatOutputVS"),skyboxPS:g.get("skyboxPS")}},o.autoRender=!0,this.configureCamera(r),a.stochastic&&d==="webgpu"){let _=o.scene.layers.getLayerByName("World"),R=(a.budget&&a.budget>0?a.budget:(Ot.mobile?rn.mobile:rn.desktop).high)*1e6;bn.cacheFits(o.graphicsDevice,R)?_&&(this.splatRenderer=new bn(o,h.camera,_,{source:a.splatSource,variant:a.variant}),l.once("firstFrame",()=>l.fire("splatRenderer:ready",this.splatRenderer))):console.warn(`stochastic renderer: a ${R/1e6}M splat budget exceeds this device's storage binding limit; the sorted renderer draws`)}let m=l.on("xrMode:changed",()=>{this.destroyed||(this.configureCamera(r),this.splatRenderer?.setEnabled(c.xrMode===null))});this.onDestroy(()=>m.off()),this.setMiniStats(a.ministats),this.onDestroy(()=>{this.miniStats?.destroy(),this.miniStats=null});let f=new U,v=new U,y=new xi;o.on("framerender",()=>{let _=h.getWorldTransform(),R=h.camera.projectionMatrix;o.renderNextFrame||(this.miniStats?.enabled||!Aa(_.data,v.data)||!Aa(R.data,f.data))&&(o.renderNextFrame=!0),o.renderNextFrame&&(v.copy(_),f.copy(R))});let k=_=>{let R=e.camera;R.setPosition(_.position),R.setEulerAngles(_.angles),R.camera.fov=_.fov,R.camera.horizontalFov=u.width>u.height;let L=y.halfExtents.length();Ar.sub2(y.center,_.position);let M=Ar.dot(R.forward),T=Math.max(M+L,.01),V=Math.max(M-L,T/(1024*16));R.camera.farClip=T,R.camera.nearClip=Math.min(1,V)};o.on("update",_=>{o.xr.active||this.inputController&&this.cameraManager&&(this.inputController.update(_,this.cameraManager.camera.distance),this.cameraManager.update(_,this.inputController.frame),k(this.cameraManager.camera))}),o.on("prerender",()=>{this.voxelOverlay?.update()}),l.on("firstFrame",()=>{c.animationPaused=!!a.noanim,c.loaded=!0,a.exposeGlobals&&(window.scrubTo=async _=>{this.seek(_),c.animationPaused=!0;let R,L=new Promise(M=>{R=M,o.once("frameend",R)});try{await this.untilDestroyed(L)}finally{o.off("frameend",R)}},window.animationDuration=c.animationDuration,window.app=o,window.captureFrame=_=>this.captureFrame(_))});let{gsplat:w}=o.scene;w.lodUpdateAngle=Er,w.lodBehindPenalty=Lr,w.minContribution=1,w.alphaClip=1/255,w.antiAlias=a.aa,w.radialSorting=!0,w.debug=a.colorize?Ki:Zi;let P=()=>{};a.fullload||t.then(_=>{if(this.destroyed)return;let R=_.gsplat,M=R.resource?.octree?.lodLevels;M&&(R.lodRangeMax=R.lodRangeMin=M-1)},P);let S;t.then(()=>{if(this.destroyed)return;let _=o.systems.gsplat,R=0,L=0,M=(T,V,B,I)=>{L+=Math.max(0,R-I),R=I;let C=L+I;C>0&&(c.progress=Math.max(c.progress,Math.min(99,Math.trunc(L/C*100))))};_.on("frame:ready",M),S=()=>_.off("frame:ready",M),this.onDestroy(S)},P);let b=null,z;n?.then(_=>{this.destroyed||!_||(b=_,z?.(_))},P),Promise.all([t,s]).then(_=>{if(this.destroyed)return;let R=_[0].gsplat;this.xr.setManipulationTarget(_[0]);let L=R.customAabb;L&&y.setFromTransformedAabb(L,_[0].getWorldTransform()),this.picker=this.splatRenderer?new $o(o,h,this.splatRenderer):new Po(o,h),l.fire("picker:ready",this.picker),this.inputController=new Oo(e,this.picker),this.cameraManager=new So(e,y),k(this.cameraManager.camera),a.ui&&(this.navCursor=new qo(o,h,null,l,c,a.reticle)),z=C=>{if(this.inputController.collision=C,this.cameraManager.setCollision(C),this.navCursor&&(this.navCursor.collision=C),c.hasCollision=!0,c.walkAllowed=Mo(y,C),C instanceof hs&&d!=="webgl"){let A=D=>{D&&!this.voxelOverlay&&(this.voxelOverlay=new Xo(o,C,h),this.voxelOverlay.mode=a.heatmap?"heatmap":"overlay"),this.voxelOverlay&&(this.voxelOverlay.enabled=D),o.renderNextFrame=!0};l.on("collisionOverlayEnabled:changed",A),c.hasCollisionOverlay=!0,A(c.collisionOverlayEnabled)}else C instanceof un&&(this.meshOverlay=new Bo(o,C,h,!!this.cameraFrame),c.hasCollisionOverlay=!0,l.on("collisionOverlayEnabled:changed",A=>{this.meshOverlay.enabled=A,o.renderNextFrame=!0}));o.renderNextFrame=!0},b&&z(b),this.debugPanel=new Eo(e,this.cameraManager,this.picker,this.splatRenderer,{shown:()=>!!this.miniStats?.enabled,show:C=>this.setMiniStats(C)});let M=()=>{let C=()=>{if(a.budget!==void 0&&Number.isFinite(a.budget)&&a.budget>0)return a.budget;if(Yo&&c.xrMode!==null)return Jd;let A=Ot.mobile?rn.mobile:rn.desktop;return c.performanceMode?A.low:A.high};w.splatBudget=C()*1e6,w.colorUpdateAngle=c.performanceMode?1:.2,w.lodBehindPenalty=c.xrMode===null?Lr:1,w.lodUpdateAngle=c.xrMode===null?Er:0,R.lodRangeMin=0,R.lodRangeMax=1e3,o.renderNextFrame=!0};a.fullload&&M();let T=o.systems.gsplat;T.on("frame:request",()=>{o.renderNextFrame=!0});let B=!(R.resource?.octree??null),I=(C,A,D,O)=>{(O>0||o.stats.frame.gsplats>0)&&(B=!0),D&&O===0&&B&&(T.off("frame:ready",I),S?.(),c.progress=100,o.autoRender=!1,l.on("performanceMode:changed",M),l.on("xrMode:changed",M),M(),this.splatRenderer||(w.renderer=Qd[d]),o.once("frameend",()=>{l.fire("firstFrame"),window.firstFrame?.()}))};T.on("frame:ready",I)},P)}requireAlive(e){if(this.destroyed)throw new Error(`${e}: the viewer has been destroyed`)}requireLoaded(e){if(this.requireAlive(e),!this.global.state.loaded)throw new Error(`${e}: the viewer is not loaded`)}frameScene(){this.requireLoaded("frameScene"),this.global.events.fire("inputEvent","frame")}resetCamera(){this.requireLoaded("resetCamera"),this.global.events.fire("inputEvent","reset")}toggleWalk(){this.requireLoaded("toggleWalk"),this.global.events.fire("inputEvent","toggleWalk")}selectAnnotation(e){this.requireLoaded("selectAnnotation");let{settings:t,state:s,app:n}=this.global;if(e!==null){if(!Number.isInteger(e)||e<0||e>=t.annotations.length)throw new RangeError("selectAnnotation: index must be an integer in range or null");this.cameraManager.selectAnnotation(t.annotations[e])}s.selectedAnnotation=e,n.renderNextFrame=!0}setMoveInput(e,t){if(this.requireLoaded("setMoveInput"),!Number.isFinite(e)||!Number.isFinite(t))throw new Error("setMoveInput: axes must be finite numbers");this.inputController.setMoveInput(Math.max(-1,Math.min(1,e)),Math.max(-1,Math.min(1,t)))}async requestFullscreen(){return this.requireAlive("requestFullscreen"),this.untilDestroyed(this.fullscreen.request())}async exitFullscreen(){return this.requireAlive("exitFullscreen"),this.untilDestroyed(this.fullscreen.exit())}async startXR(e){return this.requireLoaded("startXR"),this.xr.start(e)}async endXR(){return this.requireAlive("endXR"),this.xr.end()}seek(e){this.requireLoaded("seek");let{state:t,app:s}=this.global;if(!t.hasAnimation)throw new Error("seek: no animation track");if(!Number.isFinite(e))throw new Error("seek: time must be finite");this.cameraManager.seek(e),s.renderNextFrame=!0}captureFrame({time:e,width:t=480,height:s=t,supersample:n}={}){let o=async()=>{if(await this.ready,this.destroyed)throw new Error("captureFrame: the viewer has been destroyed");let{app:a,camera:l,state:c}=this.global;this.capture||(this.capture=new Co(a,l.camera,()=>this.cameraFrame??null));let h=this.capture.grab({time:e,width:t,height:s,supersample:n,scrub:d=>{c.hasAnimation&&(c.animationPaused=!0,this.seek(d))}});return this.untilDestroyed(h)},r=this.captureQueue.then(o,o);return this.captureQueue=r.then(()=>{},()=>{}),r}untilDestroyed(e){let t,s=new Promise((n,o)=>{t=o});return this.abortHandlers.add(t),Promise.race([e,s]).finally(()=>{this.abortHandlers.delete(t)})}onDestroy(e){if(this.destroyed){e();return}this.disposers.push(e)}setMiniStats(e){if(e===!!this.miniStats?.enabled)return;if(this.miniStats){if(this.miniStats.enabled=e,this.miniStats.div.style.display=e?"":"none",e){let s=this.miniStats.activeSizeIndex;this.miniStats.activeSizeIndex=s,this.global.app.renderNextFrame=!0}return}let t=$n.getDefaultOptions();t.cpu.enabled=!1,t.stats=t.stats.filter(s=>s.name!=="DrawCalls"),t.stats.push({name:"VRAM",stats:["vram.tex"],decimalPlaces:1,multiplier:1/(1024*1024),unitsName:"MB",watermark:1024},{name:"Splats",stats:["frame.gsplats"],decimalPlaces:3,multiplier:1/1e6,unitsName:"M",watermark:5}),this.miniStats=new $n(this.global.app,t),this.global.app.renderNextFrame=!0}destroy(){if(this.destroyed)return;this.destroyed=!0;let e=()=>new Error("the viewer has been destroyed");this.failReady(e());for(let r of this.abortHandlers)r(e());this.abortHandlers.clear();let{app:t}=this.global;this.fullscreen.destroy(),this.xr.destroy(),this.debugPanel?.destroy(),this.navCursor?.destroy(),this.inputController?.destroy(),this.voxelOverlay?.destroy(),this.meshOverlay?.destroy(),this.picker?.release(),this.splatRenderer?.destroy(),this.splatRenderer=null,this.capture?.destroy(),this.capture=null,this.cameraFrame&&(this.cameraFrame.destroy(),this.cameraFrame=null),eo(t.graphicsDevice,!1);for(let r of this.disposers.reverse())r();this.disposers.length=0,window.app===t&&(delete window.app,delete window.scrubTo,delete window.captureFrame,delete window.animationDuration);let s=t.graphicsDevice,n=s.gl??null,o=s.wgpu??null;t.destroy(),n?.getExtension("WEBGL_lose_context")?.loseContext(),o?.destroy(),this.global.root.remove()}configureCamera(e){let{global:t}=this,{app:s,config:n,camera:o,state:r}=t,{postEffectSettings:a}=e,{background:l}=e,c=n.hpr??e.highPrecisionRendering,h=!n.nofx&&(tu(a)||c);if(r.xrMode===null&&h){this.cameraFrame||(this.cameraFrame=new pa(s,o.camera));let{cameraFrame:p}=this;p.enabled=!0,p.rendering.toneMapping=Rr[e.tonemapping],p.rendering.renderFormats=c?[ks,zn]:[],eu(p,a),p.update(),ie.get(s.graphicsDevice,"glsl").set("gsplatOutputVS",Zd),ie.get(s.graphicsDevice,"wgsl").set("gsplatOutputVS",Kd),ie.get(s.graphicsDevice,"glsl").set("skyboxPS",an(this.origChunks.glsl.skyboxPS,"gammaCorrectOutput(toneMap(processEnvironment(linear)))","pow(toneMap(processEnvironment(linear)) + 0.0000001, vec3(1.0 / 2.2))","glsl skyboxPS gamma override")),ie.get(s.graphicsDevice,"wgsl").set("skyboxPS",an(this.origChunks.wgsl.skyboxPS,"gammaCorrectOutput(toneMap(processEnvironment(linear)))","pow(toneMap(processEnvironment(linear)) + 0.0000001, vec3f(1.0 / 2.2))","wgsl skyboxPS gamma override")),eo(s.graphicsDevice,!0),o.camera.clearColor=new K(l.color)}else this.cameraFrame&&(this.cameraFrame.destroy(),this.cameraFrame=null),ie.get(s.graphicsDevice,"glsl").set("gsplatOutputVS",this.origChunks.glsl.gsplatOutputVS),ie.get(s.graphicsDevice,"wgsl").set("gsplatOutputVS",this.origChunks.wgsl.gsplatOutputVS),ie.get(s.graphicsDevice,"glsl").set("skyboxPS",this.origChunks.glsl.skyboxPS),ie.get(s.graphicsDevice,"wgsl").set("skyboxPS",this.origChunks.wgsl.skyboxPS),eo(s.graphicsDevice,!1),o.camera.toneMapping=Rr[e.tonemapping],o.camera.clearColor=r.xrMode==="ar"?new K(0,0,0,0):new K(l.color);let u=s.scene.layers.getLayerById(Ii);u&&(u.enabled=r.xrMode!=="ar"),this.meshOverlay?.setCameraFrameEnabled(!!this.cameraFrame)}},nl=(i,e)=>e||new URL(i,location.href).pathname.split("/").pop(),nu=(i,e)=>nl(i,e)?.toLowerCase()!=="lod-meta.json",ou=async(i,e,t,s)=>{let{contents:n,contentUrl:o,contentFilename:r}=e,a=n,l=nl(o,r),c=l.toLowerCase()==="meta.json"?await(await n).json():void 0;if(s())throw new Error("loadGsplat: the viewer was destroyed while loading");let h=new Ps(l,"gsplat",{url:o,filename:l,contents:a},c);return new Promise((d,u)=>{h.on("load",()=>{let g=new ce("gsplat",i);g.setLocalEulerAngles(0,0,180),g.addComponent("gsplat",{unified:!0,asset:h}),i.root.addChild(g),d(g)});let p=0;h.on("progress",(g,m)=>{let f=Math.min(1,g/m)*100;f>p&&(p=f,t(Math.trunc(p)))}),h.on("error",g=>{console.log(g),u(g)}),i.assets.add(h),i.assets.load(h)})},iu=(i,e)=>new Promise((t,s)=>{let n=new Ps("skybox","texture",{url:e},{type:"rgbp",mipmaps:!1,addressu:"repeat",addressv:"clamp"});n.on("load",()=>{t(n)}),n.on("error",o=>{console.log(o),s(o)}),i.assets.add(n),i.assets.load(n)}),au=async(i,e)=>{let t=e.renderer==="webgpu",s=await Ti(i,{deviceTypes:t?["webgpu"]:[],antialias:!1,depth:!0,stencil:!1,xrCompatible:!0,powerPreference:"high-performance"});console.log(`Renderer: ${s.deviceType}`);let n=s.deviceType==="webgpu"?"webgpu":"webgl";s.maxPixelRatio=window.devicePixelRatio;let o=new to(i,{graphicsDevice:s,mouse:new zi(i),touch:new Fi(i),keyboard:new Di(window)});e.stochastic&&n==="webgpu"&&(o.scene.gsplatCentersEnabled=!1),o.loader.getHandler("texture").imgParser.crossOrigin="anonymous";let r=new ce("camera root",o);o.root.addChild(r);let a=new ce("camera",o);r.addChild(a);let l=new ce("light",o);return l.setEulerAngles(35,45,0),l.addComponent("light",{color:new K(1,.98,.957),intensity:1}),o.root.addChild(l),o.scene.ambientLight.set(.51,.55,.65),{app:o,camera:a,renderer:n}},ol=i=>({width:i.clientWidth,height:i.clientHeight}),il=(i,e,t)=>{if(!e.width||!e.height)return;let s=Ot.mobile?1080:2160,o=Math.min(s/Math.min(screen.width,screen.height),window.devicePixelRatio)*(t?.5:1),r=Math.ceil(e.width*o),a=Math.ceil(e.height*o);(r!==i.width||a!==i.height)&&(i.width=r,i.height=a)},ru=i=>{let{app:e,events:t,state:s}=i,{canvas:n}=e.graphicsDevice,o=ol(n),r=()=>{e.xr?.active||il(n,o,s.performanceMode)},a=new ResizeObserver(l=>{let c=l[0]?.contentBoxSize?.[0];c&&c.inlineSize&&c.blockSize&&(o.width=c.inlineSize,o.height=c.blockSize,e.renderNextFrame=!0)});return a.observe(n),t.on("performanceMode:changed",()=>{e.renderNextFrame=!0}),e.on("framerender",r),e._allowResize=!1,r(),()=>a.disconnect()},lu=i=>{let e=new Image;return e.src=i,e},cu=i=>({contentUrl:i.contentUrl,contentFilename:i.contentFilename,posterUrl:i.posterUrl,skyboxUrl:i.skyboxUrl,collisionUrl:i.collisionUrl,poster:i.poster??(i.posterUrl?lu(i.posterUrl):void 0),contents:i.contents??(nu(i.contentUrl,i.contentFilename)?fetch(i.contentUrl):void 0),renderer:i.renderer??"webgpu",ui:i.ui??!0,reticle:i.reticle??!1,noanim:i.noanim??!1,nofx:i.nofx??!1,hpr:i.hpr,ministats:i.ministats??!1,colorize:i.colorize??!1,fullload:i.fullload??!1,aa:i.aa??!1,stochastic:i.stochastic??!1,splatSource:i.splatSource??"workbuffer",variant:i.variant??"",budget:i.budget,heatmap:i.heatmap??!1,debug:i.debug??!1,lang:i.lang,exposeGlobals:i.exposeGlobals??!1}),al=async i=>{let{container:e}=i,t=cu(i),s=document.createElement("div");s.className="sse-viewer",t.ui?s.innerHTML=jc:s.appendChild(document.createElement("canvas")),e.appendChild(s);let n=s.querySelector("canvas"),o=new vi;t.poster&&t.ui&&qc(s,t.poster,o);let r=typeof i.settings=="string"?await(await fetch(i.settings)).json():await i.settings,a=hc(Ot.mobile);il(n,ol(n),a.performanceMode);let{app:l,camera:c,renderer:h}=await au(n,t),d=cc(t.lang,s),u=Ql(o,{loaded:!1,...a,progress:0,inputMode:Ot.mobile?"touch":"desktop",cameraMode:"orbit",hasAnimation:!1,animationDuration:0,animationTime:0,animationPaused:!0,hasAR:!1,hasVR:!1,canStartAR:!1,canStartVR:!1,xrMode:null,hasCollision:!1,hasCollisionOverlay:!1,walkAllowed:!1,collisionOverlayEnabled:!1,isFullscreen:!1,controlsHidden:!1,selectedAnnotation:null,inputEnabled:!0}),p={app:l,settings:gc(r),config:t,state:u,events:o,camera:c,renderer:h,root:s,localize:d},g=ru(p);l.start(),c.addComponent("camera");let m=!1,f=ou(l,t,b=>{u.progress=b},()=>m),v=t.skyboxUrl&&iu(l,t.skyboxUrl).then(b=>{l.scene.envAtlas=b.resource}).catch(b=>{console.warn("Failed to load skybox:",b)}),y;t.collisionUrl&&(new URL(t.collisionUrl,location.href).pathname.split(".").pop()?.toLowerCase()==="glb"?y=un.fromGlb(l,t.collisionUrl).catch(z=>(console.warn("Failed to load mesh collision:",z),null)):y=Kl(t.collisionUrl).catch(z=>(console.warn("Failed to load voxel data:",z),null)));let k;if(p.settings.soundUrl){let b=new Audio(p.settings.soundUrl);b.crossOrigin="anonymous";let z=()=>{b&&b.play()};s.addEventListener("click",z,{capture:!0,once:!0}),k=()=>{s.removeEventListener("click",z,{capture:!0}),b.pause()}}let w=new Zo(p,f,v,y);w.onDestroy(dc(o));let P={app:l,state:u,events:o,annotations:p.settings.annotations,captureFrame:b=>w.captureFrame(b),seek:b=>w.seek(b),frameScene:()=>w.frameScene(),resetCamera:()=>w.resetCamera(),toggleWalk:()=>w.toggleWalk(),selectAnnotation:b=>w.selectAnnotation(b),setMoveInput:(b,z)=>w.setMoveInput(b,z),requestFullscreen:()=>w.requestFullscreen(),exitFullscreen:()=>w.exitFullscreen(),startXR:b=>w.startXR(b),endXR:()=>w.endXR(),destroy:()=>w.destroy(),cameraManager:()=>w.cameraManager},S=t.ui?Hc(p,P,()=>w.picker,()=>w.cameraManager?.transitionProgress()??1):null;return w.onDestroy(()=>{m=!0}),w.onDestroy(g),S&&w.onDestroy(S),k&&w.onDestroy(k),P};console.log(`SuperSplat Viewer v${zr} | Engine v${Tn} (${gi})`);var hu=Object.freeze([0,0,0]),du="linear",rl=85,uu=(i="environment")=>i==="environment"?{position:[0,2,0],target:[2,2,0],fov:rl}:{position:[2,2,-2],target:[0,0,0],fov:rl},pu=()=>({sharpness:{enabled:!1,amount:0},bloom:{enabled:!1,intensity:.1,blurLevel:2},grading:{enabled:!1,brightness:1,contrast:1,saturation:1,tint:[1,1,1]},vignette:{enabled:!1,intensity:.5,inner:.3,outer:.75,curvature:1},fringing:{enabled:!1,intensity:.5}}),ll=(i="environment")=>({version:2,tonemapping:du,highPrecisionRendering:!1,background:{color:[...hu]},postEffectSettings:pu(),animTracks:[],cameras:[{initial:uu(i)}],annotations:[],startMode:"default"});var fs=i=>(i!==null&&typeof i=="object"&&(Object.values(i).forEach(fs),Object.freeze(i)),i),ep=fs({min:10,max:120,step:1}),tp=fs({sharpness:{amount:{min:0,max:1,step:.01}},bloom:{intensity:{min:0,max:.1,step:.01},blurLevel:{min:1,max:16,step:1}},grading:{brightness:{min:0,max:3,step:.01},contrast:{min:.5,max:1.5,step:.01},saturation:{min:0,max:2,step:.01}},vignette:{intensity:{min:0,max:1,step:.01},inner:{min:0,max:3,step:.01},outer:{min:0,max:3,step:.01},curvature:{min:.01,max:10,step:.01}},fringing:{intensity:{min:0,max:100,step:1}}}),sp=fs({duration:{min:.1,max:600},frameRate:{min:1,max:120},smoothness:{min:0,max:1},maxKeyframes:1e3,nameMax:120,maxTracks:10}),np=fs({maxCount:25,titleMax:60,textMax:280});var ot=null;function ii(){if(ot!==null)return ot;let i=16,e=document.createElement("canvas").getContext("webgl2");if(!e||!e.getExtension("EXT_color_buffer_float"))return ot=!0,ot;let t=e.createTexture();e.bindTexture(e.TEXTURE_2D,t),e.texStorage2D(e.TEXTURE_2D,1,e.RGBA16F,i,i),e.bindFramebuffer(e.FRAMEBUFFER,e.createFramebuffer()),e.framebufferTexture2D(e.FRAMEBUFFER,e.COLOR_ATTACHMENT0,e.TEXTURE_2D,t,0),e.enable(e.SCISSOR_TEST);let s=4,n=(r,a)=>[(r+1)/32,(a+1)/32,(r+a+1)/64,.5];for(let r=0;r<i;r+=s)for(let a=0;a<i;a+=s)e.scissor(a,r,s,s),e.clearColor(...n(a,r)),e.clear(e.COLOR_BUFFER_BIT);let o=new Float32Array(i*i*4);e.readPixels(0,0,i,i,e.RGBA,e.FLOAT,o),ot=!0;for(let r=0;r<i&&ot;r++)for(let a=0;a<i&&ot;a++)ot=n(a-a%s,r-r%s).every((c,h)=>Math.abs(o[(r*i+a)*4+h]-c)<.001);return ot}var oi=[4,8,16,32,48],fu=6,cl=48,mu=.05,ms=class{constructor(e,t){this.app=e;this.camera=t}soundness=new WeakMap;toldUnsound=!1;pick(e,t){return Promise.resolve(this.find(e,t,!1)?.position??null)}pickSurface(e,t){return Promise.resolve(this.find(e,t,!0))}drawn(){let e=this.app.renderer.gsplatDirector,t=this.app.scene.layers.getLayerByName("World"),s=e?.camerasMap.get(this.camera.camera?.camera)?.layersMap.get(t)?.gsplatManager?.world,o=(s?.getState(s.lastWorldStateVersion)?.splats??[]).filter(a=>a.resource?.centers),r=o.filter(a=>this.sound(a.resource.centers,a.resource.aabb));return o.length>0&&r.length===0&&!this.toldUnsound&&(this.toldUnsound=!0,console.warn("[splat viewer] the splats' centres are scrambled as well, read back from the graphics card the same way:","nothing can be found under the pointer in this browser, short of turning its protection against fingerprinting off for this site.")),r.map(a=>({centres:a.resource.centers,ranges:a.intervals.length>0?a.intervals:[0,a.resource.numSplats],world:a.node.getWorldTransform()}))}sound(e,t){let s=this.soundness.get(e);if(s===void 0){if(!t)s=!0;else{let n=t.getMin(),o=t.getMax(),r=t.halfExtents.length()*.05,a=e.length/3,l=Math.max(1,Math.floor(a/256)),c=0,h=0;for(let d=0;d<a;d+=l){c++;let u=e[d*3],p=e[d*3+1],g=e[d*3+2];u>=n.x-r&&u<=o.x+r&&p>=n.y-r&&p<=o.y+r&&g>=n.z-r&&g<=o.z+r&&h++}s=h>=c*.95}this.soundness.set(e,s)}return s}find(e,t,s){let n=this.camera.camera,o=this.app.graphicsDevice.canvas,r=o.clientWidth,a=o.clientHeight;if(!n||!r||!a)return null;let l=new U().mul2(n.projectionMatrix,n.viewMatrix),c=e*2-1,h=1-t*2,d=s?cl:oi[oi.length-1],u=n.nearClip,p=[],g=new U;for(let{centres:_,ranges:R,world:L}of this.drawn()){let M=g.mul2(l,L).data,T=L.data;for(let V=0;V<R.length;V+=2){let B=Math.min(R[V+1],_.length/3);for(let I=R[V];I<B;I++){let C=_[I*3],A=_[I*3+1],D=_[I*3+2],O=M[3]*C+M[7]*A+M[11]*D+M[15];if(O<=u)continue;let H=((M[0]*C+M[4]*A+M[8]*D+M[12])/O-c)*r/2;if(H>d||H<-d)continue;let N=((M[1]*C+M[5]*A+M[9]*D+M[13])/O-h)*a/2,E=H*H+N*N;E<=d*d&&p.push({depth:O,pixels2:E,x:T[0]*C+T[4]*A+T[8]*D+T[12],y:T[1]*C+T[5]*A+T[9]*D+T[13],z:T[2]*C+T[6]*A+T[10]*D+T[14]})}}}let m=[];for(let _ of oi)if(m=p.filter(R=>R.pixels2<=_*_).map(R=>R.depth),m.length>=fu)break;if(m.length===0)return null;let f=gu(m.sort((_,R)=>_-R)),v=this.camera.getPosition(),y=this.camera.forward,k=(_,R)=>{let L=n.screenToWorld(_,R,1,new x).sub(v).normalize();return L.mulScalar(f/L.dot(y)).add(v)},w=k(e*r,t*a);if(!s)return{position:w,normal:new x};let P=k(e*r+1,t*a).distance(w)*(cl/2),S=p.filter(_=>(_.x-w.x)**2+(_.y-w.y)**2+(_.z-w.z)**2<=P*P),b=vu(S)??new x,z=new x().sub2(v,w);return b.lengthSq()===0?b.copy(z):b.dot(z)<0&&b.mulScalar(-1),{position:w,normal:b.normalize()}}};function gu(i){let e=Math.min(i.length,3);for(let t=0;t<i.length;t++){let s=t;for(;s+1<i.length&&i[s+1]<=i[t]*(1+mu);)s++;if(s-t+1>=e)return i[t+s>>1]}return i[0]}function vu(i){if(i.length<5)return null;let e=0,t=0,s=0;for(let g of i)e+=g.x,t+=g.y,s+=g.z;e/=i.length,t/=i.length,s/=i.length;let n=0,o=0,r=0,a=0,l=0,c=0;for(let g of i){let m=g.x-e,f=g.y-t,v=g.z-s;n+=m*m,o+=m*f,r+=m*v,a+=f*f,l+=f*v,c+=v*v}let h=a*c-l*l,d=n*c-r*r,u=n*a-o*o,p=Math.max(h,d,u);return p<=0?null:p===h?new x(h,r*l-o*c,o*l-r*a):p===d?new x(r*l-o*c,d,o*r-l*n):new x(o*l-r*a,o*r-l*n,u)}xa();var hl=document.getElementById("viewer"),Kt=document.getElementById("tags"),we=document.getElementById("placing"),ws=new URLSearchParams(window.location.search);for(let[i,e]of[["accent","--app-accent"],["background","--app-background"]]){let t=ws.get(i);t&&document.documentElement.style.setProperty(e,t)}var be=null,Zt=0,Sn="-y";function xe(i){window.parent.postMessage(i,window.location.origin)}window.addEventListener("message",i=>{if(i.origin!==window.location.origin||i.source!==window.parent)return;let e=i.data;if(e?.type==="load")gl(e.url,e.fileName,e.up??null).catch(t=>{console.error("Splat viewer: could not load",e.fileName,t),xe({type:"error",message:t?.message??String(t)})});else if(e?.type==="tags")wl(e.tags,e.alignment??null);else if(e?.type==="goToTag")Fu(e.id);else if(e?.type==="mode")Pu(e.mode);else if(e?.type==="placing")_s(e.on);else if(e?.type==="calibration")_l(e.calibration);else if(e?.type==="cancel-load")wu();else if(e?.type==="measuring")Vu(e.on);else if(e?.type==="measurement")Ou(e.measurement);else if(e?.type==="snapshot"){let{id:t}=e;Rl().catch(s=>(console.error("[splat viewer] could not take the picture.",s),null)).then(s=>xe({type:"snapshot",id:t,image:s}))}else e?.type==="signed"&&(ui.get(e.id)?.(e.url),ui.delete(e.id))});var dl="https://splat.invalid/",ui=new Map,yu=0;function xu(i){return new Promise((e,t)=>{let s=++yu;ui.set(s,n=>n?e(n):t(new Error(`No link to ${i}`))),xe({type:"sign",id:s,file:i})})}var ul=window.fetch.bind(window);window.fetch=(i,e)=>{let t=typeof i=="string"?i:i instanceof URL?i.href:i.url;if(!t.startsWith(dl))return ul(i,e);let s=decodeURIComponent(t.slice(dl.length).split(/[?#]/)[0]);return xu(s).then(n=>ul(n,e))};var pl=!1;async function gl(i,e,t){if(pl){location.reload();return}pl=!0;let s=++Zt;be?.destroy(),be=null,zt=null,Ge=!1,_s(!1),hl.replaceChildren(),Sn=t??"-y",xe({type:"progress",fraction:0});let n=i,o=e;if(/\.(spz|splat|ksplat)$/i.test(e)){let c=new Uint8Array(await(await fetch(i)).arrayBuffer()),{toCompressedPly:h}=await import("./converter-SLBB5MHG.js"),d=await h(c,e);if(s!==Zt)return;n=URL.createObjectURL(d),o="scene.compressed.ply"}let r=ll();r.background.color=[...bu()];let a=await al({container:hl,settings:r,contentUrl:n,contentFilename:o,renderer:ws.has("webgl")?"webgl":void 0,lang:ws.get("lang")??void 0});if(s!==Zt){a.destroy();return}be=a,Cu(a),a.events.on("picker:ready",c=>{if(s!==Zt)return;let h=Ft();h&&Lu(c,new ms(a.app,h.entity),a.app.graphicsDevice.isWebGL2),zt=c}),a.events.on("progress:changed",c=>xe({type:"progress",fraction:c/100})),a.events.on("cameraMode:changed",c=>{xe({type:"mode",mode:c==="fly"||c==="walk"?"walk":"orbit"})});let l=()=>{if(s!==Zt)return;ku();let c=a.app.graphicsDevice;console.info("[splat viewer] loaded:",c.deviceType,`${c.width}x${c.height} drawn,`,`${c.canvas.clientWidth}x${c.canvas.clientHeight} on the page, pixel ratio ${window.devicePixelRatio},`,`WebGL read-back ${ii()?"faithful":"SCRAMBLED"}`),xe({type:"loaded",kind:"splats",count:null,up:Sn}),a.app.on("frameend",es),es()};a.state.loaded?l():a.events.once("loaded:changed",l)}function wu(){Zt++,location.reload()}function bu(){let[,i,e,t,s]=getComputedStyle(document.body).backgroundColor.match(/rgba?\((\d+), (\d+), (\d+)(?:, ([\d.]+))?\)/)??[];return i===void 0||s==="0"?[224/255,247/255,255/255]:[Number(i)/255,Number(e)/255,Number(t)/255]}function vt(){return be?.app.root.findByName("gsplat")??null}function _u(i){let e=new x;e[i.replace("-","")]=i.startsWith("-")?-1:1;let t=e.dot(x.UP);if(t>.9999)return new Q;if(t<-.9999)return new Q().setFromAxisAngle(x.BACK,180);let s=new x().cross(e,x.UP).normalize();return new Q().setFromAxisAngle(s,Math.acos(t)*(180/Math.PI))}function ku(){let i=vt();i&&Sn!=="-y"&&(i.setLocalRotation(_u(Sn)),be?.frameScene())}var Mu=1.4,Su=Math.cos(Math.PI/180*40);function Cu(i){let e=i.events,t=e.fire.bind(e),s=null,n=()=>{s&&(s=null,t("navTarget:clear"))};e.fire=(o,...r)=>{let[a,l]=r,c=i.cameraManager?.();if(o==="navigateTo"&&i.state.cameraMode==="fly"&&c&&a&&l&&l.clone().normalize().y>=Su){n();let h=c.camera,d=a.clone().add(new x(0,Mu,0)),u=h.position.distance(d);return s={pitch:h.angles.x,yaw:h.angles.y,from:h.position.clone(),to:d,time:0,duration:Math.min(2,Math.max(.6,.5+u*.15)),last:h.position.clone(),lastPitch:h.angles.x},t("navTarget:set",a,l),e}return(o==="navigateTo"||o==="navigateCancel")&&n(),t(o,...r)},i.app.on("update",o=>{let r=i.cameraManager?.();if(!s||!r)return;let a=r.camera;if(i.state.cameraMode!=="fly"||a.position.distance(s.last)>.001||Math.abs(a.angles.x-s.lastPitch)>.01||Math.abs(a.angles.y-s.yaw)>.01){n();return}s.time=Math.min(1,s.time+o/s.duration);let l=s.time*s.time*(3-2*s.time);a.position.lerp(s.from,s.to,l),a.angles.set(s.pitch*(1-l),s.yaw,0),r.snap(),s.last.copy(a.position),s.lastPitch=a.angles.x,s.time>=1&&n()})}function Pu(i){be?.state.loaded&&(be.state.cameraMode=i==="walk"?be.state.walkAllowed?"walk":"fly":"orbit")}var zt=null;function Lu(i,e,t){if(t&&!ii()||ws.has("centres")){i.pick=(a,l)=>e.pick(a,l),i.pickSurface=(a,l)=>e.pickSurface(a,l),console.warn("[splat viewer] the spot under the pointer is found from the splats' centres:",ws.has("centres")?"asked to.":"this browser scrambles what WebGL reads back (protection against fingerprinting).");return}let s=i.pick,n=i.pickSurface,o=!1,r=a=>(a&&!o&&(o=!0,console.warn("[splat viewer] the graphics card found nothing under the pointer, the splats' centres did: they are used where it finds nothing.")),a);i.pick=async(a,l)=>await s(a,l)??r(await e.pick(a,l)),i.pickSurface=async(a,l)=>await n(a,l)??r(await e.pickSurface(a,l))}var vl=Math.PI/180,Eu=Math.cos(vl*25),Ru=Math.sin(vl*20);function Au(i){let e=i.clone().normalize();return Math.abs(e.y)>=Eu?new x(0,Math.sign(e.y),0):Math.abs(e.y)<=Ru?(e.y=0,e.normalize()):e}var fl=!1;async function yl(i,e){if(!zt||!vt())return null;let t=we.getBoundingClientRect(),s=(i-t.left)/t.width,n=(e-t.top)/t.height;if(!fl)try{let o=await zt.pickSurface(s,n);return o?{position:o.position.clone(),normal:Au(o.normal)}:null}catch(o){fl=!0,console.error("[splat viewer] finding the surface under the pointer failed; the point alone is used.",o)}try{let o=await zt.pick(s,n);return o?{position:o.clone(),normal:x.UP.clone()}:null}catch(o){return console.error("[splat viewer] finding the point under the pointer failed.",o),null}}var se={element:document.createElement("div"),disc:document.createElement("div"),stem:document.createElementNS("http://www.w3.org/2000/svg","line")};se.element.className="tag ghost";se.disc.className="disc";se.element.appendChild(se.disc);se.stem.classList.add("ghost");var Ee=null;function _s(i){i&&we.style.display!=="block"&&(Qt.nothing=!1,Qt.hidden=!1),we.style.display=i?"block":"none",we.classList.remove("missed"),i&&!se.element.parentNode&&(Kt.appendChild(se.element),Jt.appendChild(se.stem)),Ee=null,Ln()}var ys=null,ai=!1;async function xl(){if(ai||!ys)return;ai=!0;let i=ys;ys=null;try{if(Ee=await yl(i.x,i.y),we.classList.toggle("missed",!Ee),!Ee&&!Qt.nothing){Qt.nothing=!0;let e=we.getBoundingClientRect();console.warn("[splat viewer] nothing found under the pointer at",i,"over",`${e.width}x${e.height}`,"- picker",zt?"ready":"NOT READY","- model",vt()?"in":"NOT IN")}Ln()}finally{ai=!1}xl()}we.addEventListener("mousemove",i=>{ys={x:i.clientX,y:i.clientY},xl()});we.addEventListener("mouseleave",()=>{ys=null,Ee=null,Ln()});we.addEventListener("click",async i=>{let e=vt(),t=await yl(i.clientX,i.clientY);if(!e||!t){we.classList.add("missed");return}let s=e.getWorldTransform().clone().invert(),n=s.transformPoint(t.position,new x),o=s.transformVector(t.normal,new x).normalize();if(Ge){xe({type:"measure-point",position:{x:n.x,y:n.y,z:n.z}});return}_s(!1),xe({type:"picked",position:{x:n.x,y:n.y,z:n.z},normal:{x:o.x,y:o.y,z:o.z}})});window.addEventListener("keydown",i=>{i.key==="Escape"&&we.style.display==="block"?(Ge=!1,_s(!1),xe({type:"placing-cancelled"})):at&&(i.key==="Backspace"||(i.ctrlKey||i.metaKey)&&!i.shiftKey&&i.key.toLowerCase()==="z")&&(i.preventDefault(),xe({type:"calibration-undo"}))});function Ln(){let i=Ft(),t=!!Ee&&!!i&&we.style.display==="block"&&(at||Ge?Sl(se.element,Ee.position,i)&&ye(se.stem):bl(se.element,se.stem,Ee.position,Ee.normal,i));$u(t&&Ge?Ee.position:null),t||(se.element.style.display="none",se.stem.style.display="none",Ee&&i&&we.style.display==="block"&&!Qt.hidden&&(Qt.hidden=!0,console.warn("[splat viewer] the ghost is found but cannot be drawn:",Tu(Ee.position,i))))}var Qt={nothing:!1,hidden:!1};function Tu(i,e){let t=e.entity.getPosition(),s=new x().sub2(i,t).dot(e.entity.forward),n=e.worldToScreen(i,new x);return`spot (${i.x.toFixed(2)}, ${i.y.toFixed(2)}, ${i.z.toFixed(2)}), ${s.toFixed(3)} ahead of the camera (near clip ${e.nearClip.toFixed(3)}), on screen at (${n.x.toFixed(0)}, ${n.y.toFixed(0)})`}var Du=.3,Jt=document.getElementById("stems"),it=new Map,Cn=new U;function zu(i){if(!i)return new U;let e=180/Math.PI,t=new Q().setFromAxisAngle(x.RIGHT,i.rotation.x*e).mul(new Q().setFromAxisAngle(x.UP,i.rotation.y*e)).mul(new Q().setFromAxisAngle(x.BACK,i.rotation.z*e));return new U().setTRS(new x(i.translation.x,i.translation.y,i.translation.z),t,new x(i.scale,i.scale,i.scale))}function wl(i,e){Cn=zu(e);let t=new Set(i.map(s=>s.id));it.forEach((s,n)=>{t.has(n)||(s.element.remove(),s.stem.remove(),it.delete(n))}),i.forEach(s=>{let n=it.get(s.id);if(!n){let o=document.createElement("div");o.className="tag";let r=document.createElement("button");r.type="button",r.className="disc",r.addEventListener("click",c=>{c.stopPropagation(),xe({type:"tagClick",id:s.id})});let a=document.createElement("div");a.className="bubble",o.append(a,r),Kt.appendChild(o);let l=document.createElementNS("http://www.w3.org/2000/svg","line");Jt.appendChild(l),n={tag:s,element:o,disc:r,bubble:a,stem:l,spot:null},it.set(s.id,n)}n.tag=s,n.bubble.textContent=s.label??"",n.disc.setAttribute("aria-label",s.label??""),n.disc.style.backgroundColor=s.color??"#1E88E5",n.disc.style.backgroundImage=s.iconUrl?`url("${s.iconUrl}")`:"none"}),Ml(),es()}var ri=new x,_n=new x,kn=new x,gt=new x,li=new x,gs=new x,pi=new x;function Ft(){return be?.app.root.findComponents("camera")[0]}function bl(i,e,t,s,n){let o=n.entity.getPosition(),r=n.entity.forward,a=l=>pi.sub2(l,o).dot(r)>n.nearClip;return _n.copy(s).normalize().mulScalar(Du).add(t),!a(t)||!a(_n)?!1:(n.worldToScreen(t,li),n.worldToScreen(_n,gs),e.style.display="",e.setAttribute("x1",String(li.x)),e.setAttribute("y1",String(li.y)),e.setAttribute("x2",String(gs.x)),e.setAttribute("y2",String(gs.y)),i.style.display="block",i.style.transform=`translate(${gs.x}px, ${gs.y}px)`,i.style.zIndex=String(Math.max(1,1e6-Math.round(pi.sub2(_n,o).length()*100))),!0)}function es(){let i=vt(),e=Ft();it.forEach(t=>{let{tag:s,element:n,stem:o}=t,r=!1;if(i&&e){let a=i.getWorldTransform();kn.set(s.position.x,s.position.y,s.position.z),s.inModelFrame||Cn.transformPoint(kn,kn),a.transformPoint(kn,ri),s.normal&&(gt.set(s.normal.x,s.normal.y,s.normal.z),s.inModelFrame||Cn.transformVector(gt,gt),a.transformVector(gt,gt)),(!s.normal||gt.lengthSq()===0)&>.copy(x.UP),r=bl(n,o,ri,gt,e),t.spot=r?(t.spot??new x).copy(ri):null}r||(n.style.display="none",o.style.display="none")}),Iu(),qu(),Ln()}function Fu(i){let e=it.get(i)?.element;e&&(e.classList.remove("pointed"),e.offsetWidth,e.classList.add("pointed"))}var at=null,Dt=[],ci=new x,xs=new x,hi=new x;function _l(i){at=i;let e=i?.pairs.length??0;for(;Dt.length>e;){let t=Dt.pop();t.element.remove(),t.link.remove()}for(;Dt.length<e;){let t=document.createElement("div");t.className="pin";let s=document.createElementNS("http://www.w3.org/2000/svg","line");s.classList.add("link"),Kt.appendChild(t),Jt.insertBefore(s,Jt.firstChild),Dt.push({element:t,link:s})}Dt.forEach((t,s)=>{t.element.textContent=String(s+1)}),kl(),Ml(),es()}function kl(){se.element.classList.toggle("pin-ghost",!!at&&!Ge),se.element.classList.toggle("measure-ghost",Ge),se.disc.textContent=at&&!Ge?String(at.pairs.length+1):""}function Ml(){let i=new Set(at?.pairs.map(e=>e.tagId));it.forEach((e,t)=>{e.element.classList.toggle("matched",i.has(t)),e.stem.classList.toggle("matched",i.has(t)),e.element.classList.toggle("selected",at?.selected===t)})}function bs(i,e){return pi.sub2(e,i.entity.getPosition()).dot(i.entity.forward)>i.nearClip}function Sl(i,e,t){return bs(t,e)?(t.worldToScreen(e,xs),i.style.display="block",i.style.transform=`translate(${xs.x}px, ${xs.y}px)`,!0):!1}function ye(i){return i.style.display="none",!0}function Iu(){let i=vt(),e=Ft();Dt.forEach((t,s)=>{let n=at?.pairs[s];if(!n||!i||!e){ye(t.element),ye(t.link);return}if(i.getWorldTransform().transformPoint(ci.set(n.target.x,n.target.y,n.target.z),ci),!Sl(t.element,ci,e)){ye(t.element),ye(t.link);return}let o=it.get(n.tagId)?.spot;if(!o||!bs(e,o)){ye(t.link);return}e.worldToScreen(o,hi),t.link.style.display="",t.link.setAttribute("x1",String(hi.x)),t.link.setAttribute("y1",String(hi.y)),t.link.setAttribute("x2",String(xs.x)),t.link.setAttribute("y2",String(xs.y))})}var ts=null,Ge=!1,Cl=[],Pl=[],Pn=[],Re={line:document.createElementNS("http://www.w3.org/2000/svg","line"),label:document.createElement("div")};Re.line.classList.add("measure","rubber");Re.label.className="measure-label rubber";function Vu(i){i!==Ge&&(Ge=i,kl(),_s(i))}function Ou(i){ts=i;let e=i?.points.length??0,t=Math.max(0,e-1);di(Cl,e,()=>{let s=document.createElement("div");return s.className="measure-point",Kt.appendChild(s)}),di(Pl,t,()=>{let s=document.createElementNS("http://www.w3.org/2000/svg","line");return s.classList.add("measure"),Jt.appendChild(s)}),di(Pn,t,()=>{let s=document.createElement("div");return s.className="measure-label",Kt.appendChild(s)}),Pn.forEach((s,n)=>{s.textContent=`${(i?.distances[n]??0).toFixed(2)} m`}),Re.line.parentNode||(Jt.appendChild(Re.line),Kt.appendChild(Re.label)),es()}function di(i,e,t){for(;i.length>e;)i.pop().remove();for(;i.length<e;)i.push(t())}var Bu=[],Nu=[],vs=new x,Mn=new x;function Ll(i){let e=vt();return!e||!ts?[]:ts.points.map((t,s)=>{let n=Bu[s]??=new x;return e.getWorldTransform().transformPoint(n.set(t.x,t.y,t.z),n),bs(i,n)?i.worldToScreen(n,Nu[s]??=new x):null})}function El(i,e,t){i.style.display="",i.setAttribute("x1",String(e.x)),i.setAttribute("y1",String(e.y)),i.setAttribute("x2",String(t.x)),i.setAttribute("y2",String(t.y))}function fi(i,e,t){i.style.display="block",i.style.transform=`translate(${e}px, ${t}px) translate(-50%, -50%)`}function qu(){let i=Ft(),e=i?Ll(i):[];Cl.forEach((t,s)=>{let n=e[s];n?fi(t,n.x,n.y):ye(t)}),Pl.forEach((t,s)=>{let n=e[s],o=e[s+1];n&&o?(El(t,n,o),fi(Pn[s],(n.x+o.x)/2,(n.y+o.y)/2)):(ye(t),ye(Pn[s]))})}function $u(i){let e=Ft(),t=vt(),s=ts?.points[ts.points.length-1];if(!i||!e||!t||!s||!bs(e,i)){ye(Re.line),ye(Re.label);return}if(t.getWorldTransform().transformPoint(vs.set(s.x,s.y,s.z),vs),!bs(e,vs)){ye(Re.line),ye(Re.label);return}let n=t.getWorldTransform().getScale(new x).x,o=vs.distance(i)/n/(Cn.getScale(new x).x||1),r=e.worldToScreen(vs,new x);e.worldToScreen(i,Mn),El(Re.line,r,Mn),Re.label.textContent=`${o.toFixed(2)} m`,fi(Re.label,(r.x+Mn.x)/2,(r.y+Mn.y)/2)}async function Rl(){let i=Ft();if(!be?.state.loaded||!i)return null;let e=be.app.graphicsDevice.canvas,t=Math.min(1,800/e.clientWidth,600/e.clientHeight),s=Math.max(1,Math.round(e.clientWidth*t)),n=Math.max(1,Math.round(e.clientHeight*t)),o=await be.captureFrame({width:s,height:n}),r=document.createElement("canvas");r.width=s,r.height=n;let a=r.getContext("2d"),l=Uint8ClampedArray.from(atob(o.data),h=>h.charCodeAt(0));a.putImageData(new ImageData(l,s,n),0,0);let c=Ll(i).map(h=>h&&{x:h.x*t,y:h.y*t});return a.lineWidth=3,a.strokeStyle=ml,c.forEach((h,d)=>{let u=c[d-1];u&&h&&(a.beginPath(),a.moveTo(u.x,u.y),a.lineTo(h.x,h.y),a.stroke())}),c.forEach(h=>{h&&(a.beginPath(),a.arc(h.x,h.y,6,0,Math.PI*2),a.fillStyle=ml,a.fill(),a.lineWidth=2,a.strokeStyle="#fff",a.stroke())}),a.font="bold 13px sans-serif",a.textAlign="center",a.textBaseline="middle",c.forEach((h,d)=>{let u=c[d-1];if(u&&h){let p=`${(ts?.distances[d-1]??0).toFixed(2)} m`,g=(u.x+h.x)/2,m=(u.y+h.y)/2,f=a.measureText(p).width+16;a.fillStyle="rgba(0, 0, 0, 0.78)",a.beginPath(),a.roundRect(g-f/2,m-12,f,24,9),a.fill(),a.fillStyle="#fff",a.fillText(p,g,m+1)}}),r.toDataURL("image/jpeg",.85)}var ml="#FFB103";window.__splatViewer={viewer:()=>be,picker:()=>zt,CentresPicker:ms,load:gl,setTags:wl,setCalibration:_l,markers:it,pins:Dt,placeTags:es,snapshot:Rl};xe({type:"ready"});
|