@needle-tools/engine 2.58.3-pre → 2.59.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 (68) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/needle-engine.js +8820 -8736
  3. package/dist/needle-engine.umd.cjs +195 -195
  4. package/lib/engine/debug/debug_overlay.js +11 -0
  5. package/lib/engine/debug/debug_overlay.js.map +1 -1
  6. package/lib/engine/engine_addressables.js +8 -5
  7. package/lib/engine/engine_addressables.js.map +1 -1
  8. package/lib/engine/engine_element.js +2 -1
  9. package/lib/engine/engine_element.js.map +1 -1
  10. package/lib/engine/engine_gameobject.d.ts +3 -1
  11. package/lib/engine/engine_gameobject.js +31 -2
  12. package/lib/engine/engine_gameobject.js.map +1 -1
  13. package/lib/engine/engine_hot_reload.d.ts +2 -0
  14. package/lib/engine/engine_hot_reload.js +15 -2
  15. package/lib/engine/engine_hot_reload.js.map +1 -1
  16. package/lib/engine/engine_input.d.ts +2 -0
  17. package/lib/engine/engine_input.js +16 -0
  18. package/lib/engine/engine_input.js.map +1 -1
  19. package/lib/engine/engine_utils.d.ts +1 -1
  20. package/lib/engine/engine_utils.js +5 -2
  21. package/lib/engine/engine_utils.js.map +1 -1
  22. package/lib/engine/extensions/NEEDLE_gameobject_data.js +6 -4
  23. package/lib/engine/extensions/NEEDLE_gameobject_data.js.map +1 -1
  24. package/lib/engine-components/Animator.js +10 -6
  25. package/lib/engine-components/Animator.js.map +1 -1
  26. package/lib/engine-components/AnimatorController.d.ts +1 -1
  27. package/lib/engine-components/AnimatorController.js +19 -12
  28. package/lib/engine-components/AnimatorController.js.map +1 -1
  29. package/lib/engine-components/Component.d.ts +1 -0
  30. package/lib/engine-components/Component.js +4 -1
  31. package/lib/engine-components/Component.js.map +1 -1
  32. package/lib/engine-components/OrbitControls.d.ts +7 -2
  33. package/lib/engine-components/OrbitControls.js +34 -15
  34. package/lib/engine-components/OrbitControls.js.map +1 -1
  35. package/lib/engine-components/ParticleSystem.d.ts +1 -0
  36. package/lib/engine-components/ParticleSystem.js +15 -2
  37. package/lib/engine-components/ParticleSystem.js.map +1 -1
  38. package/lib/engine-components/WebXR.js.map +1 -1
  39. package/lib/engine-components/timeline/PlayableDirector.js +5 -2
  40. package/lib/engine-components/timeline/PlayableDirector.js.map +1 -1
  41. package/lib/engine-components/timeline/TimelineTracks.d.ts +1 -0
  42. package/lib/engine-components/timeline/TimelineTracks.js +14 -3
  43. package/lib/engine-components/timeline/TimelineTracks.js.map +1 -1
  44. package/lib/engine-components/ui/EventSystem.d.ts +10 -5
  45. package/lib/engine-components/ui/EventSystem.js +45 -41
  46. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  47. package/lib/engine-components/ui/Raycaster.js +2 -2
  48. package/lib/engine-components/ui/Raycaster.js.map +1 -1
  49. package/lib/tsconfig.tsbuildinfo +1 -1
  50. package/package.json +3 -3
  51. package/src/engine/debug/debug_overlay.ts +17 -5
  52. package/src/engine/engine_addressables.ts +8 -5
  53. package/src/engine/engine_element.ts +2 -1
  54. package/src/engine/engine_gameobject.ts +34 -3
  55. package/src/engine/engine_hot_reload.ts +16 -5
  56. package/src/engine/engine_input.ts +11 -0
  57. package/src/engine/engine_utils.ts +4 -3
  58. package/src/engine/extensions/NEEDLE_gameobject_data.ts +11 -6
  59. package/src/engine-components/Animator.ts +12 -9
  60. package/src/engine-components/AnimatorController.ts +20 -13
  61. package/src/engine-components/Component.ts +5 -1
  62. package/src/engine-components/OrbitControls.ts +44 -19
  63. package/src/engine-components/ParticleSystem.ts +18 -3
  64. package/src/engine-components/WebXR.ts +1 -0
  65. package/src/engine-components/timeline/PlayableDirector.ts +4 -2
  66. package/src/engine-components/timeline/TimelineTracks.ts +15 -4
  67. package/src/engine-components/ui/EventSystem.ts +48 -42
  68. package/src/engine-components/ui/Raycaster.ts +2 -2
@@ -345,7 +345,6 @@ class VelocityBehaviour extends ParticleSystemBaseBehaviour {
345
345
 
346
346
  initialize(particle: Particle): void {
347
347
  const factor = 1 + this.scaleFactorDiff;
348
-
349
348
  particle.startSpeed = this.system.main.startSpeed.evaluate(Math.random(), Math.random()) * factor;
350
349
  particle.velocity.copy(this.system.shape.getDirection(particle.position)).multiplyScalar(particle.startSpeed);
351
350
  if (this.system.inheritVelocity?.enabled) {
@@ -414,6 +413,10 @@ class VelocityBehaviour extends ParticleSystemBaseBehaviour {
414
413
  // const factor = this.system.worldScale.x;
415
414
  limitVelocityOverLifetime.apply(particle.position, baseVelocity, particle.velocity, particle.size, t01, delta, 1);
416
415
  }
416
+
417
+ if (this.system.worldspace) {
418
+ particle.velocity.multiply(this.system.worldScale);
419
+ }
417
420
  }
418
421
  }
419
422
 
@@ -421,14 +424,19 @@ const $colorLerpFactor = Symbol("colorLerpFactor");
421
424
  class ColorBehaviour extends ParticleSystemBaseBehaviour {
422
425
  type: string = "NeedleColor";
423
426
 
424
- initialize(particle: Particle): void {
425
- const col = this.system.main.startColor.evaluate(particle.age / particle.life, Math.random());
427
+ initialize(_particle: Particle): void {
428
+ }
429
+
430
+ private _init(particle: Particle) {
431
+ const col = this.system.main.startColor.evaluate(Math.random());
426
432
  particle.startColor.set(col.r, col.g, col.b, col.alpha);
427
433
  particle.color.copy(particle.startColor);
428
434
  particle[$colorLerpFactor] = Math.random();
429
435
  }
430
436
 
431
437
  update(particle: Particle, _delta: number): void {
438
+ if (particle.age === 0)
439
+ this._init(particle);
432
440
  if (this.system.colorOverLifetime.enabled) {
433
441
  const t = particle.age / particle.life;
434
442
  const col = this.system.colorOverLifetime.color.evaluate(t, particle[$colorLerpFactor]);
@@ -767,6 +775,13 @@ export class ParticleSystem extends Behaviour implements IParticleSystem {
767
775
  this.addSubParticleSystems();
768
776
  }
769
777
 
778
+ onDestroy(): void {
779
+ this._container?.removeFromParent();
780
+ this._batchSystem?.removeFromParent();
781
+ this._particleSystem?.emitter.removeFromParent();
782
+ this._particleSystem?.dispose();
783
+ }
784
+
770
785
  onEnable() {
771
786
  if (this.inheritVelocity)
772
787
  this.inheritVelocity.system = this;
@@ -233,6 +233,7 @@ export class WebXR extends Behaviour {
233
233
  this._vrButton = vrButton;
234
234
  buttonsContainer.appendChild(vrButton);
235
235
  }
236
+
236
237
  setTimeout(() => {
237
238
  WebXR.resetButtonStyles(vrButton);
238
239
  WebXR.resetButtonStyles(arButton);
@@ -126,8 +126,9 @@ export class PlayableDirector extends Behaviour {
126
126
  }
127
127
 
128
128
  onDestroy(): void {
129
- for (const track of this._audioTracks) {
130
- track.onDestroy?.();
129
+ for (const tracks of this._allTracks) {
130
+ for (const track of tracks)
131
+ track.onDestroy?.();
131
132
  }
132
133
  }
133
134
 
@@ -151,6 +152,7 @@ export class PlayableDirector extends Behaviour {
151
152
  }
152
153
 
153
154
  stop() {
155
+ for(const track of this._audioTracks) track.stop();
154
156
  if (this._isPlaying) {
155
157
  this._time = 0;
156
158
  this._isPlaying = false;
@@ -182,7 +182,7 @@ export class AnimationTrackHandler extends TrackHandler {
182
182
  clip.tracks.push(track);
183
183
  this.createPositionInterpolant(clip, clipModel, track);
184
184
  }
185
- else if(!foundRotationTrack){
185
+ else if (!foundRotationTrack) {
186
186
  const trackName = clip.tracks[0].name.substring(0, indexOfProperty) + ".quaternion";
187
187
  if (debug) console.warn("Create quaternion track", objName, targetObj);
188
188
  const rot = targetObj.quaternion;
@@ -442,6 +442,8 @@ export class AnimationTrackHandler extends TrackHandler {
442
442
 
443
443
  }
444
444
 
445
+ const muteAudioTracks = getParam("mutetimeline");
446
+
445
447
  export class AudioTrackHandler extends TrackHandler {
446
448
  models: Array<Models.ClipModel> = [];
447
449
 
@@ -469,7 +471,8 @@ export class AudioTrackHandler extends TrackHandler {
469
471
  const audio = new THREE.Audio(this.listener);
470
472
  audio.setVolume(model.asset.volume);
471
473
  const loader = new THREE.AudioLoader();
472
- console.log(path, this.director.sourceId);
474
+ if (debug)
475
+ console.log(path, this.director.sourceId);
473
476
  loader.load(path, (buffer) => {
474
477
  audio.setBuffer(buffer);
475
478
  audio.loop = model.asset.loop;
@@ -495,18 +498,26 @@ export class AudioTrackHandler extends TrackHandler {
495
498
  }
496
499
  }
497
500
 
501
+ stop() {
502
+ for (let i = 0; i < this.audio.length; i++) {
503
+ const audio = this.audio[i];
504
+ if (audio?.isPlaying)
505
+ audio.stop();
506
+ }
507
+ }
498
508
  evaluate(time: number) {
509
+ if(muteAudioTracks) return;
499
510
  if (this.track.muted) return;
500
511
  for (let i = 0; i < this.models.length; i++) {
501
512
  const model = this.models[i];
502
513
  const audio = this.audio[i];
503
514
  if (time >= model.start && time <= model.end) {
504
- if (this.director.isPaused) {
515
+ if (this.director.isPlaying == false) {
505
516
  if (audio.isPlaying)
506
517
  audio.stop();
507
518
  if (this.lastTime === time) continue;
508
519
  }
509
- if (!audio.isPlaying) {
520
+ else if (!audio.isPlaying) {
510
521
  audio.offset = model.clipIn + (time - model.start) * model.timeScale;
511
522
  audio.play();
512
523
  }
@@ -16,6 +16,11 @@ import { $shadowDomOwner } from "./BaseUIComponent";
16
16
 
17
17
  const debug = getParam("debugeventsystem");
18
18
 
19
+ export enum EventSystemEvents {
20
+ BeforeHandleInput = "BeforeHandleInput",
21
+ AfterHandleInput = "AfterHandleInput",
22
+ }
23
+
19
24
  export class EventSystem extends Behaviour {
20
25
 
21
26
 
@@ -45,6 +50,16 @@ export class EventSystem extends Behaviour {
45
50
  return this._eventSystemMap.get(context)!;
46
51
  }
47
52
 
53
+ static get(ctx: Context): EventSystem | null {
54
+ const systems = this._eventSystemMap.get(ctx);
55
+ if (systems && systems.length > 0) return systems[0];
56
+ return null;
57
+ }
58
+
59
+ static get instance(): EventSystem | null {
60
+ return this.systems[0];
61
+ }
62
+
48
63
  //@ts-ignore
49
64
  static ensureUpdateMeshUI(instance, context: Context) {
50
65
  MeshUIHelper.update(instance, context);
@@ -53,12 +68,8 @@ export class EventSystem extends Behaviour {
53
68
  MeshUIHelper.markDirty();
54
69
  }
55
70
 
56
- static get instance(): EventSystem | null {
57
- return this.systems[0];
58
- }
59
-
60
- private orbitControl: OrbitControls | null = null;
61
- private orbitControlWasEnabled: boolean = false;
71
+ // private orbitControl: OrbitControls | null = null;
72
+ // private orbitControlWasEnabled: boolean = false;
62
73
  private raycaster: Raycaster[] = [];
63
74
 
64
75
  constructor() {
@@ -66,6 +77,9 @@ export class EventSystem extends Behaviour {
66
77
  EventSystem.systems.push(this);
67
78
  }
68
79
 
80
+ get hasActiveUI() { return this.currentActiveMeshUIComponents.length > 0; }
81
+ get isHoveringObjects() { return this.objectsHoveredThisFrame.length > 0; }
82
+
69
83
  onDestroy(): void {
70
84
  EventSystem.systems.splice(EventSystem.systems.indexOf(this), 1);
71
85
  }
@@ -90,7 +104,7 @@ export class EventSystem extends Behaviour {
90
104
  private _selectStartFn?: any;
91
105
  private _selectEndFn?: any;
92
106
  private _selectUpdateFn?: any;
93
- private _onBeforeUpdateFn?: any;
107
+ private _handleEventFn?: any;
94
108
 
95
109
  onEnable(): void {
96
110
 
@@ -150,9 +164,14 @@ export class EventSystem extends Behaviour {
150
164
  WebXRController.addEventListener(ControllerEvents.Update, this._selectUpdateFn);
151
165
 
152
166
  // TODO: unregister
153
- this._onBeforeUpdateFn ??= this.onBeforeUpdate.bind(this);
154
- this.context.pre_update_callbacks.push(this._onBeforeUpdateFn);
155
- this.context.input.addEventListener(InputEvents.PointerDown, this._onBeforeUpdateFn);
167
+ this._handleEventFn ??= this.onHandleEvents.bind(this);
168
+ this.context.pre_update_callbacks.push(this._handleEventFn);
169
+ // If we subscribe to those events here we get duplicate event handling for e.g. button clicks
170
+ // I think this was done as a workaround for the InputField where we needed to handle the event within the browser event method
171
+ // without it (via pre_update callbacks) we handle the events outside of the browser loop delayed which doesnt work for some cases
172
+ // this.context.input.addEventListener(InputEvents.PointerDown, this._handleEventFn);
173
+ // this.context.input.addEventListener(InputEvents.PointerUp, this._handleEventFn);
174
+ // this.context.input.addEventListener(InputEvents.PointerMove, this._handleEventFn);
156
175
  }
157
176
 
158
177
  onDisable(): void {
@@ -160,8 +179,10 @@ export class EventSystem extends Behaviour {
160
179
  WebXRController.removeEventListener(ControllerEvents.SelectEnd, this._selectEndFn);
161
180
  WebXRController.removeEventListener(ControllerEvents.Update, this._selectUpdateFn);
162
181
 
163
- this.context.pre_update_callbacks.splice(this.context.pre_update_callbacks.indexOf(this._onBeforeUpdateFn), 1);
164
- this.context.input.removeEventListener(InputEvents.PointerDown, this._onBeforeUpdateFn);
182
+ this.context.pre_update_callbacks.splice(this.context.pre_update_callbacks.indexOf(this._handleEventFn), 1);
183
+ // this.context.input.removeEventListener(InputEvents.PointerDown, this._handleEventFn);
184
+ // this.context.input.removeEventListener(InputEvents.PointerUp, this._handleEventFn);
185
+ // this.context.input.removeEventListener(InputEvents.PointerMove, this._handleEventFn);
165
186
  }
166
187
 
167
188
 
@@ -180,7 +201,8 @@ export class EventSystem extends Behaviour {
180
201
 
181
202
  private _didMove: boolean = false;
182
203
 
183
- onBeforeUpdate() {
204
+
205
+ onHandleEvents() {
184
206
  this.objectsHoveredThisFrame.length = 0;
185
207
  this.resetMeshUIStates();
186
208
 
@@ -196,7 +218,6 @@ export class EventSystem extends Behaviour {
196
218
  this._didMove = true;
197
219
  }
198
220
 
199
- const hits = this.performRaycast(null);
200
221
  let pointerId = 0;
201
222
  for (const i of this.context.input.foreachPointerId()) {
202
223
  const isDown = this.context.input.getPointerDown(i);
@@ -206,6 +227,7 @@ export class EventSystem extends Behaviour {
206
227
  break;
207
228
  }
208
229
  }
230
+
209
231
  const ptr = this.context.input.getPointerEvent(pointerId);
210
232
  // console.log(ptr);
211
233
  const args: PointerEventData = new PointerEventData(ptr);
@@ -215,42 +237,25 @@ export class EventSystem extends Behaviour {
215
237
  args.isDown = this.context.input.getPointerDown(pointerId);
216
238
  args.isUp = this.context.input.getPointerUp(pointerId);
217
239
  args.isPressed = this.context.input.getPointerPressed(pointerId);
218
- // if(args.isClicked || args.isUp) console.log("clicked", args);
219
- this.lastPointerEvent = args;
240
+ if (debug && args.isClicked) console.log("CLICK", pointerId);
241
+
242
+ const hits = this.performRaycast(null);
220
243
  if (!hits) return;
244
+ this.lastPointerEvent = args;
221
245
 
222
- // handle orbit control before handling input events
223
- // this is just so button events that modify OrbitControls.enabled work
224
- let orbitControlComponent: OrbitControls | null = null;
225
- let previousOrbitControlState: boolean | null = null;
226
- if (this.context.input.mouseDown && this.currentActiveMeshUIComponents.length > 0 && this.context.mainCameraComponent) {
227
- orbitControlComponent = GameObject.getComponent(this.context.mainCameraComponent.gameObject, OrbitControls) ?? null;
228
- if (orbitControlComponent) {
229
- previousOrbitControlState = orbitControlComponent.enabled;
230
- orbitControlComponent.enabled = false;
231
- }
246
+ const evt = {
247
+ sender: this,
248
+ args: args,
249
+ hasActiveUI: this.currentActiveMeshUIComponents.length > 0,
232
250
  }
233
-
234
- // if (args.isClicked)
235
- // console.log(this.guid, ...hits);
251
+ this.dispatchEvent(new CustomEvent(EventSystemEvents.BeforeHandleInput, { detail: evt }))
236
252
  this.handleIntersections(hits, args);
237
-
238
- if (orbitControlComponent) {
239
- this.orbitControl = orbitControlComponent;
240
- if (this.orbitControl?.enabled) {
241
- this.orbitControlWasEnabled = this.orbitControl.enabled;
242
- this.orbitControl.enabled = false;
243
- }
244
- else if (this.orbitControl && !this.context.input.mousePressed) {
245
- this.orbitControl.enabled = this.orbitControlWasEnabled;
246
- this.orbitControl = null;
247
- }
248
- }
249
-
253
+ this.dispatchEvent(new CustomEvent(EventSystemEvents.AfterHandleInput, { detail: evt }))
250
254
  }
251
255
 
252
256
  private _tempComponentsArray: Behaviour[] = [];
253
257
  onBeforeRender() {
258
+
254
259
  if (this.lastPointerEvent)
255
260
  this.lastPointerEvent.used = false;
256
261
  else return;
@@ -374,6 +379,7 @@ export class EventSystem extends Behaviour {
374
379
  const actualGo = parent[$shadowDomOwner].gameObject;
375
380
  if (actualGo) {
376
381
  const res = UIRaycastUtils.isInteractable(actualGo, this.out);
382
+ // console.log(actualGo, res);
377
383
  if (!res) return this.out.canvasGroup?.interactable ?? false;
378
384
  canvasGroup = this.out.canvasGroup ?? null;
379
385
 
@@ -9,11 +9,11 @@ export class Raycaster extends Behaviour {
9
9
  }
10
10
 
11
11
  onEnable(): void {
12
- EventSystem.instance?.register(this);
12
+ EventSystem.get(this.context)?.register(this);
13
13
  }
14
14
 
15
15
  onDisable(): void {
16
- EventSystem.instance?.unregister(this);
16
+ EventSystem.get(this.context)?.unregister(this);
17
17
  }
18
18
 
19
19
  performRaycast(_opts: RaycastOptions | null = null): THREE.Intersection[] | null {