@hology/core 0.0.270 → 0.0.271

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 (54) hide show
  1. package/dist/effects/sequence/sequence-player.js +1 -1
  2. package/dist/effects/vfx/gpu-vfx-world-service.d.ts +2 -0
  3. package/dist/effects/vfx/gpu-vfx-world-service.js +1 -1
  4. package/dist/effects/vfx/vfx-actor.js +1 -1
  5. package/dist/effects/vfx/vfx-render-preparation.d.ts +3 -0
  6. package/dist/effects/vfx/vfx-render-preparation.js +1 -1
  7. package/dist/gameplay/actors/builtin/components/spline-component.d.ts +7 -39
  8. package/dist/gameplay/actors/builtin/components/spline-component.js +1 -1
  9. package/dist/gameplay/actors/builtin/navmesh-actor.d.ts +16 -2
  10. package/dist/gameplay/actors/builtin/navmesh-actor.js +1 -1
  11. package/dist/gameplay/actors/builtin/water-spline-volume-actor.d.ts +2 -2
  12. package/dist/gameplay/actors/builtin/water-spline-volume-actor.js +1 -1
  13. package/dist/gameplay/ai/build-tile.js +1 -1
  14. package/dist/gameplay/ai/dynamic-tiled-navmesh.js +1 -1
  15. package/dist/gameplay/ai/dynamic-tiled-navmesh.worker.js +1 -1
  16. package/dist/gameplay/ai/navigation.d.ts +2 -0
  17. package/dist/gameplay/ai/navigation.js +1 -1
  18. package/dist/gameplay/services/physics/physics-system.d.ts +1 -1
  19. package/dist/gameplay/services/physics/physics-system.js +1 -1
  20. package/dist/gameplay/services/render.js +1 -1
  21. package/dist/navigation/bake.d.ts +68 -0
  22. package/dist/navigation/bake.js +4 -0
  23. package/dist/navigation/cache.d.ts +11 -0
  24. package/dist/navigation/cache.js +4 -0
  25. package/dist/navigation/spline-data.d.ts +64 -0
  26. package/dist/navigation/spline-data.js +4 -0
  27. package/dist/navigation/water-spline-geometry.d.ts +23 -0
  28. package/dist/navigation/water-spline-geometry.js +4 -0
  29. package/dist/scene/asset-resource-loader.d.ts +2 -2
  30. package/dist/scene/asset-resource-loader.js +1 -1
  31. package/dist/scene/bootstrap.js +1 -1
  32. package/dist/scene/collision/authored-colliders.d.ts +4 -0
  33. package/dist/scene/collision/authored-colliders.js +4 -0
  34. package/dist/scene/collision/rapier-shape.d.ts +7 -0
  35. package/dist/scene/collision/rapier-shape.js +4 -0
  36. package/dist/scene/materializer.d.ts +3 -0
  37. package/dist/scene/materializer.js +1 -1
  38. package/dist/scene/objects/shapes.js +1 -1
  39. package/dist/scene/runtime-backend-service.d.ts +1 -0
  40. package/dist/scene/runtime-bundled-backend-service.d.ts +1 -0
  41. package/dist/scene/runtime-bundled-backend-service.js +1 -1
  42. package/dist/test/authored-collision-rescale.test.js +1 -1
  43. package/dist/test/gpu-vfx-preload.test.d.ts +2 -0
  44. package/dist/test/gpu-vfx-preload.test.js +4 -0
  45. package/dist/test/materializer-prefetch.test.js +1 -1
  46. package/dist/test/navigation-bake-backend.test.d.ts +2 -0
  47. package/dist/test/navigation-bake-backend.test.js +4 -0
  48. package/dist/test/navigation-bake.test.d.ts +2 -0
  49. package/dist/test/navigation-bake.test.js +4 -0
  50. package/dist/test/sequence-vfx.test.js +1 -1
  51. package/dist/test/shader-graph.test.js +1 -1
  52. package/dist/test/vfx-render-preparation.test.js +1 -1
  53. package/package.json +6 -2
  54. package/tsconfig.tsbuildinfo +1 -1
@@ -1,4 +1,4 @@
1
- import{__decorate as e,__metadata as t}from"tslib";import{Service as a}from"typedi";import{Camera as i}from"three";import{FrameAudioListener as r}from"../../utils/three/frame-audio-listener.js";import{RenderingView as s}from"../../rendering.js";import{BehaviorSubject as n,Subject as h,takeUntil as o}from"rxjs";import*as m from"three";import{GameTimeScheduler as u}from"./game-time-scheduler.js";import{createCameraShakeSample as l,resetCameraShakeSample as c,sampleCameraShake as d}from"./camera-shake.js";export{defaultPostProcessEffectStage,postProcessEffectStages}from"../../rendering/post-process-effect.js";let p=class{constructor(e){this.view=e,this.tick=new h,this.lateTick=new h,this.audioListener=new r,this.pausedChanged=new n(!1),this.mutedChanged=new n(!1),this._muted=!1,this._masterVolume=1,this._cameraShakeIntensity=1,this._timeScale=1,this._hitStopRemaining=0,this.scheduler=new u,this.cameraOverrideId=0,this.cameraOverrides=[],this.cameraShakeId=0,this.gameplayCameraShakes=[],this.cameraShakeContributions=new Map,this.presentationCamera=null,this.accumulatedCameraShakeSample=l(),this.scratchCameraShakeSample=l(),this._updateFrameIndex=0,this.outlined=[],this.baseCamera=e.camera,this.sourceCamera=e.camera,e.onLoop(e=>{this.handleFrame(e)}),e.camera.add(this.audioListener),window.hology_view=this}get activeBackend(){return this.view.activeBackend}get requestedBackend(){return this.view.requestedBackend}get vfxBackendPreference(){return this.view.vfxBackendPreference}get webgpuVfxInstancesPerTemplate(){return this.view.webgpuVfxInstancesPerTemplate}get webgpuVfxTemplateMemoryBudgetBytes(){return this.view.webgpuVfxTemplateMemoryBudgetBytes}get webgpuVfxWorldMemoryBudgetBytes(){return this.view.webgpuVfxWorldMemoryBudgetBytes}get webgpuVfxColdTemplateSeconds(){return this.view.webgpuVfxColdTemplateSeconds}get webGpuDevice(){return this.view.webGpuDevice}get experimentalWebGpuDevice(){return this.webGpuDevice}registerWebGpuRenderGraphExtension(e){return this.view.registerWebGpuRenderGraphExtension(e)}registerExperimentalWebGpuRenderGraphExtension(e){return this.registerWebGpuRenderGraphExtension(e)}get updateFrameIndex(){return this._updateFrameIndex}handleFrame(e){this._updateFrameIndex++;let t=this._timeScale,a=0;this._hitStopRemaining>0?(this._hitStopRemaining-=1e3*e,this._hitStopRemaining<=0&&(this._hitStopRemaining=0),t=0):a=e*this._timeScale,this.advanceGameplayCameraShakes(a),this.tick.next(a),this.scheduler.update(1e3*a),this.lateTick.next(a),this.view.simulationTimeScale=t,this.refreshCameraPresentation()}set timeScale(e){this._timeScale=Math.max(0,e)}get timeScale(){return this._timeScale}get time(){return performance.now()/1e3}applyHitStop(e){this._hitStopRemaining=Math.max(this._hitStopRemaining,e)}set fpsCap(e){this.view.fpsCap=e}get fpsCap(){return this.view.fpsCap}set showStats(e){this.view.showStats=e}get showStats(){return this.view.showStats}setProfilingEnabled(e){this.view.setProfilingEnabled(e)}getLastFrameProfile(){return this.view.getLastFrameProfile()}setLightVolume(e){this.view.lightVolume=e}getLightVolume(){return this.view.lightVolume}addPostProcessVolume(e){this.view.addPostProcessVolume(e)}removePostProcessVolume(e){this.view.removePostProcessVolume(e)}addPostProcessEffect(e,t){return this.view.addPostProcessEffect(e,t)}onUpdate(e){return null==e?this.tick:this.tick.pipe(o(e.disposed))}onLateUpdate(e){return null==e?this.lateTick:this.lateTick.pipe(o(e.disposed))}setCamera(e){const t=e instanceof i?e:e.camera.instance;this.baseCamera=t,0===this.cameraOverrides.length&&(this.sourceCamera=t,this.refreshCameraPresentation())}getCamera(){return this.view.camera}getSourceCamera(){return this.sourceCamera}getBaseCamera(){return this.baseCamera}applyCameraShake(e){const t=function(e){return{duration:Math.max(0,e.duration??0),attack:e.attack??0,decay:e.decay??0,frequency:e.frequency??24,positionAmplitude:e.positionAmplitude?[...e.positionAmplitude]:[0,0,0],rotationAmplitude:e.rotationAmplitude?[...e.rotationAmplitude]:[0,0,0],seed:e.seed}}(e);if(t.duration<=0)return{release:()=>{}};const a={id:++this.cameraShakeId,request:t,elapsedTime:0,seedOverride:t.seed};this.gameplayCameraShakes.push(a),this.refreshCameraPresentation();let i=!1;return{release:()=>{i||(i=!0,this.releaseGameplayCameraShake(a.id))}}}setCameraShakeContribution(e,t,a,i,r){if(a.duration<=0)return void this.removeCameraShakeContribution(e,t);let s=this.cameraShakeContributions.get(e);s||(s=new Map,this.cameraShakeContributions.set(e,s));const n=s.get(t);n?(n.request=a,n.elapsedTime=Math.max(0,i),n.seedOverride=r):s.set(t,{key:t,request:a,elapsedTime:Math.max(0,i),seedOverride:r}),this.refreshCameraPresentation()}removeCameraShakeContribution(e,t){const a=this.cameraShakeContributions.get(e);a?.delete(t)&&(0===a.size&&this.cameraShakeContributions.delete(e),this.refreshCameraPresentation())}clearCameraShakeContributionsForOwner(e){this.cameraShakeContributions.delete(e)&&this.refreshCameraPresentation()}pushCameraOverride(e,t,a=0){const i={id:++this.cameraOverrideId,camera:e,owner:t,priority:a};this.cameraOverrides.push(i),this.applyTopCameraOverride();let r=!1;return{release:()=>{r||(r=!0,this.releaseCameraOverrideEntry(i))}}}releaseCameraOverridesForOwner(e){const t=this.cameraOverrides.filter(t=>t.owner!==e);t.length!==this.cameraOverrides.length&&(this.cameraOverrides=t,this.applyTopCameraOverride())}releaseCameraOverrideEntry(e){const t=this.cameraOverrides.indexOf(e);-1!==t&&(this.cameraOverrides.splice(t,1),this.applyTopCameraOverride())}applyTopCameraOverride(){const e=this.getTopCameraOverride();this.sourceCamera=e?.camera??this.baseCamera,this.refreshCameraPresentation()}getTopCameraOverride(){let e;for(const t of this.cameraOverrides)(!e||t.priority>e.priority||t.priority===e.priority&&t.id>e.id)&&(e=t);return e}applyRenderedCamera(e){this.view.camera!==e&&this.view.setCamera(e),e.add(this.audioListener)}releaseGameplayCameraShake(e){const t=this.gameplayCameraShakes.findIndex(t=>t.id===e);-1!==t&&(this.gameplayCameraShakes.splice(t,1),this.refreshCameraPresentation())}advanceGameplayCameraShakes(e){if(!(e<=0||0===this.gameplayCameraShakes.length))for(let t=this.gameplayCameraShakes.length-1;t>=0;t--){const a=this.gameplayCameraShakes[t];a.elapsedTime+=e,a.elapsedTime>a.request.duration&&this.gameplayCameraShakes.splice(t,1)}}refreshCameraPresentation(){if(!this.hasActiveCameraShake())return void this.applyRenderedCamera(this.sourceCamera);const e=this.ensurePresentationCamera();this.copyCameraState(this.sourceCamera,e),this.accumulateCameraShakeSamples(),this.applyCameraShakeSample(e),this.applyRenderedCamera(e)}hasActiveCameraShake(){if(this.gameplayCameraShakes.length>0)return!0;for(const e of this.cameraShakeContributions.values())if(e.size>0)return!0;return!1}ensurePresentationCamera(){return this.presentationCamera||(this.presentationCamera=new m.PerspectiveCamera,this.presentationCamera.name="View Controller Presentation Camera"),this.presentationCamera}copyCameraState(e,t){e.updateWorldMatrix(!0,!1),t.position.copy(e.getWorldPosition(f)),t.quaternion.copy(e.getWorldQuaternion(k)),t.scale.copy(e.getWorldScale(w)),t.layers.mask=e.layers.mask,e instanceof m.PerspectiveCamera?(t.fov=e.fov,t.near=e.near,t.far=e.far,t.aspect=e.aspect,t.zoom=e.zoom,t.focus=e.focus,t.filmGauge=e.filmGauge,t.filmOffset=e.filmOffset,t.view=null==e.view?null:{enabled:e.view.enabled,fullWidth:e.view.fullWidth,fullHeight:e.view.fullHeight,offsetX:e.view.offsetX,offsetY:e.view.offsetY,width:e.view.width,height:e.view.height},t.updateProjectionMatrix()):(t.projectionMatrix.copy(e.projectionMatrix),t.projectionMatrixInverse.copy(e.projectionMatrixInverse))}accumulateCameraShakeSamples(){c(this.accumulatedCameraShakeSample);for(let e=0;e<this.gameplayCameraShakes.length;e++)this.accumulateCameraShakeEntry(this.gameplayCameraShakes[e]);for(const e of this.cameraShakeContributions.values())for(const t of e.values())this.accumulateCameraShakeEntry(t)}accumulateCameraShakeEntry(e){const t=d(e.request,e.elapsedTime,this.scratchCameraShakeSample,e.seedOverride);if(t)for(let e=0;e<3;e++)this.accumulatedCameraShakeSample.position[e]+=t.position[e],this.accumulatedCameraShakeSample.rotation[e]+=t.rotation[e]}applyCameraShakeSample(e){y.copy(e.quaternion),x.set(this.accumulatedCameraShakeSample.position[0]*this._cameraShakeIntensity,this.accumulatedCameraShakeSample.position[1]*this._cameraShakeIntensity,this.accumulatedCameraShakeSample.position[2]*this._cameraShakeIntensity),x.applyQuaternion(y),e.position.add(x),P.set(this.accumulatedCameraShakeSample.rotation[0]*this._cameraShakeIntensity,this.accumulatedCameraShakeSample.rotation[1]*this._cameraShakeIntensity,this.accumulatedCameraShakeSample.rotation[2]*this._cameraShakeIntensity),b.setFromEuler(P),e.quaternion.copy(y).multiply(b),e.updateMatrixWorld(!0)}setMuted(e){this._muted=e,this.applyAudioGain(),this.mutedChanged.next(e)}getMuted(){return this._muted}set masterVolume(e){this._masterVolume=Math.min(Math.max(e,0),1),this.applyAudioGain()}get masterVolume(){return this._masterVolume}set cameraShakeIntensity(e){this._cameraShakeIntensity=Math.min(Math.max(e,0),1),this.refreshCameraPresentation()}get cameraShakeIntensity(){return this._cameraShakeIntensity}applyAudioGain(){this.audioListener.gain.gain.setValueAtTime(this._muted?0:this._masterVolume,this.audioListener.context.currentTime)}setOutlined(e){this.outlined.length=0;const t=this.outlined;for(let a=0;a<e.length;a++)t[a]=e[a];this.view.setSelectedObjects(t),this.view.setEnableOutlines(e.length>0)}getOutlined(){return this.outlined}addOutlined(e){this.outlined.includes(e)||(this.outlined.push(e),this.view.setSelectedObjects(this.outlined),this.view.setEnableOutlines(!0))}removeOutlined(e){const t=this.outlined.indexOf(e);-1!==t&&(this.outlined.splice(t,1),this.view.setSelectedObjects(this.outlined))}setOutlineColor(e){this.view.outlinePass?.visibleEdgeColor.copy(e)}getOutlineColor(){return this.view.outlinePass?.visibleEdgeColor}setOutlineThickness(e){this.view.outlinePass.edgeThickness=e}getOutlineThickness(){return this.view.outlinePass.edgeThickness}get htmlElement(){return this.view.container}getViewportSize(e=v){return this.view.getClientSize(e)}get paused(){return this.view.paused}set paused(e){e!=this.paused&&(e?this.pauseRendering():this.unpauseRendering())}pauseRendering(){this.view.paused=!0,this.pausedChanged.next(this.view.paused),this.scheduler.pause()}unpauseRendering(){this.view.paused=!1,this.pausedChanged.next(this.view.paused),this.scheduler.resume()}setInterval(e,t,a){return this.scheduler.setInterval(e,t,a)}clearInterval(e){this.scheduler.clearInterval(e)}dispose(e=!0){e&&this.view.running&&this.view.stop(),this.scheduler.dispose(),this.cameraOverrides.length=0,this.gameplayCameraShakes.length=0,this.cameraShakeContributions.clear(),this.audioListener.disposePoseUpdates(),this.audioListener.removeFromParent(),this.tick.complete(),this.lateTick.complete(),this.paused=!0}createTimer(e){return this.scheduler.createTimer(e)}getScreenPosition(e,t=g,a=this.getViewportSize()){const i=this.getCamera();return C.multiplyMatrices(i.projectionMatrix,i.matrixWorldInverse),i instanceof m.PerspectiveCamera&&(S.setFromProjectionMatrix(C),!S.containsPoint(e))?null:(t.copy(e),t.project(i),t.x=(t.x+1)/2*a.x,t.y=(1-t.y)/2*a.y,t)}};p=e([a(),t("design:paramtypes",[s])],p);export{p as ViewController};const C=new m.Matrix4,g=new m.Vector3,v=new m.Vector2,S=new m.Frustum,f=new m.Vector3,w=new m.Vector3,k=new m.Quaternion,y=new m.Quaternion,x=new m.Vector3,P=new m.Euler(0,0,0,"XYZ"),b=new m.Quaternion;/*
1
+ import{__decorate as e,__metadata as t}from"tslib";import{Service as a}from"typedi";import{Camera as i}from"three";import{FrameAudioListener as r}from"../../utils/three/frame-audio-listener.js";import{RenderingView as s}from"../../rendering.js";import{BehaviorSubject as n,Subject as h,takeUntil as o}from"rxjs";import*as m from"three";import{GameTimeScheduler as u}from"./game-time-scheduler.js";import{createCameraShakeSample as l,resetCameraShakeSample as c,sampleCameraShake as d}from"./camera-shake.js";export{defaultPostProcessEffectStage,postProcessEffectStages}from"../../rendering/post-process-effect.js";let p=class{constructor(e){this.view=e,this.tick=new h,this.lateTick=new h,this.audioListener=new r,this.pausedChanged=new n(!1),this.mutedChanged=new n(!1),this._muted=!1,this._masterVolume=1,this._cameraShakeIntensity=1,this._timeScale=1,this._hitStopRemaining=0,this.scheduler=new u,this.cameraOverrideId=0,this.cameraOverrides=[],this.cameraShakeId=0,this.gameplayCameraShakes=[],this.cameraShakeContributions=new Map,this.presentationCamera=null,this.accumulatedCameraShakeSample=l(),this.scratchCameraShakeSample=l(),this._updateFrameIndex=0,this.outlined=[],this.baseCamera=e.camera,this.sourceCamera=e.camera,e.onLoop(e=>{this.handleFrame(e)}),e.camera.add(this.audioListener),window.hology_view=this}get activeBackend(){return this.view.activeBackend}get requestedBackend(){return this.view.requestedBackend}get vfxBackendPreference(){return this.view.vfxBackendPreference}get webgpuVfxInstancesPerTemplate(){return this.view.webgpuVfxInstancesPerTemplate}get webgpuVfxTemplateMemoryBudgetBytes(){return this.view.webgpuVfxTemplateMemoryBudgetBytes}get webgpuVfxWorldMemoryBudgetBytes(){return this.view.webgpuVfxWorldMemoryBudgetBytes}get webgpuVfxColdTemplateSeconds(){return this.view.webgpuVfxColdTemplateSeconds}get webGpuDevice(){return this.view.webGpuDevice}get experimentalWebGpuDevice(){return this.webGpuDevice}registerWebGpuRenderGraphExtension(e){return this.view.registerWebGpuRenderGraphExtension(e)}registerExperimentalWebGpuRenderGraphExtension(e){return this.registerWebGpuRenderGraphExtension(e)}get updateFrameIndex(){return this._updateFrameIndex}handleFrame(e){this._updateFrameIndex++;let t=this._timeScale,a=0;this._hitStopRemaining>0?(this._hitStopRemaining-=1e3*e,this._hitStopRemaining<=0&&(this._hitStopRemaining=0),t=0):a=e*this._timeScale,this.advanceGameplayCameraShakes(a),this.tick.next(a),this.scheduler.update(1e3*a),this.lateTick.next(a),this.view.simulationTimeScale=t,this.refreshCameraPresentation()}set timeScale(e){this._timeScale=Math.max(0,e)}get timeScale(){return this._timeScale}get time(){return performance.now()/1e3}applyHitStop(e){this._hitStopRemaining=Math.max(this._hitStopRemaining,e)}set fpsCap(e){this.view.fpsCap=e}get fpsCap(){return this.view.fpsCap}set showStats(e){this.view.showStats=e}get showStats(){return this.view.showStats}setProfilingEnabled(e){this.view.setProfilingEnabled(e)}getLastFrameProfile(){return this.view.getLastFrameProfile()}setLightVolume(e){this.view.lightVolume=e}getLightVolume(){return this.view.lightVolume}addPostProcessVolume(e){this.view.addPostProcessVolume(e)}removePostProcessVolume(e){this.view.removePostProcessVolume(e)}addPostProcessEffect(e,t){return this.view.addPostProcessEffect(e,t)}onUpdate(e){return null==e?this.tick:this.tick.pipe(o(e.disposed))}onLateUpdate(e){return null==e?this.lateTick:this.lateTick.pipe(o(e.disposed))}setCamera(e){const t=e instanceof i?e:e.camera.instance;this.baseCamera=t,0===this.cameraOverrides.length&&(this.sourceCamera=t,this.refreshCameraPresentation())}getCamera(){return this.view.camera}getSourceCamera(){return this.sourceCamera}getBaseCamera(){return this.baseCamera}applyCameraShake(e){const t=function(e){return{duration:Math.max(0,e.duration??0),attack:e.attack??0,decay:e.decay??0,frequency:e.frequency??24,positionAmplitude:e.positionAmplitude?[...e.positionAmplitude]:[0,0,0],rotationAmplitude:e.rotationAmplitude?[...e.rotationAmplitude]:[0,0,0],seed:e.seed}}(e);if(t.duration<=0)return{release:()=>{}};const a={id:++this.cameraShakeId,request:t,elapsedTime:0,seedOverride:t.seed};this.gameplayCameraShakes.push(a),this.refreshCameraPresentation();let i=!1;return{release:()=>{i||(i=!0,this.releaseGameplayCameraShake(a.id))}}}setCameraShakeContribution(e,t,a,i,r){if(a.duration<=0)return void this.removeCameraShakeContribution(e,t);let s=this.cameraShakeContributions.get(e);s||(s=new Map,this.cameraShakeContributions.set(e,s));const n=s.get(t);n?(n.request=a,n.elapsedTime=Math.max(0,i),n.seedOverride=r):s.set(t,{key:t,request:a,elapsedTime:Math.max(0,i),seedOverride:r}),this.refreshCameraPresentation()}removeCameraShakeContribution(e,t){const a=this.cameraShakeContributions.get(e);a?.delete(t)&&(0===a.size&&this.cameraShakeContributions.delete(e),this.refreshCameraPresentation())}clearCameraShakeContributionsForOwner(e){this.cameraShakeContributions.delete(e)&&this.refreshCameraPresentation()}pushCameraOverride(e,t,a=0){const i={id:++this.cameraOverrideId,camera:e,owner:t,priority:a};this.cameraOverrides.push(i),this.applyTopCameraOverride();let r=!1;return{release:()=>{r||(r=!0,this.releaseCameraOverrideEntry(i))}}}releaseCameraOverridesForOwner(e){const t=this.cameraOverrides.filter(t=>t.owner!==e);t.length!==this.cameraOverrides.length&&(this.cameraOverrides=t,this.applyTopCameraOverride())}releaseCameraOverrideEntry(e){const t=this.cameraOverrides.indexOf(e);-1!==t&&(this.cameraOverrides.splice(t,1),this.applyTopCameraOverride())}applyTopCameraOverride(){const e=this.getTopCameraOverride();this.sourceCamera=e?.camera??this.baseCamera,this.refreshCameraPresentation()}getTopCameraOverride(){let e;for(const t of this.cameraOverrides)(!e||t.priority>e.priority||t.priority===e.priority&&t.id>e.id)&&(e=t);return e}applyRenderedCamera(e){this.view.camera!==e&&this.view.setCamera(e),e.add(this.audioListener)}releaseGameplayCameraShake(e){const t=this.gameplayCameraShakes.findIndex(t=>t.id===e);-1!==t&&(this.gameplayCameraShakes.splice(t,1),this.refreshCameraPresentation())}advanceGameplayCameraShakes(e){if(!(e<=0||0===this.gameplayCameraShakes.length))for(let t=this.gameplayCameraShakes.length-1;t>=0;t--){const a=this.gameplayCameraShakes[t];a.elapsedTime+=e,a.elapsedTime>a.request.duration&&this.gameplayCameraShakes.splice(t,1)}}refreshCameraPresentation(){if(!this.hasActiveCameraShake())return void this.applyRenderedCamera(this.sourceCamera);const e=this.ensurePresentationCamera();this.copyCameraState(this.sourceCamera,e),this.accumulateCameraShakeSamples(),this.applyCameraShakeSample(e),this.applyRenderedCamera(e)}hasActiveCameraShake(){if(this.gameplayCameraShakes.length>0)return!0;for(const e of this.cameraShakeContributions.values())if(e.size>0)return!0;return!1}ensurePresentationCamera(){return this.presentationCamera||(this.presentationCamera=new m.PerspectiveCamera,this.presentationCamera.name="View Controller Presentation Camera"),this.presentationCamera}copyCameraState(e,t){e.updateWorldMatrix(!0,!1),t.position.copy(e.getWorldPosition(f)),t.quaternion.copy(e.getWorldQuaternion(k)),t.scale.copy(e.getWorldScale(w)),t.layers.mask=e.layers.mask,e instanceof m.PerspectiveCamera?(t.fov=e.fov,t.near=e.near,t.far=e.far,t.aspect=e.aspect,t.zoom=e.zoom,t.focus=e.focus,t.filmGauge=e.filmGauge,t.filmOffset=e.filmOffset,t.view=null==e.view?null:{enabled:e.view.enabled,fullWidth:e.view.fullWidth,fullHeight:e.view.fullHeight,offsetX:e.view.offsetX,offsetY:e.view.offsetY,width:e.view.width,height:e.view.height},t.updateProjectionMatrix()):(t.projectionMatrix.copy(e.projectionMatrix),t.projectionMatrixInverse.copy(e.projectionMatrixInverse))}accumulateCameraShakeSamples(){c(this.accumulatedCameraShakeSample);for(let e=0;e<this.gameplayCameraShakes.length;e++)this.accumulateCameraShakeEntry(this.gameplayCameraShakes[e]);for(const e of this.cameraShakeContributions.values())for(const t of e.values())this.accumulateCameraShakeEntry(t)}accumulateCameraShakeEntry(e){const t=d(e.request,e.elapsedTime,this.scratchCameraShakeSample,e.seedOverride);if(t)for(let e=0;e<3;e++)this.accumulatedCameraShakeSample.position[e]+=t.position[e],this.accumulatedCameraShakeSample.rotation[e]+=t.rotation[e]}applyCameraShakeSample(e){y.copy(e.quaternion),x.set(this.accumulatedCameraShakeSample.position[0]*this._cameraShakeIntensity,this.accumulatedCameraShakeSample.position[1]*this._cameraShakeIntensity,this.accumulatedCameraShakeSample.position[2]*this._cameraShakeIntensity),x.applyQuaternion(y),e.position.add(x),P.set(this.accumulatedCameraShakeSample.rotation[0]*this._cameraShakeIntensity,this.accumulatedCameraShakeSample.rotation[1]*this._cameraShakeIntensity,this.accumulatedCameraShakeSample.rotation[2]*this._cameraShakeIntensity),b.setFromEuler(P),e.quaternion.copy(y).multiply(b),e.updateMatrixWorld(!0)}setMuted(e){this._muted=e,this.applyAudioGain(),this.mutedChanged.next(e)}getMuted(){return this._muted}set masterVolume(e){this._masterVolume=Math.min(Math.max(e,0),1),this.applyAudioGain()}get masterVolume(){return this._masterVolume}set cameraShakeIntensity(e){this._cameraShakeIntensity=Math.min(Math.max(e,0),1),this.refreshCameraPresentation()}get cameraShakeIntensity(){return this._cameraShakeIntensity}applyAudioGain(){this.audioListener.gain.gain.setValueAtTime(this._muted?0:this._masterVolume,this.audioListener.context.currentTime)}setOutlined(e){this.outlined.length=0;const t=this.outlined;for(let a=0;a<e.length;a++)t[a]=e[a];this.view.setSelectedObjects(t),this.view.setEnableOutlines(e.length>0)}getOutlined(){return this.outlined}addOutlined(e){this.outlined.includes(e)||(this.outlined.push(e),this.view.setSelectedObjects(this.outlined),this.view.setEnableOutlines(!0))}removeOutlined(e){const t=this.outlined.indexOf(e);-1!==t&&(this.outlined.splice(t,1),this.view.setSelectedObjects(this.outlined),this.view.setEnableOutlines(this.outlined.length>0))}setOutlineColor(e){this.view.outlinePass?.visibleEdgeColor.copy(e)}getOutlineColor(){return this.view.outlinePass?.visibleEdgeColor}setOutlineThickness(e){this.view.outlinePass.edgeThickness=e}getOutlineThickness(){return this.view.outlinePass.edgeThickness}get htmlElement(){return this.view.container}getViewportSize(e=v){return this.view.getClientSize(e)}get paused(){return this.view.paused}set paused(e){e!=this.paused&&(e?this.pauseRendering():this.unpauseRendering())}pauseRendering(){this.view.paused=!0,this.pausedChanged.next(this.view.paused),this.scheduler.pause()}unpauseRendering(){this.view.paused=!1,this.pausedChanged.next(this.view.paused),this.scheduler.resume()}setInterval(e,t,a){return this.scheduler.setInterval(e,t,a)}clearInterval(e){this.scheduler.clearInterval(e)}dispose(e=!0){e&&this.view.running&&this.view.stop(),this.scheduler.dispose(),this.cameraOverrides.length=0,this.gameplayCameraShakes.length=0,this.cameraShakeContributions.clear(),this.audioListener.disposePoseUpdates(),this.audioListener.removeFromParent(),this.tick.complete(),this.lateTick.complete(),this.paused=!0}createTimer(e){return this.scheduler.createTimer(e)}getScreenPosition(e,t=C,a=this.getViewportSize()){const i=this.getCamera();return g.multiplyMatrices(i.projectionMatrix,i.matrixWorldInverse),i instanceof m.PerspectiveCamera&&(S.setFromProjectionMatrix(g),!S.containsPoint(e))?null:(t.copy(e),t.project(i),t.x=(t.x+1)/2*a.x,t.y=(1-t.y)/2*a.y,t)}};p=e([a(),t("design:paramtypes",[s])],p);export{p as ViewController};const g=new m.Matrix4,C=new m.Vector3,v=new m.Vector2,S=new m.Frustum,f=new m.Vector3,w=new m.Vector3,k=new m.Quaternion,y=new m.Quaternion,x=new m.Vector3,P=new m.Euler(0,0,0,"XYZ"),b=new m.Quaternion;/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -0,0 +1,68 @@
1
+ /** Renderer-independent navigation input extraction and Recast baking. Safe to import in Node. */
2
+ import * as RAPIER from '@dimforge/rapier3d-simd-compat';
3
+ import { Box3, BufferGeometry, Matrix4 } from 'three';
4
+ import { type RecastConfig } from 'recast-navigation';
5
+ import type { SceneObject } from '../scene/materializer.js';
6
+ import type { Asset } from '../scene/model.js';
7
+ import type { CollisionShape } from '../scene/collision/collision-shape.js';
8
+ import { type BuildTileMeshProps } from '../gameplay/ai/build-tile.js';
9
+ import { type NavigationAreaModifier } from '../gameplay/ai/navigation-areas.js';
10
+ export { importCollisionShapes } from '../scene/collision/collision-shape-import.js';
11
+ export { createAuthoredCollisionShapes } from '../scene/collision/authored-colliders.js';
12
+ export { decodeStorageValue, parseStorageJson } from '../scene/storage/packed-json.js';
13
+ export type { CollisionShape, Asset, SceneObject };
14
+ export declare const NAVIGATION_BAKE_VERSION = "navigation-1-recast-0.39.0";
15
+ export type NavigationGeometry = {
16
+ positions: Float32Array;
17
+ indices: Uint32Array;
18
+ };
19
+ export type NavigationBakeReference = {
20
+ file: string;
21
+ settings: string;
22
+ };
23
+ export type NavigationBakes = Record<string, NavigationBakeReference>;
24
+ export type NavigationVolume = {
25
+ id: string;
26
+ bounds: Box3;
27
+ config: RecastConfig;
28
+ settings: string;
29
+ };
30
+ export type NavigationSceneEntry = {
31
+ id: string;
32
+ source: SceneObject;
33
+ matrix: Matrix4;
34
+ };
35
+ export type NavigationContribution = {
36
+ geometry?: NavigationGeometry[];
37
+ modifiers?: NavigationAreaModifier[];
38
+ };
39
+ export type NavigationBakeOptions = {
40
+ objects: SceneObject[];
41
+ assets: Asset[];
42
+ /** Model loading is supplied by the host; no textures or renderer are required. */
43
+ loadCollisionShapes(asset: Asset): Promise<CollisionShape[]>;
44
+ /** Explicit headless counterpart for game actors which create static collision or areas. */
45
+ contribute?(entry: NavigationSceneEntry): Promise<NavigationContribution | undefined>;
46
+ };
47
+ export declare function navigationConfig(bounds: Box3, params: {
48
+ cellSize?: number;
49
+ walkableClimb?: number;
50
+ walkableSlopeAngle?: number;
51
+ walkableHeight?: number;
52
+ }): RecastConfig;
53
+ export declare function navigationSettings(bounds: Box3, config: RecastConfig): string;
54
+ export declare function navigationEntries(objects: SceneObject[], assets: Asset[]): NavigationSceneEntry[];
55
+ export declare function navigationVolumes(entries: NavigationSceneEntry[]): NavigationVolume[];
56
+ export declare function initializeNavigationBaker(): Promise<unknown>;
57
+ /** Converts a Rapier shape, preserving 32-bit mesh indices. No debug inflation is applied. */
58
+ export declare function navigationShapeGeometry(shape: RAPIER.Shape): BufferGeometry;
59
+ export declare function navigationGeometryInput(geometry: BufferGeometry, matrix: Matrix4): NavigationGeometry;
60
+ /** Canonical static collision inputs; reusable by future collision-data baking. */
61
+ export declare function collectNavigationInputs(options: NavigationBakeOptions, entries: NavigationSceneEntry[], bounds: Box3): Promise<NavigationContribution>;
62
+ /** Full authored density and holes, independent of streamed render/physics sections. */
63
+ export declare function landscapeNavigationGeometry(source: SceneObject, matrix: Matrix4, bounds: Box3): NavigationGeometry[];
64
+ export declare function mergeNavigationGeometry(inputs: NavigationGeometry[]): NavigationGeometry;
65
+ export type NavigationTileBuilder = (job: BuildTileMeshProps) => Promise<Uint8Array | undefined>;
66
+ /** Jobs can run in browser workers; Node executes the same tile builder directly. */
67
+ export declare function bakeNavigation(volume: NavigationVolume, inputs: NavigationContribution, build?: NavigationTileBuilder, signal?: AbortSignal): Promise<Uint8Array>;
68
+ //# sourceMappingURL=bake.d.ts.map
@@ -0,0 +1,4 @@
1
+ import*as e from"@dimforge/rapier3d-simd-compat";import{Box3 as t,BoxGeometry as o,BufferAttribute as a,BufferGeometry as i,CapsuleGeometry as n,ConeGeometry as r,CylinderGeometry as s,Euler as l,Float32BufferAttribute as c,Matrix4 as p,Quaternion as m,SphereGeometry as f,Vector3 as h}from"three";import{ConvexGeometry as d}from"three/examples/jsm/geometries/ConvexGeometry.js";import{NavMesh as u,NavMeshParams as y,Raw as g,UnsignedCharArray as w,exportNavMesh as x,init as v,recastConfigDefaults as M}from"recast-navigation";import{ShapeLibrary as b}from"../scene/objects/shapes.js";import{createRapierCollisionShape as A}from"../scene/collision/rapier-shape.js";import{buildConfig as S,buildTile as E}from"../gameplay/ai/build-tile.js";import{NavigationArea as I}from"../gameplay/ai/navigation-areas.js";import{SplineData as T}from"./spline-data.js";import{createCrossSections as N}from"./water-spline-geometry.js";export{importCollisionShapes}from"../scene/collision/collision-shape-import.js";export{createAuthoredCollisionShapes}from"../scene/collision/authored-colliders.js";export{decodeStorageValue,parseStorageJson}from"../scene/storage/packed-json.js";export const NAVIGATION_BAKE_VERSION="navigation-1-recast-0.39.0";export function navigationConfig(e,t){const o=Math.max(t.cellSize??.2,.01),a=Math.round(1e6*Math.max(1,Math.min(e.max.x-e.min.x,e.max.z-e.min.z)))/1e6;return{...M,tileSize:Math.max(32,Math.floor(Math.min(400,Math.max(50,Math.ceil(a/2)))/o)),walkableClimb:(t.walkableClimb??.3)/o,walkableSlopeAngle:t.walkableSlopeAngle??45,walkableRadius:2,walkableHeight:(t.walkableHeight??2)/o,detailSampleDist:1,minRegionArea:6,mergeRegionArea:400,cs:o,ch:o,maxSimplificationError:1.3,maxEdgeLen:200}}export function navigationSettings(e,t){const o=e=>e.map(e=>Math.round(1e6*e)/1e6);return JSON.stringify([NAVIGATION_BAKE_VERSION,o(e.min.toArray()),o(e.max.toArray()),t])}export function navigationEntries(e,t){const o=new Map(t.map(e=>[e.id,e])),a=[];return function e(t,i,n,r){for(const s of t){const t=(new p).compose((new h).fromArray(s.position??[0,0,0]),(new m).setFromEuler((new l).fromArray(s.rotation??[0,0,0])),(new h).fromArray(s.scale??[1,1,1])).premultiply(i),c=n+s.id;if(a.push({id:c,source:s,matrix:t}),"prefab"===s.type){const a=o.get(s.assetId);if(!a?.prefab)throw new Error(`Missing navigation prefab ${s.assetId}`);if(r.has(a.id))throw new Error(`Cyclic navigation prefab ${a.id}`);const i=new Set(r).add(a.id);e(a.prefab.objects.map(e=>e.id===a.prefab.mainActorId?{...e,actor:{...e.actor,params:{...e.actor?.params,...s.prefab?.params},innerParams:s.prefab?.innerParams??e.actor?.innerParams}}:e),t,c+"/",i)}s.children&&e(s.children,t,n,r)}}(e,new p,"",new Set),a}export function navigationVolumes(e){return e.filter(e=>["NavMesh","NavMeshActor"].includes(e.source.actor?.type)).map(e=>{const o=new h,a=new m,i=new h;if(e.matrix.decompose(o,a,i),i.set(Math.abs(i.x),Math.abs(i.y),Math.abs(i.z)),Math.min(i.x,i.y,i.z)<=0)throw new Error(`Empty navigation volume ${e.id}`);const n=(new t).setFromCenterAndSize(o,i),r=navigationConfig(n,Object.fromEntries(Object.entries(e.source.actor.params??{}).map(([e,t])=>[e,t.value])));return{id:e.id,bounds:n,config:r,settings:navigationSettings(n,r)}})}let j;export function initializeNavigationBaker(){return j??(j=Promise.all([v(),e.init()]).catch(e=>{throw j=void 0,e}))}export function navigationShapeGeometry(t){switch(t.type){case e.ShapeType.Cuboid:{const e=t;return new o(2*e.halfExtents.x,2*e.halfExtents.y,2*e.halfExtents.z)}case e.ShapeType.Ball:return new f(t.radius);case e.ShapeType.Capsule:{const e=t;return new n(e.radius,2*e.halfHeight)}case e.ShapeType.Cylinder:{const e=t;return new s(e.radius,e.radius,2*e.halfHeight)}case e.ShapeType.Cone:{const e=t;return new r(e.radius,2*e.halfHeight)}case e.ShapeType.TriMesh:{const e=t,o=new i;return o.setAttribute("position",new c(e.vertices,3)),o.setIndex(new a(Uint32Array.from(e.indices),1)),o}case e.ShapeType.ConvexPolyhedron:{const e=t,o=[];for(let t=0;t<e.vertices.length;t+=3)o.push((new h).fromArray(e.vertices,t));return new d(o)}default:throw new Error(`Unsupported navigation collision shape ${t.type}`)}}export function navigationGeometryInput(e,t){const o=e.getAttribute("position"),a=new Float32Array(3*o.count),i=new h;for(let e=0;e<o.count;e++)i.fromBufferAttribute(o,e).applyMatrix4(t).toArray(a,3*e);const n=e.index?Uint32Array.from(e.index.array):Uint32Array.from({length:o.count},(e,t)=>t);if(t.determinant()<0)for(let e=0;e<n.length;e+=3)[n[e+1],n[e+2]]=[n[e+2],n[e+1]];return{positions:a,indices:n}}export async function collectNavigationInputs(t,o,a){await initializeNavigationBaker();const i=new Map(t.assets.map(e=>[e.id,e])),n=[],r=[];for(const s of o){const{source:o,matrix:l}=s,c=await(t.contribute?.(s));if(c&&(n.push(...c.geometry??[]),r.push(...c.modifiers??[])),"WaterVolume"===o.actor?.type){const e=[-.5,.5].flatMap(e=>[-.5,.5].flatMap(t=>[-.5,.5].map(o=>new h(e,t,o).applyMatrix4(l))));r.push({area:I.Water,vertices:[[-.5,0,-.5],[.5,0,-.5],[.5,0,.5],[-.5,0,.5]].map(e=>(new h).fromArray(e).applyMatrix4(l).toArray()),minHeight:Math.min(...e.map(e=>e.y)),maxHeight:Math.max(...e.map(e=>e.y))})}if("WaterSplineVolume"===o.actor?.type){const e=new T,t=o.actor.innerParams?.find(e=>"spline"===e.path.join("."))?.params.splineData;e.setData(t?.value??e.splineData,!1);const a=Number(o.actor.params?.width?.value??5),i=Number(o.actor.params?.depth?.value??2),n=N(e,a,i);for(let e=1;e<n.length;e++){const t=n[e-1],o=n[e],a=[t.leftTop,t.rightTop,o.rightTop,o.leftTop,t.leftBottom,t.rightBottom,o.rightBottom,o.leftBottom].map(e=>e.clone().applyMatrix4(l));r.push({area:I.Water,vertices:a.slice(0,4).map(e=>e.toArray()),minHeight:Math.min(...a.map(e=>e.y)),maxHeight:Math.max(...a.map(e=>e.y))})}}if(!1===o.collisionDetection||null!=o.physics?.type&&2!==o.physics.type)continue;if("landscape"===o.shape&&o.landscape){n.push(...landscapeNavigationGeometry(o,l,a));continue}let f=[];if("shape_mesh"===o.type&&o.shape&&"landscape"!==o.shape){const e=b[o.shape];if(!e)throw new Error(`Unsupported navigation primitive ${o.shape}`);const t=Object.fromEntries(Object.entries(o.shapeParams??{}).map(([e,t])=>[e,t.value])),a=e.collision(t);a&&(f=[a])}else if("asset_mesh"===o.type){const e=i.get(o.assetId);if(!e){console.warn(`[Hology navigation] Missing scene mesh ${o.assetId} on ${s.id}; no collision exists to bake`);continue}if(!0!==e.collisionDetection)continue;f=await t.loadCollisionShapes(e)}const d=new h,u=new m,y=new h;l.decompose(d,u,y);for(const t of f){const o=A(e,t,y);if(!o)throw new Error(`Invalid navigation collision on ${s.id}`);const a=t.offset.clone().multiply(y).applyQuaternion(u).add(d),i=(new p).compose(a,u.clone().multiply((new m).setFromEuler(t.rotation)),new h(1,1,1)),r=navigationShapeGeometry(o.shape);n.push(navigationGeometryInput(r,i)),r.dispose()}}return{geometry:n,modifiers:r}}export function landscapeNavigationGeometry(e,t,o){const{options:a,heightMaps:i=[],holes:n=[]}=e.landscape,{sectionSize:r,density:s,sections:l}=a;if(!Number.isInteger(s)||s<1||r<=0)throw new Error(`Invalid landscape settings on ${e.id}`);const c=o.clone().applyMatrix4(t.clone().invert()),p=l.x*r/-2,m=l.y*r/-2,f=Math.max(0,Math.ceil((c.min.x-p-r/2)/r)),d=Math.min(l.x-1,Math.floor((c.max.x-p+r/2)/r)),u=Math.max(0,Math.ceil((c.min.z-m-r/2)/r)),y=Math.min(l.y-1,Math.floor((c.max.z-m+r/2)/r)),g=new Map(i.map(e=>[`${e.x},${e.y}`,e])),w=new Map;for(const e of n)if(e.w[0]>.5){let t=w.get(e.m);t||w.set(e.m,t=new Set),t.add(e.i)}const x=[],v=new h,M=s+1;for(let e=f;e<=d;e++)for(let o=u;o<=y;o++){const a=`${e},${o}`,i=new Float32Array(M*M*3),n=new Float32Array(M*M);for(const e of g.get(a)?.points??[])n[e.i]=e.y;for(let a=0;a<M;a++)for(let l=0;l<M;l++){const c=a*M+l;v.set(p+e*r-r/2+l*r/s,n[c],m+o*r-r/2+a*r/s).applyMatrix4(t).toArray(i,3*c)}const l=[],c=w.get(a),f=t.determinant()<0,h=(e,t,o)=>{c?.has(e)||c?.has(t)||c?.has(o)||l.push(e,f?o:t,f?t:o)};for(let e=0;e<s;e++)for(let t=0;t<s;t++){const o=e*M+t,a=o+M;h(o,a,o+1),h(a,a+1,o+1)}x.push({positions:i,indices:Uint32Array.from(l)})}return x}export function mergeNavigationGeometry(e){let t=0,o=0;for(const a of e)t+=a.positions.length,o+=a.indices.length;const a=new Float32Array(t),i=new Uint32Array(o);let n=0,r=0;for(const t of e){a.set(t.positions,n);for(let e=0;e<t.indices.length;e++)i[r+e]=t.indices[e]+n/3;n+=t.positions.length,r+=t.indices.length}return{positions:a,indices:i}}export async function bakeNavigation(e,t,o,a){await initializeNavigationBaker();let{positions:i,indices:n}=mergeNavigationGeometry(t.geometry??[]);if(o&&globalThis.crossOriginIsolated&&"undefined"!=typeof SharedArrayBuffer){const e=new Float32Array(new SharedArrayBuffer(i.byteLength)),t=new Uint32Array(new SharedArrayBuffer(n.byteLength));e.set(i),t.set(n),i=e,n=t}const r=[e.bounds.min.toArray(),e.bounds.max.toArray()],s=S({recastConfig:e.config,navMeshBounds:r});g.Module.destroy(s.config);const l=new u,c=y.create({orig:e.bounds.min,tileWidth:s.tcs,tileHeight:s.tcs,maxTiles:s.maxTiles,maxPolys:s.maxPolysPerTile}),p=l.initTiled(c);if(g.Module.destroy(c.raw),!p)throw l.destroy(),new Error("Could not initialize baked navmesh");try{let c=0,p=!1;const m=(await Promise.allSettled(Array.from({length:o?4:1},async()=>{try{for(;;){if(p)return;a?.throwIfAborted();const m=c++;if(m>=s.tileWidth*s.tileHeight)return;const f=m%s.tileWidth,h=Math.floor(m/s.tileWidth),d=e.bounds.min,u=e.bounds.max,y={positions:i,indices:n,recastConfig:e.config,navMeshBounds:r,tileX:f,tileY:h,tileBoundsMin:[d.x+f*s.tcs,d.y,d.z+h*s.tcs],tileBoundsMax:[d.x+(f+1)*s.tcs,u.y,d.z+(h+1)*s.tcs],keepIntermediates:!1,areaModifiers:t.modifiers??[]};let x;if(o){const e=await o(y);a?.throwIfAborted(),e&&(x=new w,x.copy(e))}else{const e=E(y);if(g.Module.destroy(e.buildContext.raw),!e.success)throw new Error("error"in e?e.error:"Navigation tile build failed");x=e.data}if(x){const e=l.addTile(x,g.Module.DT_TILE_FREE_DATA,0);if(g.Detour.statusFailed(e.status))throw x.destroy(),new Error(`Could not add baked navigation tile ${f},${h}`)}}}catch(e){throw p=!0,e}}))).find(e=>"rejected"===e.status);if(m)throw m.reason;return x(l)}finally{l.destroy()}}/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -0,0 +1,11 @@
1
+ import { type NavigationBakeOptions } from './bake.js';
2
+ /** Content hash used for editor snapshots; binary resources are additionally versioned by the host. */
3
+ export declare function navigationCacheKey(options: NavigationBakeOptions, settings: string): Promise<string>;
4
+ export declare function editorNavigationFiles(): {
5
+ fs: typeof import('node:fs/promises');
6
+ path: typeof import('node:path');
7
+ root: string;
8
+ } | undefined;
9
+ export declare function readEditorNavigation(key: string): Promise<Uint8Array | undefined>;
10
+ export declare function writeEditorNavigation(slot: string, key: string, bytes: Uint8Array): Promise<void>;
11
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1,4 @@
1
+ import{NAVIGATION_BAKE_VERSION as t}from"./bake.js";export async function navigationCacheKey(i,a){const e=[],o=[...i.assets].sort((t,i)=>t.id.localeCompare(i.id)),n=editorNavigationFiles();if(n){for(const t of o)if("mesh"===t.type&&t.fileKey){const i=n.path.join(n.root,"public","data","asset-resources",t.fileKey);try{const a=await n.fs.stat(i);e.push([t.id,a.size,a.mtimeMs])}catch{e.push([t.id,"missing"])}}for(const t of["src","assets"]){const i=n.path.join(n.root,t);try{const a=(await n.fs.readdir(i,{recursive:!0})).sort();for(const o of a)if(/\.[cm]?[jt]sx?$/.test(o)){const a=await n.fs.stat(n.path.join(i,o));e.push([t,o,a.size,a.mtimeMs])}}catch{}}}const r=(new TextEncoder).encode(JSON.stringify([t,a,i.objects,o,e])),s=await crypto.subtle.digest("SHA-256",r);return Array.from(new Uint8Array(s),t=>t.toString(16).padStart(2,"0")).join("")}export function editorNavigationFiles(){if("undefined"==typeof window||!window.require)return;const t=window.process?.argv?.find(t=>t.startsWith("--path="))?.slice(7);return t?{fs:window.require("fs").promises,path:window.require("path"),root:t}:void 0}export async function readEditorNavigation(t){const a=editorNavigationFiles();if(!a)try{if("undefined"==typeof caches)return;const a=await(await caches.open("hology-navigation-1")).match(i(t));return a?new Uint8Array(await a.arrayBuffer()):void 0}catch{return}try{return new Uint8Array(await a.fs.readFile(a.path.join(a.root,".hology","cache","navigation-editor",t+".bin")))}catch{return}}export async function writeEditorNavigation(t,a,e){const o=editorNavigationFiles();if(!o){if("undefined"==typeof caches)return;const o=await caches.open("hology-navigation-1"),n=i("slot-"+encodeURIComponent(t)),r=await o.match(n),s=r?await r.text():void 0;return await o.put(i(a),new Response(Uint8Array.from(e))),await o.put(n,new Response(a)),void(s&&s!==a&&/^[a-f0-9]{64}$/.test(s)&&await o.delete(i(s)))}const n=o.path.join(o.root,".hology","cache","navigation-editor"),r=o.path.join(n,a+".bin");await o.fs.mkdir(n,{recursive:!0});const s=r+"."+Math.random().toString(36).slice(2)+".tmp";try{await o.fs.writeFile(s,e),await o.fs.rename(s,r);const i=o.path.join(n,encodeURIComponent(t)+".json");let c;try{c=JSON.parse(await o.fs.readFile(i,"utf8")).key}catch{}await o.fs.writeFile(i,JSON.stringify({key:a})),c&&c!==a&&/^[a-f0-9]{64}$/.test(c)&&await o.fs.unlink(o.path.join(n,c+".bin")).catch(()=>{})}finally{await o.fs.unlink(s).catch(()=>{})}}function i(t){return new URL("/__hology_navigation_cache/"+t,location.href).href}/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -0,0 +1,64 @@
1
+ import { Subject } from 'rxjs';
2
+ import { Vector3 } from 'three';
3
+ export interface SplinePoint {
4
+ /** Actor-local point position. */
5
+ position: Vector3;
6
+ /** Actor-local offset from position to the incoming Bezier handle. */
7
+ inTangent: Vector3;
8
+ /** Actor-local offset from position to the outgoing Bezier handle. */
9
+ outTangent: Vector3;
10
+ }
11
+ export interface ClosestSplinePoint {
12
+ position: Vector3;
13
+ tangent: Vector3;
14
+ t: number;
15
+ distance: number;
16
+ distanceAlongSpline: number;
17
+ }
18
+ /** A generic cubic Bezier spline whose points and tangents are actor-local. */
19
+ export declare class SplineData {
20
+ /** Compact JSON is intentional: scene files keep the complete spline on one line. */
21
+ splineData: string;
22
+ /** Emits after an in-place authoring/runtime edit. Consumers can update incrementally. */
23
+ readonly changed: Subject<SplineData>;
24
+ private _points;
25
+ private _closed;
26
+ private arcLengths;
27
+ private arcTs;
28
+ private _length;
29
+ onInit(): void;
30
+ onEndPlay(): void;
31
+ get points(): readonly SplinePoint[];
32
+ get closed(): boolean;
33
+ get length(): number;
34
+ setData(data: string, emit?: boolean): void;
35
+ setPoints(points: readonly SplinePoint[], closed?: boolean): void;
36
+ updatePoint(index: number, point: SplinePoint): void;
37
+ addPoint(point: SplinePoint, index?: number): void;
38
+ /** Splits a Bezier segment without changing the shape of the spline. */
39
+ insertPointOnSegment(segmentIndex: number, t?: number): number;
40
+ removePoint(index: number): void;
41
+ setClosed(closed: boolean): void;
42
+ applySceneParameters(parameters: {
43
+ splineData?: string;
44
+ }, changed: Set<'splineData'>): boolean;
45
+ /** Samples an actor-local position using normalized t. */
46
+ samplePosition(t: number, target?: Vector3): Vector3;
47
+ sampleTangent(t: number, target?: Vector3): Vector3;
48
+ samplePositionAtDistance(distance: number, target?: Vector3): Vector3;
49
+ sampleTangentAtDistance(distance: number, target?: Vector3): Vector3;
50
+ /** Finds the closest actor-local point to an actor-local query position. */
51
+ closestPoint(point: Vector3, target?: Vector3): ClosestSplinePoint;
52
+ private commitChange;
53
+ private get segmentCount();
54
+ private resolveSegment;
55
+ private rebuildArcLengths;
56
+ private tAtDistance;
57
+ private distanceAtT;
58
+ }
59
+ export declare function packSplineData(points: readonly SplinePoint[], closed: boolean): string;
60
+ export declare function parseSplineData(data: string): {
61
+ points: SplinePoint[];
62
+ closed: boolean;
63
+ };
64
+ //# sourceMappingURL=spline-data.d.ts.map
@@ -0,0 +1,4 @@
1
+ import{Subject as t}from"rxjs";import{Vector3 as n}from"three";const s='{"closed":false,"points":[[0,0,0,0,0,0,2,0,0],[5,0,0,-2,0,0,0,0,0]]}';export class SplineData{constructor(){this.splineData=s,this.changed=new t,this._points=[],this._closed=!1,this.arcLengths=new Float32Array(0),this.arcTs=new Float32Array(0),this._length=0}onInit(){this.setData(this.splineData,!1)}onEndPlay(){this.changed.complete()}get points(){return this._points}get closed(){return this._closed}get length(){return this._length}setData(t,n=!0){const s=parseSplineData(t);this._closed=s.closed,this._points=s.points,this.splineData=packSplineData(this._points,this._closed),this.rebuildArcLengths(),n&&this.changed.next(this)}setPoints(t,n=this._closed){this._points=t.map(i),this._closed=n,this.commitChange()}updatePoint(t,n){t<0||t>=this._points.length||(this._points[t]=i(n),this.commitChange())}addPoint(t,n=this._points.length){this._points.splice(Math.max(0,Math.min(n,this._points.length)),0,i(t)),this.commitChange()}insertPointOnSegment(t,n=.5){const s=this.segmentCount;if(t<0||t>=s)return-1;const i=(t+1)%this._points.length,e=this._points[t],o=this._points[i],a=e.position,h=e.position.clone().add(e.outTangent),r=o.position.clone().add(o.inTangent),c=o.position,l=a.clone().lerp(h,n),p=h.clone().lerp(r,n),g=r.clone().lerp(c,n),u=l.clone().lerp(p,n),m=p.clone().lerp(g,n),T=u.clone().lerp(m,n);e.outTangent.copy(l).sub(a),o.inTangent.copy(g).sub(c);const d=t+1;return this._points.splice(d,0,{position:T,inTangent:u.sub(T),outTangent:m.sub(T)}),this.commitChange(),d}removePoint(t){t<0||t>=this._points.length||(this._points.splice(t,1),this.commitChange())}setClosed(t){t!==this._closed&&(this._closed=t,this.commitChange())}applySceneParameters(t,n){return!(1!==n.size||!n.has("splineData")||"string"!=typeof t.splineData)&&(this.setData(t.splineData),!0)}samplePosition(t,s=new n){const i=this.resolveSegment(t);return i?function(t,n,s,i){const e=1-s,o=e*e*e,a=3*e*e*s,h=3*e*s*s,r=s*s*s;return i.set(t.position.x*o+(t.position.x+t.outTangent.x)*a+(n.position.x+n.inTangent.x)*h+n.position.x*r,t.position.y*o+(t.position.y+t.outTangent.y)*a+(n.position.y+n.inTangent.y)*h+n.position.y*r,t.position.z*o+(t.position.z+t.outTangent.z)*a+(n.position.z+n.inTangent.z)*h+n.position.z*r)}(i.a,i.b,i.u,s):s.set(0,0,0)}sampleTangent(t,s=new n){const i=this.resolveSegment(t);return i?function(t,n,s,i){const e=1-s;return i.set(3*e*e*t.outTangent.x+6*e*s*(n.position.x+n.inTangent.x-t.position.x-t.outTangent.x)-3*s*s*n.inTangent.x,3*e*e*t.outTangent.y+6*e*s*(n.position.y+n.inTangent.y-t.position.y-t.outTangent.y)-3*s*s*n.inTangent.y,3*e*e*t.outTangent.z+6*e*s*(n.position.z+n.inTangent.z-t.position.z-t.outTangent.z)-3*s*s*n.inTangent.z)}(i.a,i.b,i.u,s).normalize():s.set(0,0,0)}samplePositionAtDistance(t,s=new n){return this.samplePosition(this.tAtDistance(t),s)}sampleTangentAtDistance(t,s=new n){return this.sampleTangent(this.tAtDistance(t),s)}closestPoint(t,s=new n){if(0===this.arcTs.length)return{position:s.set(0,0,0),tangent:new n,t:0,distance:t.length(),distanceAlongSpline:0};let i=0,e=1/0;const o=new n;for(let n=0;n<this.arcTs.length;n++){this.samplePosition(this.arcTs[n],o);const s=o.distanceToSquared(t);s<e&&(e=s,i=this.arcTs[n])}const a=this.arcTs.length>1?1/(this.arcTs.length-1):1;let h=Math.max(0,i-a),r=Math.min(1,i+a);for(let n=0;n<8;n++){const n=h+(r-h)/3,s=r-(r-h)/3;this.samplePosition(n,o);const i=o.distanceToSquared(t);this.samplePosition(s,o),i<o.distanceToSquared(t)?r=s:h=n}return i=.5*(h+r),this.samplePosition(i,s),{position:s,tangent:this.sampleTangent(i),t:i,distance:s.distanceTo(t),distanceAlongSpline:this.distanceAtT(i)}}commitChange(){this.splineData=packSplineData(this._points,this._closed),this.rebuildArcLengths(),this.changed.next(this)}get segmentCount(){return this._points.length<2?0:this._closed?this._points.length:this._points.length-1}resolveSegment(t){const n=this.segmentCount;if(0===n)return this._points[0]?{a:this._points[0],b:this._points[0],u:0}:null;const s=Math.max(0,Math.min(1,t))*n,i=Math.min(Math.floor(s),n-1);return{a:this._points[i],b:this._points[(i+1)%this._points.length],u:s-i}}rebuildArcLengths(){const t=24*this.segmentCount;if(0===t)return this.arcTs=this._points.length?new Float32Array([0]):new Float32Array(0),this.arcLengths=this._points.length?new Float32Array([0]):new Float32Array(0),void(this._length=0);this.arcTs=new Float32Array(t+1),this.arcLengths=new Float32Array(t+1);const s=this.samplePosition(0,new n),i=new n;let e=0;for(let n=1;n<=t;n++){const o=n/t;this.samplePosition(o,i),e+=s.distanceTo(i),this.arcTs[n]=o,this.arcLengths[n]=e,s.copy(i)}this._length=e}tAtDistance(t){if(0===this._length)return 0;const n=Math.max(0,Math.min(this._length,t));let s=0,i=this.arcLengths.length-1;for(;s<i;){const t=s+i>>>1;this.arcLengths[t]<n?s=t+1:i=t}const e=s,o=Math.max(0,e-1),a=this.arcLengths[e]-this.arcLengths[o],h=a>0?(n-this.arcLengths[o])/a:0;return this.arcTs[o]+(this.arcTs[e]-this.arcTs[o])*h}distanceAtT(t){if(this.arcTs.length<2)return 0;const n=Math.max(0,Math.min(1,t))*(this.arcTs.length-1),s=Math.floor(n),i=Math.min(s+1,this.arcLengths.length-1);return this.arcLengths[s]+(this.arcLengths[i]-this.arcLengths[s])*(n-s)}}export function packSplineData(t,n){return JSON.stringify({closed:n,points:t.map(t=>[...t.position.toArray(),...t.inTangent.toArray(),...t.outTangent.toArray()])})}export function parseSplineData(t){let i;try{i=JSON.parse(t)}catch{i=JSON.parse(s)}return{closed:!0===i.closed,points:(i.points??[]).filter(t=>t.length>=9).map(t=>({position:(new n).fromArray(t,0),inTangent:(new n).fromArray(t,3),outTangent:(new n).fromArray(t,6)}))}}function i(t){return{position:t.position.clone(),inTangent:t.inTangent.clone(),outTangent:t.outTangent.clone()}}/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -0,0 +1,23 @@
1
+ import { BufferGeometry, Vector3 } from 'three';
2
+ export interface WaterSplineSampler {
3
+ readonly points: readonly unknown[];
4
+ readonly length: number;
5
+ readonly closed: boolean;
6
+ samplePositionAtDistance(distance: number, target: Vector3): Vector3;
7
+ sampleTangentAtDistance(distance: number, target: Vector3): Vector3;
8
+ }
9
+ type CrossSection = {
10
+ leftTop: Vector3;
11
+ rightTop: Vector3;
12
+ leftBottom: Vector3;
13
+ rightBottom: Vector3;
14
+ flowDirection: Vector3;
15
+ distanceAlongSpline: number;
16
+ surfaceWidth: number;
17
+ };
18
+ export declare function createCrossSections(spline: WaterSplineSampler, width: number, depth: number, sampleDistance?: number): CrossSection[];
19
+ export declare function createWaterSplineSurfaceGeometry(spline: WaterSplineSampler, width: number, sampleDistance: number, widthSubdivisions: number): BufferGeometry;
20
+ export declare function createSurfaceGeometry(spline: WaterSplineSampler, width: number, sampleDistance: number, widthSubdivisions: number): BufferGeometry;
21
+ export declare function createVolumeGeometry(spline: WaterSplineSampler, width: number, depth: number, showSurface?: boolean, sampleDistance?: number): BufferGeometry;
22
+ export {};
23
+ //# sourceMappingURL=water-spline-geometry.d.ts.map
@@ -0,0 +1,4 @@
1
+ import{BufferGeometry as t,Float32BufferAttribute as e,Vector3 as n}from"three";export function createCrossSections(t,e,a,h=.5){if(t.points.length<2||t.length<=0||e<=0||a<0)return[];const p=Math.min(513,Math.max(2,Math.ceil(t.length/Math.max(.01,h))+1)),u=[],f=Array.from({length:p},(e,o)=>t.samplePositionAtDistance(t.length*o/(p-1),new n)),g=Math.min(.5*e,.25*t.length),m=Array.from({length:p},(e,o)=>{const c=t.length*o/(p-1),i=r(t,c-g,new n),l=r(t,c+g,new n).sub(i).setY(0);return l.lengthSq()<1e-8&&t.sampleTangentAtDistance(c,l).setY(0),l.lengthSq()<1e-8&&l.set(1,0,0),l.normalize(),new n(-l.z,0,l.x)}),d=.5*e,x=Array(p).fill(d),A=Array(p).fill(d);for(let e=0;e<p;e++){if(!t.closed&&(0===e||e===p-1))continue;const n=f[0===e?p-2:e-1],o=f[e],r=f[e===p-1?1:e+1],c=Math.min(d,.6*l(n,o,r)),a=i(n,o,r);a>1e-8?x[e]=c:a<-1e-8&&(A[e]=c)}c(x,f),c(A,f);for(let e=0;e<p;e++){const o=m[e],r=f[e].clone().addScaledVector(o,x[e]),c=f[e].clone().addScaledVector(o,-A[e]);u.push({leftTop:r,rightTop:c,leftBottom:r.clone().addScaledVector(s,a),rightBottom:c.clone().addScaledVector(s,a),flowDirection:t.sampleTangentAtDistance(t.length*e/(p-1),new n).normalize(),distanceAlongSpline:t.length*e/(p-1),surfaceWidth:x[e]+A[e]})}return function(t,e,n){o(t.map(t=>t.leftTop),e),o(t.map(t=>t.rightTop),e);for(const e of t)e.leftBottom.copy(e.leftTop).addScaledVector(s,n),e.rightBottom.copy(e.rightTop).addScaledVector(s,n),e.surfaceWidth=e.leftTop.distanceTo(e.rightTop)}(u,t.closed,a),u}function o(t,e){const n=e?t.length-1:t.length;if(!(n<3))for(let o=0;o<2;o++){const o=t.map(t=>t.clone()),r=e?n:n-1;for(let c=e?0:1;c<r;c++){const e=o[(c-1+n)%n],r=o[(c+1)%n];t[c].lerpVectors(e,r,.5).lerp(o[c],.5)}e&&t[t.length-1].copy(t[0])}}function r(t,e,n){if(!t.closed)return t.samplePositionAtDistance(e,n);const o=(e%t.length+t.length)%t.length;return t.samplePositionAtDistance(o,n)}function c(t,e){for(let n=1;n<t.length;n++)t[n]=Math.min(t[n],t[n-1]+.5*e[n].distanceTo(e[n-1]));for(let n=t.length-2;n>=0;n--)t[n]=Math.min(t[n],t[n+1]+.5*e[n].distanceTo(e[n+1]))}function i(t,e,n){return(e.x-t.x)*(n.z-e.z)-(e.z-t.z)*(n.x-e.x)}function l(t,e,n){const o=Math.hypot(e.x-t.x,e.z-t.z),r=Math.hypot(n.x-e.x,n.z-e.z),c=Math.hypot(n.x-t.x,n.z-t.z),i=Math.abs((e.x-t.x)*(n.z-t.z)-(e.z-t.z)*(n.x-t.x));return o<1e-6||r<1e-6||c<1e-6||i<1e-8?1/0:o*r*c/(2*i)}export function createWaterSplineSurfaceGeometry(t,e,n,o){return createSurfaceGeometry(t,e,n,o)}export function createSurfaceGeometry(n,o,r,c){const i=new t,l=createCrossSections(n,o,0,r),s=Math.max(1,Math.round(c))+1;if(l.length<2)return i.setAttribute("position",new e([],3)),i;const h=[],p=[],u=[],f=[],g=[];for(const t of l)for(let e=0;e<s;e++){const o=e/(s-1);h.push(...t.leftTop.clone().lerp(t.rightTop,o).toArray()),p.push(o,n.length>0?t.distanceAlongSpline/n.length:0),u.push(...t.flowDirection.toArray()),f.push(Math.min(o,1-o)*t.surfaceWidth),g.push(t.distanceAlongSpline)}const m=[];for(let t=1;t<l.length;t++)for(let e=1;e<s;e++){const n=(t-1)*s+e-1,o=t*s+e-1;a(m,n,o,o+1,n+1)}return i.setAttribute("position",new e(h,3)),i.setAttribute("uv",new e(p,2)),i.setAttribute("waterFlowDirection",new e(u,3)),i.setAttribute("waterDistanceFromBank",new e(f,1)),i.setAttribute("waterDistanceAlongSpline",new e(g,1)),i.setIndex(m),i.computeVertexNormals(),i.computeBoundingBox(),i.computeBoundingSphere(),i}export function createVolumeGeometry(n,o,r,c=!1,i=.5){const l=new t,s=createCrossSections(n,o,r,i);if(s.length<2)return l.setAttribute("position",new e([],3)),l;const h=s.flatMap(t=>[...t.leftTop.toArray(),...t.rightTop.toArray(),...t.leftBottom.toArray(),...t.rightBottom.toArray()]),p=[];for(let t=1;t<s.length;t++){const e=4*(t-1),n=4*t;c||a(p,e,n,n+1,e+1),a(p,e+2,e+3,n+3,n+2),a(p,e,e+2,n+2,n),a(p,e+1,n+1,n+3,e+3)}if(!n.closed){a(p,0,1,3,2);const t=4*(s.length-1);a(p,t,t+2,t+3,t+1)}return l.setAttribute("position",new e(h,3)),l.setIndex(p),l.computeVertexNormals(),l.computeBoundingBox(),l.computeBoundingSphere(),l}function a(t,e,n,o,r){t.push(e,n,o,e,o,r)}const s=new n(0,-1,0);/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -1,6 +1,6 @@
1
1
  import type { Material } from 'three';
2
2
  import { BufferGeometry, Object3D, Texture } from 'three';
3
- import type { AnimationRigData, Asset, AssetId, ModelColliderData } from './model.js';
3
+ import type { AnimationRigData, Asset, AssetId } from './model.js';
4
4
  import type { CollisionShape } from './collision/collision-shape.js';
5
5
  import * as THREE from 'three';
6
6
  import type { SceneObjectPhysicsSettings } from './materializer.js';
@@ -117,7 +117,7 @@ export declare class AssetMeshInstance extends Object3D {
117
117
  collisionShapes?: CollisionShape[];
118
118
  physics?: SceneObjectPhysicsSettings;
119
119
  }
120
- export declare function createAuthoredCollisionShapes(colliders: ModelColliderData[], assetRescale?: number): CollisionShape[];
120
+ export { createAuthoredCollisionShapes } from './collision/authored-colliders.js';
121
121
  export declare function getElectronArg(name: string): string;
122
122
  /**
123
123
  * Removes triangles that contain non-finite positions or have effectively no
@@ -1,4 +1,4 @@
1
- import{parseAnimationClips as e}from"./animation-binary.js";import{AudioLoader as t,BufferGeometry as r,Euler as a,Group as i,LoadingManager as s,Mesh as n,Object3D as o,Texture as l,TextureLoader as c,Vector3 as h}from"three";import{MTLLoader as u,OBJLoader as m,LUTCubeLoader as d}from"three-stdlib";import{cloneMesh as p,mergeCompatibleSkinnedMeshes as f}from"../utils/mesh.js";import{pathJoin as g}from"../utils/files.js";import{Subject as y,firstValueFrom as w}from"rxjs";import{BoxCollisionShape as x,CapsuleCollisionShape as M,CollisionShapeSource as b,CylinderCollisionShape as A,SphereCollisionShape as L}from"./collision/collision-shape.js";import{importCollisionShapes as S,isCollisionMesh as _}from"./collision/collision-shape-import.js";import*as v from"three";import{iterateMaterials as T}from"../utils/materials.js";import{applyMaterial as R,getConvertedFbxToGlbAssignedMaterialTextureFlipY as C}from"./materializer.js";import{BufferGeometryUtils as B,DRACOLoader as F,EXRLoader as P,FBXLoader as I,GLTFLoader as k,KTX2Loader as K,TGALoader as D,UltraHDRLoader as j}from"three/examples/jsm/Addons.js";import{disposeScene as W}from"../utils/three/cleanup.js";import{resolveAnimationRigBasisRotation as U,setAnimationClipRigId as z}from"../gameplay/animation/retarget.js";(()=>{const e=new Uint8Array([255,0,0,255]),t=new v.DataTexture(e,1,1,v.RGBAFormat);t.needsUpdate=!0})();const G=new F;G.setDecoderConfig({type:"js"}),G.setDecoderPath("/assets/draco/");const $=["glb","gltf","fbx","obj"];export class AssetResourceLoader{onError(e){console.error(e)}constructor(){this.cache=new Map,this.textureCache=new Map,this.loadingManager=new s,this.glbLoader=new k(this.loadingManager).setDRACOLoader(G),this.fbxLoader=new I(this.loadingManager),this.objLoader=new m(this.loadingManager),this.textureLoader=new c(this.loadingManager),this.tgaLoader=new D(this.loadingManager),this.exrLoader=new P(this.loadingManager),this.cubeLoader=new d(this.loadingManager),this.hdrLoader=new j(this.loadingManager),this.ktx2Loader=new K(this.loadingManager),this.ktx2Ready=Promise.resolve(),this._textureLoader=new v.ImageBitmapLoader(this.loadingManager),this.audioLoader=new t(this.loadingManager),this.initialisedKtx2=!1,this._retrievedMeshes=[],this._retrievedTextures=[],this.makeReady=new y,this.ready=w(this.makeReady),this.directFileLoading=!1,this._texturePromises=new Map,this._arrayTexturesByFileKey=new Map,this._arrayTexturePromisesByFileKey=new Map,this._arrayTextureSettingsByFileKey=new Map,this._animationPromises=new Map,this._animationCache=new Map,this.asyncMeshResults=new Map,this.asyncAudioResults=new Map,this.clipsRequests=new Set,this.timeToLoadClips=0,this.collisionShapeCache=new Map,this.optimizedMeshes=new Set}setDirectFileLoading(e){this.directFileLoading=e}setImageBitmapDecoder(e){this.imageBitmapDecoder=e}decodeImageBitmap(e){return this.imageBitmapDecoder?.(e)}configureKtx2(){this.initialisedKtx2=!0;null!=getElectronArg("windowId")||import.meta.url.includes(".vite")||import.meta.url.includes("hology/packages/core")?this.ktx2Loader.setTranscoderPath("/assets/basis/"):this.ktx2Loader.setTranscoderPath(new URL("../assets/basis/",import.meta.url).href),this.ktx2Loader.setWorkerLimit(6)}initKtx2(e){this.configureKtx2(),this.ktx2Loader.detectSupport(e)}initKtx2ForWebGpu(){this.configureKtx2(),this.ktx2Ready=Promise.resolve(navigator.gpu?.requestAdapter({powerPreference:"high-performance"})??null).then(e=>{this.ktx2Loader.detectSupport({isWebGPURenderer:!0,hasFeature:t=>!0===e?.features.has(t)})})}setDataDir(e){this.basePath=g(e,"asset-resources"),this.makeReady.next(!0)}getUri(e){if(e.startsWith("asset://"))return e;const t=function(e){return E.get(e)}(e),r=getElectronArg("windowId");return null!=r||import.meta.url.includes(".vite")||import.meta.url.includes("hology/packages/core")?g(this.basePath,e)+(null!=r?`?windowId=${r}${null!=t?`&v=${t}`:""}`:""):new URL(g("..",this.basePath,e),import.meta.url).href}async getTexture(e){if(!e||!e.fileKey)return null;if("png"===e.fileFormat||e.fileFormat,this._texturePromises.has(e.id))return this._texturePromises.get(e.id);const t=this._getTexture(e).finally(()=>{this._texturePromises.delete(e.id)});return this._texturePromises.set(e.id,t),t}async _getTexture(e){if(null==e||null==e.fileKey)return null;if(await this.ready,e.fileKey.toLowerCase().endsWith(".ktx2")&&await this.ktx2Ready,this.textureCache.has(e.id)){const t=this.textureCache.get(e.id);if(null!=e.texture){const r=X(t,e);t.flipY!==r?(t.needsUpdate=!0,t.flipY=r):t.wrapS!==Y(e.texture?.wrapS)?(t.needsUpdate=!0,t.wrapS=Y(e.texture?.wrapS)):t.wrapT!==Y(e.texture?.wrapT)&&(t.wrapT=Y(e.texture?.wrapT),t.needsUpdate=!0)}const r=J(V(e));t.colorSpace!==r&&(t.colorSpace=r,t.needsUpdate=!0)}else try{const t=this._getTextureLoader(e.fileKey),r=null!=this.imageBitmapDecoder&&/\.(png|jpe?g)$/i.test(e.fileKey),a=r||this.directFileLoading&&t===this.textureLoader,i=this.getUri(e.fileKey),s=r?this.imageBitmapDecoder(i).catch(t=>(console.warn(`Worker image decode failed for ${e.name}; falling back to ImageBitmapLoader`,t),this._textureLoader.loadAsync(i))):(a?this._textureLoader:t).loadAsync(i);await s.then(t=>(a&&((t=new l(t)).needsUpdate=!0),t.texture3D?t=t.texture3D:(t.wrapS=Y(e.texture?.wrapS),t.wrapT=Y(e.texture?.wrapT),t.flipY=X(t,e),t.colorSpace=J(V(e))),this.textureCache.set(e.id,t),this._retrievedTextures.push(t),t))}catch(t){return console.warn("Failed to load texture "+e.name,t),null}const t=this.textureCache.get(e.id);return null!=t&&(t.userData.assetId=e.id),t}async getTextureArray(e){const t=e.texture?.textureArrayFileKey,r=e.texture?.textureArrayLayer;if(!t||null==r)return{texture:null,layerIndex:null};let a=this._arrayTexturesByFileKey.get(t);if(null==a){let e=this._arrayTexturePromisesByFileKey.get(t);null==e&&(e=this.loadTextureArray(t).finally(()=>{this._arrayTexturePromisesByFileKey.delete(t)}),this._arrayTexturePromisesByFileKey.set(t,e)),a=await e}return this.applyTextureArraySettings(t,a,e),{texture:a,layerIndex:r}}async loadTextureArray(e){const t=this._getTextureLoader(e);let r=await t.loadAsync(this.getUri(e));return r instanceof l||(r=r.texture3D),this._arrayTexturesByFileKey.set(e,r),this._retrievedTextures.push(r),r}applyTextureArraySettings(e,t,r){const a=function(e){return{wrapS:Y(e.texture?.wrapS),wrapT:Y(e.texture?.wrapT)}}(r),i=this._arrayTextureSettingsByFileKey.get(e);if(null!=i&&(n=a,(s=i).wrapS!==n.wrapS||s.wrapT!==n.wrapT))return void console.warn(`Texture array ${e} is used with incompatible texture settings. Texture arrays share wrapping; rebuild arrays so incompatible textures are grouped separately.`);var s,n;null==i&&this._arrayTextureSettingsByFileKey.set(e,a);let o=!1;t.wrapS!==a.wrapS&&(t.wrapS=a.wrapS,o=!0),t.wrapT!==a.wrapT&&(t.wrapT=a.wrapT,o=!0),!1!==t.flipY&&(t.flipY=!1,o=!0),o&&(t.needsUpdate=!0)}_getTextureLoader(e){return e?.toLowerCase().endsWith(".tga")?this.tgaLoader:e?.toLowerCase().endsWith(".ktx2")?this.ktx2Loader:e?.toLowerCase().endsWith(".exr")?this.exrLoader:e?.toLowerCase().endsWith(".cube")?this.cubeLoader:this.textureLoader}clearCache(e){this.clearCacheByAsset(e)}clearCacheByAsset(e){null!=e&&(this.clearCacheById(e.id),null!=e.fileKey&&(Array.from(this.cache.keys()).forEach(t=>{t.startsWith(e.fileKey)&&this.cache.delete(t)}),Array.from(this.asyncMeshResults.keys()).forEach(t=>{t.startsWith(e.fileKey)&&this.asyncMeshResults.delete(t)}),Array.from(this._animationCache.keys()).forEach(t=>{t.startsWith(e.fileKey+"|")&&this._animationCache.delete(t)}),Array.from(this._animationPromises.keys()).forEach(t=>{t.startsWith(e.fileKey+"|")&&this._animationPromises.delete(t)}),O(e.fileKey)),null!=e.materialLib&&O(e.materialLib))}clearCacheById(e){const t=Array.from(this.cache.values()).find(t=>t.scene.userData.assetId===e);t&&Array.from(this.cache.keys()).forEach(e=>{this.cache.get(e)===t&&this.cache.delete(e)}),this.textureCache.delete(e),this._texturePromises.delete(e),Array.from(this.collisionShapeCache.keys()).forEach(t=>{t.startsWith(e+"|")&&this.collisionShapeCache.delete(t)})}async getMesh(e,t){if(await this.ready,null==e)return console.error("No asset was provided"),{scene:new i,animations:[]};if(!$.includes(e.fileFormat?.toLowerCase()))return console.error("Unsupported mesh file format "+e.fileFormat,e),{scene:new i,animations:[]};const r=e.fileKey+(!0===t?.mergeGeomtries?"1":"0");if(!this.cache.has(r))try{this.asyncMeshResults.has(r)||this.asyncMeshResults.set(r,this.loadMesh(e).finally(()=>this.asyncMeshResults.delete(r))),this.cache.set(r,await this.asyncMeshResults.get(r))}catch(e){return this.onError(e),{scene:new i,animations:[]}}const a=this.cache.get(r),s=a.scene,o=a.animations;if(!1===t?.clone)return{scene:s,animations:o};const l=this.computeCollisionShapes(e,s),c=(e.mesh?.secondaryMotion?.regions.length??0)>0;!0!==t?.mergeGeomtries||c||this.optimizeDrawGroups(s);const h=p(s);h.traverse(e=>{e instanceof n&&e.material instanceof Array&&(e.material=e.material.slice())});const u=new AssetMeshInstance;u.add(h),u.collisionShapes=l,u.animations=o,u.userData.assetId=e.id,u.userData.secondaryMotion=e.mesh?.secondaryMotion,function(e){const t=e.children.length>0?e.children:[e];for(let e=0;e<t.length;e++)r(t[e],"",e);function r(e,t,a){const i=e instanceof n?"Mesh":"Group",s=`${t}/${e.name?.trim()?.length>0?e.name:i}[${a}]`;e.userData.secondaryMotionMeshPath=s;for(let t=0;t<e.children.length;t++)r(e.children[t],s,t)}}(u);const m=e.mesh?.animationRigId??e.anim?.rigId;if(u.userData.animationRigId=m,h.userData.animationRigId=m,null!=m&&null!=this.animationRigProvider){const e=await this.animationRigProvider(m);u.userData.animationRig=e,h.userData.animationRig=e,null!=e&&!1!==t?.applyRigBasis&&u.quaternion.copy(U(e))}const d=e.receiveShadow??!1,f=e.castShadow??!1;if(h.traverse(e=>{e.castShadow=f,e.receiveShadow=d}),!0===t?.rescale&&"number"==typeof e.mesh?.rescale&&u.scale.setScalar(e.mesh?.rescale),!0===t?.applyMaterials){const r=t?.materialResolver??this.materialProvider;if(null!=r)for(const t of e.materialAssignments??[])await R(u,t,r,void 0,{textureFlipY:C(e)});else console.warn(`applyMaterials was set to true for ${e.name} but no material resolver was provided.`)}return{scene:u,animations:o}}async getAudio(e){await this.ready;const t=e.fileKey;if(this.asyncAudioResults.has(t))return await this.asyncAudioResults.get(t);const r=this.audioLoader.loadAsync(this.getUri(e.fileKey)).finally(()=>{setTimeout(()=>{this.asyncAudioResults.delete(t)},1e3)});return this.asyncAudioResults.set(t,r),r}async getAnimationClip(t){if(null==t||null==t.fileKey||null==t.anim?.clip)return console.warn("Asset or animation clip name is not defined",t),null;const r=t.anim.clip,a=t.fileKey,i=a+"|"+r;if(this._animationCache.has(i))return this._animationCache.get(i);if(this._animationPromises.has(i))return this._animationPromises.get(i);this.clipsRequests.add(t.id);const s=(async()=>{try{const s=("anim"===t.fileFormat?e(await new v.FileLoader(this.loadingManager).setResponseType("arraybuffer").loadAsync(this.getUri(a))):(await this.getMesh(t,{mergeGeomtries:!1,clone:!1})).animations).find(e=>e.name===r);return null!=s&&(z(s,t.anim?.rigId),this._animationCache.set(i,s)),s}catch(e){return this.onError(e),null}finally{setTimeout(()=>this._animationPromises.delete(i),1e3)}})();return this._animationPromises.set(i,s),s}computeCollisionShapes(e,t){if(!0!==e.collisionDetection)return[];const r=e.id+"|"+(e.mesh?.collisions?.shapeType??"")+"|"+(e.mesh?.rescale??1)+"|"+JSON.stringify(e.mesh?.colliders??[]);if(!this.collisionShapeCache.has(r)){const a=e.mesh?.colliders??[],i=a.length>0?createAuthoredCollisionShapes(a,e.mesh?.rescale):S(t,e);this.collisionShapeCache.set(r,i)}return this.collisionShapeCache.get(r)}async loadMesh(e){return await this.ready,await this.loadByAsset(e).then(t=>(t.scene.traverse(t=>{if(t instanceof n&&t.geometry instanceof r){const r=removeInvalidFaces(t.geometry);r>0&&console.warn(`Removed ${r} invalid face${1===r?"":"s"} from ${e.name}/${t.name||"unnamed mesh"}`)}t instanceof n&&t.material&&function(e){let t=e.material;if(null==t)return;Array.isArray(t)?e.material=t.map(ee):e.material=ee(t)}(t),_(t)?t.visible=!1:t instanceof n&&t.geometry instanceof r&&(t.geometry.hasAttribute("normal")||t.geometry.computeVertexNormals())}),this._retrievedMeshes.push(t.scene),t.scene=function(e,t){let r=!1;if(t.traverseVisible(e=>{Q.test(e.name)&&(r=!0)}),!r)return t;const a=[],i=[t];for(;i.length>0;){const e=i.shift(),t=e.name.match(Q);if(null!=t){const r=parseInt(t[1]);if(0!==r){console.warn(`Skipping LOD level ${r} for now as LOD is not fully supported`),a.push(e);continue}}i.push(...e.children)}for(const e of a)e.removeFromParent();return t}(0,t.scene),function(e){const t=H;H.length=0,e.traverse(e=>{(e instanceof v.Camera||e instanceof v.Light)&&t.push(e)}),t.forEach(e=>e.removeFromParent())}(t.scene),t.scene.userData.assetId=e.id,t))}optimizeDrawGroups(e){if(this.optimizedMeshes.has(e.uuid))return;this.optimizedMeshes.add(e.uuid),e.updateWorldMatrix(!0,!0);const t=[];e.traverse(e=>{e instanceof n&&!(e instanceof v.SkinnedMesh)&&e.geometry instanceof r&&Array.isArray(e.material)&&!_(e)&&t.push({mesh:e,geometry:e.geometry,materialArray:e.material,parent:e.parent})});for(const{mesh:r,geometry:a,materialArray:i,parent:s}of t){r.removeFromParent();for(let t=0;t<i.length;t++){const o=i[t],l=a.groups.filter(e=>e.materialIndex===t);if(0===l.length)continue;const c=this.extractGeometryGroups(a,l);c.groups.length>1&&B.mergeGroups(c);const h=new n(c,o);h.copy(r,!1),h.geometry=c,h.material=o,s?s.add(h):e.add(h)}a.dispose()}f(e,_);let a=!0,i=0,s=0;if(e.traverse(e=>{if(e instanceof n&&e.geometry instanceof r&&!_(e)){i++;const t=Object.keys(e.geometry.attributes).length;t!==s&&0!==s&&(a=!1),s=t}else(e instanceof v.SkinnedMesh||e instanceof v.Bone)&&(a=!1)}),i>1&&a){const t=new Map;e.traverse(a=>{if(a instanceof n&&a.geometry instanceof r&&!_(a)){if(Array.isArray(a.material))return;a.updateWorldMatrix(!0,!0);const r=a.material?.uuid||"default";t.has(r)||t.set(r,{material:a.material,geometries:[],objects:[]});const i=t.get(r),s=e.matrixWorld.clone().invert().multiply(a.matrixWorld);i.geometries.push(a.geometry.clone().applyMatrix4(s)),i.objects.push(a)}});for(const e of t.values())for(const t of e.objects)t.removeFromParent();for(const r of t.values()){let t,a=r.geometries;if(a.some(e=>null!=e.index)&&(a=a.map(e=>{if(null!=e.index)return e;const t=e.getAttribute("position");if(null==t)return e;const r=t.count>65535?new Uint32Array(t.count):new Uint16Array(t.count);for(let e=0;e<r.length;e++)r[e]=e;return e.setIndex(new v.BufferAttribute(r,1)),e})),t=1===r.geometries.length?a[0]:N(a)?B.mergeGeometries(a,!0):null,null!=t)t.groups.length>1&&B.mergeGroups(t),e.add(new n(t,r.material));else for(const t of a)e.add(new n(t,r.material))}}e.traverse(e=>{if(e instanceof n&&e.geometry instanceof r&&!_(e)){const t=e.geometry;!Array.isArray(e.material)&&t.groups.length>1&&B.mergeGroups(t)}})}extractGeometryGroups(e,t){const a=e.index;if(!a){const a=new r;let i=0;for(const e of t)i+=e.count;if(0===i)return a;for(const r in e.attributes){const s=e.attributes[r],n=s.itemSize,o=s.array,l=new(0,o.constructor)(i*n);let c=0;for(const e of t){const t=e.start*n,r=e.count*n;for(let e=0;e<r;e++)l[c+e]=o[t+e];c+=r}const h=new v.BufferAttribute(l,n,s.normalized);a.setAttribute(r,h)}return a.groups=[{start:0,count:i,materialIndex:0}],a}const i=[];for(const e of t)for(let t=0;t<e.count;t++){const r=a.getX(e.start+t);i.push(r)}if(0===i.length)return new r;const s=new Map,n=[];let o=0;for(const e of i)s.has(e)||(s.set(e,o),o++),n.push(s.get(e));if(0===n.length||0===s.size)return new r;const l=new r;for(const t in e.attributes){const r=e.attributes[t],a=r.itemSize,i=r.array,n=new(0,i.constructor)(s.size*a);for(const[e,t]of s)for(let r=0;r<a;r++)n[t*a+r]=i[e*a+r];const o=new v.BufferAttribute(n,a,r.normalized);l.setAttribute(t,o)}const c=new((n.length>0?Math.max(...n):0)>65535?Uint32Array:Uint16Array)(n.length);for(let e=0;e<n.length;e++)c[e]=n[e];return l.setIndex(new v.BufferAttribute(c,1)),l.groups=[{start:0,count:n.length,materialIndex:0}],l}async loadByAsset(e){this.fbxLoader;const t=this.getUri(e.fileKey);switch(e.fileFormat){case"glb":case"gltf":return this.glbLoader.loadAsync(t).then(t=>{const r=!0===e.originalFileKey?.toLowerCase().endsWith(".fbx")&&1===t.scene.children.length?t.scene.children[0]:t.scene;return r!==t.scene&&t.scene.remove(r),{scene:r,animations:t.animations}});case"fbx":if(this.directFileLoading){const e=await fetch(t);if(!e.ok)throw new Error(`Failed to load FBX ${t}: ${e.status}`);const r=await e.arrayBuffer(),a=this.fbxLoader.parse(r,v.LoaderUtils.extractUrlBase(t));return{scene:a,animations:a.animations}}return this.fbxLoader.loadAsync(t).then(e=>({scene:e,animations:e.animations}));case"obj":if(null!=e.materialLib){const t=new u;t.materialOptions={normalizeRGB:!1};const r=await t.loadAsync(this.getUri(e.materialLib));this.objLoader.setMaterials(r)}return this.objLoader.loadAsync(t).then(e=>(q(e),e)).then(e=>({scene:e,animations:e.animations}))}}disposeAll(){this.cache.clear(),this._retrievedMeshes.forEach(e=>W(e)),this._retrievedTextures.forEach(e=>e.dispose()),this._retrievedMeshes.length=0,this._retrievedTextures.length=0}}const E=new Map;function O(e){E.set(e,(E.get(e)??0)+1)}function N(e){if(e.length<2)return!0;const t=e[0],r=Object.keys(t.attributes).sort(),a=Object.keys(t.morphAttributes).sort();return e.slice(1).every(e=>{if(null==e.index!=(null==t.index))return!1;if(e.morphTargetsRelative!==t.morphTargetsRelative)return!1;const i=Object.keys(e.attributes).sort(),s=Object.keys(e.morphAttributes).sort();if(i.join("|")!==r.join("|"))return!1;if(s.join("|")!==a.join("|"))return!1;for(const a of r){const r=t.getAttribute(a),i=e.getAttribute(a);if(r.itemSize!==i.itemSize||r.normalized!==i.normalized||r.array.constructor!==i.array.constructor)return!1}for(const r of a){const a=t.morphAttributes[r],i=e.morphAttributes[r];if(a.length!==i.length)return!1;for(let e=0;e<a.length;e++)if(a[e].itemSize!==i[e].itemSize||a[e].normalized!==i[e].normalized||a[e].array.constructor!==i[e].array.constructor)return!1}return!0})}function q(e){if(e instanceof n)for(const t of T(e.material))t instanceof v.MeshPhongMaterial&&!t.color.isLinear&&(t.color.isLinear=!0);e.children?.forEach(q)}export class AssetMeshInstance extends o{}export function createAuthoredCollisionShapes(e,t=1){const r=null!=t&&0!==t?1/t:1;return e.map(e=>{let t;switch(e.type){case"box":t=new x((new h).fromArray(e.size).multiplyScalar(r));break;case"sphere":t=new L(e.radius*r);break;case"capsule":t=new M(e.length*r,e.radius*r);break;case"cylinder":t=new A(e.radius*r,e.radius*r,e.height*r,16,new a)}return t.source=b.custom,t.offset.fromArray(e.position??[0,0,0]).multiplyScalar(r),t.rotation.fromArray(e.rotation??[0,0,0,"XYZ"]),t})}export function getElectronArg(e){const t=`--${e}=`,r=window.process?.argv.find(e=>e.startsWith(t));return r?.substring(t.length)}function Y(e){switch(e){case"clamp":return v.ClampToEdgeWrapping;case"repeat":return v.RepeatWrapping;case"mirror":return v.MirroredRepeatWrapping}return v.RepeatWrapping}function X(e,t){const r=e;return!0!==r.isCompressedTexture&&!0!==r.isCompressedArrayTexture&&(t.texture?.flipY??!0)}function V(e){return e.texture?.colorSpace??function(e){if("exr"===e.fileFormat||e.fileKey?.toLowerCase().endsWith(".exr"))return"linear";const t=`${e.name} ${e.fileKey??""}`.toLowerCase();if(Z.some(e=>t.includes(e)))return"linear";return"srgb"}(e)}function J(e){return"linear"===e?v.LinearSRGBColorSpace:v.SRGBColorSpace}const Z=["_normal","_nrm","_nor","_norm","_roughness","_rough","_metalness","_metallic","_metal","_ao","_ambient","_occlusion","_mask","_height","_disp","_displacement","_bump","_opacity"];new v.Matrix4;const H=[];const Q=/_LOD(\d+)/;export function removeInvalidFaces(e){const t=e.getAttribute("position");if(null==t||t.itemSize<3)return 0;const r=e.index,a=r?.count??t.count,i=Math.floor(a/3),s=[],n=new Uint32Array(i+1),o=new h,l=new h,c=new h;let u=0;for(let e=0;e<i;e++){const a=3*e,i=r?.getX(a)??a,h=r?.getX(a+1)??a+1,m=r?.getX(a+2)??a+2;let d=Number.isInteger(i)&&Number.isInteger(h)&&Number.isInteger(m)&&i>=0&&h>=0&&m>=0&&i<t.count&&h<t.count&&m<t.count;if(d){o.fromBufferAttribute(t,i),l.fromBufferAttribute(t,h).sub(o),c.fromBufferAttribute(t,m).sub(o);const e=Math.max(l.lengthSq(),c.lengthSq(),l.distanceToSquared(c)),r=l.cross(c).lengthSq();d=Number.isFinite(e)&&Number.isFinite(r)&&e>0&&r>Number.EPSILON*e*e}d?s.push(i,h,m):u++,n[e+1]=s.length}if(0===u)return 0;if(e.setIndex(s),e.groups.length>0){const t=e.groups.slice();e.clearGroups();for(const r of t){const t=Math.max(0,Math.ceil(r.start/3)),a=Math.min(i,Math.floor((r.start+r.count)/3));if(a<=t)continue;const s=n[t],o=n[a]-s;o>0&&e.addGroup(s,o,r.materialIndex)}}const m=Math.max(0,Math.ceil(e.drawRange.start/3)),d=Number.isFinite(e.drawRange.count)?Math.min(i,Math.floor((e.drawRange.start+e.drawRange.count)/3)):i,p=Math.min(m,i),f=Math.min(i,Math.max(p,d)),g=n[p];return e.setDrawRange(g,n[f]-g),u}function ee(e){let t=e instanceof v.MeshPhysicalMaterial&&0===e.sheen&&0===e.anisotropy;if("MeshLambertMaterial"===e.type||"MeshPhongMaterial"===e.type||t){let t={};(e instanceof v.MeshLambertMaterial||e instanceof v.MeshPhongMaterial||e instanceof v.MeshPhysicalMaterial)&&(t.color=e.color,t.map=e.map,t.bumpMap=e.bumpMap,t.bumpScale=e.bumpScale,t.lightMap=e.lightMap,t.lightMapIntensity=e.lightMapIntensity,t.aoMap=e.aoMap,t.aoMapIntensity=e.aoMapIntensity,t.emissiveMap=e.emissiveMap,t.alphaMap=e.alphaMap,t.envMap=e.envMap,t.envMapRotation=e.envMapRotation,t.wireframe=e.wireframe,t.wireframeLinewidth=e.wireframeLinewidth,t.fog=e.fog,t.opacity=e.opacity,t.transparent=e.transparent,t.side=e.side,t.depthTest=e.depthTest,t.depthWrite=e.depthWrite,t.alphaTest=e.alphaTest,t.displacementMap=e.displacementMap,t.displacementScale=e.displacementScale,t.displacementBias=e.displacementBias);const r=new v.MeshStandardMaterial(t);return r.userData={...e.userData??{}},r.name=e.name,r}return e}/*
1
+ import{createAuthoredCollisionShapes as e}from"./collision/authored-colliders.js";import{parseAnimationClips as t}from"./animation-binary.js";import{AudioLoader as r,BufferGeometry as a,Group as i,LoadingManager as s,Mesh as n,Object3D as o,Texture as l,TextureLoader as c,Vector3 as h}from"three";import{MTLLoader as u,OBJLoader as m,LUTCubeLoader as d}from"three-stdlib";import{cloneMesh as p,mergeCompatibleSkinnedMeshes as f}from"../utils/mesh.js";import{pathJoin as g}from"../utils/files.js";import{Subject as y,firstValueFrom as w}from"rxjs";import{importCollisionShapes as x,isCollisionMesh as M}from"./collision/collision-shape-import.js";import*as b from"three";import{iterateMaterials as A}from"../utils/materials.js";import{applyMaterial as L,getConvertedFbxToGlbAssignedMaterialTextureFlipY as S}from"./materializer.js";import{BufferGeometryUtils as _,DRACOLoader as v,EXRLoader as T,FBXLoader as R,GLTFLoader as C,KTX2Loader as B,TGALoader as F,UltraHDRLoader as P}from"three/examples/jsm/Addons.js";import{disposeScene as I}from"../utils/three/cleanup.js";import{resolveAnimationRigBasisRotation as K,setAnimationClipRigId as k}from"../gameplay/animation/retarget.js";(()=>{const e=new Uint8Array([255,0,0,255]),t=new b.DataTexture(e,1,1,b.RGBAFormat);t.needsUpdate=!0})();const D=new v;D.setDecoderConfig({type:"js"}),D.setDecoderPath("/assets/draco/");const j=["glb","gltf","fbx","obj"];export class AssetResourceLoader{onError(e){console.error(e)}constructor(){this.cache=new Map,this.textureCache=new Map,this.loadingManager=new s,this.glbLoader=new C(this.loadingManager).setDRACOLoader(D),this.fbxLoader=new R(this.loadingManager),this.objLoader=new m(this.loadingManager),this.textureLoader=new c(this.loadingManager),this.tgaLoader=new F(this.loadingManager),this.exrLoader=new T(this.loadingManager),this.cubeLoader=new d(this.loadingManager),this.hdrLoader=new P(this.loadingManager),this.ktx2Loader=new B(this.loadingManager),this.ktx2Ready=Promise.resolve(),this._textureLoader=new b.ImageBitmapLoader(this.loadingManager),this.audioLoader=new r(this.loadingManager),this.initialisedKtx2=!1,this._retrievedMeshes=[],this._retrievedTextures=[],this.makeReady=new y,this.ready=w(this.makeReady),this.directFileLoading=!1,this._texturePromises=new Map,this._arrayTexturesByFileKey=new Map,this._arrayTexturePromisesByFileKey=new Map,this._arrayTextureSettingsByFileKey=new Map,this._animationPromises=new Map,this._animationCache=new Map,this.asyncMeshResults=new Map,this.asyncAudioResults=new Map,this.clipsRequests=new Set,this.timeToLoadClips=0,this.collisionShapeCache=new Map,this.optimizedMeshes=new Set}setDirectFileLoading(e){this.directFileLoading=e}setImageBitmapDecoder(e){this.imageBitmapDecoder=e}decodeImageBitmap(e){return this.imageBitmapDecoder?.(e)}configureKtx2(){this.initialisedKtx2=!0;null!=getElectronArg("windowId")||import.meta.url.includes(".vite")||import.meta.url.includes("hology/packages/core")?this.ktx2Loader.setTranscoderPath("/assets/basis/"):this.ktx2Loader.setTranscoderPath(new URL("../assets/basis/",import.meta.url).href),this.ktx2Loader.setWorkerLimit(6)}initKtx2(e){this.configureKtx2(),this.ktx2Loader.detectSupport(e)}initKtx2ForWebGpu(){this.configureKtx2(),this.ktx2Ready=Promise.resolve(navigator.gpu?.requestAdapter({powerPreference:"high-performance"})??null).then(e=>{this.ktx2Loader.detectSupport({isWebGPURenderer:!0,hasFeature:t=>!0===e?.features.has(t)})})}setDataDir(e){this.basePath=g(e,"asset-resources"),this.makeReady.next(!0)}getUri(e){if(e.startsWith("asset://"))return e;const t=function(e){return W.get(e)}(e),r=getElectronArg("windowId");return null!=r||import.meta.url.includes(".vite")||import.meta.url.includes("hology/packages/core")?g(this.basePath,e)+(null!=r?`?windowId=${r}${null!=t?`&v=${t}`:""}`:""):new URL(g("..",this.basePath,e),import.meta.url).href}async getTexture(e){if(!e||!e.fileKey)return null;if("png"===e.fileFormat||e.fileFormat,this._texturePromises.has(e.id))return this._texturePromises.get(e.id);const t=this._getTexture(e).finally(()=>{this._texturePromises.delete(e.id)});return this._texturePromises.set(e.id,t),t}async _getTexture(e){if(null==e||null==e.fileKey)return null;if(await this.ready,e.fileKey.toLowerCase().endsWith(".ktx2")&&await this.ktx2Ready,this.textureCache.has(e.id)){const t=this.textureCache.get(e.id);if(null!=e.texture){const r=E(t,e);t.flipY!==r?(t.needsUpdate=!0,t.flipY=r):t.wrapS!==$(e.texture?.wrapS)?(t.needsUpdate=!0,t.wrapS=$(e.texture?.wrapS)):t.wrapT!==$(e.texture?.wrapT)&&(t.wrapT=$(e.texture?.wrapT),t.needsUpdate=!0)}const r=N(O(e));t.colorSpace!==r&&(t.colorSpace=r,t.needsUpdate=!0)}else try{const t=this._getTextureLoader(e.fileKey),r=null!=this.imageBitmapDecoder&&/\.(png|jpe?g)$/i.test(e.fileKey),a=r||this.directFileLoading&&t===this.textureLoader,i=this.getUri(e.fileKey),s=r?this.imageBitmapDecoder(i).catch(t=>(console.warn(`Worker image decode failed for ${e.name}; falling back to ImageBitmapLoader`,t),this._textureLoader.loadAsync(i))):(a?this._textureLoader:t).loadAsync(i);await s.then(t=>(a&&((t=new l(t)).needsUpdate=!0),t.texture3D?t=t.texture3D:(t.wrapS=$(e.texture?.wrapS),t.wrapT=$(e.texture?.wrapT),t.flipY=E(t,e),t.colorSpace=N(O(e))),this.textureCache.set(e.id,t),this._retrievedTextures.push(t),t))}catch(t){return console.warn("Failed to load texture "+e.name,t),null}const t=this.textureCache.get(e.id);return null!=t&&(t.userData.assetId=e.id),t}async getTextureArray(e){const t=e.texture?.textureArrayFileKey,r=e.texture?.textureArrayLayer;if(!t||null==r)return{texture:null,layerIndex:null};let a=this._arrayTexturesByFileKey.get(t);if(null==a){let e=this._arrayTexturePromisesByFileKey.get(t);null==e&&(e=this.loadTextureArray(t).finally(()=>{this._arrayTexturePromisesByFileKey.delete(t)}),this._arrayTexturePromisesByFileKey.set(t,e)),a=await e}return this.applyTextureArraySettings(t,a,e),{texture:a,layerIndex:r}}async loadTextureArray(e){const t=this._getTextureLoader(e);let r=await t.loadAsync(this.getUri(e));return r instanceof l||(r=r.texture3D),this._arrayTexturesByFileKey.set(e,r),this._retrievedTextures.push(r),r}applyTextureArraySettings(e,t,r){const a=function(e){return{wrapS:$(e.texture?.wrapS),wrapT:$(e.texture?.wrapT)}}(r),i=this._arrayTextureSettingsByFileKey.get(e);if(null!=i&&(n=a,(s=i).wrapS!==n.wrapS||s.wrapT!==n.wrapT))return void console.warn(`Texture array ${e} is used with incompatible texture settings. Texture arrays share wrapping; rebuild arrays so incompatible textures are grouped separately.`);var s,n;null==i&&this._arrayTextureSettingsByFileKey.set(e,a);let o=!1;t.wrapS!==a.wrapS&&(t.wrapS=a.wrapS,o=!0),t.wrapT!==a.wrapT&&(t.wrapT=a.wrapT,o=!0),!1!==t.flipY&&(t.flipY=!1,o=!0),o&&(t.needsUpdate=!0)}_getTextureLoader(e){return e?.toLowerCase().endsWith(".tga")?this.tgaLoader:e?.toLowerCase().endsWith(".ktx2")?this.ktx2Loader:e?.toLowerCase().endsWith(".exr")?this.exrLoader:e?.toLowerCase().endsWith(".cube")?this.cubeLoader:this.textureLoader}clearCache(e){this.clearCacheByAsset(e)}clearCacheByAsset(e){null!=e&&(this.clearCacheById(e.id),null!=e.fileKey&&(Array.from(this.cache.keys()).forEach(t=>{t.startsWith(e.fileKey)&&this.cache.delete(t)}),Array.from(this.asyncMeshResults.keys()).forEach(t=>{t.startsWith(e.fileKey)&&this.asyncMeshResults.delete(t)}),Array.from(this._animationCache.keys()).forEach(t=>{t.startsWith(e.fileKey+"|")&&this._animationCache.delete(t)}),Array.from(this._animationPromises.keys()).forEach(t=>{t.startsWith(e.fileKey+"|")&&this._animationPromises.delete(t)}),U(e.fileKey)),null!=e.materialLib&&U(e.materialLib))}clearCacheById(e){const t=Array.from(this.cache.values()).find(t=>t.scene.userData.assetId===e);t&&Array.from(this.cache.keys()).forEach(e=>{this.cache.get(e)===t&&this.cache.delete(e)}),this.textureCache.delete(e),this._texturePromises.delete(e),Array.from(this.collisionShapeCache.keys()).forEach(t=>{t.startsWith(e+"|")&&this.collisionShapeCache.delete(t)})}async getMesh(e,t){if(await this.ready,null==e)return console.error("No asset was provided"),{scene:new i,animations:[]};if(!j.includes(e.fileFormat?.toLowerCase()))return console.error("Unsupported mesh file format "+e.fileFormat,e),{scene:new i,animations:[]};const r=e.fileKey+(!0===t?.mergeGeomtries?"1":"0");if(!this.cache.has(r))try{this.asyncMeshResults.has(r)||this.asyncMeshResults.set(r,this.loadMesh(e).finally(()=>this.asyncMeshResults.delete(r))),this.cache.set(r,await this.asyncMeshResults.get(r))}catch(e){return this.onError(e),{scene:new i,animations:[]}}const a=this.cache.get(r),s=a.scene,o=a.animations;if(!1===t?.clone)return{scene:s,animations:o};const l=this.computeCollisionShapes(e,s),c=(e.mesh?.secondaryMotion?.regions.length??0)>0;!0!==t?.mergeGeomtries||c||this.optimizeDrawGroups(s);const h=p(s);h.traverse(e=>{e instanceof n&&e.material instanceof Array&&(e.material=e.material.slice())});const u=new AssetMeshInstance;u.add(h),u.collisionShapes=l,u.animations=o,u.userData.assetId=e.id,u.userData.secondaryMotion=e.mesh?.secondaryMotion,function(e){const t=e.children.length>0?e.children:[e];for(let e=0;e<t.length;e++)r(t[e],"",e);function r(e,t,a){const i=e instanceof n?"Mesh":"Group",s=`${t}/${e.name?.trim()?.length>0?e.name:i}[${a}]`;e.userData.secondaryMotionMeshPath=s;for(let t=0;t<e.children.length;t++)r(e.children[t],s,t)}}(u);const m=e.mesh?.animationRigId??e.anim?.rigId;if(u.userData.animationRigId=m,h.userData.animationRigId=m,null!=m&&null!=this.animationRigProvider){const e=await this.animationRigProvider(m);u.userData.animationRig=e,h.userData.animationRig=e,null!=e&&!1!==t?.applyRigBasis&&u.quaternion.copy(K(e))}const d=e.receiveShadow??!1,f=e.castShadow??!1;if(h.traverse(e=>{e.castShadow=f,e.receiveShadow=d}),!0===t?.rescale&&"number"==typeof e.mesh?.rescale&&u.scale.setScalar(e.mesh?.rescale),!0===t?.applyMaterials){const r=t?.materialResolver??this.materialProvider;if(null!=r)for(const t of e.materialAssignments??[])await L(u,t,r,void 0,{textureFlipY:S(e)});else console.warn(`applyMaterials was set to true for ${e.name} but no material resolver was provided.`)}return{scene:u,animations:o}}async getAudio(e){await this.ready;const t=e.fileKey;if(this.asyncAudioResults.has(t))return await this.asyncAudioResults.get(t);const r=this.audioLoader.loadAsync(this.getUri(e.fileKey)).finally(()=>{setTimeout(()=>{this.asyncAudioResults.delete(t)},1e3)});return this.asyncAudioResults.set(t,r),r}async getAnimationClip(e){if(null==e||null==e.fileKey||null==e.anim?.clip)return console.warn("Asset or animation clip name is not defined",e),null;const r=e.anim.clip,a=e.fileKey,i=a+"|"+r;if(this._animationCache.has(i))return this._animationCache.get(i);if(this._animationPromises.has(i))return this._animationPromises.get(i);this.clipsRequests.add(e.id);const s=(async()=>{try{const s=("anim"===e.fileFormat?t(await new b.FileLoader(this.loadingManager).setResponseType("arraybuffer").loadAsync(this.getUri(a))):(await this.getMesh(e,{mergeGeomtries:!1,clone:!1})).animations).find(e=>e.name===r);return null!=s&&(k(s,e.anim?.rigId),this._animationCache.set(i,s)),s}catch(e){return this.onError(e),null}finally{setTimeout(()=>this._animationPromises.delete(i),1e3)}})();return this._animationPromises.set(i,s),s}computeCollisionShapes(t,r){if(!0!==t.collisionDetection)return[];const a=t.id+"|"+(t.mesh?.collisions?.shapeType??"")+"|"+(t.mesh?.rescale??1)+"|"+JSON.stringify(t.mesh?.colliders??[]);if(!this.collisionShapeCache.has(a)){const i=t.mesh?.colliders??[],s=i.length>0?e(i,t.mesh?.rescale):x(r,t);this.collisionShapeCache.set(a,s)}return this.collisionShapeCache.get(a)}async loadMesh(e){return await this.ready,await this.loadByAsset(e).then(t=>(t.scene.traverse(t=>{if(t instanceof n&&t.geometry instanceof a){const r=removeInvalidFaces(t.geometry);r>0&&console.warn(`Removed ${r} invalid face${1===r?"":"s"} from ${e.name}/${t.name||"unnamed mesh"}`)}t instanceof n&&t.material&&function(e){let t=e.material;if(null==t)return;Array.isArray(t)?e.material=t.map(V):e.material=V(t)}(t),M(t)?t.visible=!1:t instanceof n&&t.geometry instanceof a&&(t.geometry.hasAttribute("normal")||t.geometry.computeVertexNormals())}),this._retrievedMeshes.push(t.scene),t.scene=function(e,t){let r=!1;if(t.traverseVisible(e=>{X.test(e.name)&&(r=!0)}),!r)return t;const a=[],i=[t];for(;i.length>0;){const e=i.shift(),t=e.name.match(X);if(null!=t){const r=parseInt(t[1]);if(0!==r){console.warn(`Skipping LOD level ${r} for now as LOD is not fully supported`),a.push(e);continue}}i.push(...e.children)}for(const e of a)e.removeFromParent();return t}(0,t.scene),function(e){const t=Y;Y.length=0,e.traverse(e=>{(e instanceof b.Camera||e instanceof b.Light)&&t.push(e)}),t.forEach(e=>e.removeFromParent())}(t.scene),t.scene.userData.assetId=e.id,t))}optimizeDrawGroups(e){if(this.optimizedMeshes.has(e.uuid))return;this.optimizedMeshes.add(e.uuid),e.updateWorldMatrix(!0,!0);const t=[];e.traverse(e=>{e instanceof n&&!(e instanceof b.SkinnedMesh)&&e.geometry instanceof a&&Array.isArray(e.material)&&!M(e)&&t.push({mesh:e,geometry:e.geometry,materialArray:e.material,parent:e.parent})});for(const{mesh:r,geometry:a,materialArray:i,parent:s}of t){r.removeFromParent();for(let t=0;t<i.length;t++){const o=i[t],l=a.groups.filter(e=>e.materialIndex===t);if(0===l.length)continue;const c=this.extractGeometryGroups(a,l);c.groups.length>1&&_.mergeGroups(c);const h=new n(c,o);h.copy(r,!1),h.geometry=c,h.material=o,s?s.add(h):e.add(h)}a.dispose()}f(e,M);let r=!0,i=0,s=0;if(e.traverse(e=>{if(e instanceof n&&e.geometry instanceof a&&!M(e)){i++;const t=Object.keys(e.geometry.attributes).length;t!==s&&0!==s&&(r=!1),s=t}else(e instanceof b.SkinnedMesh||e instanceof b.Bone)&&(r=!1)}),i>1&&r){const t=new Map;e.traverse(r=>{if(r instanceof n&&r.geometry instanceof a&&!M(r)){if(Array.isArray(r.material))return;r.updateWorldMatrix(!0,!0);const a=r.material?.uuid||"default";t.has(a)||t.set(a,{material:r.material,geometries:[],objects:[]});const i=t.get(a),s=e.matrixWorld.clone().invert().multiply(r.matrixWorld);i.geometries.push(r.geometry.clone().applyMatrix4(s)),i.objects.push(r)}});for(const e of t.values())for(const t of e.objects)t.removeFromParent();for(const r of t.values()){let t,a=r.geometries;if(a.some(e=>null!=e.index)&&(a=a.map(e=>{if(null!=e.index)return e;const t=e.getAttribute("position");if(null==t)return e;const r=t.count>65535?new Uint32Array(t.count):new Uint16Array(t.count);for(let e=0;e<r.length;e++)r[e]=e;return e.setIndex(new b.BufferAttribute(r,1)),e})),t=1===r.geometries.length?a[0]:z(a)?_.mergeGeometries(a,!0):null,null!=t)t.groups.length>1&&_.mergeGroups(t),e.add(new n(t,r.material));else for(const t of a)e.add(new n(t,r.material))}}e.traverse(e=>{if(e instanceof n&&e.geometry instanceof a&&!M(e)){const t=e.geometry;!Array.isArray(e.material)&&t.groups.length>1&&_.mergeGroups(t)}})}extractGeometryGroups(e,t){const r=e.index;if(!r){const r=new a;let i=0;for(const e of t)i+=e.count;if(0===i)return r;for(const a in e.attributes){const s=e.attributes[a],n=s.itemSize,o=s.array,l=new(0,o.constructor)(i*n);let c=0;for(const e of t){const t=e.start*n,r=e.count*n;for(let e=0;e<r;e++)l[c+e]=o[t+e];c+=r}const h=new b.BufferAttribute(l,n,s.normalized);r.setAttribute(a,h)}return r.groups=[{start:0,count:i,materialIndex:0}],r}const i=[];for(const e of t)for(let t=0;t<e.count;t++){const a=r.getX(e.start+t);i.push(a)}if(0===i.length)return new a;const s=new Map,n=[];let o=0;for(const e of i)s.has(e)||(s.set(e,o),o++),n.push(s.get(e));if(0===n.length||0===s.size)return new a;const l=new a;for(const t in e.attributes){const r=e.attributes[t],a=r.itemSize,i=r.array,n=new(0,i.constructor)(s.size*a);for(const[e,t]of s)for(let r=0;r<a;r++)n[t*a+r]=i[e*a+r];const o=new b.BufferAttribute(n,a,r.normalized);l.setAttribute(t,o)}const c=new((n.length>0?Math.max(...n):0)>65535?Uint32Array:Uint16Array)(n.length);for(let e=0;e<n.length;e++)c[e]=n[e];return l.setIndex(new b.BufferAttribute(c,1)),l.groups=[{start:0,count:n.length,materialIndex:0}],l}async loadByAsset(e){this.fbxLoader;const t=this.getUri(e.fileKey);switch(e.fileFormat){case"glb":case"gltf":return this.glbLoader.loadAsync(t).then(t=>{const r=!0===e.originalFileKey?.toLowerCase().endsWith(".fbx")&&1===t.scene.children.length?t.scene.children[0]:t.scene;return r!==t.scene&&t.scene.remove(r),{scene:r,animations:t.animations}});case"fbx":if(this.directFileLoading){const e=await fetch(t);if(!e.ok)throw new Error(`Failed to load FBX ${t}: ${e.status}`);const r=await e.arrayBuffer(),a=this.fbxLoader.parse(r,b.LoaderUtils.extractUrlBase(t));return{scene:a,animations:a.animations}}return this.fbxLoader.loadAsync(t).then(e=>({scene:e,animations:e.animations}));case"obj":if(null!=e.materialLib){const t=new u;t.materialOptions={normalizeRGB:!1};const r=await t.loadAsync(this.getUri(e.materialLib));this.objLoader.setMaterials(r)}return this.objLoader.loadAsync(t).then(e=>(G(e),e)).then(e=>({scene:e,animations:e.animations}))}}disposeAll(){this.cache.clear(),this._retrievedMeshes.forEach(e=>I(e)),this._retrievedTextures.forEach(e=>e.dispose()),this._retrievedMeshes.length=0,this._retrievedTextures.length=0}}const W=new Map;function U(e){W.set(e,(W.get(e)??0)+1)}function z(e){if(e.length<2)return!0;const t=e[0],r=Object.keys(t.attributes).sort(),a=Object.keys(t.morphAttributes).sort();return e.slice(1).every(e=>{if(null==e.index!=(null==t.index))return!1;if(e.morphTargetsRelative!==t.morphTargetsRelative)return!1;const i=Object.keys(e.attributes).sort(),s=Object.keys(e.morphAttributes).sort();if(i.join("|")!==r.join("|"))return!1;if(s.join("|")!==a.join("|"))return!1;for(const a of r){const r=t.getAttribute(a),i=e.getAttribute(a);if(r.itemSize!==i.itemSize||r.normalized!==i.normalized||r.array.constructor!==i.array.constructor)return!1}for(const r of a){const a=t.morphAttributes[r],i=e.morphAttributes[r];if(a.length!==i.length)return!1;for(let e=0;e<a.length;e++)if(a[e].itemSize!==i[e].itemSize||a[e].normalized!==i[e].normalized||a[e].array.constructor!==i[e].array.constructor)return!1}return!0})}function G(e){if(e instanceof n)for(const t of A(e.material))t instanceof b.MeshPhongMaterial&&!t.color.isLinear&&(t.color.isLinear=!0);e.children?.forEach(G)}export class AssetMeshInstance extends o{}export{createAuthoredCollisionShapes}from"./collision/authored-colliders.js";export function getElectronArg(e){const t=`--${e}=`,r=window.process?.argv.find(e=>e.startsWith(t));return r?.substring(t.length)}function $(e){switch(e){case"clamp":return b.ClampToEdgeWrapping;case"repeat":return b.RepeatWrapping;case"mirror":return b.MirroredRepeatWrapping}return b.RepeatWrapping}function E(e,t){const r=e;return!0!==r.isCompressedTexture&&!0!==r.isCompressedArrayTexture&&(t.texture?.flipY??!0)}function O(e){return e.texture?.colorSpace??function(e){if("exr"===e.fileFormat||e.fileKey?.toLowerCase().endsWith(".exr"))return"linear";const t=`${e.name} ${e.fileKey??""}`.toLowerCase();if(q.some(e=>t.includes(e)))return"linear";return"srgb"}(e)}function N(e){return"linear"===e?b.LinearSRGBColorSpace:b.SRGBColorSpace}const q=["_normal","_nrm","_nor","_norm","_roughness","_rough","_metalness","_metallic","_metal","_ao","_ambient","_occlusion","_mask","_height","_disp","_displacement","_bump","_opacity"];new b.Matrix4;const Y=[];const X=/_LOD(\d+)/;export function removeInvalidFaces(e){const t=e.getAttribute("position");if(null==t||t.itemSize<3)return 0;const r=e.index,a=r?.count??t.count,i=Math.floor(a/3),s=[],n=new Uint32Array(i+1),o=new h,l=new h,c=new h;let u=0;for(let e=0;e<i;e++){const a=3*e,i=r?.getX(a)??a,h=r?.getX(a+1)??a+1,m=r?.getX(a+2)??a+2;let d=Number.isInteger(i)&&Number.isInteger(h)&&Number.isInteger(m)&&i>=0&&h>=0&&m>=0&&i<t.count&&h<t.count&&m<t.count;if(d){o.fromBufferAttribute(t,i),l.fromBufferAttribute(t,h).sub(o),c.fromBufferAttribute(t,m).sub(o);const e=Math.max(l.lengthSq(),c.lengthSq(),l.distanceToSquared(c)),r=l.cross(c).lengthSq();d=Number.isFinite(e)&&Number.isFinite(r)&&e>0&&r>Number.EPSILON*e*e}d?s.push(i,h,m):u++,n[e+1]=s.length}if(0===u)return 0;if(e.setIndex(s),e.groups.length>0){const t=e.groups.slice();e.clearGroups();for(const r of t){const t=Math.max(0,Math.ceil(r.start/3)),a=Math.min(i,Math.floor((r.start+r.count)/3));if(a<=t)continue;const s=n[t],o=n[a]-s;o>0&&e.addGroup(s,o,r.materialIndex)}}const m=Math.max(0,Math.ceil(e.drawRange.start/3)),d=Number.isFinite(e.drawRange.count)?Math.min(i,Math.floor((e.drawRange.start+e.drawRange.count)/3)):i,p=Math.min(m,i),f=Math.min(i,Math.max(p,d)),g=n[p];return e.setDrawRange(g,n[f]-g),u}function V(e){let t=e instanceof b.MeshPhysicalMaterial&&0===e.sheen&&0===e.anisotropy;if("MeshLambertMaterial"===e.type||"MeshPhongMaterial"===e.type||t){let t={};(e instanceof b.MeshLambertMaterial||e instanceof b.MeshPhongMaterial||e instanceof b.MeshPhysicalMaterial)&&(t.color=e.color,t.map=e.map,t.bumpMap=e.bumpMap,t.bumpScale=e.bumpScale,t.lightMap=e.lightMap,t.lightMapIntensity=e.lightMapIntensity,t.aoMap=e.aoMap,t.aoMapIntensity=e.aoMapIntensity,t.emissiveMap=e.emissiveMap,t.alphaMap=e.alphaMap,t.envMap=e.envMap,t.envMapRotation=e.envMapRotation,t.wireframe=e.wireframe,t.wireframeLinewidth=e.wireframeLinewidth,t.fog=e.fog,t.opacity=e.opacity,t.transparent=e.transparent,t.side=e.side,t.depthTest=e.depthTest,t.depthWrite=e.depthWrite,t.alphaTest=e.alphaTest,t.displacementMap=e.displacementMap,t.displacementScale=e.displacementScale,t.displacementBias=e.displacementBias);const r=new b.MeshStandardMaterial(t);return r.userData={...e.userData??{}},r.name=e.name,r}return e}/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -1,4 +1,4 @@
1
- import{polyfill as e}from"../utils/polyfill.js";import{SceneDataService as t}from"./scene-data-service.js";import{SceneMaterializer as n}from"./materializer.js";import{basicSceneSetup as i}from"./sky.js";import{NetMode as r}from"../gameplay/net/net-session.js";export async function loadScene(a,s,o,c,m,p,d,l,f,w,j){e();const{scene:y,renderer:h,camera:u}=a,S=new Set(y.children);i(y);const b=await l.getScene(s);if(null==b)throw Error(`Could not find scene with name ${s}`);const g=await l.getSceneData(b.id),v=new t,O=Object.entries(c).map(([e,t])=>({name:e,type:t})),z=Object.entries(m).map(([e,t])=>({name:e,type:t})),E=Object.entries(p).map(([e,t])=>({name:e,type:t})),I=new n(y,v,f,w,a,O,z,d,E);I.inEditor=!1,I.detailTier=j?.detailTier,I.netMode=j?.nodeMode??r.none;const M=j?.persistence?.enabled?await I.preparePersistence(j.persistence,b.id,g):g;return v.initiate(M),await I.initWithInstancing(),{scene:y,view:a,materializer:I,assetResourceLoader:w,assetsService:f,actors:I.actorInstances,sceneRoots:y.children.filter(e=>!S.has(e))}}/*
1
+ import{polyfill as e}from"../utils/polyfill.js";import{SceneDataService as t}from"./scene-data-service.js";import{SceneMaterializer as n}from"./materializer.js";import{basicSceneSetup as i}from"./sky.js";import{NetMode as a}from"../gameplay/net/net-session.js";export async function loadScene(r,s,o,c,m,d,p,l,w,f,j){e();const{scene:g,renderer:y,camera:h}=r,u=new Set(g.children);i(g);const S=await l.getScene(s);if(null==S)throw Error(`Could not find scene with name ${s}`);const v=await l.getSceneData(S.id),b=new t,k=Object.entries(c).map(([e,t])=>({name:e,type:t})),O=Object.entries(m).map(([e,t])=>({name:e,type:t})),z=Object.entries(d).map(([e,t])=>({name:e,type:t})),B=new n(g,b,w,f,r,k,O,p,z);B.inEditor=!1,B.navigationBakes=await(l.getSceneNavigationBakes?.(S.id))??{},B.detailTier=j?.detailTier,B.netMode=j?.nodeMode??a.none;const E=j?.persistence?.enabled?await B.preparePersistence(j.persistence,S.id,v):v;return b.initiate(E),await B.initWithInstancing(),{scene:g,view:r,materializer:B,assetResourceLoader:f,assetsService:w,actors:B.actorInstances,sceneRoots:g.children.filter(e=>!u.has(e))}}/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -0,0 +1,4 @@
1
+ import type { ModelColliderData } from '../model.js';
2
+ import { CollisionShape } from './collision-shape.js';
3
+ export declare function createAuthoredCollisionShapes(colliders: ModelColliderData[], assetRescale?: number): CollisionShape[];
4
+ //# sourceMappingURL=authored-colliders.d.ts.map
@@ -0,0 +1,4 @@
1
+ import{Euler as r,Vector3 as e}from"three";import{BoxCollisionShape as a,CapsuleCollisionShape as o,CollisionShapeSource as t,CylinderCollisionShape as s,SphereCollisionShape as i}from"./collision-shape.js";export function createAuthoredCollisionShapes(n,l=1){const c=null!=l&&0!==l?1/l:1;return n.map(n=>{let l;switch(n.type){case"box":l=new a((new e).fromArray(n.size).multiplyScalar(c));break;case"sphere":l=new i(n.radius*c);break;case"capsule":l=new o(n.length*c,n.radius*c);break;case"cylinder":l=new s(n.radius*c,n.radius*c,n.height*c,16,new r)}return l.source=t.custom,l.offset.fromArray(n.position??[0,0,0]).multiplyScalar(c),l.rotation.fromArray(n.rotation??[0,0,0,"XYZ"]),l})}/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -0,0 +1,7 @@
1
+ import type * as RAPIER from '@dimforge/rapier3d-simd-compat';
2
+ import * as THREE from 'three';
3
+ import { Vector3 } from 'three';
4
+ import { CollisionShape } from './collision-shape.js';
5
+ export declare function createRapierCollisionShape(rapier: typeof RAPIER, shapeInfo: CollisionShape, scale: Vector3): RAPIER.ColliderDesc;
6
+ export declare function extractFloat32Array(attr: THREE.BufferAttribute | THREE.InterleavedBufferAttribute): Float32Array;
7
+ //# sourceMappingURL=rapier-shape.d.ts.map
@@ -0,0 +1,4 @@
1
+ import*as e from"three";import{Vector3 as t}from"three";import*as o from"three/examples/jsm/utils/BufferGeometryUtils.js";import{calculateEffectiveScale as r}from"../../utils/three/traverse.js";import{BoxCollisionShape as n,CapsuleCollisionShape as i,ConeCollisionShape as s,ConvexPolyhedronCollisionShape as l,CylinderCollisionShape as a,PlaneCollisionShape as c,SphereCollisionShape as m,TrimeshCollisionShape as u}from"./collision-shape.js";const f=new WeakMap;export function createRapierCollisionShape(h,x,y){if(x instanceof n)return h.ColliderDesc.cuboid(x.dimensions.x*y.x/2,x.dimensions.y*y.y/2,x.dimensions.z*y.z/2);if(x instanceof i){return h.ColliderDesc.capsule(x.length/2*y.y,x.radius*Math.max(y.z,y.x))}if(x instanceof u){const e=null!=x.geometry.getIndex()?x.geometry:o.mergeVertices(x.geometry),t=extractFloat32Array(e.getAttribute("position"));for(let e=0;e<t.length;e+=3)t[e]*=y.x,t[e+1]*=y.y,t[e+2]*=y.z;const r=e.getIndex();return null==r?(console.error("Trimesh collision shape is missing an index buffer.",x),null):h.ColliderDesc.trimesh(t,new Uint32Array(r.array))}if(x instanceof l){let e;const t=x.mesh;if(t.isMesh)e=t.geometry;else{if(!x.mesh.isBufferGeometry)throw new Error("Unsupported convex collision geometry");e=x.mesh}let o;t.isMesh&&(o=r(t));const n=`${o?.x??1},${o?.y??1},${o?.z??1}|${y.x},${y.y},${y.z}`;let i=f.get(e);i||f.set(e,i=new Map);const s=i.get(n);if(void 0!==s)return s;const l=extractFloat32Array(e.getAttribute("position"));if(null!=o)for(let e=0;e<l.length;e+=3)l[e]*=o.x,l[e+1]*=o.y,l[e+2]*=o.z;for(let e=0;e<l.length;e+=3)l[e]*=y.x,l[e+1]*=y.y,l[e+2]*=y.z;const a=h.ColliderDesc.convexHull(l);return null==a&&console.error("Failed to compute convex hull. Points may be coplanar or too few.",{count:l.length/3}),i.set(n,a),a}if(x instanceof m){const o=2*y.x-y.y-y.z;return Math.abs(o)>.01?createRapierCollisionShape(h,new l(new e.SphereGeometry(x.radius).scale(y.x,y.y,y.z)),new t(1,1,1)):h.ColliderDesc.ball(x.radius*Math.max(y.x,y.y,y.z))}return x instanceof a?h.ColliderDesc.cylinder(x.height/2*y.y,x.radiusTop*Math.max(y.z,y.x)):x instanceof s?h.ColliderDesc.cone(x.height*y.y,x.radiusBottom/2*Math.max(y.z,y.x)):x instanceof c?h.ColliderDesc.cuboid(x.width/2*y.x,x.height/2*y.y,.001):(console.error("Unsupported shape",x),h.ColliderDesc.cuboid(1,1,1))}export function extractFloat32Array(e){const t=e.itemSize,o=e.count,r=new Float32Array(o*t);for(let n=0;n<o;n++)for(let o=0;o<t;o++)r[n*t+o]=e.getComponent(n,o);return r}/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -361,6 +361,7 @@ export declare class SceneMaterializer {
361
361
  private actorTypes;
362
362
  private actorProvider;
363
363
  private componentTypes;
364
+ private navigationRevision;
364
365
  private objectMap;
365
366
  private sceneObjectMap;
366
367
  private updateSubscription;
@@ -458,11 +459,13 @@ export declare class SceneMaterializer {
458
459
  private canUseEditorStaticDrawSet;
459
460
  private disposeEditorStaticDrawSet;
460
461
  private disposeEditorStaticDrawChunk;
462
+ private readonly vfxPreloads;
461
463
  /**
462
464
  * Materialize visual effects to upload textures
463
465
  * and compile shaders before they are first used.
464
466
  */
465
467
  preloadVisualEffects(visualEffects: Iterable<VisualEffect>): Promise<void>;
468
+ private preloadVisualEffect;
466
469
  private initVfxTextures;
467
470
  private initVfx;
468
471
  /**