@needle-tools/engine 2.63.3-pre → 2.64.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 (100) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/needle-engine.js +9760 -9635
  3. package/dist/needle-engine.umd.cjs +237 -236
  4. package/lib/engine/engine_application.d.ts +7 -1
  5. package/lib/engine/engine_application.js +11 -0
  6. package/lib/engine/engine_application.js.map +1 -1
  7. package/lib/engine/engine_constants.d.ts +1 -1
  8. package/lib/engine/engine_constants.js +1 -1
  9. package/lib/engine/engine_constants.js.map +1 -1
  10. package/lib/engine/engine_gameobject.js +2 -2
  11. package/lib/engine/engine_gameobject.js.map +1 -1
  12. package/lib/engine/engine_instancing.d.ts +1 -0
  13. package/lib/engine/engine_instancing.js +3 -2
  14. package/lib/engine/engine_instancing.js.map +1 -1
  15. package/lib/engine/engine_license.js +14 -13
  16. package/lib/engine/engine_license.js.map +1 -1
  17. package/lib/engine/engine_mainloop_utils.js +27 -52
  18. package/lib/engine/engine_mainloop_utils.js.map +1 -1
  19. package/lib/engine/engine_physics.d.ts +1 -0
  20. package/lib/engine/engine_physics.js +30 -0
  21. package/lib/engine/engine_physics.js.map +1 -1
  22. package/lib/engine/engine_serialization_builtin_serializer.d.ts +1 -0
  23. package/lib/engine/engine_serialization_builtin_serializer.js +30 -25
  24. package/lib/engine/engine_serialization_builtin_serializer.js.map +1 -1
  25. package/lib/engine/engine_serialization_core.js +8 -0
  26. package/lib/engine/engine_serialization_core.js.map +1 -1
  27. package/lib/engine/engine_setup.d.ts +1 -1
  28. package/lib/engine/engine_setup.js.map +1 -1
  29. package/lib/engine/engine_time.d.ts +1 -0
  30. package/lib/engine/engine_time.js +5 -1
  31. package/lib/engine/engine_time.js.map +1 -1
  32. package/lib/engine/engine_types.d.ts +12 -0
  33. package/lib/engine/engine_types.js.map +1 -1
  34. package/lib/engine-components/Animation.js +6 -6
  35. package/lib/engine-components/Animation.js.map +1 -1
  36. package/lib/engine-components/AnimatorController.js +2 -0
  37. package/lib/engine-components/AnimatorController.js.map +1 -1
  38. package/lib/engine-components/AudioListener.js +2 -0
  39. package/lib/engine-components/AudioListener.js.map +1 -1
  40. package/lib/engine-components/AudioSource.d.ts +2 -1
  41. package/lib/engine-components/AudioSource.js +19 -4
  42. package/lib/engine-components/AudioSource.js.map +1 -1
  43. package/lib/engine-components/Component.d.ts +1 -0
  44. package/lib/engine-components/Component.js.map +1 -1
  45. package/lib/engine-components/GridHelper.d.ts +1 -0
  46. package/lib/engine-components/GridHelper.js +7 -0
  47. package/lib/engine-components/GridHelper.js.map +1 -1
  48. package/lib/engine-components/ParticleSystem.js +13 -1
  49. package/lib/engine-components/ParticleSystem.js.map +1 -1
  50. package/lib/engine-components/Renderer.d.ts +5 -1
  51. package/lib/engine-components/Renderer.js +78 -29
  52. package/lib/engine-components/Renderer.js.map +1 -1
  53. package/lib/engine-components/WebARSessionRoot.d.ts +1 -0
  54. package/lib/engine-components/WebARSessionRoot.js +10 -0
  55. package/lib/engine-components/WebARSessionRoot.js.map +1 -1
  56. package/lib/engine-components/js-extensions/Object3D.js +11 -1
  57. package/lib/engine-components/js-extensions/Object3D.js.map +1 -1
  58. package/lib/engine-components/timeline/PlayableDirector.d.ts +5 -0
  59. package/lib/engine-components/timeline/PlayableDirector.js +36 -23
  60. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  61. package/lib/engine-components/timeline/TimelineTracks.d.ts +9 -0
  62. package/lib/engine-components/timeline/TimelineTracks.js +121 -21
  63. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  64. package/lib/tsconfig.tsbuildinfo +1 -1
  65. package/package.json +2 -1
  66. package/src/engine/engine_application.ts +14 -3
  67. package/src/engine/engine_constants.ts +1 -1
  68. package/src/engine/engine_gameobject.ts +2 -2
  69. package/src/engine/engine_instancing.ts +3 -3
  70. package/src/engine/engine_license.ts +14 -13
  71. package/src/engine/engine_mainloop_utils.ts +32 -58
  72. package/src/engine/engine_physics.ts +23 -0
  73. package/src/engine/engine_serialization_builtin_serializer.ts +36 -27
  74. package/src/engine/engine_serialization_core.ts +8 -1
  75. package/src/engine/engine_setup.ts +1 -1
  76. package/src/engine/engine_time.ts +9 -4
  77. package/src/engine/engine_types.ts +36 -22
  78. package/src/engine-components/Animation.ts +6 -5
  79. package/src/engine-components/AnimatorController.ts +1 -0
  80. package/src/engine-components/AudioListener.ts +1 -0
  81. package/src/engine-components/AudioSource.ts +20 -6
  82. package/src/engine-components/Component.ts +3 -0
  83. package/src/engine-components/GridHelper.ts +10 -2
  84. package/src/engine-components/ParticleSystem.ts +15 -2
  85. package/src/engine-components/Renderer.ts +92 -33
  86. package/src/engine-components/WebARSessionRoot.ts +14 -0
  87. package/src/engine-components/js-extensions/Object3D.ts +11 -3
  88. package/src/engine-components/timeline/PlayableDirector.ts +35 -24
  89. package/src/engine-components/timeline/TimelineTracks.ts +132 -22
  90. package/src/tsconfig.json +33 -0
  91. package/src/engine/codegen/license.js +0 -1
  92. /package/{src/plugins → plugins}/vite/build.js +0 -0
  93. /package/{src/plugins → plugins}/vite/config.js +0 -0
  94. /package/{src/plugins → plugins}/vite/gzip.js +0 -0
  95. /package/{src/plugins → plugins}/vite/index.js +0 -0
  96. /package/{src/plugins → plugins}/vite/meta.js +0 -0
  97. /package/{src/plugins → plugins}/vite/poster-client.js +0 -0
  98. /package/{src/plugins → plugins}/vite/poster.js +0 -0
  99. /package/{src/plugins → plugins}/vite/reload-client.js +0 -0
  100. /package/{src/plugins → plugins}/vite/reload.js +0 -0
@@ -21,9 +21,9 @@ export interface UIDProvider {
21
21
 
22
22
 
23
23
  export declare type CoroutineData = {
24
- comp: IComponent,
25
- main: Generator,
26
- chained?: Array<Generator>
24
+ comp: IComponent,
25
+ main: Generator,
26
+ chained?: Array<Generator>
27
27
  }
28
28
 
29
29
 
@@ -37,25 +37,25 @@ export interface IContext {
37
37
  domElement: HTMLElement;
38
38
 
39
39
  scripts: IComponent[];
40
- scripts_pausedChanged: IComponent[];
41
- // scripts with update event
42
- scripts_earlyUpdate: IComponent[];
43
- scripts_update: IComponent[];
44
- scripts_lateUpdate: IComponent[];
45
- scripts_onBeforeRender: IComponent[];
46
- scripts_onAfterRender: IComponent[];
47
- scripts_WithCorroutines: IComponent[];
48
- coroutines: { [FrameEvent: number]: Array<CoroutineData> };
49
-
50
- post_setup_callbacks: Function[];
51
- pre_update_callbacks: Function[];
52
- pre_render_callbacks: Function[];
53
- post_render_callbacks: Function[];
54
-
55
- new_scripts: IComponent[];
56
- new_script_start: IComponent[];
57
- new_scripts_pre_setup_callbacks: Function[];
58
- new_scripts_post_setup_callbacks: Function[];
40
+ scripts_pausedChanged: IComponent[];
41
+ // scripts with update event
42
+ scripts_earlyUpdate: IComponent[];
43
+ scripts_update: IComponent[];
44
+ scripts_lateUpdate: IComponent[];
45
+ scripts_onBeforeRender: IComponent[];
46
+ scripts_onAfterRender: IComponent[];
47
+ scripts_WithCorroutines: IComponent[];
48
+ coroutines: { [FrameEvent: number]: Array<CoroutineData> };
49
+
50
+ post_setup_callbacks: Function[];
51
+ pre_update_callbacks: Function[];
52
+ pre_render_callbacks: Function[];
53
+ post_render_callbacks: Function[];
54
+
55
+ new_scripts: IComponent[];
56
+ new_script_start: IComponent[];
57
+ new_scripts_pre_setup_callbacks: Function[];
58
+ new_scripts_post_setup_callbacks: Function[];
59
59
 
60
60
  stopAllCoroutinesFrom(script: IComponent);
61
61
  }
@@ -71,6 +71,13 @@ export declare interface IGameObject extends Object3D {
71
71
  guid: string | undefined;
72
72
 
73
73
  activeSelf: boolean;
74
+
75
+ /** NOTE: this is just a wrapper for devs coming from Unity. Please use this.gameObject instead. In Needle Engine this.gameObject is the same as this.gameObject.transform. See the tutorial link below for more information
76
+ * @augments Object3D
77
+ * @tutorial https://fwd.needle.tools/needle-engine/docs/transform
78
+ * */
79
+ get transform(): Object3D;
80
+
74
81
  addNewComponent<T>(type: Constructor<T>): T | null;
75
82
  removeComponent(comp: IComponent): IComponent;
76
83
  getOrAddComponent<T>(typeName: Constructor<T> | null): T;
@@ -100,12 +107,19 @@ export interface IComponent {
100
107
 
101
108
  get activeAndEnabled(): boolean;
102
109
 
110
+ /** @internal */
103
111
  __internalNewInstanceCreated();
112
+ /** @internal */
104
113
  __internalAwake();
114
+ /** @internal */
105
115
  __internalStart();
116
+ /** @internal */
106
117
  __internalEnable();
118
+ /** @internal */
107
119
  __internalDisable();
120
+ /** @internal */
108
121
  __internalDestroy();
122
+ /** @internal */
109
123
  resolveGuids?(guidsMap: GuidsMap): void;
110
124
 
111
125
  /** experimental, called when the script is registered for the first time, this is called even if the component is not enabled. */
@@ -125,11 +125,12 @@ export class Animation extends Behaviour {
125
125
  update() {
126
126
  if (!this.mixer) return;
127
127
  this.mixer.update(this.context.time.deltaTime);
128
- for (const handle of this._handles) {
129
- handle._update();
130
- }
131
- if (this._handles?.length > 0)
132
- InstancingUtil.markDirty(this.gameObject);
128
+ // this is now handled via matrix auto update
129
+ // for (const handle of this._handles) {
130
+ // handle._update();
131
+ // }
132
+ // if (this._handles?.length > 0)
133
+ // InstancingUtil.markDirty(this.gameObject);
133
134
  }
134
135
 
135
136
  getAction(name: string): THREE.AnimationAction | undefined | null {
@@ -146,6 +146,7 @@ export class AnimatorController {
146
146
  if (!this.animator) return;
147
147
  this.evaluateTransitions();
148
148
  this.updateActiveStates();
149
+ if(!this._activeState) return;
149
150
  const dt = this.animator.context.time.deltaTime;
150
151
  if (this.animator.applyRootMotion) {
151
152
  this.rootMotionHandler?.onBeforeUpdate();
@@ -16,6 +16,7 @@ export class AudioListener extends Behaviour {
16
16
 
17
17
  awake() {
18
18
  AudioSource.registerWaitForAllowAudio(() => {
19
+ if (this.destroyed) return;
19
20
  const listener = this.listener;
20
21
  if (listener == null) return;
21
22
  // if the listener is already parented to some object d0nt change it
@@ -75,7 +75,7 @@ export class AudioSource extends Behaviour {
75
75
  }
76
76
  this.callbacks.length = 0;
77
77
  };
78
- let fn = callback.bind(this);
78
+ const fn = callback.bind(this);
79
79
  document.addEventListener('pointerdown', fn);
80
80
  document.addEventListener('click', fn);
81
81
  document.addEventListener('dragstart', fn);
@@ -133,7 +133,7 @@ export class AudioSource extends Behaviour {
133
133
  get volume(): number { return this._volume; }
134
134
  set volume(val: number) {
135
135
  this._volume = val;
136
- if (this.sound) {
136
+ if (this.sound && !this.context.application.muted) {
137
137
  if (debug) console.log(this.name, "audio set volume", val);
138
138
  this.sound.setVolume(val);
139
139
  }
@@ -167,7 +167,9 @@ export class AudioSource extends Behaviour {
167
167
 
168
168
  public get ShouldPlay(): boolean { return this.shouldPlay; }
169
169
 
170
- private _focusCallback?: any;
170
+
171
+ private _focusCallback: any;
172
+ private _muteChangedCallback: any;
171
173
 
172
174
  awake() {
173
175
  this.audioLoader = new THREE.AudioLoader();
@@ -189,13 +191,22 @@ export class AudioSource extends Behaviour {
189
191
  if (this.enabled && this.playOnAwake && !this.isPlaying && AudioSource._userInteractionRegistered) {
190
192
  this.play();
191
193
  }
192
- };
194
+ }
195
+
196
+ this._muteChangedCallback = () => {
197
+ if (this.context.application.muted)
198
+ this.sound?.setVolume(0);
199
+ else
200
+ this.sound?.setVolume(this.volume);
201
+ }
193
202
 
194
203
  this.context.application.addEventListener(ApplicationEvents.Visible, this._focusCallback);
204
+ this.context.application.addEventListener(ApplicationEvents.MuteChanged, this._muteChangedCallback);
195
205
  }
196
206
 
197
207
  onDestroy() {
198
208
  this.context.application.removeEventListener(ApplicationEvents.Visible, this._focusCallback);
209
+ this.context.application.removeEventListener(ApplicationEvents.MuteChanged, this._muteChangedCallback);
199
210
  }
200
211
 
201
212
 
@@ -233,7 +244,8 @@ export class AudioSource extends Behaviour {
233
244
 
234
245
  sound.setBuffer(buffer);
235
246
  sound.loop = this._loop;
236
- sound.setVolume(this.volume);
247
+ if(this.context.application.muted) sound.setVolume(0);
248
+ else sound.setVolume(this.volume);
237
249
  sound.autoplay = this.shouldPlay;
238
250
  // sound.setDistanceModel('linear');
239
251
  // sound.setRolloffFactor(1);
@@ -313,7 +325,9 @@ export class AudioSource extends Behaviour {
313
325
  if (debug)
314
326
  console.log("play", this.sound?.getVolume(), this.sound);
315
327
  if (this.sound && !this.sound.isPlaying) {
316
- this.sound.play();
328
+ const muted = this.context.application.muted;
329
+ if(muted) this.sound.setVolume(0);
330
+ this.sound.play(muted ? .1 : 0);
317
331
  }
318
332
  }
319
333
 
@@ -23,6 +23,9 @@ abstract class GameObject extends THREE.Object3D implements THREE.Object3D, IGam
23
23
 
24
24
  guid: string | undefined;
25
25
 
26
+ // The actual implementation / prototype of threejs is modified in js-extensions/Object3D
27
+ abstract get transform(): THREE.Object3D;
28
+
26
29
  public static isDestroyed(go: THREE.Object3D): boolean {
27
30
  return isDestroyed(go);
28
31
  }
@@ -6,7 +6,7 @@ import { Color, GridHelper as _GridHelper } from "three";
6
6
  export class GridHelper extends Behaviour {
7
7
 
8
8
  @serializable()
9
- public isGizmo:boolean = false;
9
+ public isGizmo: boolean = false;
10
10
  @serializable(Color)
11
11
  private color0!: THREE.Color;
12
12
  @serializable(Color)
@@ -23,10 +23,18 @@ export class GridHelper extends Behaviour {
23
23
  const size = this.size;
24
24
  const divisions = this.divisions;
25
25
  if (!this.gridHelper) {
26
- this.gridHelper = new _GridHelper(size, divisions, this.color0 ?? new Color(.4, .4, .4), this.color1 ?? new Color(.6,.6,.6));
26
+ this.gridHelper = new _GridHelper(size, divisions, this.color0 ?? new Color(.4, .4, .4), this.color1 ?? new Color(.6, .6, .6));
27
27
  if (this.offset !== undefined)
28
28
  this.gridHelper.position.y += this.offset;
29
+ }
30
+ if (this.gridHelper)
29
31
  this.gameObject.add(this.gridHelper);
32
+ }
33
+
34
+ onDisable(): void {
35
+ if (this.gridHelper) {
36
+ this.gameObject.remove(this.gridHelper);
37
+ this.gridHelper = null;
30
38
  }
31
39
  }
32
40
  }
@@ -70,7 +70,7 @@ export class ParticleSystemRenderer extends Behaviour {
70
70
 
71
71
  getMaterial(trailEnabled: boolean = false) {
72
72
  const material = (trailEnabled === true && this.trailMaterial) ? this.trailMaterial : this.particleMaterial;
73
-
73
+
74
74
  // progressive load on start
75
75
  // TODO: figure out how to do this before particle system rendering so we only load textures for visible materials
76
76
  if (material && !suppressProgressiveLoading && material["_didRequestTextureLOD"] === undefined) {
@@ -836,7 +836,20 @@ export class ParticleSystem extends Behaviour implements IParticleSystem {
836
836
  onBeforeRender() {
837
837
  this.onUpdate();
838
838
  const dt = this.deltaTime;
839
- this._batchSystem?.update(dt);
839
+
840
+ if (this._batchSystem) {
841
+ // Updating layers on batches
842
+ // TODO: figure out a better way to do this
843
+ // Issue: https://github.com/Alchemist0823/three.quarks/issues/49
844
+ if (this.context.time.frameCount % 60 === 0) {
845
+ for (let i = 0; i < this._batchSystem.batches.length; i++) {
846
+ const batch = this._batchSystem.batches[i];
847
+ batch.layers.disableAll();
848
+ batch.layers.set(2);
849
+ }
850
+ }
851
+ this._batchSystem.update(dt);
852
+ }
840
853
  this._time += dt;
841
854
  if (this._time > this.duration) this._time = 0;
842
855
  }
@@ -5,7 +5,7 @@ import { RendererLightmap } from "./RendererLightmap";
5
5
  import { Context, FrameEvent } from "../engine/engine_setup";
6
6
  import { getParam } from "../engine/engine_utils";
7
7
  import { serializable } from "../engine/engine_serialization_decorator";
8
- import { AxesHelper, Material, Mesh, Object3D, SkinnedMesh, Texture, Vector4 } from "three";
8
+ import { AxesHelper, Material, Matrix4, Mesh, Object3D, SkinnedMesh, Texture, Vector4 } from "three";
9
9
  import { NEEDLE_render_objects } from "../engine/extensions/NEEDLE_render_objects";
10
10
  import { NEEDLE_progressive } from "../engine/extensions/NEEDLE_progressive";
11
11
  import { NEED_UPDATE_INSTANCE_KEY } from "../engine/engine_instancing";
@@ -18,6 +18,7 @@ import { showBalloonWarning } from "../engine/debug/debug";
18
18
  // for staying compatible with old code
19
19
  export { InstancingUtil } from "../engine/engine_instancing";
20
20
 
21
+ const debugRenderer = getParam("debugrenderer");
21
22
  const suppressInstancing = getParam("noInstancing");
22
23
  const debugLightmap = getParam("debuglightmaps") ? true : false;
23
24
  const debugInstancing = getParam("debuginstancing");
@@ -294,6 +295,7 @@ export class Renderer extends Behaviour implements IRenderer {
294
295
  }
295
296
 
296
297
  awake() {
298
+ if (debugRenderer) console.log("Renderer ", this.name, this);
297
299
  this.clearInstancingState();
298
300
 
299
301
  if (this.probeAnchor && debug) this.probeAnchor.add(new AxesHelper(.2));
@@ -332,6 +334,9 @@ export class Renderer extends Behaviour implements IRenderer {
332
334
  if (this.renderOrder !== undefined && this.renderOrder.length > 0)
333
335
  this.gameObject.renderOrder = this.renderOrder[0];
334
336
  }
337
+ else {
338
+ this.context.addBeforeRenderListener(this.gameObject, this.onBeforeRenderThree.bind(this));
339
+ }
335
340
 
336
341
  if (this.lightmapIndex >= 0) {
337
342
  // use the override lightmap if its not undefined
@@ -394,20 +399,14 @@ export class Renderer extends Behaviour implements IRenderer {
394
399
  this.handles = undefined;
395
400
  this.prevLayers = undefined;
396
401
  }
402
+
397
403
  setInstancingEnabled(enabled: boolean): boolean {
398
404
  if (this._isInstancingEnabled === enabled) return enabled && (this.handles === undefined || this.handles != null && this.handles.length > 0);
399
405
  this._isInstancingEnabled = enabled;
400
406
  if (enabled) {
401
- // if handles is undefined we
402
407
  if (this.handles === undefined) {
403
- this.handles = instancing.setup(this.gameObject, this.context, null, { rend: this, foundMeshes: 0 });
408
+ this.handles = instancing.setup(this, this.gameObject, this.context, null, { rend: this, foundMeshes: 0, useMatrixWorldAutoUpdate: this.useInstanceMatrixWorldAutoUpdate() });
404
409
  if (this.handles) {
405
- // const disableSelf = this.gameObject.type === "Mesh" || this.gameObject.children?.length === this.handles.length;
406
- // this.gameObject.visible = !disableSelf;
407
- // this.gameObject.type = "Object3D";
408
- // this.gameObject.material = null;
409
- // console.log("Using instancing", this.gameObject.visible);
410
- // this.gameObject.onBeforeRender = () => console.log("SHOULD NOT BE CALLED");
411
410
  GameObject.markAsInstancedRendered(this.gameObject, true);
412
411
  return true;
413
412
  }
@@ -417,8 +416,6 @@ export class Renderer extends Behaviour implements IRenderer {
417
416
  handler.updateInstanceMatrix(true);
418
417
  handler.add();
419
418
  }
420
- // this.gameObject.type = "Object3D";
421
- // this.gameObject.visible = false;
422
419
  GameObject.markAsInstancedRendered(this.gameObject, true);
423
420
  return true;
424
421
  }
@@ -429,13 +426,19 @@ export class Renderer extends Behaviour implements IRenderer {
429
426
  handler.remove();
430
427
  }
431
428
  }
432
- // this.gameObject.visible = true;
433
429
  return true;
434
430
  }
435
431
 
436
432
  return false;
437
433
  }
438
434
 
435
+ /** Return true to wrap matrix update events for instanced rendering to update instance matrices automatically when matrixWorld changes
436
+ * This is a separate method to be overrideable from user code
437
+ */
438
+ useInstanceMatrixWorldAutoUpdate() {
439
+ return true;
440
+ }
441
+
439
442
  start() {
440
443
  if (this.enableInstancing && !suppressInstancing) {
441
444
  this.setInstancingEnabled(true);
@@ -461,6 +464,7 @@ export class Renderer extends Behaviour implements IRenderer {
461
464
  }
462
465
 
463
466
  this.updateReflectionProbe();
467
+
464
468
  }
465
469
 
466
470
  onDisable() {
@@ -479,17 +483,12 @@ export class Renderer extends Behaviour implements IRenderer {
479
483
  NEEDLE_render_objects.applyStencil(this);
480
484
  }
481
485
 
482
- static envmap: THREE.Texture | null = null;
483
486
 
484
487
  onBeforeRender() {
485
488
  if (!this.gameObject) {
486
489
  return;
487
490
  }
488
491
 
489
- Renderer.envmap = this.scene.environment;
490
-
491
- const needsUpdate: boolean = this.gameObject[NEED_UPDATE_INSTANCE_KEY] === true || this.gameObject.matrixWorldNeedsUpdate;
492
-
493
492
  if (this.isMultiMaterialObject(this.gameObject) && this.gameObject.children?.length > 0) {
494
493
  for (const ch of this.gameObject.children) {
495
494
  this.applySettings(ch);
@@ -499,9 +498,14 @@ export class Renderer extends Behaviour implements IRenderer {
499
498
  this.applySettings(this.gameObject);
500
499
  }
501
500
 
502
- if (needsUpdate) {
503
- delete this.gameObject[NEED_UPDATE_INSTANCE_KEY];
504
- if (this.handles) {
501
+ if (this.handles?.length) {
502
+ // if (this.name === "Darbouka")
503
+ // console.log(this.name, this.gameObject.matrixWorldNeedsUpdate);
504
+ const needsUpdate: boolean = this.gameObject[NEED_UPDATE_INSTANCE_KEY] === true;// || this.gameObject.matrixWorldNeedsUpdate;
505
+ if (needsUpdate) {
506
+ if(debugInstancing)
507
+ console.log("UPDATE INSTANCED MATRICES", this.context.time.frame);
508
+ this.gameObject[NEED_UPDATE_INSTANCE_KEY] = false;
505
509
  const remove = false;// Math.random() < .01;
506
510
  for (let i = this.handles.length - 1; i >= 0; i--) {
507
511
  const h = this.handles[i];
@@ -539,14 +543,7 @@ export class Renderer extends Behaviour implements IRenderer {
539
543
 
540
544
  onBeforeRenderThree(_renderer, _scene, _camera, _geometry, material, _group) {
541
545
 
542
- // progressive load before rendering so we only load textures for visible materials
543
- if (!suppressProgressiveLoading && material._didRequestTextureLOD === undefined && this.allowProgressiveLoading) {
544
- material._didRequestTextureLOD = 0;
545
- if (debugProgressiveLoading) {
546
- console.log("Load material LOD", material.name);
547
- }
548
- NEEDLE_progressive.assignTextureLOD(this.context, this.sourceId, material);
549
- }
546
+ this.loadProgressiveTextures(material);
550
547
 
551
548
  if (material.envMapIntensity !== undefined) {
552
549
  const factor = this.hasLightmap ? Math.PI : 1;
@@ -607,6 +604,22 @@ export class Renderer extends Behaviour implements IRenderer {
607
604
  }
608
605
  }
609
606
 
607
+ loadProgressiveTextures(material: THREE.Material) {
608
+ // progressive load before rendering so we only load textures for visible materials
609
+ if (!suppressProgressiveLoading && material) {
610
+ if (debugProgressiveLoading && material["_didRequestTextureLOD"] === undefined)
611
+ console.warn("Progressive?", this)
612
+
613
+ if (material["_didRequestTextureLOD"] === undefined && this.allowProgressiveLoading) {
614
+ material["_didRequestTextureLOD"] = 0;
615
+ if (debugProgressiveLoading) {
616
+ console.log("Load material LOD", material.name);
617
+ }
618
+ NEEDLE_progressive.assignTextureLOD(this.context, this.sourceId, material);
619
+ }
620
+ }
621
+ }
622
+
610
623
  private applySettings(go: THREE.Object3D) {
611
624
  go.receiveShadow = this.receiveShadows;
612
625
  if (this.shadowCastingMode == ShadowCastingMode.On) {
@@ -689,17 +702,22 @@ export enum ShadowCastingMode {
689
702
 
690
703
 
691
704
 
692
- declare class InstancingSetupArgs { rend: Renderer; foundMeshes: number };
705
+ declare class InstancingSetupArgs {
706
+ rend: Renderer;
707
+ foundMeshes: number;
708
+ useMatrixWorldAutoUpdate: boolean;
709
+ };
693
710
 
694
711
  class InstancingHandler {
695
712
 
696
713
  public objs: InstancedMeshRenderer[] = [];
697
714
 
698
- public setup(obj: THREE.Object3D, context: Context, handlesArray: InstanceHandle[] | null, args: InstancingSetupArgs, level: number = 0)
715
+ public setup(renderer:Renderer, obj: THREE.Object3D, context: Context, handlesArray: InstanceHandle[] | null, args: InstancingSetupArgs, level: number = 0)
699
716
  : InstanceHandle[] | null {
700
717
 
701
718
  const res = this.tryCreateOrAddInstance(obj, context, args);
702
719
  if (res) {
720
+ renderer.loadProgressiveTextures(res.instancer.material);
703
721
  if (handlesArray === null) handlesArray = [];
704
722
  handlesArray.push(res);
705
723
  return handlesArray;
@@ -708,7 +726,7 @@ class InstancingHandler {
708
726
  if (level <= 0 && obj.type !== "Mesh") {
709
727
  const nextLevel = level + 1;
710
728
  for (const ch of obj.children) {
711
- handlesArray = this.setup(ch, context, handlesArray, args, nextLevel);
729
+ handlesArray = this.setup(renderer, ch, context, handlesArray, args, nextLevel);
712
730
  }
713
731
  }
714
732
  return handlesArray;
@@ -736,16 +754,52 @@ class InstancingHandler {
736
754
  for (const i of this.objs) {
737
755
  if (i.isFull()) continue;
738
756
  if (i.geo === geo && i.material === mat) {
739
- return i.addInstance(mesh);
757
+ const handle = i.addInstance(mesh);
758
+ if (args.useMatrixWorldAutoUpdate && handle)
759
+ this.autoUpdateInstanceMatrix(mesh, i, handle);
760
+ return handle;
740
761
  }
741
762
  }
742
763
  // console.log("Add new instance mesh renderer", obj);
743
764
  const i = new InstancedMeshRenderer(obj.name, geo, mat, 200, context);
744
765
  this.objs.push(i);
745
- return i.addInstance(mesh);
766
+ const handle = i.addInstance(mesh);
767
+ if (args.useMatrixWorldAutoUpdate && handle)
768
+ this.autoUpdateInstanceMatrix(mesh, i, handle);
769
+ return handle;
746
770
  }
747
771
  return null;
748
772
  }
773
+
774
+ private autoUpdateInstanceMatrix(obj: Object3D, _renderer: InstancedMeshRenderer, _handle: InstanceHandle) {
775
+ const original = obj.matrixWorld["multiplyMatrices"].bind(obj.matrixWorld);
776
+ let previousMatrix: THREE.Matrix4 = obj.matrixWorld.clone();
777
+
778
+ const matrixChangeWrapper = (a: Matrix4, b: Matrix4) => {
779
+ const newMatrixWorld = original(a, b);
780
+ // console.warn("MULT", obj.matrixWorldNeedsUpdate);
781
+ if (obj[NEED_UPDATE_INSTANCE_KEY] || previousMatrix.equals(newMatrixWorld) === false) {
782
+ previousMatrix.copy(newMatrixWorld)
783
+ // handle.setMatrix(newMatrixWorld);
784
+ obj[NEED_UPDATE_INSTANCE_KEY] = true;
785
+ }
786
+ return newMatrixWorld;
787
+ };
788
+ obj.matrixWorld["multiplyMatrices"] = matrixChangeWrapper;
789
+
790
+ // wrap matrixWorldNeedsUpdate
791
+ // let originalMatrixWorldNeedsUpdate = obj.matrixWorldNeedsUpdate;
792
+ // Object.defineProperty(obj, "matrixWorldNeedsUpdate", {
793
+ // get: () => {
794
+ // return originalMatrixWorldNeedsUpdate;
795
+ // },
796
+ // set: (value: boolean) => {
797
+ // if(value) console.warn("SET MATRIX WORLD NEEDS UPDATE");
798
+ // originalMatrixWorldNeedsUpdate = value;
799
+ // }
800
+ // });
801
+
802
+ }
749
803
  }
750
804
  const instancing: InstancingHandler = new InstancingHandler();
751
805
 
@@ -773,6 +827,11 @@ class InstanceHandle {
773
827
  this.instancer.updateInstance(this.object.matrixWorld, this.instanceIndex);
774
828
  }
775
829
 
830
+ setMatrix(matrix: THREE.Matrix4) {
831
+ if (this.instanceIndex < 0) return;
832
+ this.instancer.updateInstance(matrix, this.instanceIndex);
833
+ }
834
+
776
835
  add() {
777
836
  if (this.instanceIndex >= 0) return;
778
837
  this.instancer.add(this);
@@ -45,6 +45,7 @@ export class WebARSessionRoot extends Behaviour {
45
45
  private _placementPose: Matrix4 | null = null;
46
46
  private _isTouching: boolean = false;
47
47
  private _rigStartPose: Matrix4 | undefined | null = null;
48
+ private _gotFirstHitTestResult: boolean = false;
48
49
 
49
50
  onBegin(session: XRSession) {
50
51
  this._placementPose = null;
@@ -52,6 +53,7 @@ export class WebARSessionRoot extends Behaviour {
52
53
  this.gameObject.matrixAutoUpdate = false;
53
54
  this._startPose = this.gameObject.matrix.clone();
54
55
  this._rigStartPose = this.rig?.matrix.clone();
56
+ this._gotFirstHitTestResult = false;
55
57
  session.addEventListener('selectstart', this._selectStartFn);
56
58
  session.addEventListener('selectend', this._selectEndFn);
57
59
  // setTimeout(() => this.gameObject.visible = false, 1000); // TODO test on phone AR and Hololens if this was still needed
@@ -66,12 +68,24 @@ export class WebARSessionRoot extends Behaviour {
66
68
  this.rig.matrixAutoUpdate = true;
67
69
  this._initalMatrix.decompose(this.rig.position, this.rig.quaternion, this.rig.scale);
68
70
  }
71
+
72
+ // TODO this is duplicate to WebXR events AND engine events, would be better in one place
73
+ this.dispatchEvent(new CustomEvent('onBeginSession'));
69
74
  }
70
75
 
71
76
  onUpdate(rig: Object3D | null, _session: XRSession, pose: XRPose | null | undefined): boolean {
72
77
 
73
78
  if (pose && !this._placementPose) {
79
+
80
+ if (!this._gotFirstHitTestResult) {
81
+ this._gotFirstHitTestResult = true;
82
+ this.dispatchEvent(new CustomEvent('canPlaceSession', { detail: { pose: pose } }));
83
+ }
84
+
74
85
  if (this._isTouching) {
86
+ // callbacks
87
+ this.dispatchEvent(new CustomEvent('placedSession', { detail: { pose: pose } }));
88
+
75
89
  if (this.webAR) this.webAR.setReticleActive(false);
76
90
  this.placeAt(rig, new Matrix4().fromArray(pose.transform.matrix).invert());
77
91
  return true;
@@ -12,12 +12,13 @@ export function apply(object: Object3D) {
12
12
  }
13
13
  }
14
14
 
15
-
16
-
17
- // do we still need this?
18
15
  Object3D.prototype["SetActive"] = function (active: boolean) {
19
16
  this.visible = active;
20
17
  }
18
+ // e.g. when called via a UnityEvent
19
+ Object3D.prototype["setActive"] = function (active: boolean) {
20
+ this.visible = active;
21
+ }
21
22
 
22
23
  Object3D.prototype["addNewComponent"] = function <T extends Component>(type: ConstructorConcrete<T>) {
23
24
  return addNewComponent(this, new type());
@@ -67,6 +68,13 @@ if (!Object.getOwnPropertyDescriptor(Object3D.prototype, "activeSelf")) {
67
68
  });
68
69
  }
69
70
 
71
+ if (!Object.getOwnPropertyDescriptor(Object3D.prototype, "transform")) {
72
+ Object.defineProperty(Object3D.prototype, "transform", {
73
+ get: function () {
74
+ return this;
75
+ }
76
+ });
77
+ }
70
78
 
71
79
 
72
80