@needle-tools/engine 4.1.1 → 4.2.2

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 (92) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/needle-engine.bundle.js +7966 -7908
  3. package/dist/needle-engine.bundle.light.js +7961 -7903
  4. package/dist/needle-engine.bundle.light.min.js +139 -135
  5. package/dist/needle-engine.bundle.light.umd.cjs +140 -136
  6. package/dist/needle-engine.bundle.min.js +139 -135
  7. package/dist/needle-engine.bundle.umd.cjs +144 -140
  8. package/dist/needle-engine.d.ts +9 -9
  9. package/lib/engine/engine_input.d.ts +14 -2
  10. package/lib/engine/engine_input.js +41 -6
  11. package/lib/engine/engine_input.js.map +1 -1
  12. package/lib/engine/engine_serialization_core.js +16 -3
  13. package/lib/engine/engine_serialization_core.js.map +1 -1
  14. package/lib/engine/engine_types.d.ts +5 -0
  15. package/lib/engine/engine_utils.d.ts +2 -0
  16. package/lib/engine/engine_utils.js +8 -0
  17. package/lib/engine/engine_utils.js.map +1 -1
  18. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
  19. package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js.map +1 -1
  20. package/lib/engine/xr/NeedleXRController.d.ts +0 -1
  21. package/lib/engine/xr/NeedleXRController.js +0 -14
  22. package/lib/engine/xr/NeedleXRController.js.map +1 -1
  23. package/lib/engine/xr/NeedleXRSession.js +10 -2
  24. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  25. package/lib/engine-components/DragControls.js +12 -3
  26. package/lib/engine-components/DragControls.js.map +1 -1
  27. package/lib/engine-components/Duplicatable.js +2 -2
  28. package/lib/engine-components/Duplicatable.js.map +1 -1
  29. package/lib/engine-components/EventTrigger.d.ts +2 -0
  30. package/lib/engine-components/EventTrigger.js +12 -0
  31. package/lib/engine-components/EventTrigger.js.map +1 -1
  32. package/lib/engine-components/Renderer.js +5 -0
  33. package/lib/engine-components/Renderer.js.map +1 -1
  34. package/lib/engine-components/ScreenCapture.js +2 -2
  35. package/lib/engine-components/ScreenCapture.js.map +1 -1
  36. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +0 -1
  37. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js.map +1 -1
  38. package/lib/engine-components/export/usdz/USDZExporter.js +2 -2
  39. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  40. package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.js +19 -11
  41. package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.js.map +1 -1
  42. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.d.ts +7 -2
  43. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +33 -11
  44. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js.map +1 -1
  45. package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.d.ts +1 -0
  46. package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js +5 -2
  47. package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js.map +1 -1
  48. package/lib/engine-components/ui/Button.js +3 -3
  49. package/lib/engine-components/ui/Button.js.map +1 -1
  50. package/lib/engine-components/ui/EventSystem.d.ts +6 -10
  51. package/lib/engine-components/ui/EventSystem.js +35 -52
  52. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  53. package/lib/engine-components/ui/InputField.d.ts +3 -1
  54. package/lib/engine-components/ui/InputField.js +8 -0
  55. package/lib/engine-components/ui/InputField.js.map +1 -1
  56. package/lib/engine-components/utils/OpenURL.js +2 -2
  57. package/lib/engine-components/utils/OpenURL.js.map +1 -1
  58. package/lib/engine-components/webxr/WebXRImageTracking.js +5 -1
  59. package/lib/engine-components/webxr/WebXRImageTracking.js.map +1 -1
  60. package/lib/engine-components/webxr/WebXRPlaneTracking.js +2 -2
  61. package/lib/engine-components/webxr/WebXRPlaneTracking.js.map +1 -1
  62. package/lib/engine-components/webxr/controllers/XRControllerFollow.js +3 -0
  63. package/lib/engine-components/webxr/controllers/XRControllerFollow.js.map +1 -1
  64. package/lib/engine-components/webxr/controllers/XRControllerModel.d.ts +3 -0
  65. package/lib/engine-components/webxr/controllers/XRControllerModel.js +11 -0
  66. package/lib/engine-components/webxr/controllers/XRControllerModel.js.map +1 -1
  67. package/package.json +1 -1
  68. package/src/engine/engine_input.ts +47 -9
  69. package/src/engine/engine_serialization_core.ts +13 -4
  70. package/src/engine/engine_types.ts +5 -0
  71. package/src/engine/engine_utils.ts +7 -0
  72. package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
  73. package/src/engine/xr/NeedleXRController.ts +0 -14
  74. package/src/engine/xr/NeedleXRSession.ts +10 -2
  75. package/src/engine-components/DragControls.ts +13 -3
  76. package/src/engine-components/Duplicatable.ts +2 -2
  77. package/src/engine-components/EventTrigger.ts +14 -0
  78. package/src/engine-components/Renderer.ts +5 -0
  79. package/src/engine-components/ScreenCapture.ts +2 -2
  80. package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +0 -1
  81. package/src/engine-components/export/usdz/USDZExporter.ts +2 -2
  82. package/src/engine-components/export/usdz/extensions/behavior/Behaviour.ts +17 -11
  83. package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +41 -23
  84. package/src/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.ts +5 -2
  85. package/src/engine-components/ui/Button.ts +3 -3
  86. package/src/engine-components/ui/EventSystem.ts +45 -62
  87. package/src/engine-components/ui/InputField.ts +9 -1
  88. package/src/engine-components/utils/OpenURL.ts +2 -2
  89. package/src/engine-components/webxr/WebXRImageTracking.ts +5 -1
  90. package/src/engine-components/webxr/WebXRPlaneTracking.ts +2 -2
  91. package/src/engine-components/webxr/controllers/XRControllerFollow.ts +3 -0
  92. package/src/engine-components/webxr/controllers/XRControllerModel.ts +15 -0
@@ -120,19 +120,21 @@ export class BehaviorExtension implements IUSDExporterExtension {
120
120
  if (actionModel.tokenId === "StartAnimation") {
121
121
  playAnimationActions.add(actionModel);
122
122
  }
123
+ let actionType = actionModel.tokenId;
124
+ if (actionModel.type !== undefined) actionType += ":" + actionModel.type;
123
125
  const affected = actionModel.affectedObjects;
124
126
  if (affected) {
125
127
  if (Array.isArray(affected)) {
126
128
  for (const a of affected) {
127
129
  actionTargets.add(a as Target);
128
130
  //@ts-ignore
129
- if (createMermaidGraphForDebugging) mermaidGraphTopLevel += `${actionModel.id}[${actionModel.id}] -- ${actionModel.tokenId} --> ${a.uuid}(("${a.displayName || a.name || a.uuid}"))\n`;
131
+ if (createMermaidGraphForDebugging) mermaidGraphTopLevel += `${actionModel.id}[${actionModel.id}\n${actionType}] -- ${actionType} --> ${a.uuid}(("${a.displayName || a.name || a.uuid}"))\n`;
130
132
  }
131
133
  }
132
134
  else if (typeof affected === "object") {
133
135
  actionTargets.add(affected as Target);
134
136
  //@ts-ignore
135
- if (createMermaidGraphForDebugging) mermaidGraphTopLevel += `${actionModel.id}[${actionModel.id}] -- ${actionModel.tokenId} --> ${affected.uuid}(("${affected.displayName || affected.name || affected.uuid}"))\n`;
137
+ if (createMermaidGraphForDebugging) mermaidGraphTopLevel += `${actionModel.id}[${actionModel.id}\n${actionType}] -- ${actionType} --> ${affected.uuid}(("${affected.displayName || affected.name || affected.uuid}"))\n`;
136
138
  }
137
139
  else if (typeof affected === "string") {
138
140
  actionTargets.add({uuid: affected} as any as Target);
@@ -144,7 +146,7 @@ export class BehaviorExtension implements IUSDExporterExtension {
144
146
  if (typeof xform === "object") {
145
147
  actionTargets.add(xform as Target);
146
148
  //@ts-ignore
147
- if (createMermaidGraphForDebugging) mermaidGraphTopLevel += `${actionModel.id}[${actionModel.id}] -- ${actionModel.tokenId} --> ${xform.uuid}(("${xform.displayName || xform.name || xform.uuid}"))\n`;
149
+ if (createMermaidGraphForDebugging) mermaidGraphTopLevel += `${actionModel.id}[${actionModel.id}\n${actionType}] -- ${actionType} --> ${xform.uuid}(("${xform.displayName || xform.name || xform.uuid}"))\n`;
148
150
  }
149
151
  else if (typeof xform === "string") {
150
152
  actionTargets.add({uuid: xform} as any as Target);
@@ -159,19 +161,21 @@ export class BehaviorExtension implements IUSDExporterExtension {
159
161
  collectTrigger(t, action);
160
162
  }
161
163
  else if (trigger instanceof TriggerModel) {
164
+ let triggerType = trigger.tokenId;
165
+ if (trigger.type !== undefined) triggerType += ":" + trigger.type;
162
166
  if (typeof trigger.targetId === "object") {
163
167
  triggerSources.add(trigger.targetId as Target);
164
168
  //@ts-ignore
165
- if (createMermaidGraphForDebugging) mermaidGraphTopLevel += `${trigger.targetId.uuid}(("${trigger.targetId.displayName}")) --> ${trigger.id}[${trigger.id}]\n`;
169
+ if (createMermaidGraphForDebugging) mermaidGraphTopLevel += `${trigger.targetId.uuid}(("${trigger.targetId.displayName}")) --> ${trigger.id}[${trigger.id}\n${triggerType}]\n`;
166
170
  }
167
171
  //@ts-ignore
168
- if (createMermaidGraphForDebugging) mermaidGraph += `${trigger.id}((${trigger.id})) -- ${trigger.tokenId}${trigger.type ? ":" + trigger.type : ""} --> ${action.id}[${action.tokenId || action.id}]\n`;
172
+ if (createMermaidGraphForDebugging) mermaidGraph += `${trigger.id}((${trigger.id})) -- ${triggerType} --> ${action.id}[${action.tokenId || action.id}]\n`;
169
173
  }
170
174
  }
171
175
 
172
176
  // collect all targets of all triggers and actions
173
177
  for (const beh of this.behaviours) {
174
- if (createMermaidGraphForDebugging) mermaidGraph += `subgraph Behavior_${beh.id}\n`;
178
+ if (createMermaidGraphForDebugging) mermaidGraph += `subgraph ${beh.id}\n`;
175
179
  collectAction(beh.action);
176
180
  collectTrigger(beh.trigger, beh.action);
177
181
  if (createMermaidGraphForDebugging) mermaidGraph += `end\n`;
@@ -180,10 +184,12 @@ export class BehaviorExtension implements IUSDExporterExtension {
180
184
 
181
185
  if (createMermaidGraphForDebugging) {
182
186
  console.log("All USDZ behaviours", this.behaviours);
183
- console.warn("The Mermaid graph can be pasted into https://massive-mermaid.glitch.me/ or https://mermaid.live/edit. It should be in your clipboard already!");
184
- console.log(mermaidGraph);
185
- // copy to clipboard, can be pasted into https://massive-mermaid.glitch.me/ or https://mermaid.live/edit
186
- navigator.clipboard.writeText(mermaidGraph);
187
+ if (this.behaviours.length) {
188
+ console.warn("The Mermaid graph can be pasted into https://massive-mermaid.glitch.me/ or https://mermaid.live/edit. It should be in your clipboard already!");
189
+ console.log(mermaidGraph);
190
+ // copy to clipboard, can be pasted into https://massive-mermaid.glitch.me/ or https://mermaid.live/edit
191
+ navigator.clipboard.writeText(mermaidGraph);
192
+ }
187
193
  }
188
194
 
189
195
  {
@@ -212,7 +218,7 @@ export class BehaviorExtension implements IUSDExporterExtension {
212
218
  }
213
219
  }
214
220
 
215
- if (createMermaidGraphForDebugging) {
221
+ if (createMermaidGraphForDebugging && playAnimationActions.size) {
216
222
  console.log(animationsGraph);
217
223
  }
218
224
 
@@ -17,7 +17,7 @@ import { makeNameSafeForUSD,USDDocument, USDObject, USDZExporterContext } from "
17
17
  import { AnimationExtension, RegisteredAnimationInfo, type UsdzAnimation } from "../Animation.js";
18
18
  import { AudioExtension } from "./AudioExtension.js";
19
19
  import type { BehaviorExtension, UsdzBehaviour } from "./Behaviour.js";
20
- import { ActionBuilder, ActionModel, BehaviorModel, EmphasizeActionMotionType,type IBehaviorElement, Target, TriggerBuilder } from "./BehavioursBuilder.js";
20
+ import { ActionBuilder, ActionModel, BehaviorModel, EmphasizeActionMotionType,GroupActionModel,type IBehaviorElement, Target, TriggerBuilder } from "./BehavioursBuilder.js";
21
21
 
22
22
  const debug = getParam("debugusdzbehaviours");
23
23
 
@@ -203,10 +203,10 @@ export class ChangeMaterialOnClick extends Behaviour implements IPointerClickHan
203
203
  }
204
204
 
205
205
  onPointerEnter(_args: PointerEventData) {
206
- this.context.input.setCursorPointer();
206
+ this.context.input.setCursor("pointer");
207
207
  }
208
208
  onPointerExit(_: PointerEventData) {
209
- this.context.input.setCursorNormal();
209
+ this.context.input.unsetCursor("pointer");
210
210
  }
211
211
  onPointerClick(args: PointerEventData) {
212
212
  args.use();
@@ -251,7 +251,8 @@ export class ChangeMaterialOnClick extends Behaviour implements IPointerClickHan
251
251
  private targetModels!: USDObject[];
252
252
 
253
253
  private static _materialTriggersPerId: { [key: string]: ChangeMaterialOnClick[] } = {}
254
-
254
+ private static _startHiddenBehaviour: BehaviorModel | null = null;
255
+ private static _parallelStartHiddenActions: USDObject[] = [];
255
256
 
256
257
  async beforeCreateDocument(_ext: BehaviorExtension, _context) {
257
258
  this.targetModels = [];
@@ -309,23 +310,25 @@ export class ChangeMaterialOnClick extends Behaviour implements IPointerClickHan
309
310
  }
310
311
 
311
312
  private createAndAttachBehaviors(ext: BehaviorExtension, myVariants: Array<USDObject>, otherVariants: Array<USDObject>) {
312
- const start: ActionModel[] = [];
313
313
  const select: ActionModel[] = [];
314
314
 
315
315
  const fadeDuration = Math.max(0, this.fadeDuration);
316
316
 
317
317
  select.push(ActionBuilder.fadeAction([...this.targetModels, ...otherVariants], fadeDuration, false));
318
- start.push(ActionBuilder.fadeAction(myVariants, fadeDuration, false));
319
318
  select.push(ActionBuilder.fadeAction(myVariants, fadeDuration, true));
320
319
 
321
320
  ext.addBehavior(new BehaviorModel("Select_" + this.selfModel.name,
322
321
  TriggerBuilder.tapTrigger(this.selfModel),
323
322
  ActionBuilder.parallel(...select))
324
323
  );
325
- ext.addBehavior(new BehaviorModel("StartHidden_" + this.selfModel.name,
326
- TriggerBuilder.sceneStartTrigger(),
327
- ActionBuilder.parallel(...start))
328
- );
324
+ ChangeMaterialOnClick._parallelStartHiddenActions.push(...myVariants);
325
+ if (!ChangeMaterialOnClick._startHiddenBehaviour) {
326
+ ChangeMaterialOnClick._startHiddenBehaviour =
327
+ new BehaviorModel("StartHidden_" + this.selfModel.name,
328
+ TriggerBuilder.sceneStartTrigger(),
329
+ ActionBuilder.fadeAction(ChangeMaterialOnClick._parallelStartHiddenActions, fadeDuration, false));
330
+ ext.addBehavior(ChangeMaterialOnClick._startHiddenBehaviour);
331
+ }
329
332
  }
330
333
 
331
334
  private static getMaterialName(material: Material) {
@@ -526,9 +529,9 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
526
529
  sequence.push(ActionBuilder.fadeAction(selfModel, 0, false));
527
530
  sequence.push(ActionBuilder.fadeAction(this.targetModel, 0, targetState));
528
531
 
529
- ext.addBehavior(new BehaviorModel("Toggle_" + selfModel.name + "_toggleTo" + (targetState ? "On" : "Off"),
532
+ ext.addBehavior(new BehaviorModel("Toggle_" + selfModel.name + "_ToggleTo" + (targetState ? "On" : "Off"),
530
533
  TriggerBuilder.tapTrigger(selfModel),
531
- ActionBuilder.parallel(...sequence)
534
+ sequence.length > 1 ? ActionBuilder.parallel(...sequence) : sequence[0],
532
535
  ));
533
536
  }
534
537
  // We have a toggleModel, so we need to set up two sequences:
@@ -540,7 +543,7 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
540
543
  toggleSequence.push(ActionBuilder.fadeAction(this.toggleModel, 0, true));
541
544
  toggleSequence.push(ActionBuilder.fadeAction(this.targetModel, 0, targetState));
542
545
 
543
- ext.addBehavior(new BehaviorModel("Toggle_" + selfModel.name + "_toggleTo" + (targetState ? "On" : "Off"),
546
+ ext.addBehavior(new BehaviorModel("Toggle_" + selfModel.name + "_ToggleTo" + (targetState ? "On" : "Off"),
544
547
  TriggerBuilder.tapTrigger(selfModel),
545
548
  ActionBuilder.parallel(...toggleSequence)
546
549
  ));
@@ -550,7 +553,7 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
550
553
  reverseSequence.push(ActionBuilder.fadeAction(selfModel, 0, true));
551
554
  reverseSequence.push(ActionBuilder.fadeAction(this.targetModel, 0, !targetState));
552
555
 
553
- ext.addBehavior(new BehaviorModel("Toggle_" + selfModel.name + "_toggleTo" + (!targetState ? "On" : "Off"),
556
+ ext.addBehavior(new BehaviorModel("Toggle_" + selfModel.name + "_ToggleTo" + (!targetState ? "On" : "Off"),
554
557
  TriggerBuilder.tapTrigger(this.toggleModel),
555
558
  ActionBuilder.parallel(...reverseSequence)
556
559
  ));
@@ -565,10 +568,7 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
565
568
  if (this.toggleModel)
566
569
  objectsToHide.push(this.toggleModel);
567
570
 
568
- ext.addBehavior(new BehaviorModel("HideOnStart_" + this.gameObject.name,
569
- TriggerBuilder.sceneStartTrigger(),
570
- ActionBuilder.fadeAction(objectsToHide, 0, false)
571
- ));
571
+ HideOnStart.add(objectsToHide, ext);
572
572
  }
573
573
  }
574
574
 
@@ -595,6 +595,27 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
595
595
  */
596
596
  export class HideOnStart extends Behaviour implements UsdzBehaviour {
597
597
 
598
+ private static _fadeBehaviour?: BehaviorModel;
599
+ private static _fadeObjects: Array<USDObject | Object3D> = [];
600
+
601
+ static add(target: Target, ext: BehaviorExtension) {
602
+ const arr = Array.isArray(target) ? target : [target];
603
+ for (const entry of arr) {
604
+ if (!HideOnStart._fadeObjects.includes(entry)) {
605
+ console.log("adding hide on start", entry);
606
+ HideOnStart._fadeObjects.push(entry);
607
+ }
608
+ }
609
+ if (HideOnStart._fadeBehaviour === undefined) {
610
+ HideOnStart._fadeBehaviour = new BehaviorModel("HideOnStart",
611
+ TriggerBuilder.sceneStartTrigger(),
612
+ //@ts-ignore
613
+ ActionBuilder.fadeAction(HideOnStart._fadeObjects, 0, false)
614
+ );
615
+ ext.addBehavior(HideOnStart._fadeBehaviour);
616
+ }
617
+ }
618
+
598
619
  start() {
599
620
  GameObject.setActive(this.gameObject, false);
600
621
  }
@@ -603,10 +624,7 @@ export class HideOnStart extends Behaviour implements UsdzBehaviour {
603
624
  if (model.uuid === this.gameObject.uuid) {
604
625
  // we only want to mark the object as HideOnStart if it's still hidden
605
626
  if (!this.wasVisible) {
606
- ext.addBehavior(new BehaviorModel("HideOnStart_" + this.gameObject.name,
607
- TriggerBuilder.sceneStartTrigger(),
608
- ActionBuilder.fadeAction(model, 0, false)
609
- ));
627
+ HideOnStart.add(model, ext);
610
628
  }
611
629
  }
612
630
  }
@@ -752,7 +770,7 @@ export class PlayAudioOnClick extends Behaviour implements IPointerClickHandler,
752
770
  // automatically play audio on start too if the referenced AudioSource has playOnAwake enabled
753
771
  if (this.target && this.target.playOnAwake && this.target.enabled) {
754
772
  if (anyChildHasGeometry && this.trigger === "tap") {
755
- // HACK Currently (20240509) we MUST not emit this behaviour if we're also expecting the tap trigger to work.
773
+ // WORKAROUND Currently (20240509) we MUST not emit this behaviour if we're also expecting the tap trigger to work.
756
774
  // Seems to be a regression in QuickLook... audio clips can't be stopped anymore as soon as they start playing.
757
775
  console.warn("USDZExport: Audio sources that are played on tap can't also auto-play at scene start due to a QuickLook bug.");
758
776
  }
@@ -199,11 +199,14 @@ function addInputTargetComponent(model: USDObject, options: { direct: boolean, i
199
199
 
200
200
  export class TriggerBuilder {
201
201
 
202
+ private static __sceneStartTrigger?: TriggerModel;
203
+
202
204
  static sceneStartTrigger(): TriggerModel {
203
- const trigger = new TriggerModel();
204
- trigger.targetId = undefined;
205
+ if (this.__sceneStartTrigger !== undefined) return this.__sceneStartTrigger;
206
+ const trigger = new TriggerModel(undefined, "SceneStart");
205
207
  trigger.tokenId = "SceneTransition";
206
208
  trigger.type = "enter";
209
+ this.__sceneStartTrigger = trigger;
207
210
  return trigger;
208
211
  }
209
212
 
@@ -90,7 +90,7 @@ export class Button extends Behaviour implements IPointerEventHandler {
90
90
  else if (this.transition === Transition.ColorTint && this.colors) {
91
91
  this._image?.setState("hovered");
92
92
  }
93
- if (canSetCursor) this.context.input.setCursorPointer();
93
+ if (canSetCursor) this.context.input.setCursor("pointer");
94
94
  }
95
95
 
96
96
  onPointerExit() {
@@ -107,7 +107,7 @@ export class Button extends Behaviour implements IPointerEventHandler {
107
107
  else if (this.transition === Transition.ColorTint && this.colors) {
108
108
  this._image?.setState("normal");
109
109
  }
110
- this.context.input.setCursorNormal();
110
+ this.context.input.unsetCursor("pointer");
111
111
  }
112
112
 
113
113
  onPointerDown(_) {
@@ -211,7 +211,7 @@ export class Button extends Behaviour implements IPointerEventHandler {
211
211
  }
212
212
 
213
213
  onDestroy(): void {
214
- if (this._isHovered) this.context.input.setCursorNormal();
214
+ if (this._isHovered) this.context.input.unsetCursor("pointer");
215
215
  }
216
216
 
217
217
  private _requestedAnimatorTrigger?: string;
@@ -1,11 +1,12 @@
1
- import { type Intersection, Mesh,Object3D } from "three";
1
+ import { type Intersection, Mesh, Object3D } from "three";
2
2
 
3
3
  import { isDevEnvironment, showBalloonMessage } from "../../engine/debug/index.js";
4
- import { type InputEventNames, InputEvents, NEPointerEvent, NEPointerEventIntersection,PointerType } from "../../engine/engine_input.js";
4
+ import { type InputEventNames, InputEvents, NEPointerEvent, NEPointerEventIntersection, PointerType } from "../../engine/engine_input.js";
5
+ import { onInitialized } from "../../engine/engine_lifecycle_api.js";
5
6
  import { Mathf } from "../../engine/engine_math.js";
6
7
  import { RaycastOptions, type RaycastTestObjectReturnType } from "../../engine/engine_physics.js";
7
8
  import { Context } from "../../engine/engine_setup.js";
8
- import type { IComponent } from "../../engine/engine_types.js";
9
+ import { HideFlags, type IComponent } from "../../engine/engine_types.js";
9
10
  import { getParam } from "../../engine/engine_utils.js";
10
11
  import { Behaviour, GameObject } from "../Component.js";
11
12
  import { $shadowDomOwner } from "./BaseUIComponent.js";
@@ -30,47 +31,15 @@ export declare type AfterHandleInputEvent = {
30
31
 
31
32
  declare type IComponentCanMaybeReceiveEvents = IPointerEventHandler & IComponent & { interactable?: boolean };
32
33
 
34
+ onInitialized((ctx) => {
35
+ EventSystem.createIfNoneExists(ctx);
36
+ })
37
+
33
38
  /**
34
39
  * @category User Interface
35
40
  * @group Components
36
41
  */
37
42
  export class EventSystem extends Behaviour {
38
- private static _eventSystemMap = new Map<Context, EventSystem[]>();
39
-
40
- static didSearchEventSystem: boolean = false;
41
- static createIfNoneExists(context: Context) {
42
- if (!this.didSearchEventSystem) {
43
- this.didSearchEventSystem = true;
44
- if (EventSystem.systems.length <= 0) {
45
- EventSystem.systems.push(...GameObject.findObjectsOfType(EventSystem, context));
46
- }
47
- }
48
- for (const sys of EventSystem.systems) {
49
- if (sys.context === context) return; // exists
50
- }
51
- const go = new Object3D();
52
- GameObject.addComponent(go, EventSystem);
53
- context.scene.add(go);
54
- }
55
-
56
- static get systems(): EventSystem[] {
57
- const context = Context.Current;
58
- if (!this._eventSystemMap.has(context)) {
59
- this._eventSystemMap.set(context, []);
60
- }
61
- return this._eventSystemMap.get(context)!;
62
- }
63
-
64
- static get(ctx: Context): EventSystem | null {
65
- const systems = this._eventSystemMap.get(ctx);
66
- if (systems && systems.length > 0) return systems[0];
67
- return null;
68
- }
69
-
70
- static get instance(): EventSystem | null {
71
- return this.systems[0];
72
- }
73
-
74
43
  //@ts-ignore
75
44
  static ensureUpdateMeshUI(instance, context: Context, force: boolean = false) {
76
45
  MeshUIHelper.update(instance, context, force);
@@ -79,32 +48,22 @@ export class EventSystem extends Behaviour {
79
48
  MeshUIHelper.markDirty();
80
49
  }
81
50
 
82
- private raycaster: Raycaster[] = [];
83
-
84
- constructor() {
85
- super();
86
- EventSystem.systems.push(this);
51
+ static createIfNoneExists(context: Context) {
52
+ if (!context.scene.getComponent(EventSystem)) {
53
+ context.scene.addComponent(EventSystem);
54
+ }
87
55
  }
88
56
 
89
- get hasActiveUI() { return this.currentActiveMeshUIComponents.length > 0; }
90
- get isHoveringObjects() { return this.hoveredByID.size > 0; }
91
-
92
- onDestroy(): void {
93
- EventSystem.systems.splice(EventSystem.systems.indexOf(this), 1);
57
+ static get(ctx: Context): EventSystem | null {
58
+ this.createIfNoneExists(ctx);
59
+ return ctx.scene.getComponent(EventSystem);
94
60
  }
95
61
 
96
- start() {
97
- if (this.raycaster.length <= 0) {
98
- const res = GameObject.findObjectOfType(Raycaster, this.context);
99
- if (!res) {
100
- const rc = GameObject.addComponent(this.context.scene, ObjectRaycaster);
101
- this.raycaster.push(rc);
102
- if (isDevEnvironment() || debug)
103
- console.warn("Added an ObjectRaycaster to the scene because no raycaster was found.");
104
- }
105
- }
62
+ static get instance(): EventSystem | null {
63
+ return this.get(Context.Current);
106
64
  }
107
65
 
66
+ private readonly raycaster: Raycaster[] = [];
108
67
  register(rc: Raycaster) {
109
68
  if (rc && this.raycaster && !this.raycaster.includes(rc))
110
69
  this.raycaster?.push(rc);
@@ -116,6 +75,23 @@ export class EventSystem extends Behaviour {
116
75
  }
117
76
  }
118
77
 
78
+ get hasActiveUI() { return this.currentActiveMeshUIComponents.length > 0; }
79
+ get isHoveringObjects() { return this.hoveredByID.size > 0; }
80
+
81
+ awake(): void {
82
+ // We only want ONE eventsystem on the root scene
83
+ // as long as this component is not implemented in core we need to check this here
84
+ if (this.gameObject as Object3D !== this.context.scene) {
85
+ this.enabled = false;
86
+ }
87
+ }
88
+
89
+ start() {
90
+ if (!this.context.scene.getComponent(Raycaster)) {
91
+ this.context.scene.addComponent(ObjectRaycaster);
92
+ }
93
+ }
94
+
119
95
  onEnable(): void {
120
96
  this.context.input.addEventListener(InputEvents.PointerDown, this.onPointerEvent);
121
97
  this.context.input.addEventListener(InputEvents.PointerUp, this.onPointerEvent);
@@ -183,7 +159,6 @@ export class EventSystem extends Behaviour {
183
159
  options.screenPoint = this.context.input.getPointerPositionRC(pointerEvent.pointerId)!;
184
160
  }
185
161
 
186
-
187
162
  const hits = this.performRaycast(options) as Array<NEPointerEventIntersection>;
188
163
  if (hits) {
189
164
  for (const hit of hits) {
@@ -242,6 +217,14 @@ export class EventSystem extends Behaviour {
242
217
  // TODO: this implementation below should be removed and we should regularly raycast objects in the scene unless marked as "do not raycast"
243
218
  // with the introduction of the mesh-bvh based raycasting the performance impact should be greatly reduced. But this needs further testing
244
219
 
220
+ const raycasterOnObject = obj && "getComponent" in obj ? obj.getComponent(Raycaster) : null;
221
+ if (raycasterOnObject && raycasterOnObject != this._currentlyActiveRaycaster) {
222
+ return false;
223
+ }
224
+ // if (this._currentPointerEventName == "pointermove") {
225
+ // console.log(this.context.time.frame, obj.name, obj.type, obj.guid)
226
+ // }
227
+
245
228
  // check if this object is actually a UI shadow hierarchy object
246
229
  let uiOwner: Object3D | null = null;
247
230
  const isUI = isUIObject(obj);
@@ -450,7 +433,7 @@ export class EventSystem extends Behaviour {
450
433
  isShadow = true;
451
434
  }
452
435
  }
453
-
436
+
454
437
  // adding this to have a way for allowing to receive events on TMUI elements without shadow hierarchy
455
438
  // if(parent["needle:use_eventsystem"] == true){
456
439
  // // if use_eventsystem is true, we want to handle the event
@@ -671,7 +654,7 @@ export class EventSystem extends Behaviour {
671
654
  comp[symbol] = state;
672
655
  return true;
673
656
  }
674
- else {
657
+ else {
675
658
  if (!state || !state.includes(pointerId)) return false;
676
659
  const i = state.indexOf(pointerId);
677
660
  if (i !== -1) {
@@ -3,7 +3,7 @@ import { FrameEvent } from "../../engine/engine_setup.js";
3
3
  import { DeviceUtilities, getParam } from "../../engine/engine_utils.js";
4
4
  import { Behaviour, GameObject } from "../Component.js";
5
5
  import { EventList } from "../EventList.js";
6
- import { type IPointerEventHandler } from "./PointerEvents.js";
6
+ import { type IPointerEventHandler,PointerEventData } from "./PointerEvents.js";
7
7
  import { Text } from "./Text.js";
8
8
  import { tryGetUIComponent } from "./Utils.js";
9
9
 
@@ -113,6 +113,14 @@ export class InputField extends Behaviour implements IPointerEventHandler {
113
113
  this.onDeselected();
114
114
  }
115
115
 
116
+ onPointerEnter(_args: PointerEventData) {
117
+ const canSetCursor = _args.event.pointerType === "mouse" && _args.button === 0;
118
+ if(canSetCursor) this.context.input.setCursor("text");
119
+ }
120
+ onPointerExit(_args: PointerEventData) {
121
+ this.context.input.unsetCursor("text")
122
+ }
123
+
116
124
  onPointerClick(_args) {
117
125
  if (debug) console.log("CLICK", _args, InputField.active);
118
126
  InputField.activeTime = this.context.time.time;
@@ -91,12 +91,12 @@ export class OpenURL extends Behaviour implements IPointerClickHandler {
91
91
  /** @internal */
92
92
  onPointerEnter(args) {
93
93
  if (!args.used && this.clickable)
94
- this.context.input.setCursorPointer();
94
+ this.context.input.setCursor("pointer");
95
95
  }
96
96
  /** @internal */
97
97
  onPointerExit() {
98
98
  if (this.clickable)
99
- this.context.input.setCursorNormal();
99
+ this.context.input.unsetCursor("pointer");
100
100
  }
101
101
  /** @internal */
102
102
  onPointerClick(args: PointerEventData) {
@@ -160,9 +160,13 @@ class ImageTrackingExtension implements IUSDExporterExtension {
160
160
  }
161
161
 
162
162
  onAfterHierarchy(_context: USDZExporterContext, writer: USDWriter) {
163
+ const iOSVersion = DeviceUtilities.getiOSVersion();
164
+ const majorVersion = iOSVersion ? parseInt(iOSVersion.split(".")[0]) : 18;
165
+ const workaroundForFB16119331 = majorVersion >= 18;
166
+ const multiplier = workaroundForFB16119331 ? 1 : 100;
163
167
  writer.beginBlock(`def Preliminary_ReferenceImage "AnchoringReferenceImage"`);
164
168
  writer.appendLine(`uniform asset image = @image_tracking/` + this.filename + `@`);
165
- writer.appendLine(`uniform double physicalWidth = ` + (this.widthInMeters * 100).toFixed(8));
169
+ writer.appendLine(`uniform double physicalWidth = ` + (this.widthInMeters * multiplier).toFixed(8));
166
170
  writer.closeBlock();
167
171
  }
168
172
 
@@ -364,7 +364,7 @@ export class WebXRPlaneTracking extends Behaviour {
364
364
  if (newPlane instanceof Mesh) {
365
365
  disposeObjectResources(newPlane.geometry);
366
366
  newPlane.geometry = this.createGeometry(data);
367
- this.makeOccluder(newPlane, newPlane.material, this.occluder);
367
+ this.makeOccluder(newPlane, newPlane.material, this.occluder && !this.dataTemplate);
368
368
  }
369
369
  else if (newPlane instanceof Group) {
370
370
  // We want to process only one level of children on purpose here
@@ -372,7 +372,7 @@ export class WebXRPlaneTracking extends Behaviour {
372
372
  if (ch instanceof Mesh) {
373
373
  disposeObjectResources(ch.geometry);
374
374
  ch.geometry = this.createGeometry(data);
375
- this.makeOccluder(ch, ch.material, this.occluder);
375
+ this.makeOccluder(ch, ch.material, this.occluder && !this.dataTemplate);
376
376
  }
377
377
  }
378
378
  }
@@ -71,6 +71,9 @@ export class XRControllerFollow extends Behaviour {
71
71
 
72
72
  /** @internal */
73
73
  onUpdateXR(args: NeedleXREventArgs): void {
74
+ // explicit check since we're overriding activeAndEnabled
75
+ if (!this.enabled) return;
76
+
74
77
  // try to get the controller
75
78
  const ctrl = args.xr.getController(this.side);
76
79
  if (ctrl) {
@@ -113,6 +113,7 @@ export class XRControllerModel extends Behaviour {
113
113
  }
114
114
  }
115
115
  }
116
+
116
117
  onXRControllerRemoved(args: NeedleXRControllerEventArgs): void {
117
118
  console.debug("XR Controller Removed", args.controller.side, args.controller.index);
118
119
  // we need to find the index by the controller because if controller 0 is removed first then args.controller.index 1 will be at index 0
@@ -128,6 +129,19 @@ export class XRControllerModel extends Behaviour {
128
129
  entry.model = undefined;
129
130
  }
130
131
  }
132
+
133
+ onBeforeXR(_mode: XRSessionMode, args: XRSessionInit & { trackedImages: Array<any> }): void {
134
+ // When a custom hand model is used, we want to ensure we're requesting hand tracking,
135
+ // even when the platform default doesn't include it (for example, on VisionOS we don't
136
+ // request hand tracking by default because there's an additional permissions dialogue.
137
+ if (this.createHandModel && (this.customLeftHand || this.customRightHand)) {
138
+ args.optionalFeatures = args.optionalFeatures || [];
139
+ if (!args.optionalFeatures.includes("hand-tracking")) {
140
+ args.optionalFeatures.push("hand-tracking");
141
+ }
142
+ }
143
+ }
144
+
131
145
  onLeaveXR(_args: NeedleXREventArgs): void {
132
146
  for (const entry of this._models) {
133
147
  if (!entry) continue;
@@ -145,6 +159,7 @@ export class XRControllerModel extends Behaviour {
145
159
  }
146
160
  this._models.length = 0;
147
161
  }
162
+
148
163
  onBeforeRender() {
149
164
  if (!NeedleXRSession.active) return;
150
165