@hology/core 0.0.264 → 0.0.266

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 (95) hide show
  1. package/dist/effects/sequence/sequence-player.js +1 -1
  2. package/dist/effects/vfx/gpu-material-uniforms.d.ts +23 -0
  3. package/dist/effects/vfx/gpu-material-uniforms.js +4 -0
  4. package/dist/effects/vfx/gpu-sprite-runtime.js +1 -1
  5. package/dist/effects/vfx/vfx-actor.d.ts +6 -0
  6. package/dist/effects/vfx/vfx-actor.js +1 -1
  7. package/dist/effects/vfx/vfx-materializer.d.ts +1 -0
  8. package/dist/effects/vfx/vfx-materializer.js +1 -1
  9. package/dist/effects/vfx/vfx-param.js +1 -1
  10. package/dist/effects/vfx/vfx-render-preparation.d.ts +8 -0
  11. package/dist/effects/vfx/vfx-render-preparation.js +4 -0
  12. package/dist/gameplay/actors/builtin/components/character/character-animation.d.ts +53 -0
  13. package/dist/gameplay/actors/builtin/components/character/character-animation.js +1 -1
  14. package/dist/gameplay/actors/builtin/components/character/character-movement.js +1 -1
  15. package/dist/gameplay/ai/behavior-tree/behavior-tree.d.ts +33 -0
  16. package/dist/gameplay/ai/behavior-tree/behavior-tree.js +4 -0
  17. package/dist/gameplay/ai/behavior-tree/behavior-tree.test.d.ts +2 -0
  18. package/dist/gameplay/ai/behavior-tree/behavior-tree.test.js +4 -0
  19. package/dist/gameplay/ai/behavior-tree/bt.d.ts +92 -70
  20. package/dist/gameplay/ai/behavior-tree/bt.js +1 -1
  21. package/dist/gameplay/ai/behavior-tree/bt.test.d.ts +2 -0
  22. package/dist/gameplay/ai/behavior-tree/bt.test.js +4 -0
  23. package/dist/gameplay/ai/behavior-tree/move.d.ts +1 -0
  24. package/dist/gameplay/ai/behavior-tree/move.js +1 -1
  25. package/dist/gameplay/ai/index.d.ts +1 -0
  26. package/dist/gameplay/ai/index.js +1 -1
  27. package/dist/gameplay/animation/foot-contact-baker.d.ts +23 -0
  28. package/dist/gameplay/animation/foot-contact-baker.js +4 -0
  29. package/dist/gameplay/animation/foot-contact.d.ts +27 -0
  30. package/dist/gameplay/animation/foot-contact.js +4 -0
  31. package/dist/gameplay/animation/locomotion-body.d.ts +62 -0
  32. package/dist/gameplay/animation/locomotion-body.js +4 -0
  33. package/dist/gameplay/animation/pose-inertialization.d.ts +46 -0
  34. package/dist/gameplay/animation/pose-inertialization.js +4 -0
  35. package/dist/gameplay/animation/root-motion.js +1 -1
  36. package/dist/gameplay/index.d.ts +4 -0
  37. package/dist/gameplay/index.js +1 -1
  38. package/dist/gameplay/initiate.js +1 -1
  39. package/dist/gameplay/input/input-service.d.ts +4 -0
  40. package/dist/gameplay/input/input-service.js +1 -1
  41. package/dist/gameplay/rendering-host.d.ts +2 -1
  42. package/dist/gameplay/rendering-host.js +1 -1
  43. package/dist/gameplay/services/physics/physics-system.d.ts +7 -0
  44. package/dist/gameplay/services/physics/physics-system.js +1 -1
  45. package/dist/gameplay/services/render.d.ts +2 -0
  46. package/dist/gameplay/services/render.js +1 -1
  47. package/dist/rendering/webgpu-experimental-backend.d.ts +1 -1
  48. package/dist/rendering/webgpu-experimental-backend.js +1 -1
  49. package/dist/rendering.d.ts +4 -2
  50. package/dist/rendering.js +1 -1
  51. package/dist/scene/animation-binary.d.ts +6 -0
  52. package/dist/scene/animation-binary.js +4 -0
  53. package/dist/scene/asset-resource-loader.d.ts +3 -1
  54. package/dist/scene/asset-resource-loader.js +1 -1
  55. package/dist/scene/landscape/landscape-manager.d.ts +4 -1
  56. package/dist/scene/landscape/landscape-manager.js +1 -1
  57. package/dist/scene/landscape/landscape-static-draw-sets.d.ts +18 -0
  58. package/dist/scene/landscape/landscape-static-draw-sets.js +4 -0
  59. package/dist/scene/materializer.d.ts +2 -0
  60. package/dist/scene/materializer.js +1 -1
  61. package/dist/shader/shader.d.ts +8 -0
  62. package/dist/test/animation-binary.test.d.ts +2 -0
  63. package/dist/test/animation-binary.test.js +4 -0
  64. package/dist/test/character-foot-contact.test.d.ts +2 -0
  65. package/dist/test/character-foot-contact.test.js +4 -0
  66. package/dist/test/character-inertialization.test.d.ts +2 -0
  67. package/dist/test/character-inertialization.test.js +4 -0
  68. package/dist/test/character-movement-solve.test.d.ts +2 -0
  69. package/dist/test/character-movement-solve.test.js +4 -0
  70. package/dist/test/foot-contact.test.d.ts +2 -0
  71. package/dist/test/foot-contact.test.js +4 -0
  72. package/dist/test/gpu-material-uniforms.test.d.ts +2 -0
  73. package/dist/test/gpu-material-uniforms.test.js +4 -0
  74. package/dist/test/gpu-sprite-pipeline-cache.test.d.ts +2 -0
  75. package/dist/test/gpu-sprite-pipeline-cache.test.js +4 -0
  76. package/dist/test/input.test.js +1 -1
  77. package/dist/test/landscape-static-draw-sets.test.d.ts +2 -0
  78. package/dist/test/landscape-static-draw-sets.test.js +4 -0
  79. package/dist/test/locomotion-body.test.d.ts +2 -0
  80. package/dist/test/locomotion-body.test.js +4 -0
  81. package/dist/test/physics-ray-object.test.d.ts +2 -0
  82. package/dist/test/physics-ray-object.test.js +4 -0
  83. package/dist/test/pose-inertialization.test.d.ts +2 -0
  84. package/dist/test/pose-inertialization.test.js +4 -0
  85. package/dist/test/renderer-loading.test.d.ts +2 -0
  86. package/dist/test/renderer-loading.test.js +4 -0
  87. package/dist/test/rendering-host.test.js +1 -1
  88. package/dist/test/sequence-property-parameters.test.js +1 -1
  89. package/dist/test/sequence-vfx.test.js +1 -1
  90. package/dist/test/vfx-render-preparation.test.d.ts +2 -0
  91. package/dist/test/vfx-render-preparation.test.js +4 -0
  92. package/dist/utils/structs.d.ts +4 -0
  93. package/dist/utils/structs.js +4 -0
  94. package/package.json +4 -2
  95. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,4 @@
1
+ import{AnimationClip as t,AnimationMixer as r,LoopOnce as o,Vector3 as i}from"three";import{clone as e}from"three/addons/utils/SkeletonUtils.js";export class FootContactBaker{constructor(t,o,n){this.point=new i,this.root=e(t),this.left=this.root.getObjectByName(o),this.right=this.root.getObjectByName(n),this.mixer=new r(this.root)}bake(r,i={}){if(null==this.left||null==this.right||!(r.duration>0))return;const e=Math.max(8,Math.ceil(r.duration*(i.samplesPerSecond??60))),a=r.duration/e,s=[new Float32Array(e),new Float32Array(e)],h=null!=i.rootMotionTrack?[...r.tracks.filter(t=>t.name!==i.rootMotionTrack.name),i.rootMotionTrack]:r.tracks,l=new t(r.name,r.duration,h),m=this.mixer.clipAction(l);m.setLoop(o,1).play();try{for(let t=0;t<e;t++)this.mixer.setTime(t*a),this.root.updateMatrixWorld(!0),s[0][t]=this.point.setFromMatrixPosition(this.left.matrixWorld).y,s[1][t]=this.point.setFromMatrixPosition(this.right.matrixWorld).y;return{left:n(s[0],a,i),right:n(s[1],a,i)}}finally{m.stop(),this.mixer.uncacheClip(l)}}}function n(t,r,o){let i=1/0;for(const r of t)i=Math.min(i,r);const e=Math.max(0,o.heightThreshold??.045),n=Math.max(0,o.maxVerticalSpeed??.5),a=t.length,s=new Uint8Array(a);let h=0;for(let o=0;o<a;o++){const l=t[(o+a-1)%a],m=t[(o+1)%a],c=Math.abs(m-l)/(2*r);s[o]=t[o]<=i+e&&c<=n?1:0,h+=s[o]}if(h===a)return[{start:0,end:1}];if(0===h)return[];const l=s.indexOf(0),m=[],c=Math.max(2,Math.ceil((o.minimumContactTime??.05)/r));let u=-1;for(let t=1;t<=a;t++){const r=(l+t)%a;0!==s[r]?u<0&&(u=t):u>=0&&(t-u>=c&&m.push({start:(l+u)%a/a,end:r/a}),u=-1)}return m}/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -0,0 +1,27 @@
1
+ import { Vector3 } from 'three';
2
+ import type { AnimationClip } from 'three';
3
+ /** Normalized clip phases. start > end denotes a contact crossing the loop seam. */
4
+ export type FootContactInterval = {
5
+ start: number;
6
+ end: number;
7
+ };
8
+ export type AnimationFootContacts = {
9
+ left: readonly FootContactInterval[];
10
+ right: readonly FootContactInterval[];
11
+ };
12
+ export declare function getAnimationFootContacts(clip: AnimationClip | undefined): AnimationFootContacts | undefined;
13
+ /** Can also be used to replace generated contacts with authored phase intervals. */
14
+ export declare function setAnimationFootContacts(clip: AnimationClip, contacts: AnimationFootContacts | undefined): void;
15
+ export declare function isFootInContact(intervals: readonly FootContactInterval[], time: number, duration: number, looping: boolean): boolean;
16
+ /** Stateful stance anchor with bounded release. All inputs are world-space. */
17
+ export declare class FootContactAnchor {
18
+ readonly position: Vector3;
19
+ readonly forward: Vector3;
20
+ locked: boolean;
21
+ private blocked;
22
+ reset(): void;
23
+ /** Prevent recapture until the next swing, instead of repeatedly tugging an unreachable foot. */
24
+ release(): void;
25
+ update(contact: boolean, hasGround: boolean, animatedFoot: Vector3, groundTarget: Vector3, facing: Vector3, hip: Vector3, maxReach: number, maxPelvisDrop: number, maxDrift: number, maxTurnAngle: number): boolean;
26
+ }
27
+ //# sourceMappingURL=foot-contact.d.ts.map
@@ -0,0 +1,4 @@
1
+ import{MathUtils as t,Vector3 as e}from"three";const o=new WeakMap;export function getAnimationFootContacts(t){return null==t?void 0:o.get(t)}export function setAnimationFootContacts(t,e){if(null!=e){for(const t of["left","right"])for(const o of e[t])if(!Number.isFinite(o.start)||!Number.isFinite(o.end)||o.start<0||o.start>1||o.end<0||o.end>1)throw new Error("Foot contact intervals must use phases between 0 and 1");o.set(t,e)}else o.delete(t)}export function isFootInContact(e,o,i,s){if(!(i>0&&Number.isFinite(o)))return!1;const r=s?t.euclideanModulo(o/i,1):t.clamp(o/i,0,1);for(const t of e){if(0===t.start&&1===t.end)return!0;if(t.start<t.end){if(r>=t.start&&r<t.end)return!0}else if(t.start>t.end&&(r>=t.start||r<t.end))return!0}return!1}export class FootContactAnchor{constructor(){this.position=new e,this.forward=new e(0,0,1),this.locked=!1,this.blocked=!1}reset(){this.locked=!1,this.blocked=!1}release(){this.locked=!1,this.blocked=!0}update(t,e,o,i,s,r,n,a,c,h){if(!t)return this.reset(),!1;if(!e)return this.release(),!1;if(this.blocked)return!1;this.locked||(this.position.copy(i),this.forward.copy(s).setY(0).normalize(),this.locked=!0);const d=o.x-this.position.x,l=o.z-this.position.z,f=r.x-this.position.x,u=r.z-this.position.z,p=Math.max(0,r.y-this.position.y-Math.max(0,a)),x=Math.sqrt(s.x*s.x+s.z*s.z),m=x>1e-6?(s.x*this.forward.x+s.z*this.forward.z)/x:1;return(d*d+l*l>c*c||f*f+p*p+u*u>n*n||m<Math.cos(h))&&this.release(),this.locked}}/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -0,0 +1,62 @@
1
+ import { Vector3 } from 'three';
2
+ import type { Bone, Object3D } from 'three';
3
+ /** Visual response only: this never changes the character controller or root motion. */
4
+ export declare class LocomotionBodyResponse {
5
+ maxLean: number;
6
+ maxPitch: number;
7
+ maxTwist: number;
8
+ /** Reference acceleration for art-directed lean, in world units/s². */
9
+ referenceAcceleration: number;
10
+ accelerationHalfLife: number;
11
+ /** Approximate time to settle 95% of a procedural offset. */
12
+ settleTime: number;
13
+ turnLag: number;
14
+ resetDistance: number;
15
+ maxDeltaTime: number;
16
+ readonly acceleration: Vector3;
17
+ private readonly previousVelocity;
18
+ private readonly previousPosition;
19
+ private readonly pitchSpring;
20
+ private readonly rollSpring;
21
+ private readonly yawSpring;
22
+ private previousHeading;
23
+ private yawRate;
24
+ private initialized;
25
+ get pitch(): number;
26
+ get roll(): number;
27
+ get yaw(): number;
28
+ reset(): void;
29
+ /**
30
+ * Inputs are in world space. Forward is the character's horizontal facing,
31
+ * independent of velocity (so this also supports strafing).
32
+ * Inactive states discard momentum; zero time holds it for hit stop/pause.
33
+ */
34
+ update(deltaTime: number, position: Vector3, velocity: Vector3, forward: Vector3, active?: boolean): void;
35
+ }
36
+ export type LocomotionBodyBone = {
37
+ bone: Bone;
38
+ /** Contribution to body pitch/roll; negative values provide counter-rotation. */
39
+ leanWeight: number;
40
+ /** Contribution to yaw lag. */
41
+ twistWeight: number;
42
+ };
43
+ /**
44
+ * Applies offsets in a character presentation frame (+Y up, +Z forward),
45
+ * regardless of bone-local bind axes. Bind once, restore before mixer evaluation,
46
+ * then apply before IK. Only rotations are owned; animated translations survive.
47
+ */
48
+ export declare class LocomotionBodyPose {
49
+ private readonly bones;
50
+ private readonly parentRotation;
51
+ private readonly inverseParentRotation;
52
+ private readonly offset;
53
+ private readonly tempRotation;
54
+ private readonly right;
55
+ private readonly up;
56
+ private readonly forward;
57
+ private applied;
58
+ constructor(space: Object3D, bones: readonly LocomotionBodyBone[]);
59
+ restore(): void;
60
+ apply(response: LocomotionBodyResponse, weight?: number): void;
61
+ }
62
+ //# sourceMappingURL=locomotion-body.d.ts.map
@@ -0,0 +1,4 @@
1
+ import{MathUtils as t,Quaternion as i,Vector3 as e}from"three";export class LocomotionBodyResponse{constructor(){this.maxLean=t.degToRad(12),this.maxPitch=t.degToRad(8),this.maxTwist=t.degToRad(6),this.referenceAcceleration=12,this.accelerationHalfLife=.07,this.settleTime=.22,this.turnLag=.025,this.resetDistance=1,this.maxDeltaTime=.2,this.acceleration=new e,this.previousVelocity=new e,this.previousPosition=new e,this.pitchSpring=new s,this.rollSpring=new s,this.yawSpring=new s,this.previousHeading=0,this.yawRate=0,this.initialized=!1}get pitch(){return this.pitchSpring.value}get roll(){return this.rollSpring.value}get yaw(){return this.yawSpring.value}reset(){this.initialized=!1,this.acceleration.set(0,0,0),this.yawRate=0,this.pitchSpring.reset(),this.rollSpring.reset(),this.yawSpring.reset()}update(i,e,s,a,o=!0){if(!o||!Number.isFinite(i)||i<0)return void this.reset();if(0===i)return;const h=Math.atan2(a.x,a.z),n=Math.sqrt(s.x*s.x+s.z*s.z),r=this.resetDistance+n*i;if(!this.initialized||i>this.maxDeltaTime||this.previousPosition.distanceToSquared(e)>r*r)return this.reset(),this.previousVelocity.copy(s).setY(0),this.previousPosition.copy(e),this.previousHeading=h,void(this.initialized=!0);const l=1-Math.exp(-Math.LN2*i/Math.max(1e-4,this.accelerationHalfLife));this.acceleration.x+=((s.x-this.previousVelocity.x)/i-this.acceleration.x)*l,this.acceleration.z+=((s.z-this.previousVelocity.z)/i-this.acceleration.z)*l;const p=Math.atan2(Math.sin(h-this.previousHeading),Math.cos(h-this.previousHeading));this.yawRate+=(p/i-this.yawRate)*l,this.previousHeading=h,this.previousVelocity.copy(s).setY(0),this.previousPosition.copy(e);const c=Math.sqrt(a.x*a.x+a.z*a.z),u=c>1e-6?a.x/c:0,m=c>1e-6?a.z/c:1,y=this.acceleration.x*m-this.acceleration.z*u,v=this.acceleration.x*u+this.acceleration.z*m,x=Math.max(1e-4,this.referenceAcceleration),d=t.clamp(-Math.atan2(y,x),-this.maxLean,this.maxLean),f=t.clamp(.5*Math.atan2(v,x),-this.maxPitch,this.maxPitch),g=t.clamp(-this.yawRate*this.turnLag*Math.min(1,n),-this.maxTwist,this.maxTwist);this.rollSpring.update(d,this.settleTime,i,this.maxLean),this.pitchSpring.update(f,this.settleTime,i,this.maxPitch),this.yawSpring.update(g,this.settleTime,i,this.maxTwist)}}class s{constructor(){this.value=0,this.velocity=0}reset(){this.value=0,this.velocity=0}update(i,e,s,a){const o=4.75/Math.max(1e-4,e),h=this.value-i,n=this.velocity+h*o,r=Math.exp(-o*s);this.value=i+(h+n*s)*r,this.velocity=(this.velocity-n*o*s)*r;const l=Math.max(0,a);Math.abs(this.value)>l&&(this.value=t.clamp(this.value,-l,l),this.value*this.velocity>0&&(this.velocity=0))}}export class LocomotionBodyPose{constructor(t,s){this.parentRotation=new i,this.inverseParentRotation=new i,this.offset=new i,this.tempRotation=new i,this.right=new e(1,0,0),this.up=new e(0,1,0),this.forward=new e(0,0,1),this.applied=!1,this.bones=s.map(e=>{const s=[];let a=e.bone.parent;for(;null!=a&&a!==t;)s.push(a),a=a.parent;if(a!==t)throw new Error("Locomotion body bone must descend from its presentation frame");return{...e,baseQuaternion:new i,parentPath:s.reverse()}}),this.bones.sort((t,i)=>t.parentPath.length-i.parentPath.length)}restore(){if(this.applied){for(const t of this.bones)t.bone.quaternion.copy(t.baseQuaternion);this.applied=!1}}apply(i,e=1){this.restore();const s=t.clamp(e,0,1);if(0!==s&&(0!==i.pitch||0!==i.roll||0!==i.yaw)){for(const t of this.bones){t.baseQuaternion.copy(t.bone.quaternion),this.parentRotation.identity();for(const i of t.parentPath)this.parentRotation.multiply(i.quaternion);this.inverseParentRotation.copy(this.parentRotation).invert(),this.offset.setFromAxisAngle(this.up,i.yaw*t.twistWeight*s),this.tempRotation.setFromAxisAngle(this.right,i.pitch*t.leanWeight*s),this.offset.multiply(this.tempRotation),this.tempRotation.setFromAxisAngle(this.forward,i.roll*t.leanWeight*s),this.offset.multiply(this.tempRotation),this.offset.premultiply(this.inverseParentRotation).multiply(this.parentRotation),t.bone.quaternion.premultiply(this.offset).normalize()}this.applied=!0}}}/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -0,0 +1,46 @@
1
+ import type { AnimationClip, Bone } from 'three';
2
+ export type InertializationBone = {
3
+ bone: Bone;
4
+ /** Seconds until the rotation correction reaches exactly zero. */
5
+ duration: number;
6
+ /** Maximum rotation correction in radians. */
7
+ maxRotation: number;
8
+ /** Maximum outgoing angular speed to preserve, in radians/second. */
9
+ maxAngularSpeed?: number;
10
+ /** Optional translation correction, in bone-parent space units. */
11
+ maxPosition?: number;
12
+ };
13
+ /**
14
+ * Transition-only pose corrections. Restore before the mixer, update after it
15
+ * and before body response/IK. History contains this stage's output, never IK.
16
+ */
17
+ export declare class PoseInertializer {
18
+ private readonly bones;
19
+ private readonly samplers;
20
+ private readonly quaternion;
21
+ private readonly targetQuaternion;
22
+ private readonly targetPosition;
23
+ private readonly vector;
24
+ private readonly cross;
25
+ private readonly crossCross;
26
+ private elapsed;
27
+ private transitionDuration;
28
+ private samples;
29
+ private applied;
30
+ constructor(bones: readonly InertializationBone[]);
31
+ get isTransitioning(): boolean;
32
+ get hasHistory(): boolean;
33
+ /** Clears history/corrections, retaining the pending before-mixer restoration. */
34
+ reset(): void;
35
+ restore(): void;
36
+ /**
37
+ * Prepare before the mixer switches actions. Incoming velocity is sampled
38
+ * using independent track interpolants, without ticking/seeking the live mixer.
39
+ */
40
+ transitionTo(clip: AnimationClip, time?: number, timeScale?: number, looping?: boolean): boolean;
41
+ update(deltaTime: number): void;
42
+ private readonly sampleQuaternion;
43
+ private quaternionFromSample;
44
+ private getSamplers;
45
+ }
46
+ //# sourceMappingURL=pose-inertialization.d.ts.map
@@ -0,0 +1,4 @@
1
+ import{MathUtils as t,PropertyBinding as o,Quaternion as i,Vector3 as e}from"three";export class PoseInertializer{constructor(t){this.samplers=new WeakMap,this.quaternion=new i,this.targetQuaternion=new i,this.targetPosition=new e,this.vector=new e,this.cross=new e,this.crossCross=new e,this.elapsed=1/0,this.transitionDuration=0,this.samples=0,this.applied=!1,this.sampleQuaternion=new i,this.bones=t.map(t=>({profile:t,bone:t.bone,baseQuaternion:new i,basePosition:new e,outputQuaternion:new i,outputPosition:new e,angularVelocity:new e,linearVelocity:new e,rotationOffset:new e,rotationVelocity:new e,positionOffset:new e,positionVelocity:new e,positionApplied:!1}))}get isTransitioning(){return this.elapsed<this.transitionDuration}get hasHistory(){return this.samples>=2}reset(){this.samples=0,this.elapsed=1/0,this.transitionDuration=0;for(const t of this.bones)t.angularVelocity.set(0,0,0),t.linearVelocity.set(0,0,0)}restore(){if(this.applied){for(const t of this.bones)t.bone.quaternion.copy(t.baseQuaternion),t.positionApplied&&t.bone.position.copy(t.basePosition);this.applied=!1}}transitionTo(t,o=0,i=1,e=!0){if(!this.hasHistory||!(t.duration>0)||!Number.isFinite(i)||i<0)return!1;const a=this.getSamplers(t),l=1/240,p=n(o,t.duration,e),u=n(o+l*i,t.duration,e);this.transitionDuration=0;for(let t=0;t<this.bones.length;t++){const o=this.bones[t],i=a[t];if(o.rotationOffset.set(0,0,0),o.rotationVelocity.set(0,0,0),o.positionOffset.set(0,0,0),o.positionVelocity.set(0,0,0),null!=i.rotation&&o.profile.duration>0){this.targetQuaternion.fromArray(i.rotation.evaluate(p)).normalize(),this.quaternion.copy(this.targetQuaternion).invert().premultiply(o.outputQuaternion),s(this.quaternion,o.rotationOffset),o.rotationOffset.clampLength(0,Math.max(0,o.profile.maxRotation)),r(o.rotationOffset,this.quaternion),this.targetQuaternion.invert().premultiply(this.quaternionFromSample(i.rotation,u)),s(this.targetQuaternion,this.vector),this.vector.multiplyScalar(240).applyQuaternion(this.quaternion),o.rotationVelocity.copy(o.angularVelocity).sub(this.vector);const t=o.rotationOffset.lengthSq(),e=Math.sqrt(t),n=t<1e-6?1/12+t/720:(1-e/2/Math.tan(e/2))/t;this.cross.crossVectors(o.rotationOffset,o.rotationVelocity),this.crossCross.crossVectors(o.rotationOffset,this.cross),o.rotationVelocity.addScaledVector(this.cross,-.5).addScaledVector(this.crossCross,n),this.transitionDuration=Math.max(this.transitionDuration,o.profile.duration)}null!=i.position&&(o.profile.maxPosition??0)>0&&o.profile.duration>0&&(this.targetPosition.fromArray(i.position.evaluate(p)),o.positionOffset.subVectors(o.outputPosition,this.targetPosition),o.positionOffset.clampLength(0,o.profile.maxPosition),this.vector.fromArray(i.position.evaluate(u)).sub(this.targetPosition).multiplyScalar(240),o.positionVelocity.copy(o.linearVelocity).sub(this.vector),this.transitionDuration=Math.max(this.transitionDuration,o.profile.duration))}return this.elapsed=0,this.transitionDuration>0}update(t){if(!Number.isFinite(t)||t<0||t>.2)return void this.reset();t>0&&(this.elapsed+=t);let o=!1;for(const i of this.bones){if(i.baseQuaternion.copy(i.bone.quaternion),i.basePosition.copy(i.bone.position),i.positionApplied=!1,this.elapsed<i.profile.duration){const t=this.elapsed/i.profile.duration,e=t*t*t,n=e*t,s=n*t,a=1-10*e+15*n-6*s,l=(t-6*e+8*n-3*s)*i.profile.duration;this.vector.copy(i.rotationOffset).multiplyScalar(a).addScaledVector(i.rotationVelocity,l),this.vector.clampLength(0,Math.max(0,i.profile.maxRotation)),r(this.vector,this.quaternion),i.bone.quaternion.premultiply(this.quaternion).normalize(),(i.profile.maxPosition??0)>0&&(this.vector.copy(i.positionOffset).multiplyScalar(a).addScaledVector(i.positionVelocity,l),this.vector.clampLength(0,i.profile.maxPosition),i.bone.position.add(this.vector),i.positionApplied=!0),o=!0}t>0&&(this.samples>0&&(this.quaternion.copy(i.outputQuaternion).invert().premultiply(i.bone.quaternion),s(this.quaternion,i.angularVelocity),i.angularVelocity.multiplyScalar(1/t).clampLength(0,i.profile.maxAngularSpeed??12),i.linearVelocity.subVectors(i.bone.position,i.outputPosition).divideScalar(t)),i.outputQuaternion.copy(i.bone.quaternion),i.outputPosition.copy(i.bone.position))}this.applied=o,t>0&&(this.samples=Math.min(2,this.samples+1))}quaternionFromSample(t,o){return this.sampleQuaternion.fromArray(t.evaluate(o)).normalize()}getSamplers(t){const i=this.samplers.get(t);if(null!=i)return i;const e=this.bones.map(()=>({})),n=new Map(this.bones.map((t,o)=>[t.bone.name,o]));for(const i of t.tracks){const t=o.parseTrackName(i.name),s="bones"===t.objectName?t.objectIndex:t.nodeName,r=null!=s?n.get(s):void 0;null!=r&&("quaternion"===t.propertyName?e[r].rotation=i.createInterpolant():"position"===t.propertyName&&(e[r].position=i.createInterpolant()))}return this.samplers.set(t,e),e}}function n(o,i,e){return e?t.euclideanModulo(o,i):t.clamp(o,0,i)}function s(t,o){const i=t.w<0?-1:1,e=Math.sqrt(t.x**2+t.y**2+t.z**2),n=e<1e-8?2*i:2*Math.atan2(e,i*t.w)*i/e;return o.set(t.x*n,t.y*n,t.z*n)}function r(t,o){const i=t.length(),e=i<1e-8?.5:Math.sin(i/2)/i;return o.set(t.x*e,t.y*e,t.z*e,Math.cos(i/2)).normalize()}/*
2
+ * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
+ * See the LICENSE.md file for details.
4
+ */
@@ -1,4 +1,4 @@
1
- import{AnimationClip as t,Vector3 as o,Quaternion as e,Matrix3 as n,Matrix4 as r}from"three";import{getAnimationClipRigId as i,setAnimationClipRigId as a}from"./retarget.js";export class RootMotionClip extends t{constructor(){super(...arguments),this.displacement=0,this.fixedInPlace=!1}static fromClip(t,s=!1,l,c){const u=new RootMotionClip(t.name,t.duration,t.tracks.slice(),t.blendMode);if(u.fixedInPlace=s,u.rootBone=l,u.animationRoot=c,u.source=t,u.uuid=t.uuid,a(u,i(t)),u.motionTrack=null!=l?t.tracks.find(t=>t.name===`${l.name}.position`):t.tracks.find(t=>t.name.endsWith(".position")),u.motionTrack){u.tracks.splice(u.tracks.indexOf(u.motionTrack),1);const t=(new o).fromArray(u.motionTrack.values,0),i=(new o).fromArray(u.motionTrack.values,u.motionTrack.values.length-3),a=new o(0,1,0);if(null!=l?.parent){l.parent.updateWorldMatrix(!0,!1);const t=l.parent.getWorldQuaternion(new e);a.applyQuaternion(t.invert()).normalize()}const s=u.motionTrack.clone(),d=new o;for(let t=0;t<u.motionTrack.values.length;t+=3){d.fromArray(u.motionTrack.values,t);const o=d.dot(a);d.copy(a).multiplyScalar(o),s.values[t+0]=d.x,s.values[t+1]=d.y,s.values[t+2]=d.z}u.tracks.push(s);const m=i.sub(t);if(m.addScaledVector(a,-m.dot(a)),null!=l?.parent&&null!=c){c.updateWorldMatrix(!0,!1),l.parent.updateWorldMatrix(!0,!1);const t=(new r).copy(c.matrixWorld).invert().multiply(l.parent.matrixWorld);m.applyMatrix3((new n).setFromMatrix4(t))}u.displacement=m.length()}else console.error("Could not find root motion track",t,l);return u}static fromClipWithDistance(t,o){const e=new RootMotionClip(t.name,t.duration,t.tracks.slice(),t.blendMode);return e.fixedInPlace=!0,e.source=t,e.uuid=t.uuid,a(e,i(t)),e.displacement=o,e}clone(){const t=RootMotionClip.fromClip(this.source.clone(),this.fixedInPlace,this.rootBone,this.animationRoot);return t.displacement=this.displacement,t}}let s=53912381;export class BoneLayer{constructor(){this.uuid=s++,this.order=0,this.boneMask=[]}}/*
1
+ import{AnimationClip as t,Vector3 as o,Quaternion as e,Matrix3 as n,Matrix4 as r}from"three";import{getAnimationClipRigId as i,setAnimationClipRigId as a}from"./retarget.js";import{getAnimationFootContacts as s,setAnimationFootContacts as l}from"./foot-contact.js";export class RootMotionClip extends t{constructor(){super(...arguments),this.displacement=0,this.fixedInPlace=!1}static fromClip(t,c=!1,u,d){const m=new RootMotionClip(t.name,t.duration,t.tracks.slice(),t.blendMode);if(m.fixedInPlace=c,m.rootBone=u,m.animationRoot=d,m.source=t,m.uuid=t.uuid,a(m,i(t)),l(m,s(t)),m.motionTrack=null!=u?t.tracks.find(t=>t.name===`${u.name}.position`):t.tracks.find(t=>t.name.endsWith(".position")),m.motionTrack){m.tracks.splice(m.tracks.indexOf(m.motionTrack),1);const t=(new o).fromArray(m.motionTrack.values,0),i=(new o).fromArray(m.motionTrack.values,m.motionTrack.values.length-3),a=new o(0,1,0);if(null!=u?.parent){u.parent.updateWorldMatrix(!0,!1);const t=u.parent.getWorldQuaternion(new e);a.applyQuaternion(t.invert()).normalize()}const s=m.motionTrack.clone(),l=new o;for(let t=0;t<m.motionTrack.values.length;t+=3){l.fromArray(m.motionTrack.values,t);const o=l.dot(a);l.copy(a).multiplyScalar(o),s.values[t+0]=l.x,s.values[t+1]=l.y,s.values[t+2]=l.z}m.tracks.push(s);const c=i.sub(t);if(c.addScaledVector(a,-c.dot(a)),null!=u?.parent&&null!=d){d.updateWorldMatrix(!0,!1),u.parent.updateWorldMatrix(!0,!1);const t=(new r).copy(d.matrixWorld).invert().multiply(u.parent.matrixWorld);c.applyMatrix3((new n).setFromMatrix4(t))}m.displacement=c.length()}else console.error("Could not find root motion track",t,u);return m}static fromClipWithDistance(t,o){const e=new RootMotionClip(t.name,t.duration,t.tracks.slice(),t.blendMode);return e.fixedInPlace=!0,e.source=t,e.uuid=t.uuid,a(e,i(t)),l(e,s(t)),e.displacement=o,e}clone(){const t=RootMotionClip.fromClip(this.source.clone(),this.fixedInPlace,this.rootBone,this.animationRoot);return t.displacement=this.displacement,l(t,s(this)),t}}let c=53912381;export class BoneLayer{constructor(){this.uuid=c++,this.order=0,this.boneMask=[]}}/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -21,6 +21,10 @@ export * from './services/physics/physics-system.js';
21
21
  export * from './animation/anim-sm.js';
22
22
  export * from './animation/root-motion.js';
23
23
  export * from './animation/retarget.js';
24
+ export * from './animation/locomotion-body.js';
25
+ export * from './animation/foot-contact.js';
26
+ export * from './animation/foot-contact-baker.js';
27
+ export * from './animation/pose-inertialization.js';
24
28
  export * from './services/asset-loader.js';
25
29
  export * from './services/pointer-events.js';
26
30
  export { VisualEffect } from '../effects/vfx/vfx-param.js';
@@ -1,4 +1,4 @@
1
- import"reflect-metadata";export{Container as DIContainer}from"typedi";export*from"./initiate.js";export*from"./game-mode.js";export*from"./player-lifecycle-coordinator.js";export*from"./player-sessions.js";export*from"./game-state-context.js";export*from"./network-travel-coordinator.js";export*from"./rendering-host.js";export*from"./inject.js";export{Service,Inject}from"typedi";export{ActorFactory}from"./actors/factory.js";export{Actor,ActorLifetime,BaseActor,getActorClassById,getActorClassId,getRegisteredActorClasses,getActorOptions}from"./actors/actor.js";export{Component,ActorComponent,attach,Attach,getComponentClassById,getComponentClassId,getRegisteredComponentClasses}from"./actors/component.js";export*from"./services/world.js";export*from"./services/scene-transition.js";export*from"./services/game-time-scheduler.js";export*from"./services/camera-shake.js";export*from"./services/render.js";export*from"./services/physics/physics-system.js";export*from"./animation/anim-sm.js";export*from"./animation/root-motion.js";export*from"./animation/retarget.js";export*from"./services/asset-loader.js";export*from"./services/pointer-events.js";export{VisualEffect}from"../effects/vfx/vfx-param.js";export{DataAssetRef}from"../scene/objects/data-asset.js";export{SceneRef,sceneId}from"../scene/scene-ref.js";export{ModelRef}from"../scene/model-ref.js";export*from"../shader/parameter.js";export*from"./ai/index.js";export*from"../effects/vfx/index.js";export*from"./services/shader-provider.js";export{EventGraphCallable,EventGraphEventSource,EventGraphProperty,getEventGraphCallableInfo,getEventGraphCallableInfos,getEventGraphCallableMethods,getEventGraphEventSourceInfo,getEventGraphEventSourceInfos,getEventGraphEventSourceMethods,getEventGraphPropertyInfo,getEventGraphPropertyInfos,getEventGraphProperties,isEventGraphEventSource}from"./event-graph/decorators.js";export{EventGraphCompiler,validateEventGraphDocument}from"./event-graph/document-compiler.js";export{createDefaultEventGraphDocument,hasExecutableEventGraphDocument}from"./event-graph/model.js";export{EVENT_GRAPH_NODE_DEFINITIONS,getEventGraphNodePorts,getEventGraphPortDefinition}from"./event-graph/registry.js";export{EventGraphInstance}from"./event-graph/runtime.js";export{analyzeEventGraphTypes,assertEventGraphTypes,createEventGraphTypeContext,getEventGraphMembersForType}from"./event-graph/type-inference.js";export{EVENT_GRAPH_ACTOR_TYPE,EVENT_GRAPH_ACTOR_CLASS_TYPE,EVENT_GRAPH_ANY_TYPE,EVENT_GRAPH_BOOLEAN_TYPE,EVENT_GRAPH_COMPONENT_TYPE,EVENT_GRAPH_NUMBER_TYPE,EVENT_GRAPH_OBJECT_TYPE,EVENT_GRAPH_STRING_TYPE,EVENT_GRAPH_VOID_TYPE,EVENT_GRAPH_VECTOR2_TYPE,EVENT_GRAPH_VECTOR3_TYPE,eventGraphActorClassType,eventGraphActorType,eventGraphComponentType,eventGraphTypeName,isEventGraphTypeAssignable}from"./event-graph/types.js";export*from"./net/net-connection.js";export*from"./net/net-session.js";export*from"./services/actor-persistence.js";export*from"./actors/builtin/components/actor-transform-persistence.js";/*
1
+ import"reflect-metadata";export{Container as DIContainer}from"typedi";export*from"./initiate.js";export*from"./game-mode.js";export*from"./player-lifecycle-coordinator.js";export*from"./player-sessions.js";export*from"./game-state-context.js";export*from"./network-travel-coordinator.js";export*from"./rendering-host.js";export*from"./inject.js";export{Service,Inject}from"typedi";export{ActorFactory}from"./actors/factory.js";export{Actor,ActorLifetime,BaseActor,getActorClassById,getActorClassId,getRegisteredActorClasses,getActorOptions}from"./actors/actor.js";export{Component,ActorComponent,attach,Attach,getComponentClassById,getComponentClassId,getRegisteredComponentClasses}from"./actors/component.js";export*from"./services/world.js";export*from"./services/scene-transition.js";export*from"./services/game-time-scheduler.js";export*from"./services/camera-shake.js";export*from"./services/render.js";export*from"./services/physics/physics-system.js";export*from"./animation/anim-sm.js";export*from"./animation/root-motion.js";export*from"./animation/retarget.js";export*from"./animation/locomotion-body.js";export*from"./animation/foot-contact.js";export*from"./animation/foot-contact-baker.js";export*from"./animation/pose-inertialization.js";export*from"./services/asset-loader.js";export*from"./services/pointer-events.js";export{VisualEffect}from"../effects/vfx/vfx-param.js";export{DataAssetRef}from"../scene/objects/data-asset.js";export{SceneRef,sceneId}from"../scene/scene-ref.js";export{ModelRef}from"../scene/model-ref.js";export*from"../shader/parameter.js";export*from"./ai/index.js";export*from"../effects/vfx/index.js";export*from"./services/shader-provider.js";export{EventGraphCallable,EventGraphEventSource,EventGraphProperty,getEventGraphCallableInfo,getEventGraphCallableInfos,getEventGraphCallableMethods,getEventGraphEventSourceInfo,getEventGraphEventSourceInfos,getEventGraphEventSourceMethods,getEventGraphPropertyInfo,getEventGraphPropertyInfos,getEventGraphProperties,isEventGraphEventSource}from"./event-graph/decorators.js";export{EventGraphCompiler,validateEventGraphDocument}from"./event-graph/document-compiler.js";export{createDefaultEventGraphDocument,hasExecutableEventGraphDocument}from"./event-graph/model.js";export{EVENT_GRAPH_NODE_DEFINITIONS,getEventGraphNodePorts,getEventGraphPortDefinition}from"./event-graph/registry.js";export{EventGraphInstance}from"./event-graph/runtime.js";export{analyzeEventGraphTypes,assertEventGraphTypes,createEventGraphTypeContext,getEventGraphMembersForType}from"./event-graph/type-inference.js";export{EVENT_GRAPH_ACTOR_TYPE,EVENT_GRAPH_ACTOR_CLASS_TYPE,EVENT_GRAPH_ANY_TYPE,EVENT_GRAPH_BOOLEAN_TYPE,EVENT_GRAPH_COMPONENT_TYPE,EVENT_GRAPH_NUMBER_TYPE,EVENT_GRAPH_OBJECT_TYPE,EVENT_GRAPH_STRING_TYPE,EVENT_GRAPH_VOID_TYPE,EVENT_GRAPH_VECTOR2_TYPE,EVENT_GRAPH_VECTOR3_TYPE,eventGraphActorClassType,eventGraphActorType,eventGraphComponentType,eventGraphTypeName,isEventGraphTypeAssignable}from"./event-graph/types.js";export*from"./net/net-connection.js";export*from"./net/net-session.js";export*from"./services/actor-persistence.js";export*from"./actors/builtin/components/actor-transform-persistence.js";/*
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 e from"typedi";import{BehaviorSubject as t,Subject as i}from"rxjs";import{loadScene as n}from"../scene/bootstrap.js";import{ActorFactory as s}from"./actors/factory.js";import{SceneMaterializer as r}from"../scene/materializer.js";import{SceneDataService as o}from"../scene/scene-data-service.js";import{World as a}from"./services/world.js";import{ViewController as c}from"./services/render.js";import{RenderingView as l,GraphicsRuntime as p}from"../rendering.js";import{PhysicsSystem as h}from"./services/physics/physics-system.js";import{MeshComponent as d}from"./actors/builtin/components/mesh-component.js";import{builtInComponents as m}from"./actors/builtin/components/index.js";import{InputService as u}from"./input/index.js";import{RuntimeAssetsService as g}from"../scene/runtime-asset-service.js";import{AssetResourceLoader as w}from"../scene/asset-resource-loader.js";import{AssetLoader as f}from"./services/asset-loader.js";import{RuntimeBundledBackendService as y}from"../scene/runtime-bundled-backend-service.js";import{AssetsProvider as S}from"../scene/assets-provider.js";import{ShaderProvider as v}from"./services/shader-provider.js";import{PointerEvents as b}from"./services/pointer-events.js";import{GpuVfxWorldService as I}from"../effects/vfx/gpu-vfx-world-service.js";import{NetMode as j}from"./net/net-session.js";import{GameStateContext as A}from"./game-state-context.js";import{PlayerSessions as V}from"./player-sessions.js";import{polyfillClient as P}from"./polyfill.js";import{withInjectionContext as C}from"./inject.js";import{RenderingHost as R}from"./rendering-host.js";import{SceneTransitionService as N}from"./services/scene-transition.js";import{SceneRef as x}from"../scene/scene-ref.js";import{ActorPersistence as z}from"./services/actor-persistence.js";let k=0;export function initiateApplication(t,i){const n="function"==typeof t?t:void 0,s=i??t;var r;P(),"element"in s&&null!=s.element&&(r=s.element,Object.assign(r.style,{position:"absolute",top:"0",left:"0",width:"100%",height:"100%",overflow:"hidden"}));const o=s.renderingHost??(null!=s.renderingView?new R(s.renderingView,!1,("renderingViewFactory"in s?s.renderingViewFactory:void 0)??(()=>new l(s.renderingView.container,s.renderingView.options))):function(e,t,i){const n=new p,s=()=>function(e,t,i,n){return new l(e,{graphicsRuntime:n,backend:t?.backend??"webgpu",enableXR:!0===i?.enabled,maxPixelRatio:t?.maxPixelRatio,resolutionScale:t?.resolutionScale,dynamicResolution:t?.dynamicResolution,upscaling:t?.upscaling,webgpuPostProcessing:t?.webgpuPostProcessing,webgpuDirectionalShadows:t?.webgpuDirectionalShadows,webgpuVfx:t?.webgpuVfx??"auto",msaa:t?.msaa,fpsCap:t?.fpsCap,depthPrepass:{enabled:!0===t?.depthPrepass?.enabled},bloom:{enabled:!1!==t?.bloom?.enabled},reflection:{enabled:!1!==t?.reflection?.enabled},occlusionCulling:{enabled:!1!==t?.occlusionCulling?.enabled},shadows:{enabled:t?.shadows?.enabled??!0,autoUpdate:t?.shadows?.autoUpdate??!0,cascadeUpdateIntervals:[22,40,60,120]}})}(e,t,i,n);return new R(s(),!0,s)}(s.element,s.rendering,s.xr)),a=e.of("hology-application-"+ ++k),c=new HologyApplicationRuntime(s,a,o,n??s.defaultGameClass);return c.startInitialization(),c}export function initiateGame(e,t){const{sceneName:i,detailTier:n,multi:s,localPlayer:r,persistence:o,...a}=t,c=initiateApplication(e,a),l=c.ready.then(()=>c.loadGame({sceneName:i,detailTier:n,multi:s,localPlayer:r,persistence:o}));return new HologyRuntime(c,l)}export class GameInstance{onPreload(){}onStart(){}onUnload(){return this.onShutdown()}onShutdown(){}}export class HologyApplicationRuntime{constructor(e,n,s,r){this.config=e,this.applicationContainer=n,this.renderingHost=s,this.defaultGameClass=r,this.stateSubject=new t({type:"initializing"}),this.state=this.stateSubject.asObservable(),this.shutdownSubject=new i,this.shutdownStarted=this.shutdownSubject.asObservable(),this.transitionTail=Promise.resolve(),this.initializationPromise=Promise.resolve(),this.transitionIntent=0,this.shutdownFlag=!1,this.ready=new Promise((e,t)=>{this.resolveReady=e,this.rejectReady=t})}get renderingView(){return this.renderingHost.activeView}get currentState(){return this.stateSubject.value}startInitialization(){this.initializationPromise=this.initialize()}async initialize(){try{if(this.registerApplicationServices(),await this.renderingHost.applicationView.compileAsync(),this.shutdownFlag)return void this.rejectReady(H());const e=this.getApplicationService(u);this.renderingHost.start(t=>e.update(t)),this.setState({type:"menu"}),this.resolveReady()}catch(e){this.setState({type:"error",operation:"initialize",error:e}),this.rejectReady(e)}}loadGame(e,t){const i="function"==typeof e?e:this.defaultGameClass,n=t??e;if(null==i)return Promise.reject(new Error("loadGame() requires a game class because no default was configured"));const s=++this.transitionIntent;return this.enqueue(async()=>(await this.ready,this.assertRunning(),this.checkIntent(s),null!=this.session&&await this.disposeSession(this.session,!0),this.checkIntent(s),this.performLoad(i,n,s)))}unloadGame(){const e=++this.transitionIntent;return this.enqueue(async()=>{if(await this.ready,!this.shutdownFlag&&e===this.transitionIntent){try{null!=this.session&&await this.disposeSession(this.session,!0)}catch(t){throw this.shutdownFlag||e!==this.transitionIntent||this.setState({type:"error",operation:"unload",error:t}),t}this.shutdownFlag||e!==this.transitionIntent||this.setState({type:"menu"})}})}transitionScene(e){const t="string"==typeof e?{scene:new x(e,e)}:e instanceof x?{scene:e}:e,i=this.transitionIntent;return this.enqueue(async()=>{await this.ready,this.assertRunning(),this.checkIntent(i);const e=this.session;if(null==e)throw new Error("transitionScene() requires an active game session");try{return await this.performSceneTransition(e,t,i),e}catch(e){throw G(e)||this.shutdownFlag||i!==this.transitionIntent||this.setState({type:"error",operation:"load",error:e}),e}})}async shutdown(){this.shutdownFlag||(this.shutdownFlag=!0,++this.transitionIntent,this.shutdownSubject.next(),this.shutdownSubject.complete(),await this.initializationPromise.catch(()=>{}),await this.transitionTail.catch(()=>{}),null!=this.session&&await this.disposeSession(this.session,!1),this.getApplicationService(u).stop(),this.getOptionalApplicationService(b)?.stop(),this.getApplicationService(w).disposeAll(),this.getApplicationService(g).dispose(),this.getApplicationService(c).dispose(!1),this.renderingHost.shutdown(),e.reset(this.applicationContainer.id),this.setState({type:"stopped"}),this.stateSubject.complete())}getWorld(){return this.session?.world}getGameInstance(){return this.session?.gameInstance}getApplicationService(e){return this.applicationContainer.get(e)}getSessionService(e){return this.session?.container.get(e)}getService(e){return null!=this.session?this.session.container.get(e):this.applicationContainer.get(e)}registerRenderStage(e){return this.renderingHost.registerStage(e)}registerApplicationServices(){const e=new c(this.renderingView);this.applicationContainer.set(l,this.renderingHost.applicationView),this.applicationContainer.set(c,e);const t=new y(this.config.runtimeDataUrl),i=new g(t),n=new w;n.setDataDir(this.config.dataDir),n.setDirectFileLoading(!0===this.config.directAssetFileLoading),"webgpu"===this.renderingHost.applicationView.requestedBackend?n.initKtx2ForWebGpu():n.initKtx2(this.renderingHost.applicationView.renderer);const s=Object.entries(this.config.shaders).map(([e,t])=>({name:e,type:t}));this.applicationContainer.set(y,t),this.applicationContainer.set(S,i),this.applicationContainer.set(g,i),this.applicationContainer.set(w,n),this.applicationContainer.set(f,new f(n,i,s)),this.applicationContainer.set(v,new v(s));const r=C(this.applicationContainer,e=>e.get(u));this.applicationContainer.set(u,r)}async performLoad(t,i,s){if(null!=i.persistence&&null!=i.multi?.session&&i.multi.session.mode!==j.none)throw new Error("Actor persistence currently supports single-player sessions only");const o=this.createSession(i);this.session=o;try{this.loading(i.sceneName,"physics",.08),await Promise.all([o.physics.start(),o.renderingView.initializeBackend()]),this.checkIntent(s),this.loading(i.sceneName,"runtime-data",.2),await this.getApplicationService(y).preloadData(),this.checkIntent(s),this.loading(i.sceneName,"scene",.42);const a=await n(o.renderingView,i.sceneName,this.config.dataDir,this.config.shaders,this.config.actors,o.components,o.actorFactory,this.getApplicationService(y),this.getApplicationService(g),this.getApplicationService(w),{detailTier:i.detailTier,nodeMode:i.multi?.session.mode??j.none,persistence:o.world.persistence});if(o.materializer.dispose(),o.materializer=a.materializer,o.sceneRoots=a.sceneRoots,o.container.set(r,a.materializer),o.world.materializer=a.materializer,o.world.scene=a.scene,o.physics.scene=a.scene,this.checkIntent(s),null!=i.multi?.session){const{NetService:e}=await import("./net/service/net-service.js"),t=C(o.container,t=>t.get(e));t.session=i.multi.session,t.start(),o.stopPlayers=()=>t.stop()}if(e.import([d]),await this.restoreSceneActors(o,a.materializer,a.actors,s),o.physics.addFromScene(a.scene),await o.world.startSceneEventGraphs(a.materializer),this.checkIntent(s),o.gameInstance=C(o.container,e=>(e.set({id:t,type:t}),e.get(t))),this.loading(i.sceneName,"game-preload",.58),o.gameInstance instanceof GameInstance&&await o.gameInstance.onPreload(),this.checkIntent(s),this.loading(i.sceneName,"shaders",.7),await o.renderingView.compileAsync(),this.checkIntent(s),this.loading(i.sceneName,"textures",.82),await o.renderingView.initTexturesAsync(),this.checkIntent(s),"webgpu"===o.renderingView.activeBackend){this.loading(i.sceneName,"vfx-warmup",.94);const e=o.container.get(I).beginPipelineWarmup();try{const e=performance.now();console.info("[Hology GPU VFX warmup] Rendering loading frame",i.sceneName),o.renderingView.render(0),console.info("[Hology GPU VFX warmup] Render returned; waiting for GPU queue",{sceneName:i.sceneName,renderMs:performance.now()-e}),await(o.renderingView.webGpuDevice?.queue.onSubmittedWorkDone()),console.info("[Hology GPU VFX warmup] GPU queue completed",{sceneName:i.sceneName,totalMs:performance.now()-e})}finally{e()}}return this.checkIntent(s),o.gameInstance instanceof GameInstance&&await o.gameInstance.onStart(),this.checkIntent(s),o.viewController.paused=!1,this.renderingHost.activate(o.renderingView),this.setState({type:"playing",sceneName:i.sceneName}),o}catch(e){throw await this.disposeSession(o,!1),G(e)||this.shutdownFlag||s!==this.transitionIntent||this.setState({type:"error",operation:"load",error:e}),e}}createSession(t){const i=e.of("hology-session-"+ ++k);this.copyApplicationServices(i);const n=this.renderingHost.createView(),p=new c(n);p.paused=!0,i.set(l,n),i.set(c,p);const d={...m,...this.config.components??{}},u=new z;null!=t.persistence&&u.configure(t.persistence),i.set(z,u);const f=new s(i,{inEditor:!1});f.classes=this.config.actors,i.set(s,f);const y=new r(n.scene,new o,this.getApplicationService(g),this.getApplicationService(w),n,Object.entries(this.config.shaders).map(([e,t])=>({name:e,type:t})),Object.entries(this.config.actors).map(([e,t])=>({name:e,type:t})),f,Object.entries(d).map(([e,t])=>({name:e,type:t})));y.inEditor=!1,y.netMode=t.multi?.session.mode??j.none,i.set(r,y);const S=C(i,e=>e.get(a));u.enabled&&(S.persistence=u),S.scene=n.scene,i.set(a,S);const v=C(i,e=>e.get(h));i.set(h,v);const b=new N;b.bind(async e=>{await this.transitionScene(e)}),i.set(N,b);const I=C(i,e=>e.get(V));i.set(V,I);const P=C(i,e=>e.get(A));return i.set(A,P),null==t.multi?.session&&I.startStandalone(t.localPlayer??{id:"local-player",displayName:"Player"}),{sceneName:t.sceneName,detailTier:t.detailTier,container:i,renderingView:n,viewController:p,actorFactory:f,materializer:y,world:S,physics:v,components:d,sceneTransition:b,sceneRoots:[],netMode:t.multi?.session.mode??j.none,stopPlayers:null==t.multi?.session?()=>{I.stop(),P.stop()}:void 0}}async performSceneTransition(e,t,i){if(e.netMode!==j.none)throw new Error("In-session scene transitions are not yet supported for network sessions");const s=t.scene.id.trim();if(0===s.length)throw new Error("A destination scene id is required");const o=e.sceneName,a=e.viewController.paused;e.viewController.paused=!0,this.renderingHost.activate(this.renderingHost.applicationView),e.world.persistence?.captureAll(),this.setState({type:"unloading",sceneName:o}),e.world.stopSceneEventGraphs();const c=e.world.removeSceneLifetimeActors(e.sceneRoots);for(const t of e.sceneRoots)e.physics.removeRemoved(t),t.removeFromParent();e.sceneRoots=[],e.materializer.dispose(c),this.checkIntent(i),this.loading(s,"scene",.42);const l=await n(e.renderingView,s,this.config.dataDir,this.config.shaders,this.config.actors,e.components,e.actorFactory,this.getApplicationService(y),this.getApplicationService(g),this.getApplicationService(w),{detailTier:e.detailTier,nodeMode:e.netMode,persistence:e.world.persistence});this.checkIntent(i),e.materializer=l.materializer,e.sceneRoots=l.sceneRoots,e.sceneName=s,e.container.set(r,l.materializer),e.world.materializer=l.materializer,await this.restoreSceneActors(e,l.materializer,l.actors,i),e.physics.addFromScene(l.scene),await e.world.startSceneEventGraphs(l.materializer),await(t.afterLoad?.(e.world)),this.checkIntent(i),this.loading(s,"shaders",.7),await e.renderingView.compileAsync(),this.checkIntent(i),this.loading(s,"textures",.82),await e.renderingView.initTexturesAsync(),this.checkIntent(i),e.sceneTransition.publish({previousSceneName:o,sceneName:s}),e.viewController.paused=a,this.renderingHost.activate(e.renderingView),this.setState({type:"playing",sceneName:s})}async restoreSceneActors(e,t,i,n){if(null==e.world.persistence){for(const t of i)e.world.addActor(t);return}const s=i.filter(t=>!e.world.actors.includes(t));for(const t of s)e.world.addActor(t,void 0,void 0,void 0,!0);const r=t.registerPersistentActors();await e.world.persistence.restore(r,()=>this.checkIntent(n)),this.checkIntent(n);for(const t of s)e.world.beginActorPlay(t)}copyApplicationServices(e){const t=[u,S,g,y,w,f,v];for(const i of t)e.set(i,this.applicationContainer.get(i))}async disposeSession(t,i){if(t.disposed)return;let n;t.disposed=!0,i&&this.setState({type:"unloading",sceneName:t.sceneName});try{t.viewController.paused=!0,t.world.persistence?.captureAll(),t.stopPlayers?.(),t.sceneTransition.dispose(),t.world.stopSceneEventGraphs(),t.gameInstance instanceof GameInstance&&await t.gameInstance.onUnload()}catch(e){n=e}finally{for(const e of[...t.world.actors])t.world.removeActor(e);t.physics.stop(),t.materializer.dispose(),this.renderingHost.activeView===t.renderingView&&this.renderingHost.activate(this.renderingHost.applicationView),t.viewController.dispose(!1),this.renderingHost.releaseView(t.renderingView),e.reset(t.container.id),this.session===t&&(this.session=void 0)}if(null!=n)throw n}enqueue(e){const t=this.transitionTail.then(e,e);return this.transitionTail=t.catch(()=>{}),t}checkIntent(e){if(this.assertRunning(),e!==this.transitionIntent)throw H()}assertRunning(){if(this.shutdownFlag)throw new Error("Hology application has been shut down")}loading(e,t,i){this.setState({type:"loading",sceneName:e,stage:t,progress:i})}setState(e){this.stateSubject.next(e)}getOptionalApplicationService(e){return this.applicationContainer.has(e)?this.applicationContainer.get(e):void 0}}export class HologyRuntime{constructor(e,t){this.application=e,this.ready=t.then(()=>!0),this.shutdownStarted=e.shutdownStarted}get status(){return"playing"===this.application.currentState.type?5:"stopped"===this.application.currentState.type||"error"===this.application.currentState.type?10:0}get gameInstance(){return this.application.getGameInstance()}get isShutdown(){return"stopped"===this.application.currentState.type}getWorld(){return this.application.getWorld()}getService(e){return this.application.getService(e)}shutdown(){return this.application.shutdown()}}function H(){return new DOMException("Superseded by a newer game transition","AbortError")}function G(e){return e instanceof DOMException&&"AbortError"===e.name}/*
1
+ import e from"typedi";import{BehaviorSubject as t,Subject as i}from"rxjs";import{loadScene as n}from"../scene/bootstrap.js";import{ActorFactory as s}from"./actors/factory.js";import{SceneMaterializer as r}from"../scene/materializer.js";import{SceneDataService as o}from"../scene/scene-data-service.js";import{World as a}from"./services/world.js";import{ViewController as c}from"./services/render.js";import{RenderingView as l,GraphicsRuntime as p}from"../rendering.js";import{PhysicsSystem as h}from"./services/physics/physics-system.js";import{MeshComponent as d}from"./actors/builtin/components/mesh-component.js";import{builtInComponents as m}from"./actors/builtin/components/index.js";import{InputService as u}from"./input/index.js";import{RuntimeAssetsService as g}from"../scene/runtime-asset-service.js";import{AssetResourceLoader as w}from"../scene/asset-resource-loader.js";import{AssetLoader as f}from"./services/asset-loader.js";import{RuntimeBundledBackendService as y}from"../scene/runtime-bundled-backend-service.js";import{AssetsProvider as S}from"../scene/assets-provider.js";import{ShaderProvider as v}from"./services/shader-provider.js";import{PointerEvents as b}from"./services/pointer-events.js";import{GpuVfxWorldService as I}from"../effects/vfx/gpu-vfx-world-service.js";import{NetMode as j}from"./net/net-session.js";import{GameStateContext as A}from"./game-state-context.js";import{PlayerSessions as V}from"./player-sessions.js";import{polyfillClient as P}from"./polyfill.js";import{withInjectionContext as C}from"./inject.js";import{RenderingHost as R}from"./rendering-host.js";import{SceneTransitionService as z}from"./services/scene-transition.js";import{SceneRef as H}from"../scene/scene-ref.js";import{ActorPersistence as N}from"./services/actor-persistence.js";let x=0;export function initiateApplication(t,i){const n="function"==typeof t?t:void 0,s=i??t;var r;P(),"element"in s&&null!=s.element&&(r=s.element,Object.assign(r.style,{position:"absolute",top:"0",left:"0",width:"100%",height:"100%",overflow:"hidden"}));const o=s.renderingHost??(null!=s.renderingView?new R(s.renderingView,!1,("renderingViewFactory"in s?s.renderingViewFactory:void 0)??(()=>new l(s.renderingView.container,s.renderingView.options))):function(e,t,i){const n=new p,s=()=>function(e,t,i,n){return new l(e,{graphicsRuntime:n,backend:t?.backend??"webgpu",enableXR:!0===i?.enabled,maxPixelRatio:t?.maxPixelRatio,resolutionScale:t?.resolutionScale,dynamicResolution:t?.dynamicResolution,upscaling:t?.upscaling,webgpuPostProcessing:t?.webgpuPostProcessing,webgpuDirectionalShadows:t?.webgpuDirectionalShadows,webgpuVfx:t?.webgpuVfx??"auto",msaa:t?.msaa,fpsCap:t?.fpsCap,depthPrepass:{enabled:!0===t?.depthPrepass?.enabled},bloom:{enabled:!1!==t?.bloom?.enabled},reflection:{enabled:!1!==t?.reflection?.enabled},occlusionCulling:{enabled:!1!==t?.occlusionCulling?.enabled},shadows:{enabled:t?.shadows?.enabled??!0,autoUpdate:t?.shadows?.autoUpdate??!0,cascadeUpdateIntervals:[22,40,60,120]}})}(e,t,i,n);return new R(s(),!0,s)}(s.element,s.rendering,s.xr)),a=e.of("hology-application-"+ ++x),c=new HologyApplicationRuntime(s,a,o,n??s.defaultGameClass);return c.startInitialization(),c}export function initiateGame(e,t){const{sceneName:i,detailTier:n,multi:s,localPlayer:r,persistence:o,...a}=t,c=initiateApplication(e,a),l=c.ready.then(()=>c.loadGame({sceneName:i,detailTier:n,multi:s,localPlayer:r,persistence:o}));return new HologyRuntime(c,l)}export class GameInstance{onPreload(){}onStart(){}onUnload(){return this.onShutdown()}onShutdown(){}}export class HologyApplicationRuntime{constructor(e,n,s,r){this.config=e,this.applicationContainer=n,this.renderingHost=s,this.defaultGameClass=r,this.stateSubject=new t({type:"initializing"}),this.state=this.stateSubject.asObservable(),this.shutdownSubject=new i,this.shutdownStarted=this.shutdownSubject.asObservable(),this.transitionTail=Promise.resolve(),this.initializationPromise=Promise.resolve(),this.transitionIntent=0,this.shutdownFlag=!1,this.ready=new Promise((e,t)=>{this.resolveReady=e,this.rejectReady=t})}get renderingView(){return this.renderingHost.activeView}get currentState(){return this.stateSubject.value}startInitialization(){this.initializationPromise=this.initialize()}async initialize(){try{if(this.registerApplicationServices(),await this.renderingHost.applicationView.compileAsync(),this.shutdownFlag)return void this.rejectReady(k());const e=this.getApplicationService(u);this.renderingHost.start(t=>e.update(t),t=>e.sampleGamepadButtons(t)),this.setState({type:"menu"}),this.resolveReady()}catch(e){this.setState({type:"error",operation:"initialize",error:e}),this.rejectReady(e)}}loadGame(e,t){const i="function"==typeof e?e:this.defaultGameClass,n=t??e;if(null==i)return Promise.reject(new Error("loadGame() requires a game class because no default was configured"));const s=++this.transitionIntent;return this.enqueue(async()=>(await this.ready,this.assertRunning(),this.checkIntent(s),null!=this.session&&await this.disposeSession(this.session,!0),this.checkIntent(s),this.performLoad(i,n,s)))}unloadGame(){const e=++this.transitionIntent;return this.enqueue(async()=>{if(await this.ready,!this.shutdownFlag&&e===this.transitionIntent){try{null!=this.session&&await this.disposeSession(this.session,!0)}catch(t){throw this.shutdownFlag||e!==this.transitionIntent||this.setState({type:"error",operation:"unload",error:t}),t}this.shutdownFlag||e!==this.transitionIntent||this.setState({type:"menu"})}})}transitionScene(e){const t="string"==typeof e?{scene:new H(e,e)}:e instanceof H?{scene:e}:e,i=this.transitionIntent;return this.enqueue(async()=>{await this.ready,this.assertRunning(),this.checkIntent(i);const e=this.session;if(null==e)throw new Error("transitionScene() requires an active game session");try{return await this.performSceneTransition(e,t,i),e}catch(e){throw G(e)||this.shutdownFlag||i!==this.transitionIntent||this.setState({type:"error",operation:"load",error:e}),e}})}async shutdown(){this.shutdownFlag||(this.shutdownFlag=!0,++this.transitionIntent,this.shutdownSubject.next(),this.shutdownSubject.complete(),await this.initializationPromise.catch(()=>{}),await this.transitionTail.catch(()=>{}),null!=this.session&&await this.disposeSession(this.session,!1),this.getApplicationService(u).stop(),this.getOptionalApplicationService(b)?.stop(),this.getApplicationService(w).disposeAll(),this.getApplicationService(g).dispose(),this.getApplicationService(c).dispose(!1),this.renderingHost.shutdown(),e.reset(this.applicationContainer.id),this.setState({type:"stopped"}),this.stateSubject.complete())}getWorld(){return this.session?.world}getGameInstance(){return this.session?.gameInstance}getApplicationService(e){return this.applicationContainer.get(e)}getSessionService(e){return this.session?.container.get(e)}getService(e){return null!=this.session?this.session.container.get(e):this.applicationContainer.get(e)}registerRenderStage(e){return this.renderingHost.registerStage(e)}registerApplicationServices(){const e=new c(this.renderingView);this.applicationContainer.set(l,this.renderingHost.applicationView),this.applicationContainer.set(c,e);const t=new y(this.config.runtimeDataUrl),i=new g(t),n=new w;n.setDataDir(this.config.dataDir),n.setDirectFileLoading(!0===this.config.directAssetFileLoading),"webgpu"===this.renderingHost.applicationView.requestedBackend?n.initKtx2ForWebGpu():n.initKtx2(this.renderingHost.applicationView.renderer);const s=Object.entries(this.config.shaders).map(([e,t])=>({name:e,type:t}));this.applicationContainer.set(y,t),this.applicationContainer.set(S,i),this.applicationContainer.set(g,i),this.applicationContainer.set(w,n),this.applicationContainer.set(f,new f(n,i,s)),this.applicationContainer.set(v,new v(s));const r=C(this.applicationContainer,e=>e.get(u));this.applicationContainer.set(u,r)}async performLoad(t,i,s){if(null!=i.persistence&&null!=i.multi?.session&&i.multi.session.mode!==j.none)throw new Error("Actor persistence currently supports single-player sessions only");const o=this.createSession(i);this.session=o;try{this.loading(i.sceneName,"physics",.08),await Promise.all([o.physics.start(),o.renderingView.initializeBackend()]),this.checkIntent(s),this.loading(i.sceneName,"runtime-data",.2),await this.getApplicationService(y).preloadData(),this.checkIntent(s),this.loading(i.sceneName,"scene",.42);const a=await n(o.renderingView,i.sceneName,this.config.dataDir,this.config.shaders,this.config.actors,o.components,o.actorFactory,this.getApplicationService(y),this.getApplicationService(g),this.getApplicationService(w),{detailTier:i.detailTier,nodeMode:i.multi?.session.mode??j.none,persistence:o.world.persistence});if(o.materializer.dispose(),o.materializer=a.materializer,o.sceneRoots=a.sceneRoots,o.container.set(r,a.materializer),o.world.materializer=a.materializer,o.world.scene=a.scene,o.physics.scene=a.scene,this.checkIntent(s),null!=i.multi?.session){const{NetService:e}=await import("./net/service/net-service.js"),t=C(o.container,t=>t.get(e));t.session=i.multi.session,t.start(),o.stopPlayers=()=>t.stop()}if(e.import([d]),await this.restoreSceneActors(o,a.materializer,a.actors,s),o.physics.addFromScene(a.scene),await o.world.startSceneEventGraphs(a.materializer),this.checkIntent(s),o.gameInstance=C(o.container,e=>(e.set({id:t,type:t}),e.get(t))),this.loading(i.sceneName,"game-preload",.58),o.gameInstance instanceof GameInstance&&await o.gameInstance.onPreload(),this.checkIntent(s),this.loading(i.sceneName,"shaders",.7),await o.renderingView.compileAsync(),this.checkIntent(s),this.loading(i.sceneName,"textures",.82),await o.renderingView.initTexturesAsync(),this.checkIntent(s),"webgpu"===o.renderingView.activeBackend){this.loading(i.sceneName,"vfx-warmup",.94);const e=o.container.get(I).beginPipelineWarmup();try{const e=performance.now();console.info("[Hology GPU VFX warmup] Rendering loading frame",i.sceneName),o.renderingView.render(0),console.info("[Hology GPU VFX warmup] Render returned; waiting for GPU queue",{sceneName:i.sceneName,renderMs:performance.now()-e}),await(o.renderingView.webGpuDevice?.queue.onSubmittedWorkDone()),console.info("[Hology GPU VFX warmup] GPU queue completed",{sceneName:i.sceneName,totalMs:performance.now()-e})}finally{e()}}return this.checkIntent(s),o.gameInstance instanceof GameInstance&&await o.gameInstance.onStart(),this.checkIntent(s),o.viewController.paused=!1,this.renderingHost.activate(o.renderingView),this.setState({type:"playing",sceneName:i.sceneName}),o}catch(e){throw await this.disposeSession(o,!1),G(e)||this.shutdownFlag||s!==this.transitionIntent||this.setState({type:"error",operation:"load",error:e}),e}}createSession(t){const i=e.of("hology-session-"+ ++x);this.copyApplicationServices(i);const n=this.renderingHost.createView(),p=new c(n);p.paused=!0,i.set(l,n),i.set(c,p);const d={...m,...this.config.components??{}},u=new N;null!=t.persistence&&u.configure(t.persistence),i.set(N,u);const f=new s(i,{inEditor:!1});f.classes=this.config.actors,i.set(s,f);const y=new r(n.scene,new o,this.getApplicationService(g),this.getApplicationService(w),n,Object.entries(this.config.shaders).map(([e,t])=>({name:e,type:t})),Object.entries(this.config.actors).map(([e,t])=>({name:e,type:t})),f,Object.entries(d).map(([e,t])=>({name:e,type:t})));y.inEditor=!1,y.netMode=t.multi?.session.mode??j.none,i.set(r,y);const S=C(i,e=>e.get(a));u.enabled&&(S.persistence=u),S.scene=n.scene,i.set(a,S);const v=C(i,e=>e.get(h));i.set(h,v);const b=new z;b.bind(async e=>{await this.transitionScene(e)}),i.set(z,b);const I=C(i,e=>e.get(V));i.set(V,I);const P=C(i,e=>e.get(A));return i.set(A,P),null==t.multi?.session&&I.startStandalone(t.localPlayer??{id:"local-player",displayName:"Player"}),{sceneName:t.sceneName,detailTier:t.detailTier,container:i,renderingView:n,viewController:p,actorFactory:f,materializer:y,world:S,physics:v,components:d,sceneTransition:b,sceneRoots:[],netMode:t.multi?.session.mode??j.none,stopPlayers:null==t.multi?.session?()=>{I.stop(),P.stop()}:void 0}}async performSceneTransition(e,t,i){if(e.netMode!==j.none)throw new Error("In-session scene transitions are not yet supported for network sessions");const s=t.scene.id.trim();if(0===s.length)throw new Error("A destination scene id is required");const o=e.sceneName,a=e.viewController.paused;e.viewController.paused=!0,this.renderingHost.activate(this.renderingHost.applicationView),e.world.persistence?.captureAll(),this.setState({type:"unloading",sceneName:o}),e.world.stopSceneEventGraphs();const c=e.world.removeSceneLifetimeActors(e.sceneRoots);for(const t of e.sceneRoots)e.physics.removeRemoved(t),t.removeFromParent();e.sceneRoots=[],e.materializer.dispose(c),this.checkIntent(i),this.loading(s,"scene",.42);const l=await n(e.renderingView,s,this.config.dataDir,this.config.shaders,this.config.actors,e.components,e.actorFactory,this.getApplicationService(y),this.getApplicationService(g),this.getApplicationService(w),{detailTier:e.detailTier,nodeMode:e.netMode,persistence:e.world.persistence});this.checkIntent(i),e.materializer=l.materializer,e.sceneRoots=l.sceneRoots,e.sceneName=s,e.container.set(r,l.materializer),e.world.materializer=l.materializer,await this.restoreSceneActors(e,l.materializer,l.actors,i),e.physics.addFromScene(l.scene),await e.world.startSceneEventGraphs(l.materializer),await(t.afterLoad?.(e.world)),this.checkIntent(i),this.loading(s,"shaders",.7),await e.renderingView.compileAsync(),this.checkIntent(i),this.loading(s,"textures",.82),await e.renderingView.initTexturesAsync(),this.checkIntent(i),e.sceneTransition.publish({previousSceneName:o,sceneName:s}),e.viewController.paused=a,this.renderingHost.activate(e.renderingView),this.setState({type:"playing",sceneName:s})}async restoreSceneActors(e,t,i,n){if(null==e.world.persistence){for(const t of i)e.world.addActor(t);return}const s=i.filter(t=>!e.world.actors.includes(t));for(const t of s)e.world.addActor(t,void 0,void 0,void 0,!0);const r=t.registerPersistentActors();await e.world.persistence.restore(r,()=>this.checkIntent(n)),this.checkIntent(n);for(const t of s)e.world.beginActorPlay(t)}copyApplicationServices(e){const t=[u,S,g,y,w,f,v];for(const i of t)e.set(i,this.applicationContainer.get(i))}async disposeSession(t,i){if(t.disposed)return;let n;t.disposed=!0,i&&this.setState({type:"unloading",sceneName:t.sceneName});try{t.viewController.paused=!0,t.world.persistence?.captureAll(),t.stopPlayers?.(),t.sceneTransition.dispose(),t.world.stopSceneEventGraphs(),t.gameInstance instanceof GameInstance&&await t.gameInstance.onUnload()}catch(e){n=e}finally{for(const e of[...t.world.actors])t.world.removeActor(e);t.physics.stop(),t.materializer.dispose(),this.renderingHost.activeView===t.renderingView&&this.renderingHost.activate(this.renderingHost.applicationView),t.viewController.dispose(!1),this.renderingHost.releaseView(t.renderingView),e.reset(t.container.id),this.session===t&&(this.session=void 0)}if(null!=n)throw n}enqueue(e){const t=this.transitionTail.then(e,e);return this.transitionTail=t.catch(()=>{}),t}checkIntent(e){if(this.assertRunning(),e!==this.transitionIntent)throw k()}assertRunning(){if(this.shutdownFlag)throw new Error("Hology application has been shut down")}loading(e,t,i){this.setState({type:"loading",sceneName:e,stage:t,progress:i})}setState(e){const t=this.renderingHost.applicationView;"loading"===e.type?t.stopAnimationLoop():"menu"!==e.type&&("error"!==e.type||"initialize"===e.operation)||this.renderingHost.activeView!==t||t.running||this.renderingHost.activate(t),this.stateSubject.next(e)}getOptionalApplicationService(e){return this.applicationContainer.has(e)?this.applicationContainer.get(e):void 0}}export class HologyRuntime{constructor(e,t){this.application=e,this.ready=t.then(()=>!0),this.shutdownStarted=e.shutdownStarted}get status(){return"playing"===this.application.currentState.type?5:"stopped"===this.application.currentState.type||"error"===this.application.currentState.type?10:0}get gameInstance(){return this.application.getGameInstance()}get isShutdown(){return"stopped"===this.application.currentState.type}getWorld(){return this.application.getWorld()}getService(e){return this.application.getService(e)}shutdown(){return this.application.shutdown()}}function k(){return new DOMException("Superseded by a newer game transition","AbortError")}function G(e){return e instanceof DOMException&&"AbortError"===e.name}/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -20,6 +20,9 @@ export declare class InputService {
20
20
  readonly onDeviceChanged: Subject<InputDevice>;
21
21
  private prevGamepadButtonStates;
22
22
  private suppressedGamepadButtons;
23
+ private sampledGamepadButtonStates;
24
+ private pendingGamepadButtonTransitions;
25
+ private frameGamepadButtonTransitions;
23
26
  getLastActiveDevice(): InputDevice;
24
27
  /**
25
28
  * Ignores button transitions for any gamepad buttons currently held down
@@ -46,6 +49,7 @@ export declare class InputService {
46
49
  private onWheel;
47
50
  start(): void;
48
51
  stop(): void;
52
+ private sampleGamepadButtonsFrom;
49
53
  update(deltaTime: number): void;
50
54
  /**
51
55
  * @deprecated Use bindToggle or bindDelta instead
@@ -1,4 +1,4 @@
1
- import{__decorate as e}from"tslib";import{Service as t}from"typedi";import{inject as s}from"../inject.js";import{ViewController as i}from"../services/render.js";import{MathUtils as n}from"three";import{Subject as o}from"rxjs";let a=class{constructor(){this.view=s(i),this.inputs=new Map,this.deltaCallbacks=new Map,this.toggleCallbacks=new Map,this.keybinds=new Map,this.mousebinds=new Map,this.touchmoveBinds=new Map,this.wheelbinds=new Map,this.gamepadButtonBinds=new Map,this.gamepadAxisBinds=new Map,this.lastActiveDevice="kbm",this.onDeviceChanged=new o,this.prevGamepadButtonStates=new Map,this.suppressedGamepadButtons=new Map,this.onKeyDown=e=>{this.setLastActiveDevice("kbm"),this.view.paused||"button"in e&&!l()||this.keybinds.forEach((t,s)=>{for(const i of t)if(i.test(e))if(this.inputs.has(s))this.inputs.get(s)(!0);else if(this.toggleCallbacks.has(s)){this.toggleCallbacks.get(s)(!0);this.activeToggleCallbacks.findIndex(e=>e.keybind===i&&e.toggle===this.toggleCallbacks.get(s))<0&&this.activeToggleCallbacks.push({keybind:i,toggle:this.toggleCallbacks.get(s)})}else console.warn("No input callback for registered for key bind "+i.display())})},this.activeToggleCallbacks=[],this.onKeyUp=e=>{this.setLastActiveDevice("kbm"),this.keybinds.forEach((t,s)=>{for(const i of t)if(i.test(e))if(this.inputs.has(s))this.inputs.get(s)(!1);else if(this.toggleCallbacks.has(s)){this.toggleCallbacks.get(s)(!1);const e=this.activeToggleCallbacks.findIndex(e=>e.keybind===i&&e.toggle===this.toggleCallbacks.get(s));e>=0&&this.activeToggleCallbacks.splice(e,1);for(const e of this.activeToggleCallbacks)e.toggle(!0)}else console.warn("No input callback for registered for key bind "+i.display())})},this.onMouseMove=e=>{if((Math.abs(e.movementX)>1||Math.abs(e.movementY)>1)&&this.setLastActiveDevice("kbm"),this.view.paused)return;if(!l())return;const t=n.clamp(e.movementX,-50,50),s=n.clamp(e.movementY,-50,50);Math.abs(t)>200||Math.abs(s)>200||this.mousebinds.forEach((e,i)=>{const n=this.inputs.get(i)??this.deltaCallbacks.get(i);if(null!=n)switch(e.axis){case"x":n(t*e.multiplier);break;case"y":n(s*e.multiplier)}else console.warn("No input callback for registered for mouse bind "+e.axis)})},this.onTouchStart=e=>{if(this.setLastActiveDevice("kbm"),!this.view.paused){for(let t=0;t<=e.touches.length;t++){const s=e.touches[t];if(s?.target instanceof HTMLCanvasElement)return void(this.prevTouchEvent=s)}e.preventDefault()}},this.onTouchMove=e=>{if(this.view.paused)return;if(null==this.prevTouchEvent)return;let t=null;for(let s=0;s<=e.touches.length;s++){const i=e.touches[s];if(i&&i.target instanceof HTMLCanvasElement&&i.identifier===this.prevTouchEvent.identifier){t=i;break}}null!=t&&(this.touchmoveBinds.forEach((e,s)=>{const i=this.inputs.get(s)??this.deltaCallbacks.get(s);if(null!=i)switch(e.axis){case"x":i((t.clientX-this.prevTouchEvent.clientX)*e.multiplier);break;case"y":i((t.clientY-this.prevTouchEvent.clientY)*e.multiplier)}else console.warn("No input callback for registered for mouse bind "+e.axis)}),this.prevTouchEvent=t,e.preventDefault())},this.onTouchEnd=e=>{this.prevTouchEvent=null,e.preventDefault()},this.onWheel=e=>{this.setLastActiveDevice("kbm"),this.view.paused||this.wheelbinds.forEach((t,s)=>{const i=this.inputs.get(s)??this.deltaCallbacks.get(s);null!=i?i(e.deltaY*t.multiplier):console.warn("No input callback for registered for wheel bind")})}}getLastActiveDevice(){return this.lastActiveDevice}suppressHeldGamepadButtonsUntilRelease(){const e=navigator.getGamepads();for(let t=0;t<e.length;t++){const s=e[t];if(!s||"standard"!==s.mapping)continue;let i=this.suppressedGamepadButtons.get(t);null==i&&(i=new Array(s.buttons.length).fill(!1),this.suppressedGamepadButtons.set(t,i));for(let e=0;e<s.buttons.length;e++)s.buttons[e].pressed&&(i[e]=!0)}}setLastActiveDevice(e){this.lastActiveDevice!==e&&(this.lastActiveDevice=e,this.onDeviceChanged.next(e))}start(){document.addEventListener("keydown",this.onKeyDown),document.addEventListener("mousedown",this.onKeyDown),document.addEventListener("mouseup",this.onKeyUp),document.addEventListener("keyup",this.onKeyUp),document.addEventListener("mousemove",this.onMouseMove),this.view.htmlElement.addEventListener("touchstart",this.onTouchStart,{passive:!1}),this.view.htmlElement.addEventListener("touchmove",this.onTouchMove,{passive:!1}),this.view.htmlElement.addEventListener("touchend",this.onTouchEnd,{passive:!1}),document.addEventListener("wheel",this.onWheel)}stop(){document.removeEventListener("keydown",this.onKeyDown),document.removeEventListener("mousedown",this.onKeyDown),document.removeEventListener("mouseup",this.onKeyUp),document.removeEventListener("keyup",this.onKeyUp),document.removeEventListener("mousemove",this.onMouseMove),this.view.htmlElement.removeEventListener("touchstart",this.onTouchStart),this.view.htmlElement.removeEventListener("touchmove",this.onTouchMove),this.view.htmlElement.removeEventListener("touchend",this.onTouchEnd),document.removeEventListener("wheel",this.onWheel)}update(e){if(this.view.paused)return;const t=navigator.getGamepads();for(let e=0;e<t.length;e++){const s=t[e];if(!s||"standard"!==s.mapping)continue;let i=this.prevGamepadButtonStates.get(e);null==i&&(i=new Array(s.buttons.length).fill(!1),this.prevGamepadButtonStates.set(e,i));let n=this.suppressedGamepadButtons.get(e);null==n&&(n=new Array(s.buttons.length).fill(!1),this.suppressedGamepadButtons.set(e,n)),this.gamepadButtonBinds.forEach((e,t)=>{for(const o of e){const e=o.button,a=s.buttons[e];if(!a)continue;const l=a.pressed,h=a.value,d=i[e];if(n[e]){l||(n[e]=!1);continue}if(l!==d){this.setLastActiveDevice("gamepad");const e=this.toggleCallbacks.get(t);e&&e(l)}const c=this.inputs.get(t)??this.deltaCallbacks.get(t);c&&(h>0||l||d&&0===h)&&"gamepad"===this.lastActiveDevice&&c(h)}});for(let e=0;e<s.buttons.length;e++)i[e]=s.buttons[e].pressed;this.gamepadAxisBinds.forEach((e,t)=>{for(const i of e){let e=s.axes[i.axis];if(e=Math.abs(e)<i.deadzone?0:(e-Math.sign(e)*i.deadzone)/(1-i.deadzone),0!==e&&this.setLastActiveDevice("gamepad"),"gamepad"!==this.lastActiveDevice)break;e*=i.multiplier;const n=this.inputs.get(t)??this.deltaCallbacks.get(t);n&&n(e)}})}}bind(e,t){this.inputs.set(e,t)}bindToggle(e,t){this.toggleCallbacks.set(e,t)}bindDelta(e,t){this.deltaCallbacks.set(e,t)}unbind(e){this.inputs.delete(e),this.toggleCallbacks.delete(e),this.deltaCallbacks.delete(e)}setKeybind(e,t){this.keybinds.has(e)||this.keybinds.set(e,[]),this.keybinds.get(e).push(t)}setMousebind(e,t){this.mousebinds.set(e,t)}setTouchMoveBind(e,t){this.touchmoveBinds.set(e,t)}setWheelbind(e,t){this.wheelbinds.set(e,t)}setGamepadButtonBind(e,t){this.gamepadButtonBinds.has(e)||this.gamepadButtonBinds.set(e,[]),this.gamepadButtonBinds.get(e).push(t)}setGamepadAxisBind(e,t){this.gamepadAxisBinds.has(e)||this.gamepadAxisBinds.set(e,[]),this.gamepadAxisBinds.get(e).push(t)}removeKeybind(e){this.keybinds.delete(e)}removeMousebind(e){this.mousebinds.delete(e)}removeWheelbind(e){this.wheelbinds.delete(e)}removeGamepadButtonBind(e){this.gamepadButtonBinds.delete(e)}removeGamepadAxisBind(e){this.gamepadAxisBinds.delete(e)}vibrate(e,t=1,s=1){const i=navigator.getGamepads();for(const n of i)n&&n.vibrationActuator&&n.vibrationActuator.playEffect("dual-rumble",{startDelay:0,duration:e,weakMagnitude:s,strongMagnitude:t})}};a=e([t()],a);export{a as InputService};function l(){return null!=document.pointerLockElement||null!=document.mozPointerLockElement}/*
1
+ import{__decorate as e}from"tslib";import{Service as t}from"typedi";import{inject as s}from"../inject.js";import{ViewController as i}from"../services/render.js";import{MathUtils as n}from"three";import{Subject as a}from"rxjs";let o=class{constructor(){this.view=s(i),this.inputs=new Map,this.deltaCallbacks=new Map,this.toggleCallbacks=new Map,this.keybinds=new Map,this.mousebinds=new Map,this.touchmoveBinds=new Map,this.wheelbinds=new Map,this.gamepadButtonBinds=new Map,this.gamepadAxisBinds=new Map,this.lastActiveDevice="kbm",this.onDeviceChanged=new a,this.prevGamepadButtonStates=new Map,this.suppressedGamepadButtons=new Map,this.sampledGamepadButtonStates=new Map,this.pendingGamepadButtonTransitions=new Map,this.frameGamepadButtonTransitions=new Map,this.onKeyDown=e=>{this.setLastActiveDevice("kbm"),this.view.paused||"button"in e&&!h()||this.keybinds.forEach((t,s)=>{for(const i of t)if(i.test(e))if(this.inputs.has(s))this.inputs.get(s)(!0);else if(this.toggleCallbacks.has(s)){this.toggleCallbacks.get(s)(!0);this.activeToggleCallbacks.findIndex(e=>e.keybind===i&&e.toggle===this.toggleCallbacks.get(s))<0&&this.activeToggleCallbacks.push({keybind:i,toggle:this.toggleCallbacks.get(s)})}else console.warn("No input callback for registered for key bind "+i.display())})},this.activeToggleCallbacks=[],this.onKeyUp=e=>{this.setLastActiveDevice("kbm"),this.keybinds.forEach((t,s)=>{for(const i of t)if(i.test(e))if(this.inputs.has(s))this.inputs.get(s)(!1);else if(this.toggleCallbacks.has(s)){this.toggleCallbacks.get(s)(!1);const e=this.activeToggleCallbacks.findIndex(e=>e.keybind===i&&e.toggle===this.toggleCallbacks.get(s));e>=0&&this.activeToggleCallbacks.splice(e,1);for(const e of this.activeToggleCallbacks)e.toggle(!0)}else console.warn("No input callback for registered for key bind "+i.display())})},this.onMouseMove=e=>{if((Math.abs(e.movementX)>1||Math.abs(e.movementY)>1)&&this.setLastActiveDevice("kbm"),this.view.paused)return;if(!h())return;const t=n.clamp(e.movementX,-50,50),s=n.clamp(e.movementY,-50,50);Math.abs(t)>200||Math.abs(s)>200||this.mousebinds.forEach((e,i)=>{const n=this.inputs.get(i)??this.deltaCallbacks.get(i);if(null!=n)switch(e.axis){case"x":n(t*e.multiplier);break;case"y":n(s*e.multiplier)}else console.warn("No input callback for registered for mouse bind "+e.axis)})},this.onTouchStart=e=>{if(this.setLastActiveDevice("kbm"),!this.view.paused){for(let t=0;t<=e.touches.length;t++){const s=e.touches[t];if(s?.target instanceof HTMLCanvasElement)return void(this.prevTouchEvent=s)}e.preventDefault()}},this.onTouchMove=e=>{if(this.view.paused)return;if(null==this.prevTouchEvent)return;let t=null;for(let s=0;s<=e.touches.length;s++){const i=e.touches[s];if(i&&i.target instanceof HTMLCanvasElement&&i.identifier===this.prevTouchEvent.identifier){t=i;break}}null!=t&&(this.touchmoveBinds.forEach((e,s)=>{const i=this.inputs.get(s)??this.deltaCallbacks.get(s);if(null!=i)switch(e.axis){case"x":i((t.clientX-this.prevTouchEvent.clientX)*e.multiplier);break;case"y":i((t.clientY-this.prevTouchEvent.clientY)*e.multiplier)}else console.warn("No input callback for registered for mouse bind "+e.axis)}),this.prevTouchEvent=t,e.preventDefault())},this.onTouchEnd=e=>{this.prevTouchEvent=null,e.preventDefault()},this.onWheel=e=>{this.setLastActiveDevice("kbm"),this.view.paused||this.wheelbinds.forEach((t,s)=>{const i=this.inputs.get(s)??this.deltaCallbacks.get(s);null!=i?i(e.deltaY*t.multiplier):console.warn("No input callback for registered for wheel bind")})}}getLastActiveDevice(){return this.lastActiveDevice}suppressHeldGamepadButtonsUntilRelease(){const e=navigator.getGamepads();this.sampleGamepadButtonsFrom(e,!1);for(let t=0;t<e.length;t++){const s=e[t];if(!s||"standard"!==s.mapping)continue;const i=this.suppressedGamepadButtons.get(t),n=this.pendingGamepadButtonTransitions.get(t),a=this.prevGamepadButtonStates.get(t),o=this.frameGamepadButtonTransitions.get(t);for(let e=0;e<s.buttons.length;e++)s.buttons[e].pressed&&(i[e]=!0),n[e]=0,a[e]=!1,o[e]=0}}setLastActiveDevice(e){this.lastActiveDevice!==e&&(this.lastActiveDevice=e,this.onDeviceChanged.next(e))}start(){document.addEventListener("keydown",this.onKeyDown),document.addEventListener("mousedown",this.onKeyDown),document.addEventListener("mouseup",this.onKeyUp),document.addEventListener("keyup",this.onKeyUp),document.addEventListener("mousemove",this.onMouseMove),this.view.htmlElement.addEventListener("touchstart",this.onTouchStart,{passive:!1}),this.view.htmlElement.addEventListener("touchmove",this.onTouchMove,{passive:!1}),this.view.htmlElement.addEventListener("touchend",this.onTouchEnd,{passive:!1}),document.addEventListener("wheel",this.onWheel)}stop(){document.removeEventListener("keydown",this.onKeyDown),document.removeEventListener("mousedown",this.onKeyDown),document.removeEventListener("mouseup",this.onKeyUp),document.removeEventListener("keyup",this.onKeyUp),document.removeEventListener("mousemove",this.onMouseMove),this.view.htmlElement.removeEventListener("touchstart",this.onTouchStart),this.view.htmlElement.removeEventListener("touchmove",this.onTouchMove),this.view.htmlElement.removeEventListener("touchend",this.onTouchEnd),document.removeEventListener("wheel",this.onWheel)}sampleGamepadButtons(e=!1){this.sampleGamepadButtonsFrom(navigator.getGamepads(),e)}sampleGamepadButtonsFrom(e,t){for(let s=0;s<e.length;s++){const i=e[s];if(!i||"standard"!==i.mapping)continue;let n=this.sampledGamepadButtonStates.get(s);null==n&&(n=new Array(i.buttons.length).fill(!1),this.sampledGamepadButtonStates.set(s,n),this.pendingGamepadButtonTransitions.set(s,new Array(i.buttons.length).fill(0)),this.frameGamepadButtonTransitions.set(s,new Array(i.buttons.length).fill(0)),this.prevGamepadButtonStates.set(s,new Array(i.buttons.length).fill(!1)),this.suppressedGamepadButtons.set(s,new Array(i.buttons.length).fill(!1)));const a=this.pendingGamepadButtonTransitions.get(s),o=this.suppressedGamepadButtons.get(s),h=this.prevGamepadButtonStates.get(s);for(let e=0;e<i.buttons.length;e++){const s=i.buttons[e].pressed;t?(a[e]=h[e]?1:0,s&&(o[e]=!0)):o[e]||s===n[e]||a[e]++,o[e]&&!s&&(o[e]=!1,a[e]=h[e]?1:0),n[e]=s}}}update(e){if(this.view.paused)return;const t=navigator.getGamepads();this.sampleGamepadButtonsFrom(t,!1);for(let e=0;e<t.length;e++){const s=t[e];if(!s||"standard"!==s.mapping)continue;const i=this.prevGamepadButtonStates.get(e),n=this.suppressedGamepadButtons.get(e),a=this.pendingGamepadButtonTransitions.get(e),o=this.frameGamepadButtonTransitions.get(e);for(let e=0;e<s.buttons.length;e++){o[e]=0===a[e]?0:i[e]?a[e]>1?3:2:1;const t=o[e];0!==t&&(i[e]=!!(1&t),a[e]-=3===t?2:1)}this.gamepadButtonBinds.forEach((e,t)=>{for(const a of e){const e=a.button,h=s.buttons[e];if(!h)continue;const l=h.pressed,d=h.value,r=o[e],u=0===r?i[e]:!!(2&r);if(n[e]&&2!==r)continue;if(0!==r){this.setLastActiveDevice("gamepad");const s=this.toggleCallbacks.get(t);s&&(2&r&&s(!1),1&o[e]&&!n[e]&&s(!0))}if(n[e])continue;const c=this.inputs.get(t)??this.deltaCallbacks.get(t);c&&(d>0||l||u&&0===d)&&"gamepad"===this.lastActiveDevice&&c(d)}}),this.gamepadAxisBinds.forEach((e,t)=>{for(const i of e){let e=s.axes[i.axis];if(e=Math.abs(e)<i.deadzone?0:(e-Math.sign(e)*i.deadzone)/(1-i.deadzone),0!==e&&this.setLastActiveDevice("gamepad"),"gamepad"!==this.lastActiveDevice)break;e*=i.multiplier;const n=this.inputs.get(t)??this.deltaCallbacks.get(t);n&&n(e)}})}}bind(e,t){this.inputs.set(e,t)}bindToggle(e,t){this.toggleCallbacks.set(e,t)}bindDelta(e,t){this.deltaCallbacks.set(e,t)}unbind(e){this.inputs.delete(e),this.toggleCallbacks.delete(e),this.deltaCallbacks.delete(e)}setKeybind(e,t){this.keybinds.has(e)||this.keybinds.set(e,[]),this.keybinds.get(e).push(t)}setMousebind(e,t){this.mousebinds.set(e,t)}setTouchMoveBind(e,t){this.touchmoveBinds.set(e,t)}setWheelbind(e,t){this.wheelbinds.set(e,t)}setGamepadButtonBind(e,t){this.gamepadButtonBinds.has(e)||this.gamepadButtonBinds.set(e,[]),this.gamepadButtonBinds.get(e).push(t)}setGamepadAxisBind(e,t){this.gamepadAxisBinds.has(e)||this.gamepadAxisBinds.set(e,[]),this.gamepadAxisBinds.get(e).push(t)}removeKeybind(e){this.keybinds.delete(e)}removeMousebind(e){this.mousebinds.delete(e)}removeWheelbind(e){this.wheelbinds.delete(e)}removeGamepadButtonBind(e){this.gamepadButtonBinds.delete(e)}removeGamepadAxisBind(e){this.gamepadAxisBinds.delete(e)}vibrate(e,t=1,s=1){const i=navigator.getGamepads();for(const n of i)n&&n.vibrationActuator&&n.vibrationActuator.playEffect("dual-rumble",{startDelay:0,duration:e,weakMagnitude:s,strongMagnitude:t})}};o=e([t()],o);export{o as InputService};function h(){return null!=document.pointerLockElement||null!=document.mozPointerLockElement}/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -22,13 +22,14 @@ export declare class RenderingHost {
22
22
  private nextStageId;
23
23
  private stopped;
24
24
  private frameCallback;
25
+ private inputFrameCallback?;
25
26
  private _activeView;
26
27
  constructor(applicationView: RenderingView, ownsApplicationView: boolean, viewFactory: RenderingViewFactory);
27
28
  get activeView(): RenderingView;
28
29
  get device(): GPUDevice;
29
30
  get graphicsRuntime(): import("../rendering.js").GraphicsRuntime;
30
31
  createView(): RenderingView;
31
- start(onFrame: (deltaTime: number) => void): void;
32
+ start(onFrame: (deltaTime: number) => void, onInputFrame?: (inactive: boolean) => void): void;
32
33
  activate(view: RenderingView): void;
33
34
  releaseView(view: RenderingView): void;
34
35
  registerStage(callback: (context: RenderingHostStageContext) => void): () => void;
@@ -1,4 +1,4 @@
1
- export class RenderingHost{constructor(e,t,s){this.applicationView=e,this.ownsApplicationView=t,this.viewFactory=s,this.views=new Set,this.frameDelta=new Map,this.stages=new Map,this.stageDisposers=new Map,this.nextStageId=0,this.stopped=!1,this.frameCallback=()=>{},this._activeView=e,this.views.add(e)}get activeView(){return this._activeView}get device(){return this._activeView.webGpuDevice}get graphicsRuntime(){return this.applicationView.sharedGraphicsRuntime}createView(){if(this.stopped)throw new Error("RenderingHost has been shut down");const e=this.viewFactory();if(this.views.has(e))throw new Error("RenderingViewFactory must return a new view");return e.suspendPresentation(),this.views.add(e),this.installStages(e),e}start(e){this.frameCallback=e,this.activate(this.applicationView)}activate(e){if(!this.views.has(e))throw new Error("RenderingView is not owned by this host");this._activeView!==e&&this._activeView.suspendPresentation(),this._activeView=e,this.installStages(e),e.activatePresentation(),e.loop(t=>{this.frameDelta.set(e,t),this.frameCallback(t)})}releaseView(e){if(e===this.applicationView)throw new Error("Cannot release the application view");this.views.delete(e)&&(this.removeStages(e),this.frameDelta.delete(e),e.stop())}registerStage(e){const t=++this.nextStageId;this.stages.set(t,e);for(const s of this.views)this.installStage(s,t,e);return()=>{this.stages.delete(t);for(const e of this.stageDisposers.values())e.get(t)?.(),e.delete(t)}}shutdown(){if(!this.stopped){this.stopped=!0;for(const e of this.views)this.removeStages(e),e!==this.applicationView||this.ownsApplicationView?e.stop():e.suspendPresentation();this.views.clear(),this.frameDelta.clear()}}installStages(e){for(const[t,s]of this.stages)this.installStage(e,t,s)}installStage(e,t,s){let i=this.stageDisposers.get(e);if(null==i&&(i=new Map,this.stageDisposers.set(e,i)),i.has(t))return;const a=this;i.set(t,e.registerWebGpuPresentationExtension(t=>{const i=t;i.deltaTime=a.frameDelta.get(e)??0,i.elapsedTime=e.simulationTime,s(i)}))}removeStages(e){const t=this.stageDisposers.get(e);if(null!=t){for(const e of t.values())e();this.stageDisposers.delete(e)}}}/*
1
+ export class RenderingHost{constructor(e,t,s){this.applicationView=e,this.ownsApplicationView=t,this.viewFactory=s,this.views=new Set,this.frameDelta=new Map,this.stages=new Map,this.stageDisposers=new Map,this.nextStageId=0,this.stopped=!1,this.frameCallback=()=>{},this._activeView=e,this.views.add(e)}get activeView(){return this._activeView}get device(){return this._activeView.webGpuDevice}get graphicsRuntime(){return this.applicationView.sharedGraphicsRuntime}createView(){if(this.stopped)throw new Error("RenderingHost has been shut down");const e=this.viewFactory();if(this.views.has(e))throw new Error("RenderingViewFactory must return a new view");return e.suspendPresentation(),this.views.add(e),this.installStages(e),e}start(e,t){this.frameCallback=e,this.inputFrameCallback=t,this.activate(this.applicationView)}activate(e){if(!this.views.has(e))throw new Error("RenderingView is not owned by this host");this._activeView!==e&&this._activeView.suspendPresentation(),this._activeView=e,this.installStages(e),e.activatePresentation(),e.loop(t=>{this.frameDelta.set(e,t),this.frameCallback(t)},!1,this.inputFrameCallback)}releaseView(e){if(e===this.applicationView)throw new Error("Cannot release the application view");this.views.delete(e)&&(this.removeStages(e),this.frameDelta.delete(e),e.stop())}registerStage(e){const t=++this.nextStageId;this.stages.set(t,e);for(const s of this.views)this.installStage(s,t,e);return()=>{this.stages.delete(t);for(const e of this.stageDisposers.values())e.get(t)?.(),e.delete(t)}}shutdown(){if(!this.stopped){this.stopped=!0;for(const e of this.views)this.removeStages(e),e!==this.applicationView||this.ownsApplicationView?e.stop():e.suspendPresentation();this.views.clear(),this.frameDelta.clear()}}installStages(e){for(const[t,s]of this.stages)this.installStage(e,t,s)}installStage(e,t,s){let i=this.stageDisposers.get(e);if(null==i&&(i=new Map,this.stageDisposers.set(e,i)),i.has(t))return;const a=this;i.set(t,e.registerWebGpuPresentationExtension(t=>{const i=t;i.deltaTime=a.frameDelta.get(e)??0,i.elapsedTime=e.simulationTime,s(i)}))}removeStages(e){const t=this.stageDisposers.get(e);if(null!=t){for(const e of t.values())e();this.stageDisposers.delete(e)}}}/*
2
2
  * Copyright (©) 2026 Hology Interactive AB. All rights reserved.
3
3
  * See the LICENSE.md file for details.
4
4
  */
@@ -29,6 +29,12 @@ export type InteractionGroups = number;
29
29
  export type KinematicCharacterController = RAPIER.KinematicCharacterController;
30
30
  export declare class RayTestResult {
31
31
  actor?: BaseActor;
32
+ /** Source scene object, when available. May be an asset root rather than a mesh. */
33
+ object?: Object3D;
34
+ /** Instance index for an InstancedMesh. */
35
+ instanceId?: number;
36
+ /** Instance index for a BatchedMesh. */
37
+ batchId?: number;
32
38
  hasHit: boolean;
33
39
  distance: number;
34
40
  hitPoint: Vector3;
@@ -111,6 +117,7 @@ export declare class PhysicsSystem extends AbstractPhysicsSystem {
111
117
  private actorBodies;
112
118
  private bodyActors;
113
119
  private colliders;
120
+ private colliderObjects;
114
121
  private eventQueue;
115
122
  private fixedupdateSub;
116
123
  private collisionSub;