@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.
Files changed (114) hide show
  1. package/README.md +24 -24
  2. package/esm2022/lib/components/csv-export/csv-export.component.mjs +3 -3
  3. package/esm2022/lib/components/loader/loader.component.mjs +3 -3
  4. package/esm2022/lib/components/menu-bar/avatar/avatar.component.mjs +1 -1
  5. package/esm2022/lib/components/menu-bar/menu-bar.component.mjs +3 -3
  6. package/esm2022/lib/components/menu-bar/navigation-bar/navigation-bar.component.mjs +3 -3
  7. package/esm2022/lib/components/modal-switch-visit/modal-switch-visit.component.mjs +3 -3
  8. package/esm2022/lib/components/search-bar/search-bar.component.mjs +3 -3
  9. package/esm2022/lib/components/splat-viewer/splat-viewer.component.mjs +361 -0
  10. package/esm2022/lib/components/support-modal/support-modal.component.mjs +3 -3
  11. package/esm2022/lib/config.mjs +1 -1
  12. package/esm2022/lib/helpers.service.mjs +1 -1
  13. package/esm2022/lib/matterport-extensions/nest-thermostat/CanvasImage.mjs +1 -1
  14. package/esm2022/lib/matterport-extensions/nest-thermostat/CanvasRenderer.mjs +1 -1
  15. package/esm2022/lib/matterport-extensions/nest-thermostat/NestThermostat.mjs +1 -1
  16. package/esm2022/lib/matterport-extensions/nest-thermostat/PlaneRenderer.mjs +1 -1
  17. package/esm2022/lib/matterport-extensions/scene-component/SceneComponent.mjs +1 -1
  18. package/esm2022/lib/matterport-extensions/security-camera/SecurityCamera.mjs +22 -22
  19. package/esm2022/lib/matterport-extensions/tv-player/TvPlayer.mjs +1 -1
  20. package/esm2022/lib/matterport-extensions/view-frustum-mesh/ViewFrustumMesh.mjs +22 -22
  21. package/esm2022/lib/mattertagData.mjs +1 -1
  22. package/esm2022/lib/ngx-smarterplan-core.module.mjs +9 -4
  23. package/esm2022/lib/ngx-smarterplan-core.service.mjs +1 -1
  24. package/esm2022/lib/pipes/duration-to-string.pipe.mjs +1 -1
  25. package/esm2022/lib/pipes/format-date-number-to-digits.pipe.mjs +1 -1
  26. package/esm2022/lib/pipes/hashtag-from-id.pipe.mjs +1 -1
  27. package/esm2022/lib/pipes/safe-url.pipe.mjs +1 -1
  28. package/esm2022/lib/pipes/time-date-to-local-string.pipe.mjs +1 -1
  29. package/esm2022/lib/pipes/username-from-id.pipe.mjs +1 -1
  30. package/esm2022/lib/services/amplify-cache.service.mjs +1 -1
  31. package/esm2022/lib/services/base-tab.service.mjs +1 -1
  32. package/esm2022/lib/services/baseVisibility.service.mjs +1 -1
  33. package/esm2022/lib/services/content.service.mjs +1 -1
  34. package/esm2022/lib/services/filter.service.mjs +1 -1
  35. package/esm2022/lib/services/intervention.service.mjs +1 -1
  36. package/esm2022/lib/services/locale.service.mjs +1 -1
  37. package/esm2022/lib/services/matterport-import.service.mjs +1 -1
  38. package/esm2022/lib/services/matterport-measurement.service.mjs +1 -1
  39. package/esm2022/lib/services/matterport-navigation.service.mjs +1 -1
  40. package/esm2022/lib/services/matterport-pointer.service.mjs +6 -6
  41. package/esm2022/lib/services/matterport-tag-copy.service.mjs +1 -1
  42. package/esm2022/lib/services/matterport-tag.service.mjs +12 -7
  43. package/esm2022/lib/services/matterport.service.mjs +17 -17
  44. package/esm2022/lib/services/models/affectation.service.mjs +1 -1
  45. package/esm2022/lib/services/models/base-object.service.mjs +1 -1
  46. package/esm2022/lib/services/models/capture.service.mjs +1 -1
  47. package/esm2022/lib/services/models/comment.service.mjs +1 -1
  48. package/esm2022/lib/services/models/domain.service.mjs +1 -1
  49. package/esm2022/lib/services/models/equipment.service.mjs +1 -1
  50. package/esm2022/lib/services/models/event.service.mjs +1 -1
  51. package/esm2022/lib/services/models/feature.service.mjs +1 -1
  52. package/esm2022/lib/services/models/hashtag.service.mjs +1 -1
  53. package/esm2022/lib/services/models/layer.service.mjs +1 -1
  54. package/esm2022/lib/services/models/measurement.service.mjs +1 -1
  55. package/esm2022/lib/services/models/mission.service.mjs +1 -1
  56. package/esm2022/lib/services/models/navigation.service.mjs +1 -1
  57. package/esm2022/lib/services/models/node.service.mjs +1 -1
  58. package/esm2022/lib/services/models/object3D.service.mjs +1 -1
  59. package/esm2022/lib/services/models/operation.service.mjs +1 -1
  60. package/esm2022/lib/services/models/organisation.service.mjs +1 -1
  61. package/esm2022/lib/services/models/plan.service.mjs +1 -1
  62. package/esm2022/lib/services/models/poi.service.mjs +27 -27
  63. package/esm2022/lib/services/models/profile.service.mjs +1 -1
  64. package/esm2022/lib/services/models/property.service.mjs +1 -1
  65. package/esm2022/lib/services/models/space.service.mjs +1 -1
  66. package/esm2022/lib/services/models/template.service.mjs +1 -1
  67. package/esm2022/lib/services/models/ticket.service.mjs +1 -1
  68. package/esm2022/lib/services/models/visit.service.mjs +1 -1
  69. package/esm2022/lib/services/models/zone.service.mjs +1 -1
  70. package/esm2022/lib/services/navigator.service.mjs +1 -1
  71. package/esm2022/lib/services/obj-to-glb.mjs +649 -0
  72. package/esm2022/lib/services/s3.service.mjs +43 -3
  73. package/esm2022/lib/services/search.service.mjs +1 -1
  74. package/esm2022/lib/services/support.service.mjs +1 -1
  75. package/esm2022/lib/services/sweep-locator.service.mjs +1 -1
  76. package/esm2022/lib/services/tag.service.mjs +16 -16
  77. package/esm2022/lib/services/threejs-import.service.mjs +6 -4
  78. package/esm2022/lib/services/threejs.service.mjs +867 -95
  79. package/esm2022/lib/services/user.service.mjs +1 -1
  80. package/esm2022/lib/services/utils/matterport-transform.util.mjs +1 -1
  81. package/esm2022/lib/services/validators.service.mjs +1 -1
  82. package/esm2022/lib/services/viewer.service.mjs +189 -7
  83. package/esm2022/lib/services/zone-drawer.service.mjs +1 -1
  84. package/esm2022/lib/services/zoneChange.service.mjs +1 -1
  85. package/esm2022/lib/tokens/matterport-sdk-key.token.mjs +1 -1
  86. package/esm2022/lib/validators/email.directive.mjs +1 -1
  87. package/esm2022/lib/validators/no-empty.directive.mjs +1 -1
  88. package/esm2022/lib/validators/number.directive.mjs +1 -1
  89. package/esm2022/lib/validators/text.directive.mjs +1 -1
  90. package/esm2022/public-api.mjs +3 -1
  91. package/fesm2022/smarterplan-ngx-smarterplan-core.mjs +2239 -233
  92. package/fesm2022/smarterplan-ngx-smarterplan-core.mjs.map +1 -1
  93. package/lib/components/splat-viewer/splat-viewer.component.d.ts +98 -0
  94. package/lib/ngx-smarterplan-core.module.d.ts +7 -6
  95. package/lib/services/matterport-tag.service.d.ts +5 -5
  96. package/lib/services/obj-to-glb.d.ts +66 -0
  97. package/lib/services/s3.service.d.ts +7 -0
  98. package/lib/services/threejs.service.d.ts +336 -16
  99. package/lib/services/viewer.service.d.ts +110 -8
  100. package/package.json +1 -1
  101. package/public-api.d.ts +2 -0
  102. package/src/assets/viewers/splat/chunk-BYLI4YBI.js +17037 -0
  103. package/src/assets/viewers/splat/chunk-LDV6YPMG.js +1 -0
  104. package/src/assets/viewers/splat/chunk-WZ5Z4TU3.js +105719 -0
  105. package/src/assets/viewers/splat/convert-4DRLH77X.js +1 -0
  106. package/src/assets/viewers/splat/convert.html +11 -0
  107. package/src/assets/viewers/splat/converter-SLBB5MHG.js +1 -0
  108. package/src/assets/viewers/splat/index.html +90 -0
  109. package/src/assets/viewers/splat/spz-DEYRea_v-QE6YSGIB.js +5292 -0
  110. package/src/assets/viewers/splat/viewer-W4X3FWQJ.js +2704 -0
  111. package/src/assets/viewers/splat/viewer.css +1096 -0
  112. package/src/assets/viewers/splat/webp.wasm +0 -0
  113. package/src/assets/viewers/splat/worker.mjs +1383 -0
  114. 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)&&gt.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"});