@needle-tools/engine 2.37.0-pre → 2.38.0-pre

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 (82) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/needle-engine.d.ts +80 -13
  3. package/dist/needle-engine.js +349 -349
  4. package/dist/needle-engine.js.map +4 -4
  5. package/dist/needle-engine.min.js +25 -25
  6. package/dist/needle-engine.min.js.map +4 -4
  7. package/lib/engine/engine_addressables.d.ts +3 -1
  8. package/lib/engine/engine_addressables.js +12 -5
  9. package/lib/engine/engine_addressables.js.map +1 -1
  10. package/lib/engine/engine_element.js +3 -2
  11. package/lib/engine/engine_element.js.map +1 -1
  12. package/lib/engine/engine_element_overlay.js +4 -3
  13. package/lib/engine/engine_element_overlay.js.map +1 -1
  14. package/lib/engine/engine_input.d.ts +2 -0
  15. package/lib/engine/engine_input.js +14 -3
  16. package/lib/engine/engine_input.js.map +1 -1
  17. package/lib/engine/engine_physics.d.ts +2 -4
  18. package/lib/engine/engine_physics.js +75 -30
  19. package/lib/engine/engine_physics.js.map +1 -1
  20. package/lib/engine/engine_physics.types.d.ts +7 -0
  21. package/lib/engine/engine_physics.types.js +8 -0
  22. package/lib/engine/engine_physics.types.js.map +1 -1
  23. package/lib/engine/engine_setup.d.ts +7 -1
  24. package/lib/engine/engine_setup.js +10 -2
  25. package/lib/engine/engine_setup.js.map +1 -1
  26. package/lib/engine/engine_types.d.ts +1 -1
  27. package/lib/engine/engine_types.js.map +1 -1
  28. package/lib/engine-components/Animation.d.ts +1 -0
  29. package/lib/engine-components/Animation.js +7 -0
  30. package/lib/engine-components/Animation.js.map +1 -1
  31. package/lib/engine-components/AnimatorController.js +14 -7
  32. package/lib/engine-components/AnimatorController.js.map +1 -1
  33. package/lib/engine-components/Camera.d.ts +1 -0
  34. package/lib/engine-components/Camera.js +20 -5
  35. package/lib/engine-components/Camera.js.map +1 -1
  36. package/lib/engine-components/CharacterController.d.ts +31 -0
  37. package/lib/engine-components/CharacterController.js +167 -0
  38. package/lib/engine-components/CharacterController.js.map +1 -0
  39. package/lib/engine-components/Collider.d.ts +10 -4
  40. package/lib/engine-components/Collider.js +18 -8
  41. package/lib/engine-components/Collider.js.map +1 -1
  42. package/lib/engine-components/Light.d.ts +2 -0
  43. package/lib/engine-components/Light.js +13 -2
  44. package/lib/engine-components/Light.js.map +1 -1
  45. package/lib/engine-components/Renderer.js +4 -0
  46. package/lib/engine-components/Renderer.js.map +1 -1
  47. package/lib/engine-components/RigidBody.d.ts +6 -1
  48. package/lib/engine-components/RigidBody.js +62 -25
  49. package/lib/engine-components/RigidBody.js.map +1 -1
  50. package/lib/engine-components/SmoothFollow.d.ts +2 -1
  51. package/lib/engine-components/SmoothFollow.js +25 -17
  52. package/lib/engine-components/SmoothFollow.js.map +1 -1
  53. package/lib/engine-components/WebXR.js +3 -4
  54. package/lib/engine-components/WebXR.js.map +1 -1
  55. package/lib/engine-components/codegen/components.d.ts +3 -0
  56. package/lib/engine-components/codegen/components.js +3 -0
  57. package/lib/engine-components/codegen/components.js.map +1 -1
  58. package/package.json +1 -1
  59. package/src/engine/codegen/register_types.js +12 -0
  60. package/src/engine/dist/engine_physics.js +739 -0
  61. package/src/engine/dist/engine_setup.js +777 -0
  62. package/src/engine/engine_addressables.ts +18 -8
  63. package/src/engine/engine_element.ts +3 -2
  64. package/src/engine/engine_element_overlay.ts +4 -3
  65. package/src/engine/engine_input.ts +12 -3
  66. package/src/engine/engine_physics.ts +82 -47
  67. package/src/engine/engine_physics.types.ts +9 -0
  68. package/src/engine/engine_setup.ts +26 -17
  69. package/src/engine/engine_types.ts +2 -1
  70. package/src/engine-components/Animation.ts +8 -0
  71. package/src/engine-components/AnimatorController.ts +16 -11
  72. package/src/engine-components/Camera.ts +21 -4
  73. package/src/engine-components/CharacterController.ts +171 -0
  74. package/src/engine-components/Collider.ts +22 -12
  75. package/src/engine-components/Light.ts +17 -3
  76. package/src/engine-components/Renderer.ts +5 -1
  77. package/src/engine-components/RigidBody.ts +63 -29
  78. package/src/engine-components/SmoothFollow.ts +21 -18
  79. package/src/engine-components/WebXR.ts +3 -4
  80. package/src/engine-components/codegen/components.ts +3 -0
  81. package/src/engine-components/dist/CharacterController.js +123 -0
  82. package/src/engine-components/dist/RigidBody.js +458 -0
@@ -5,7 +5,7 @@ import { Context } from "./engine_setup";
5
5
  import { Group, Object3D, Scene } from "three";
6
6
  import { processNewScripts } from "./engine_mainloop_utils";
7
7
  import { InstantiateEvent, registerPrefabProvider, syncInstantiate } from "./engine_networking_instantiate";
8
- import { download } from "./engine_web_api";
8
+ import { download, hash } from "./engine_web_api";
9
9
  import { getLoader } from "./engine_gltf";
10
10
  import { SourceIdentifier } from "./engine_types";
11
11
  import { destroy, instantiate, InstantiateOptions } from "./engine_gameobject";
@@ -51,11 +51,11 @@ export class AssetReference {
51
51
 
52
52
  static getOrCreate(sourceId: SourceIdentifier, uri: string, context: Context): AssetReference {
53
53
  const fullPath = getPath(sourceId, uri);
54
- if(debug) console.log("GetOrCreate Addressable from", sourceId, uri, "FinalPath=", fullPath);
54
+ if (debug) console.log("GetOrCreate Addressable from", sourceId, uri, "FinalPath=", fullPath);
55
55
  const addressables = context.addressables;
56
56
  const existing = addressables.findAssetReference(fullPath);
57
57
  if (existing) return existing;
58
- const ref = new AssetReference(fullPath);
58
+ const ref = new AssetReference(fullPath, context.hash);
59
59
  addressables.registerAssetReference(ref);
60
60
  return ref;
61
61
  }
@@ -63,6 +63,7 @@ export class AssetReference {
63
63
  get asset(): any {
64
64
  return this._glbRoot ?? this._asset;
65
65
  }
66
+
66
67
  protected set asset(val: any) {
67
68
  this._asset = val;
68
69
  }
@@ -70,7 +71,7 @@ export class AssetReference {
70
71
  private _loading?: PromiseLike<any>;
71
72
 
72
73
  get uri(): string {
73
- return this._uri!;
74
+ return this._uri;
74
75
  }
75
76
 
76
77
  get rawAsset(): any { return this._asset; }
@@ -80,11 +81,20 @@ export class AssetReference {
80
81
  private _uri: string;
81
82
  private _progressListeners: ProgressCallback[] = [];
82
83
 
84
+ private _hash?: string;
85
+ private _hashedUri: string;
86
+
83
87
  private _isLoadingRawBinary: boolean = false;
84
88
  private _rawBinary?: ArrayBuffer | null;
85
89
 
86
- constructor(uri: string) {
90
+ constructor(uri: string, hash?: string) {
87
91
  this._uri = uri;
92
+ this._hash = hash;
93
+ if (uri.includes("?v="))
94
+ this._hashedUri = uri;
95
+ else
96
+ this._hashedUri = hash ? uri + "?v=" + hash : uri;
97
+
88
98
  registerPrefabProvider(this._uri, this.onResolvePrefab.bind(this));
89
99
  }
90
100
 
@@ -120,8 +130,8 @@ export class AssetReference {
120
130
  if (this._isLoadingRawBinary) return null;
121
131
  if (this._rawBinary !== undefined) return this._rawBinary;
122
132
  this._isLoadingRawBinary = true;
123
- if (debug) console.log("Preload", this.uri);
124
- const res = await download(this.uri, p => {
133
+ if (debug) console.log("Preload", this._hashedUri);
134
+ const res = await download(this._hashedUri, p => {
125
135
  this.raiseProgressEvent(p);
126
136
  });
127
137
  this._rawBinary = res?.buffer ?? null;
@@ -148,7 +158,7 @@ export class AssetReference {
148
158
  }
149
159
  else {
150
160
  if (debug) console.log("Load async", this.uri);
151
- this._loading = getLoader().loadSync(context, this.uri, null, true, prog => {
161
+ this._loading = getLoader().loadSync(context, this._hashedUri, null, true, prog => {
152
162
  this.raiseProgressEvent(prog);
153
163
  });
154
164
  }
@@ -133,7 +133,8 @@ export class EngineElement extends HTMLElement implements INeedleEngineComponent
133
133
  if (srcAttributeValue) src = srcAttributeValue;
134
134
 
135
135
  const alias = this.getAttribute("alias");
136
- this._context = new Context({ name: src, domElement: this, alias: alias });
136
+ const hash = this.getAttribute("hash");
137
+ this._context = new Context({ name: src, domElement: this, alias: alias, hash: hash ?? undefined });
137
138
  this._watcher = new EngineElementSourceFileWatcher(this.getAttribute("id") ?? alias ?? "", this._context);
138
139
 
139
140
  if (src && src.length > 0) {
@@ -234,7 +235,7 @@ export class EngineElement extends HTMLElement implements INeedleEngineComponent
234
235
  }
235
236
 
236
237
  static get observedAttributes() {
237
- return ["src", "loadstart", "progress", "loadfinished", "dracoDecoderPath", "dracoDecoderType", "ktx2DecoderPath"];
238
+ return ["hash", "src", "loadstart", "progress", "loadfinished", "dracoDecoderPath", "dracoDecoderType", "ktx2DecoderPath"];
238
239
  }
239
240
 
240
241
  attributeChangedCallback(name: string, _oldValue: string, newValue: string) {
@@ -73,11 +73,12 @@ export class AROverlayHandler {
73
73
  return element;
74
74
  }
75
75
  if (element.children) {
76
- // @ts-ignore
77
- for (const ch of element.children) {
76
+ for (let i = 0; i < element.children.length; i++) {
77
+ const ch = element.children[i] as HTMLElement;
78
78
  if (!ch || !ch.classList) continue;
79
- if (ch.classList.contains(arContainerClassName))
79
+ if (ch.classList.contains(arContainerClassName)) {
80
80
  return ch;
81
+ }
81
82
  }
82
83
  }
83
84
 
@@ -147,6 +147,10 @@ export class Input extends EventTarget {
147
147
  if (i >= this._mouseWheelChanged.length) return false;
148
148
  return this._mouseWheelChanged[i];
149
149
  }
150
+ getMouseWheelDeltaY(i: number = 0): number {
151
+ if (i >= this._mouseWheelDeltaY.length) return 0;
152
+ return this._mouseWheelDeltaY[i];
153
+ }
150
154
  getPointerEvent(i: number): Event | undefined {
151
155
  if (i >= this._pointerEvent.length) return undefined;
152
156
  return this._pointerEvent[i] ?? undefined;
@@ -169,6 +173,7 @@ export class Input extends EventTarget {
169
173
  private _pointerIds: number[] = [];
170
174
  private _pointerTypes: string[] = [""];
171
175
  private _mouseWheelChanged: boolean[] = [false];
176
+ private _mouseWheelDeltaY: number[] = [0];
172
177
  private _pointerEvent: Event[] = [];
173
178
 
174
179
  getKeyDown(): string | null {
@@ -269,11 +274,12 @@ export class Input extends EventTarget {
269
274
  this._pointerClick[i] = false;
270
275
  for (let i = 0; i < this._pointerDoubleClick.length; i++)
271
276
  this._pointerDoubleClick[i] = false;
272
- for (const pt of this._pointerPositionsDelta) {
277
+ for (const pt of this._pointerPositionsDelta)
273
278
  pt.set(0, 0);
274
- }
275
279
  for (let i = 0; i < this._mouseWheelChanged.length; i++)
276
280
  this._mouseWheelChanged[i] = false;
281
+ for (let i = 0; i < this._mouseWheelDeltaY.length; i++)
282
+ this._mouseWheelDeltaY[i] = 0;
277
283
  }
278
284
 
279
285
 
@@ -307,9 +313,12 @@ export class Input extends EventTarget {
307
313
  this.onDispatchEvent(InputEvents.KeyUp, new KeyEventArgs(evt));
308
314
  }
309
315
 
310
- private onMouseWheel(_evt) {
316
+ private onMouseWheel(evt: WheelEvent) {
317
+ if (this._mouseWheelDeltaY.length <= 0) this._mouseWheelDeltaY.push(0);
311
318
  if (this._mouseWheelChanged.length <= 0) this._mouseWheelChanged.push(false);
312
319
  this._mouseWheelChanged[0] = true;
320
+ const current = this._mouseWheelDeltaY[0];
321
+ this._mouseWheelDeltaY[0] = current + evt.deltaY;
313
322
  }
314
323
 
315
324
  private onTouchMove(_evt) {
@@ -1,23 +1,19 @@
1
1
  import { BasicDepthPacking, Box3, BufferAttribute, BufferGeometry, Camera, Intersection, Layers, LineBasicMaterial, LineSegments, Matrix4, Mesh, NormalAnimationBlendMode, Object3D, Quaternion, Ray, Raycaster, Sphere, Vector2, Vector3 } from 'three'
2
2
  import { Context } from './engine_setup';
3
3
  import { getParam } from "./engine_utils"
4
- import { getWorldPosition, getWorldQuaternion, getWorldScale, setWorldPosition, setWorldPositionXYZ, setWorldQuaternionXYZW } from "./engine_three_utils"
4
+ import { getWorldPosition, getWorldQuaternion, getWorldScale, setWorldPositionXYZ, setWorldQuaternionXYZW } from "./engine_three_utils"
5
5
  import {
6
6
  IComponent,
7
- IGameObject,
8
7
  ICollider,
9
8
  IRigidbody,
10
9
  Collision,
11
- ICollisionContext,
12
- ContactPoint,
13
- Vec3
10
+ ContactPoint
14
11
  } from './engine_types';
15
12
  import { InstancingUtil } from './engine_instancing';
16
13
  import { foreachComponent } from './engine_gameobject';
17
14
 
18
- import RAPIER, { ActiveEvents, Collider, ColliderDesc, EventQueue, RigidBody, TempContactManifold, World } from '@dimforge/rapier3d-compat';
19
- import { CollisionDetectionMode, RigidbodyConstraints } from '../engine/engine_physics.types';
20
- import { showBalloonWarning } from './debug/debug';
15
+ import RAPIER, { ActiveEvents, Collider, ColliderDesc, EventQueue, RigidBody, RigidBodyType, World } from '@dimforge/rapier3d-compat';
16
+ import { CollisionDetectionMode } from '../engine/engine_physics.types';
21
17
  export type Rapier = typeof RAPIER;
22
18
 
23
19
 
@@ -33,6 +29,8 @@ declare type PhysicsBody = {
33
29
 
34
30
  const $componentKey = Symbol("needle component");
35
31
  const $bodyKey = Symbol("physics body");
32
+ const $colliderRigidbody = Symbol("rigidbody");
33
+ // const $removed = Symbol("removed");
36
34
 
37
35
  export class RaycastOptions {
38
36
  ray: Ray | undefined = undefined;
@@ -209,6 +207,7 @@ export class Physics {
209
207
 
210
208
  private _tempPosition: Vector3 = new Vector3();
211
209
  private _tempQuaternion: Quaternion = new Quaternion();
210
+ private _tempScale: Vector3 = new Vector3();
212
211
  private _tempMatrix: Matrix4 = new Matrix4();
213
212
 
214
213
  private static _didLoadPhysicsEngine: boolean = false;
@@ -266,6 +265,14 @@ export class Physics {
266
265
  this.createCollider(collider, desc, center);
267
266
  }
268
267
 
268
+ addCapsuleCollider(collider: ICollider, center: Vector3, height: number, radius: number) {
269
+ const obj = collider.gameObject;
270
+ const scale = getWorldScale(obj, this._tempPosition);
271
+ if(debugPhysics) console.log("capsule scale", scale, height, radius);
272
+ const desc = ColliderDesc.capsule(height * .5 * scale.y - radius, radius * scale.x);
273
+ this.createCollider(collider, desc, center);
274
+ }
275
+
269
276
  addMeshCollider(collider: ICollider, mesh: Mesh, convex: boolean) {
270
277
  const geo = mesh.geometry;
271
278
  if (!geo) {
@@ -308,9 +315,10 @@ export class Physics {
308
315
  useExplicitMassProperties
309
316
  } = this.getRigidbody(collider, this._tempMatrix);
310
317
 
311
- matrix.decompose(this._tempPosition, this._tempQuaternion, new Vector3());
318
+ matrix.decompose(this._tempPosition, this._tempQuaternion, this._tempScale);
319
+ getWorldScale(collider.gameObject, this._tempScale);
312
320
  if (center)
313
- this._tempPosition.add(center);
321
+ this._tempPosition.add(center).multiply(this._tempScale);
314
322
  desc.setTranslation(this._tempPosition.x, this._tempPosition.y, this._tempPosition.z);
315
323
  desc.setRotation(this._tempQuaternion);
316
324
 
@@ -373,13 +381,15 @@ export class Physics {
373
381
 
374
382
  }
375
383
 
384
+ collider[$colliderRigidbody] = rigidBody;
385
+
376
386
  return { rigidBody: rigidBody, useExplicitMassProperties: useExplicitMassProperties };
377
387
  }
378
388
 
379
389
  removeBody(obj: IComponent) {
380
390
  const body = obj[$bodyKey];
381
391
  obj[$bodyKey] = null;
382
- if (body) {
392
+ if (body && this.world) {
383
393
  const index = this.objects.findIndex(o => o === obj);
384
394
  if (index >= 0) {
385
395
  const body = this.bodies[index];
@@ -387,20 +397,37 @@ export class Physics {
387
397
  this.objects.splice(index, 1);
388
398
 
389
399
  if (body instanceof Collider) {
390
- this.world?.removeCollider(body as Collider, true);
400
+ const collider = body as Collider;
401
+ this.world?.removeCollider(collider, true);
402
+
403
+ // remove the rigidbody if it doesnt have colliders anymore
404
+ const rb = collider.parent();
405
+ if (rb && rb.numColliders() <= 0) {
406
+ this.world?.removeRigidBody(rb);
407
+ }
391
408
  }
392
409
  else if (body instanceof RigidBody) {
393
- this.world?.removeRigidBody(body as RigidBody);
410
+ // TODO: running this code below causes a crash in rapier
411
+ // const rb = body as RigidBody;
412
+ // console.log("colliders", rb.numColliders())
413
+ // for (let i = 0; i < rb.numColliders(); i++) {
414
+ // const col = rb.collider(i);
415
+ // this.world?.removeCollider(col, true);
416
+ // }
417
+ // console.log("colliders", rb.numColliders(), rb)
418
+ // console.log(rb.handle, rb.userData);
419
+ // if (rb.userData === undefined)
420
+ // this.world?.removeRigidBody(rb);
394
421
  }
395
422
 
396
423
  // check if we need to remove the rigidbody too
397
- const col = obj as ICollider;
398
- if (col.isCollider && col.attachedRigidbody) {
399
- const rb = col.attachedRigidbody[$bodyKey];
400
- if (rb && rb.numColliders() <= 0 && rb.world() === this.world) {
401
- this.world?.removeRigidBody(rb);
402
- }
403
- }
424
+ // const col = obj as ICollider;
425
+ // if (col.isCollider && col.attachedRigidbody) {
426
+ // const rb = col.attachedRigidbody[$bodyKey] as RigidBody;
427
+ // if (rb && rb.numColliders() <= 0) {
428
+ // // this.world?.removeRigidBody(rb);
429
+ // }
430
+ // }
404
431
  }
405
432
  }
406
433
  }
@@ -526,10 +553,22 @@ export class Physics {
526
553
  continue;
527
554
  }
528
555
 
556
+
529
557
  // sync
530
558
  const pos = body.translation();
531
- setWorldPositionXYZ(obj.gameObject, pos.x, pos.y, pos.z);
532
559
  const rot = body.rotation();
560
+ // make sure to keep the collider offset
561
+ const center = obj["center"] as Vector3;
562
+ if (center && center.isVector3) {
563
+ this._tempQuaternion.set(rot.x, rot.y, rot.z, rot.w);
564
+ const offset = this._tempPosition.copy(center).applyQuaternion(this._tempQuaternion);
565
+ const scale = getWorldScale(obj.gameObject);
566
+ offset.multiply(scale);
567
+ pos.x -= offset.x;
568
+ pos.y -= offset.y;
569
+ pos.z -= offset.z;
570
+ }
571
+ setWorldPositionXYZ(obj.gameObject, pos.x, pos.y, pos.z);
533
572
  setWorldQuaternionXYZW(obj.gameObject, rot.x, rot.y, rot.z, rot.w);
534
573
  }
535
574
  }
@@ -542,13 +581,27 @@ export class Physics {
542
581
 
543
582
  const worldPosition = getWorldPosition(obj, this._tempPosition);
544
583
  const worldQuaternion = getWorldQuaternion(obj, this._tempQuaternion);
584
+ const type = body.bodyType();
585
+ switch (type) {
586
+ case RigidBodyType.Fixed:
587
+ case RigidBodyType.KinematicPositionBased:
588
+ case RigidBodyType.KinematicVelocityBased:
589
+ if (translation)
590
+ body.setNextKinematicTranslation(worldPosition);
591
+ if (rotation)
592
+ body.setNextKinematicRotation(worldQuaternion);
593
+ break;
594
+ default:
595
+ if (translation)
596
+ body.setTranslation(worldPosition, false);
597
+ if (rotation)
598
+ body.setRotation(worldQuaternion, false);
599
+ break;
600
+
601
+ }
602
+ body.wakeUp();
545
603
  // physicsBody.setBodyType(RAPIER.RigidBodyType.Fixed);
546
- if (translation)
547
- body.setTranslation(worldPosition, false);
548
- if (rotation)
549
- body.setRotation(worldQuaternion, false);
550
604
  // physicsBody.setLinvel(vel, false);
551
- body.wakeUp();
552
605
 
553
606
  // update velocity
554
607
  // const pos = physicsBody.translation();
@@ -600,25 +653,6 @@ export class Physics {
600
653
  }
601
654
 
602
655
 
603
- export interface IColliderProvider {
604
- getCollider(obj: Object3D): ICollider;
605
- }
606
-
607
- let colliderProvider: IColliderProvider | null = null;
608
- export function registerColliderProvider(prov: IColliderProvider) {
609
- colliderProvider = prov;
610
- }
611
-
612
- class CollisionContext implements ICollisionContext {
613
-
614
- getCollider(obj: Object3D<Event>): ICollider {
615
- return colliderProvider!.getCollider(obj);
616
- }
617
-
618
- }
619
-
620
-
621
-
622
656
 
623
657
  /** responsible of processing collision events for the component system */
624
658
  class PhysicsCollisionHandler {
@@ -642,7 +676,8 @@ class PhysicsCollisionHandler {
642
676
  const col2 = this.world!.getCollider(handle2);
643
677
  const colliderComponent1 = col1[$componentKey];
644
678
  const colliderComponent2 = col2[$componentKey];
645
- // console.log("EVT", colliderComponent1.name, colliderComponent2.name, started);
679
+ if (debugCollisions)
680
+ console.log("EVT", colliderComponent1.name, colliderComponent2.name, started, col1, col2);
646
681
  if (colliderComponent1 && colliderComponent2) {
647
682
  if (started) {
648
683
  this.onCollisionStarted(colliderComponent1, col1, colliderComponent2, col2);
@@ -710,7 +745,7 @@ class PhysicsCollisionHandler {
710
745
  for (const active of this.activeTriggers) {
711
746
  const c = active.component;
712
747
  if (c.activeAndEnabled && c.onTriggerStay) {
713
- const arg = active.collider;
748
+ const arg = active.otherCollider;
714
749
  c.onTriggerStay(arg);
715
750
  }
716
751
  }
@@ -16,4 +16,13 @@ export enum RigidbodyConstraints {
16
16
  FreezeRotationZ = 64,
17
17
  FreezeRotation = 112,
18
18
  FreezeAll = 126,
19
+ }
20
+
21
+
22
+ export enum Axes {
23
+ None = 0,
24
+ X = 2,
25
+ Y = 4,
26
+ Z = 8,
27
+ All = ~0,
19
28
  }
@@ -58,6 +58,7 @@ export class ContextArgs {
58
58
  alias: string | undefined | null = undefined;
59
59
  domElement: HTMLElement | null;
60
60
  renderer?: THREE.WebGLRenderer = undefined;
61
+ hash?: string;
61
62
 
62
63
  constructor(domElement: HTMLElement | null) {
63
64
  this.domElement = domElement ?? document.body;
@@ -70,7 +71,8 @@ export enum FrameEvent {
70
71
  LateUpdate = 2,
71
72
  OnBeforeRender = 3,
72
73
  OnAfterRender = 4,
73
- PhysicsStep = 10,
74
+ PrePhysicsStep = 9,
75
+ PostPhysicsStep = 10,
74
76
  }
75
77
 
76
78
  export enum XRSessionMode {
@@ -81,12 +83,12 @@ export enum XRSessionMode {
81
83
  export declare type OnBeforeRenderCallback = (renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera, geometry: THREE.BufferGeometry, material: THREE.Material, group: THREE.Group) => void
82
84
 
83
85
 
84
- export function registerComponent(script : IComponent, context?: Context) {
85
- if (!script) return;
86
- const new_scripts = context?.new_scripts ?? Context.Current.new_scripts;
87
- if (!new_scripts.includes(script)) {
88
- new_scripts.push(script);
89
- }
86
+ export function registerComponent(script: IComponent, context?: Context) {
87
+ if (!script) return;
88
+ const new_scripts = context?.new_scripts ?? Context.Current.new_scripts;
89
+ if (!new_scripts.includes(script)) {
90
+ new_scripts.push(script);
91
+ }
90
92
  }
91
93
 
92
94
  export class Context {
@@ -105,13 +107,16 @@ export class Context {
105
107
  alias: string | undefined | null;
106
108
  isManagedExternally: boolean = false;
107
109
 
110
+ /** used to append to loaded assets */
111
+ hash?: string;
112
+
108
113
  domElement: HTMLElement;
109
114
  get resolutionScaleFactor() { return this._resolutionScaleFactor; }
110
115
  /** use to scale the resolution up or down of the renderer. default is 1 */
111
- set resolutionScaleFactor(val: number) {
112
- if(val === this._resolutionScaleFactor) return;
113
- if(typeof val !== "number") return;
114
- if(val <= 0) {
116
+ set resolutionScaleFactor(val: number) {
117
+ if (val === this._resolutionScaleFactor) return;
118
+ if (typeof val !== "number") return;
119
+ if (val <= 0) {
115
120
  console.error("Invalid resolution scale factor", val);
116
121
  return;
117
122
  }
@@ -124,7 +129,9 @@ export class Context {
124
129
  get domX(): number { return this.domElement.offsetLeft; }
125
130
  get domY(): number { return this.domElement.offsetTop; }
126
131
  get isInXR() { return this.renderer.xr?.isPresenting || false; }
127
- xrSessionMode : XRSessionMode | undefined = undefined;
132
+ xrSessionMode: XRSessionMode | undefined = undefined;
133
+ get isInVR() { return this.xrSessionMode === XRSessionMode.ImmersiveVR; }
134
+ get isInAR() { return this.xrSessionMode === XRSessionMode.ImmersiveAR; }
128
135
  get xrSession() { return this.renderer.xr?.getSession(); }
129
136
  get arOverlayElement(): HTMLElement {
130
137
  const el = this.domElement as any;
@@ -182,7 +189,7 @@ export class Context {
182
189
  rendererData: RendererData;
183
190
  addressables: Addressables;
184
191
  lightmaps: ILightDataRegistry;
185
- players : PlayerViewManager;
192
+ players: PlayerViewManager;
186
193
 
187
194
  private _sizeChanged: boolean = false;
188
195
  private _isCreated: boolean = false;
@@ -193,6 +200,7 @@ export class Context {
193
200
  this.name = args?.name || "";
194
201
  this.alias = args?.alias;
195
202
  this.domElement = args?.domElement || document.body;
203
+ this.hash = args?.hash;
196
204
  if (args?.renderer) {
197
205
  this.renderer = args.renderer;
198
206
  this.isManagedExternally = true;
@@ -232,7 +240,7 @@ export class Context {
232
240
  this.lightmaps = new LightDataRegistry(this);
233
241
  this.players = new PlayerViewManager(this);
234
242
 
235
- window.addEventListener('resize', () => this._sizeChanged = true );
243
+ window.addEventListener('resize', () => this._sizeChanged = true);
236
244
  const ro = new ResizeObserver(_ => this._sizeChanged = true);
237
245
  ro.observe(this.domElement);
238
246
  }
@@ -335,13 +343,13 @@ export class Context {
335
343
  }
336
344
 
337
345
  removeCamera(cam?: Camera | null) {
338
- if(!cam) return;
346
+ if (!cam) return;
339
347
  const index = this._cameraStack.indexOf(cam);
340
348
  if (index >= 0) this._cameraStack.splice(index, 1);
341
349
 
342
350
  if (this.mainCameraComponent === cam) {
343
351
  this.mainCameraComponent = undefined;
344
-
352
+
345
353
  if (this._cameraStack.length > 0) {
346
354
  const last = this._cameraStack[this._cameraStack.length - 1];
347
355
  this.setCurrentCamera(last);
@@ -569,8 +577,9 @@ export class Context {
569
577
  const physicsSteps = 1;
570
578
  const dt = this.time.deltaTime / physicsSteps;
571
579
  for (let i = 0; i < physicsSteps; i++) {
580
+ this.executeCoroutines(FrameEvent.PrePhysicsStep);
572
581
  this.physics.step(dt);
573
- this.executeCoroutines(FrameEvent.PhysicsStep);
582
+ this.executeCoroutines(FrameEvent.PostPhysicsStep);
574
583
  }
575
584
  }
576
585
  catch (err) {
@@ -173,7 +173,7 @@ export class ContactPoint {
173
173
  /// all info in here must be readonly because the object is only created once per started collision
174
174
  export class Collision {
175
175
 
176
- private readonly contacts: ContactPoint[];
176
+ readonly contacts: ContactPoint[];
177
177
 
178
178
  constructor(obj: Object3D, otherCollider: ICollider, contacts: ContactPoint[]) {
179
179
  this.me = obj;
@@ -201,6 +201,7 @@ export class Collision {
201
201
  return this.collider?.attachedRigidbody;
202
202
  }
203
203
 
204
+
204
205
 
205
206
  // private _normal?: Vector3;
206
207
  // get normal(): Vector3 {
@@ -112,6 +112,14 @@ export class Animation extends Behaviour {
112
112
  return this.actions?.find(a => a.getClip().name === name);
113
113
  }
114
114
 
115
+ get isPlaying() {
116
+ for (let i = 0; i < this._currentActions.length; i++) {
117
+ if (this._currentActions[i].isRunning())
118
+ return true;
119
+ }
120
+ return false;
121
+ }
122
+
115
123
  play(clipOrNumber: AnimationClip | number | string, options?: PlayOptions): Promise<AnimationAction> | void {
116
124
  this.init();
117
125
  if (!this.mixer) return;
@@ -218,7 +218,7 @@ export class AnimatorController {
218
218
  if (!allConditionsAreMet) continue;
219
219
 
220
220
  if (debug && allConditionsAreMet) {
221
- console.log("All conditions are met", transition);
221
+ // console.log("All conditions are met", transition);
222
222
  }
223
223
 
224
224
  // disable triggers
@@ -232,16 +232,16 @@ export class AnimatorController {
232
232
  if (action) {
233
233
  const dur = state.motion.clip!.duration;
234
234
  const normalizedTime = dur <= 0 ? 1 : action.time / dur;
235
- if (!transition.hasExitTime
236
- || (normalizedTime >= transition.exitTime && action.time >= action.getClip().duration
237
- )
238
- ) {
235
+ const makeTransition = transition.hasExitTime ? normalizedTime >= transition.exitTime : true;
236
+ // console.log(state.name, makeTransition, transition.hasExitTime, normalizedTime, transition.exitTime)
237
+ if (makeTransition) {
239
238
  // if (transition.hasExitTime && transition.exitTime >= .9999)
240
239
  action.clampWhenFinished = true;
241
240
  // else action.clampWhenFinished = false;
242
- if (debug) {
243
- console.log("transition to " + transition.destinationState, transition);
244
- console.log(action.time, transition.exitTime);
241
+ if (debug)
242
+ {
243
+ console.log("transition to " + transition.destinationState, transition, normalizedTime, transition.exitTime, transition.hasExitTime);
244
+ // console.log(action.time, transition.exitTime);
245
245
  }
246
246
  this.transitionTo(transition.destinationState as State, transition.duration, transition.offset);
247
247
  // use the first transition that matches all conditions and make the transition as soon as in range
@@ -252,7 +252,7 @@ export class AnimatorController {
252
252
  this.transitionTo(transition.destinationState as State, transition.duration, transition.offset);
253
253
  return;
254
254
  }
255
- break;
255
+ // if none of the transitions can be made continue searching for another transition meeting the conditions
256
256
  }
257
257
 
258
258
  let didTriggerLooping = false;
@@ -376,7 +376,13 @@ export class AnimatorController {
376
376
  }
377
377
 
378
378
  private createAction(clip: AnimationClip) {
379
- this._mixer.uncacheClip(clip);
379
+
380
+ // uncache clip causes issues when multiple states use the same clip
381
+ // this._mixer.uncacheClip(clip);
382
+ // instead only uncache the action when one already exists to make sure
383
+ // we get unique actions per state
384
+ const existing = this._mixer.existingAction(clip);
385
+ if (existing) this._mixer.uncacheAction(clip, this.animator?.gameObject);
380
386
 
381
387
  if (this.animator?.applyRootMotion) {
382
388
  if (!this.rootMotionHandler) {
@@ -387,7 +393,6 @@ export class AnimatorController {
387
393
  return this.rootMotionHandler.createClip(this._mixer, root, clip);
388
394
  }
389
395
  else {
390
-
391
396
  const action = this._mixer.clipAction(clip);
392
397
  return action;
393
398
  }