@needle-tools/engine 3.5.1-alpha → 3.5.3-alpha

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 (109) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/needle-engine.js +29554 -29281
  3. package/dist/needle-engine.min.js +357 -356
  4. package/dist/needle-engine.umd.cjs +354 -353
  5. package/lib/engine/codegen/register_types.js +4 -2
  6. package/lib/engine/codegen/register_types.js.map +1 -1
  7. package/lib/engine/debug/debug_overlay.js +2 -1
  8. package/lib/engine/debug/debug_overlay.js.map +1 -1
  9. package/lib/engine/engine_components.js +2 -1
  10. package/lib/engine/engine_components.js.map +1 -1
  11. package/lib/engine/engine_element_loading.js +4 -4
  12. package/lib/engine/engine_element_loading.js.map +1 -1
  13. package/lib/engine/engine_gameobject.js +2 -0
  14. package/lib/engine/engine_gameobject.js.map +1 -1
  15. package/lib/engine/engine_input.js +4 -1
  16. package/lib/engine/engine_input.js.map +1 -1
  17. package/lib/engine/engine_license.d.ts +2 -0
  18. package/lib/engine/engine_license.js +25 -4
  19. package/lib/engine/engine_license.js.map +1 -1
  20. package/lib/engine/engine_physics_rapier.js +2 -1
  21. package/lib/engine/engine_physics_rapier.js.map +1 -1
  22. package/lib/engine/engine_serialization_core.js +16 -1
  23. package/lib/engine/engine_serialization_core.js.map +1 -1
  24. package/lib/engine/extensions/NEEDLE_lighting_settings.js +10 -1
  25. package/lib/engine/extensions/NEEDLE_lighting_settings.js.map +1 -1
  26. package/lib/engine-components/Component.js +0 -3
  27. package/lib/engine-components/Component.js.map +1 -1
  28. package/lib/engine-components/SceneSwitcher.d.ts +9 -0
  29. package/lib/engine-components/SceneSwitcher.js +128 -0
  30. package/lib/engine-components/SceneSwitcher.js.map +1 -1
  31. package/lib/engine-components/codegen/components.d.ts +2 -1
  32. package/lib/engine-components/codegen/components.js +2 -1
  33. package/lib/engine-components/codegen/components.js.map +1 -1
  34. package/lib/engine-components/export/usdz/Extension.d.ts +3 -2
  35. package/lib/engine-components/export/usdz/ThreeUSDZExporter.d.ts +9 -5
  36. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js +64 -20
  37. package/lib/engine-components/export/usdz/ThreeUSDZExporter.js.map +1 -1
  38. package/lib/engine-components/export/usdz/USDZExporter.d.ts +2 -0
  39. package/lib/engine-components/export/usdz/USDZExporter.js +44 -9
  40. package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
  41. package/lib/engine-components/export/usdz/extensions/behavior/AudioExtension.d.ts +9 -0
  42. package/lib/engine-components/export/usdz/extensions/behavior/AudioExtension.js +48 -0
  43. package/lib/engine-components/export/usdz/extensions/behavior/AudioExtension.js.map +1 -0
  44. package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.d.ts +4 -5
  45. package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.js +19 -10
  46. package/lib/engine-components/export/usdz/extensions/behavior/Behaviour.js.map +1 -1
  47. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.d.ts +9 -0
  48. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +75 -5
  49. package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js.map +1 -1
  50. package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.d.ts +20 -0
  51. package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js +45 -0
  52. package/lib/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.js.map +1 -1
  53. package/lib/engine-components/ui/BaseUIComponent.d.ts +1 -0
  54. package/lib/engine-components/ui/BaseUIComponent.js +8 -4
  55. package/lib/engine-components/ui/BaseUIComponent.js.map +1 -1
  56. package/lib/engine-components/ui/Canvas.js +1 -1
  57. package/lib/engine-components/ui/Canvas.js.map +1 -1
  58. package/lib/engine-components/ui/EventSystem.js +3 -0
  59. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  60. package/lib/engine-components/ui/Image.d.ts +3 -1
  61. package/lib/engine-components/ui/Image.js +15 -1
  62. package/lib/engine-components/ui/Image.js.map +1 -1
  63. package/lib/engine-components/ui/Interfaces.d.ts +1 -1
  64. package/lib/engine-components/ui/Layout.js +2 -0
  65. package/lib/engine-components/ui/Layout.js.map +1 -1
  66. package/lib/engine-components/ui/PointerEvents.d.ts +8 -1
  67. package/lib/engine-components/ui/PointerEvents.js +9 -1
  68. package/lib/engine-components/ui/PointerEvents.js.map +1 -1
  69. package/lib/engine-components/ui/RaycastUtils.js +5 -0
  70. package/lib/engine-components/ui/RaycastUtils.js.map +1 -1
  71. package/lib/engine-components/ui/RectTransform.d.ts +2 -2
  72. package/lib/engine-components/ui/RectTransform.js +11 -12
  73. package/lib/engine-components/ui/RectTransform.js.map +1 -1
  74. package/lib/engine-components/ui/Text.d.ts +0 -2
  75. package/lib/engine-components/ui/Text.js +0 -5
  76. package/lib/engine-components/ui/Text.js.map +1 -1
  77. package/lib/tsconfig.tsbuildinfo +1 -1
  78. package/package.json +1 -1
  79. package/plugins/vite/license.js +2 -2
  80. package/src/engine/codegen/register_types.js +4 -2
  81. package/src/engine/debug/debug_overlay.ts +2 -1
  82. package/src/engine/engine_components.ts +2 -1
  83. package/src/engine/engine_element_loading.ts +4 -4
  84. package/src/engine/engine_gameobject.ts +3 -0
  85. package/src/engine/engine_input.ts +4 -1
  86. package/src/engine/engine_license.ts +25 -4
  87. package/src/engine/engine_physics_rapier.ts +2 -1
  88. package/src/engine/engine_serialization_core.ts +17 -1
  89. package/src/engine/extensions/NEEDLE_lighting_settings.ts +11 -1
  90. package/src/engine-components/Component.ts +1 -3
  91. package/src/engine-components/SceneSwitcher.ts +136 -1
  92. package/src/engine-components/codegen/components.ts +2 -1
  93. package/src/engine-components/export/usdz/Extension.ts +3 -2
  94. package/src/engine-components/export/usdz/ThreeUSDZExporter.ts +89 -21
  95. package/src/engine-components/export/usdz/USDZExporter.ts +48 -11
  96. package/src/engine-components/export/usdz/extensions/behavior/AudioExtension.ts +63 -0
  97. package/src/engine-components/export/usdz/extensions/behavior/Behaviour.ts +28 -16
  98. package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +98 -21
  99. package/src/engine-components/export/usdz/extensions/behavior/BehavioursBuilder.ts +48 -2
  100. package/src/engine-components/ui/BaseUIComponent.ts +8 -3
  101. package/src/engine-components/ui/Canvas.ts +1 -1
  102. package/src/engine-components/ui/EventSystem.ts +5 -1
  103. package/src/engine-components/ui/Image.ts +16 -1
  104. package/src/engine-components/ui/Interfaces.ts +1 -1
  105. package/src/engine-components/ui/Layout.ts +2 -0
  106. package/src/engine-components/ui/PointerEvents.ts +16 -2
  107. package/src/engine-components/ui/RaycastUtils.ts +6 -1
  108. package/src/engine-components/ui/RectTransform.ts +11 -11
  109. package/src/engine-components/ui/Text.ts +1 -3
@@ -6,11 +6,12 @@ import { IPointerClickHandler } from "../../../../ui/PointerEvents";
6
6
  import { RegisteredAnimationInfo, UsdzAnimation } from "../Animation";
7
7
  import { getWorldPosition, getWorldQuaternion, getWorldScale, setWorldPosition, setWorldQuaternion, setWorldScale } from "../../../../../engine/engine_three_utils";
8
8
 
9
- import { Object3D, Material, Vector3, Quaternion, AnimationAction } from "three";
10
- import { USDObject } from "../../ThreeUSDZExporter";
9
+ import { Object3D, Material, Vector3, Quaternion } from "three";
10
+ import { USDDocument, USDObject } from "../../ThreeUSDZExporter";
11
11
 
12
12
  import { BehaviorExtension, UsdzBehaviour } from "./Behaviour";
13
- import { ActionBuilder, ActionModel, BehaviorModel, IBehaviorElement, MotionType, Space, TriggerBuilder } from "./BehavioursBuilder";
13
+ import { ActionBuilder, ActionModel, AuralMode, BehaviorModel, IBehaviorElement, MotionType, PlayAction, Space, TriggerBuilder } from "./BehavioursBuilder";
14
+ import { AudioSource } from "../../../../AudioSource";
14
15
 
15
16
  export class ChangeTransformOnClick extends Behaviour implements IPointerClickHandler, UsdzBehaviour {
16
17
 
@@ -44,7 +45,7 @@ export class ChangeTransformOnClick extends Behaviour implements IPointerClickHa
44
45
 
45
46
  const thisScale = getWorldScale(this.object).clone();
46
47
  const targetScale = getWorldScale(this.target).clone();
47
-
48
+
48
49
  const dist = thisPos.distanceTo(targetPos);
49
50
  const rotDist = thisRot.angleTo(targetRot);
50
51
  const scaleDist = thisScale.distanceTo(targetScale);
@@ -63,10 +64,10 @@ export class ChangeTransformOnClick extends Behaviour implements IPointerClickHa
63
64
 
64
65
  t01 += this.context.time.deltaTime / this.duration;
65
66
  if (t01 > 1) t01 = 1;
66
-
67
+
67
68
  // apply ease-in-out
68
69
  // https://easings.net/
69
- eased = t01 < 0.5 ? 4 * t01 * t01 * t01 : 1 - Math.pow(-2 * t01 + 2, 3) / 2;
70
+ eased = t01 < 0.5 ? 4 * t01 * t01 * t01 : 1 - Math.pow(-2 * t01 + 2, 3) / 2;
70
71
 
71
72
  this.targetPos.lerpVectors(thisPos, targetPos, eased);
72
73
  this.targetRot.slerpQuaternions(thisRot, targetRot, eased);
@@ -83,7 +84,7 @@ export class ChangeTransformOnClick extends Behaviour implements IPointerClickHa
83
84
  }
84
85
 
85
86
  private *moveRelative() {
86
-
87
+
87
88
  if (!this.target || !this.object) return;
88
89
 
89
90
  const thisPos = this.object.position.clone();
@@ -107,10 +108,10 @@ export class ChangeTransformOnClick extends Behaviour implements IPointerClickHa
107
108
 
108
109
  t01 += this.context.time.deltaTime / this.duration;
109
110
  if (t01 > 1) t01 = 1;
110
-
111
+
111
112
  // apply ease-in-out
112
113
  // https://easings.net/
113
- eased = t01 < 0.5 ? 4 * t01 * t01 * t01 : 1 - Math.pow(-2 * t01 + 2, 3) / 2;
114
+ eased = t01 < 0.5 ? 4 * t01 * t01 * t01 : 1 - Math.pow(-2 * t01 + 2, 3) / 2;
114
115
 
115
116
  this.object.position.lerpVectors(thisPos, this.targetPos, eased);
116
117
  this.object.quaternion.slerpQuaternions(thisRot, this.targetRot, eased);
@@ -120,7 +121,7 @@ export class ChangeTransformOnClick extends Behaviour implements IPointerClickHa
120
121
  }
121
122
 
122
123
  this.coroutine = null;
123
- }
124
+ }
124
125
 
125
126
  onPointerClick() {
126
127
  if (this.coroutine) this.stopCoroutine(this.coroutine);
@@ -313,11 +314,12 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
313
314
  hideClickedObject = true;
314
315
  targetState = !this.target.visible;
315
316
 
316
- // TODO check where we have to create the clone; here it doesn't show up
317
+ if (!this.selfModel.parent || this.selfModel.parent.isEmpty())
318
+ USDDocument.createEmptyParent(this.selfModel);
319
+
317
320
  this.toggleModel = this.selfModel.clone();
318
321
  this.toggleModel.name += "_toggle";
319
- if (this.selfModel.parent)
320
- this.selfModel.parent.add(this.toggleModel);
322
+ this.selfModel.parent!.add(this.toggleModel);
321
323
  }
322
324
 
323
325
  const sequence: ActionModel[] = [];
@@ -344,8 +346,8 @@ export class SetActiveOnClick extends Behaviour implements IPointerClickHandler,
344
346
  ActionBuilder.sequence(...toggleSequence)
345
347
  ));
346
348
 
347
- ext.addBehavior(new BehaviorModel("HideOnStart_" + this.gameObject.name,
348
- TriggerBuilder.sceneStartTrigger(),
349
+ ext.addBehavior(new BehaviorModel("HideOnStart_" + this.gameObject.name,
350
+ TriggerBuilder.sceneStartTrigger(),
349
351
  ActionBuilder.fadeAction(this.toggleModel, 0, false)
350
352
  ));
351
353
  }
@@ -363,8 +365,8 @@ export class HideOnStart extends Behaviour implements UsdzBehaviour {
363
365
 
364
366
  createBehaviours(ext, model, _context) {
365
367
  if (model.uuid === this.gameObject.uuid)
366
- ext.addBehavior(new BehaviorModel("HideOnStart_" + this.gameObject.name,
367
- TriggerBuilder.sceneStartTrigger(),
368
+ ext.addBehavior(new BehaviorModel("HideOnStart_" + this.gameObject.name,
369
+ TriggerBuilder.sceneStartTrigger(),
368
370
  ActionBuilder.fadeAction(model, 0, false)
369
371
  ));
370
372
  }
@@ -394,8 +396,7 @@ export class EmphasizeOnClick extends Behaviour implements UsdzBehaviour {
394
396
  createBehaviours(ext, model, _context) {
395
397
  if (!this.target) return;
396
398
 
397
- if (model.uuid === this.gameObject.uuid)
398
- {
399
+ if (model.uuid === this.gameObject.uuid) {
399
400
  const emphasize = new BehaviorModel("emphasize " + this.name,
400
401
  TriggerBuilder.tapTrigger(this.gameObject),
401
402
  ActionBuilder.emphasize(this.target, this.duration, this.motionType, undefined, "basic"),
@@ -407,6 +408,82 @@ export class EmphasizeOnClick extends Behaviour implements UsdzBehaviour {
407
408
  afterCreateDocument(_ext, _context) { }
408
409
  }
409
410
 
411
+ export class PlayAudioOnClick extends Behaviour implements IPointerClickHandler, UsdzBehaviour {
412
+
413
+ @serializable(AudioSource)
414
+ target?: AudioSource;
415
+
416
+ @serializable(URL)
417
+ clip: string = "";
418
+
419
+ @serializable()
420
+ toggleOnClick: boolean = false;
421
+
422
+ onPointerClick() {
423
+ if (!this.target && !this.clip) return;
424
+
425
+ if (!this.target) {
426
+
427
+ const newAudioSource = this.gameObject.addNewComponent(AudioSource);
428
+ if (newAudioSource) {
429
+ newAudioSource.spatialBlend = 1;
430
+ newAudioSource.volume = 1;
431
+ newAudioSource.loop = false;
432
+
433
+ this.target = newAudioSource;
434
+ }
435
+ }
436
+
437
+ if (this.target) {
438
+
439
+ if (this.target.isPlaying && this.toggleOnClick) {
440
+ this.target.stop();
441
+ }
442
+ else {
443
+ if (!this.toggleOnClick && this.target.isPlaying) {
444
+ this.target.stop();
445
+ }
446
+ if (this.clip) this.target.play(this.clip);
447
+ else this.target.play();
448
+ }
449
+ }
450
+ }
451
+
452
+ createBehaviours(ext, model, _context) {
453
+ if (!this.target && !this.clip) return;
454
+ if (model.uuid === this.gameObject.uuid) {
455
+
456
+ const clipUrl = this.clip ? this.clip : this.target ? this.target.clip : undefined;
457
+ if (!clipUrl) return;
458
+
459
+ const playbackTarget = this.target ? this.target.gameObject : this.gameObject;
460
+ const clipName = clipUrl.split("/").pop();
461
+ const volume = this.target ? this.target.volume : 1;
462
+ const auralMode = this.target && this.target.spatialBlend == 0 ? AuralMode.NonSpatial : AuralMode.Spatial;
463
+ const playClip = new BehaviorModel("playAudio " + this.name,
464
+ TriggerBuilder.tapTrigger(this.gameObject),
465
+ ActionBuilder.playAudioAction(playbackTarget, "audio/" + clipName, PlayAction.Play, volume, auralMode),
466
+ );
467
+ ext.addBehavior(playClip);
468
+ }
469
+ }
470
+
471
+ async afterSerialize(_ext, context) {
472
+ if (!this.target && !this.clip) return;
473
+ const clipUrl = this.clip ? this.clip : this.target ? this.target.clip : undefined;
474
+ if (!clipUrl) return;
475
+ const clipName = clipUrl.split("/").pop();
476
+
477
+ const audio = await fetch(this.clip);
478
+ const audioBlob = await audio.blob();
479
+ const arrayBuffer = await audioBlob.arrayBuffer();
480
+
481
+ const audioData: Uint8Array = new Uint8Array(arrayBuffer)
482
+
483
+ context.files["audio/" + clipName] = audioData;
484
+ }
485
+ }
486
+
410
487
  export class PlayAnimationOnClick extends Behaviour implements IPointerClickHandler, UsdzBehaviour, UsdzAnimation {
411
488
 
412
489
  @serializable(Object3D)
@@ -417,7 +494,7 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
417
494
 
418
495
  @serializable()
419
496
  stateName?: string;
420
-
497
+
421
498
  @serializable()
422
499
  stateNameAfterPlaying?: string;
423
500
 
@@ -486,7 +563,7 @@ export class PlayAnimationOnClick extends Behaviour implements IPointerClickHand
486
563
 
487
564
  createAnimation(ext, model, _context) {
488
565
  if (this.target && this.animator) {
489
-
566
+
490
567
  const state = this.animator?.runtimeAnimatorController?.findState(this.stateName);
491
568
  this.stateAnimationModel = model;
492
569
  this.stateAnimation = ext.registerAnimation(this.target, state?.motion.clip);
@@ -74,7 +74,7 @@ function resolve(targetObject: Target, document: USDDocument): string {
74
74
  let obj = targetObject[i];
75
75
  if (typeof obj === "string")
76
76
  str += obj;
77
- else if ( typeof obj === "object") {
77
+ else if (typeof obj === "object") {
78
78
  //@ts-ignore
79
79
  if (obj.isObject3D) {
80
80
  //@ts-ignore
@@ -237,6 +237,13 @@ export enum Space {
237
237
  Absolute = "absolute"
238
238
  };
239
239
 
240
+ // https://developer.apple.com/documentation/arkit/usdz_schemas_for_ar/actions_and_triggers/preliminary_action/multipleperformoperation
241
+ export enum MultiplePerformOperation {
242
+ Allow = "allow",
243
+ Ignore = "ignore",
244
+ Stop = "stop",
245
+ }
246
+
240
247
  export class ActionModel implements IBehaviorElement {
241
248
 
242
249
  private static global_id: number = 0;
@@ -257,6 +264,10 @@ export class ActionModel implements IBehaviorElement {
257
264
  reversed?: boolean;
258
265
  pingPong?: boolean;
259
266
  xFormTarget?: Target | string;
267
+ audio?: string;
268
+ gain?: number;
269
+ auralMode?: string;
270
+ multiplePerformOperation?: string;
260
271
 
261
272
  clone(): ActionModel {
262
273
  const copy = new ActionModel();
@@ -319,6 +330,18 @@ export class ActionModel implements IBehaviorElement {
319
330
  this.xFormTarget = resolve(this.xFormTarget, document);
320
331
  writer.appendLine(`rel xformTarget = ${this.xFormTarget}`)
321
332
  }
333
+ if (typeof this.audio === "string") {
334
+ writer.appendLine(`asset audio = @${this.audio}@`);
335
+ }
336
+ if (typeof this.gain ==="number") {
337
+ writer.appendLine(`double gain = ${this.gain}`);
338
+ }
339
+ if (typeof this.auralMode === "string") {
340
+ writer.appendLine(`token auralMode = "${this.auralMode}"`);
341
+ }
342
+ if (typeof this.multiplePerformOperation === "string") {
343
+ writer.appendLine(`token multiplePerformOperation = "${this.multiplePerformOperation}"`);
344
+ }
322
345
  writer.closeBlock();
323
346
  }
324
347
  }
@@ -355,6 +378,18 @@ class Vec3 {
355
378
  }
356
379
  }
357
380
 
381
+ export enum PlayAction {
382
+ Play = "play",
383
+ Pause = "pause",
384
+ Stop = "stop",
385
+ }
386
+
387
+ export enum AuralMode {
388
+ Spatial = "spatial",
389
+ NonSpatial = "nonSpatial",
390
+ Ambient = "ambient",
391
+ }
392
+
358
393
  export class ActionBuilder {
359
394
 
360
395
  static sequence(...params: IBehaviorElement[]) {
@@ -436,7 +471,7 @@ export class ActionBuilder {
436
471
  act.tokenId = "Emphasize";
437
472
  act.duration = duration;
438
473
  act.style = style ?? "basic";
439
- act.motionType = MotionType[motionType];
474
+ act.motionType = MotionType[motionType];
440
475
  act.moveDistance = moveDistance;
441
476
  return act;
442
477
  }
@@ -454,6 +489,17 @@ export class ActionBuilder {
454
489
  return act;
455
490
  }
456
491
 
492
+ static playAudioAction(targets: Target, audio: string, type: PlayAction = PlayAction.Play, gain: number = 1, auralMode: AuralMode = AuralMode.Spatial) {
493
+ const act = new ActionModel(targets);
494
+ act.tokenId = "Audio";
495
+ act.type = type;
496
+ act.audio = audio;
497
+ act.gain = gain;
498
+ act.auralMode = auralMode;
499
+ act.multiplePerformOperation = MultiplePerformOperation.Allow;
500
+ return act;
501
+ }
502
+
457
503
  }
458
504
 
459
505
  export { Vec3 as USDVec3 }
@@ -26,6 +26,12 @@ export class BaseUIComponent extends Behaviour {
26
26
 
27
27
  isRoot() { return this.Root?.gameObject === this.gameObject; }
28
28
 
29
+ get canvas() {
30
+ const cv = this.Root as any as ICanvas;
31
+ if (cv?.isCanvas) return cv;
32
+ return null;
33
+ }
34
+
29
35
  markDirty() {
30
36
  EventSystem.markUIDirty(this.context);
31
37
  }
@@ -50,10 +56,9 @@ export class BaseUIComponent extends Behaviour {
50
56
  return this._root;
51
57
  }
52
58
 
59
+ // TODO: rename to canvas
53
60
  protected get Canvas() {
54
- const cv = this.Root as any as ICanvas;
55
- if (cv?.isCanvas) return cv;
56
- return null;
61
+ return this.canvas;
57
62
  }
58
63
 
59
64
  // private _intermediate?: Object3D;
@@ -193,7 +193,7 @@ export class Canvas extends UIRootComponent implements ICanvas {
193
193
  this.previousParent = this.gameObject.parent;
194
194
  // console.log(this.previousParent?.name + "/" + this.gameObject.name);
195
195
 
196
- if (this.renderOnTop) {
196
+ if (this.renderOnTop || this.screenspace) {
197
197
  // This is just a test but in reality it should be combined with all world canvases with render on top in one render pass
198
198
  this.gameObject.removeFromParent();
199
199
  }
@@ -255,7 +255,7 @@ export class EventSystem extends Behaviour {
255
255
  if (!hits) return;
256
256
  this.lastPointerEvent = args;
257
257
 
258
- const evt : AfterHandleInputEvent = {
258
+ const evt: AfterHandleInputEvent = {
259
259
  sender: this,
260
260
  args: args,
261
261
  hasActiveUI: this.currentActiveMeshUIComponents.length > 0,
@@ -433,6 +433,10 @@ export class EventSystem extends Behaviour {
433
433
  }
434
434
  }
435
435
 
436
+ if (comp.onPointerMove) {
437
+ comp.onPointerMove(args);
438
+ }
439
+
436
440
  if (args.isDown) {
437
441
  if (comp.onPointerDown && !this.raisedPointerDownEvents.includes(comp)) {
438
442
  comp.onPointerDown(args);
@@ -5,13 +5,28 @@ import { MaskableGraphic } from './Graphic';
5
5
 
6
6
  class Sprite {
7
7
  @serializable(Texture)
8
- texture?: THREE.Texture;
8
+ texture: Texture | null = null;
9
9
 
10
10
  rect?: { width: number, height: number };
11
11
  }
12
12
 
13
13
  export class Image extends MaskableGraphic {
14
14
 
15
+ set image(img: Texture | null) {
16
+ if (this.sprite)
17
+ this.sprite.texture = img;
18
+ else {
19
+ this.sprite = new Sprite();
20
+ this.sprite.texture = img;
21
+ }
22
+ this.onAfterCreated();
23
+ }
24
+ get image(): Texture | null {
25
+ if (this.sprite)
26
+ return this.sprite.texture;
27
+ return null;
28
+ }
29
+
15
30
  @serializable(Sprite)
16
31
  get sprite(): Sprite | undefined {
17
32
  return this._sprite;
@@ -1,7 +1,7 @@
1
1
  import { Behaviour } from "../Component";
2
2
  import { IComponent } from "../../engine/engine_types";
3
3
 
4
- export interface ICanvas {
4
+ export interface ICanvas extends IComponent {
5
5
  get isCanvas(): boolean;
6
6
  get screenspace(): boolean;
7
7
  registerTransform(rt: IRectTransform);
@@ -250,6 +250,8 @@ export abstract class HorizontalOrVerticalLayoutGroup extends LayoutGroup {
250
250
  const rt = GameObject.getComponent(ch, RectTransform);
251
251
  if (rt?.activeAndEnabled) {
252
252
  rt.pivot?.set(.5, .5);
253
+ rt.anchorMin.set(0, 1);
254
+ rt.anchorMax.set(0, 1);
253
255
  // Horizontal padding
254
256
  const x = totalWidth * .5 + leftOffset * .5;
255
257
  if (rt.anchoredPosition.x !== x)
@@ -11,12 +11,22 @@ export class PointerEventData implements IInputEventArgs {
11
11
  // TODO: should we make this a getter and return the input used state instead? -> this.context.getPointerUsed(this.pointerId);
12
12
  used: boolean = false;
13
13
 
14
- Use() {
14
+ use() {
15
15
  this.used = true;
16
16
  if (this.pointerId !== undefined)
17
17
  this.input.setPointerUsed(this.pointerId);
18
18
  }
19
19
 
20
+ stopPropagation() {
21
+ this.event?.stopImmediatePropagation();
22
+ }
23
+
24
+ /**@deprecated use use() */
25
+ Use() {
26
+ this.use();
27
+ }
28
+
29
+ /**@deprecated use stopPropagation() */
20
30
  StopPropagation() {
21
31
  this.event?.stopImmediatePropagation();
22
32
  }
@@ -52,6 +62,10 @@ export interface IPointerEnterHandler {
52
62
  onPointerEnter?(args: PointerEventData);
53
63
  }
54
64
 
65
+ export interface IPointerMoveHandler {
66
+ onPointerMove?(args: PointerEventData);
67
+ }
68
+
55
69
  export interface IPointerExitHandler {
56
70
  onPointerExit?(args: PointerEventData);
57
71
  }
@@ -61,4 +75,4 @@ export interface IPointerClickHandler {
61
75
  }
62
76
 
63
77
  export interface IPointerEventHandler extends IPointerDownHandler,
64
- IPointerUpHandler, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler { }
78
+ IPointerUpHandler, IPointerEnterHandler, IPointerMoveHandler, IPointerExitHandler, IPointerClickHandler { }
@@ -20,7 +20,12 @@ export class UIRaycastUtils {
20
20
  };
21
21
 
22
22
  static isInteractable(obj: THREE.Object3D, out?: { canvasGroup?: ICanvasGroup, graphic?: IGraphic }): boolean {
23
- if(obj === null || obj === undefined || !obj.visible) return false;
23
+ // reset state
24
+ if (out) {
25
+ out.canvasGroup = undefined;
26
+ out.graphic = undefined;
27
+ }
28
+ if (obj === null || obj === undefined || !obj.visible) return false;
24
29
 
25
30
  obj = this.getObject(obj);
26
31
 
@@ -31,6 +31,9 @@ const tempQuaternion = new Quaternion();
31
31
 
32
32
  export class RectTransform extends BaseUIComponent implements IRectTransform, IRectTransformChangedReceiver {
33
33
 
34
+ get parent() {
35
+ return this._parentRectTransform;
36
+ }
34
37
  offset: number = .01;
35
38
 
36
39
  // @serializable(Object3D)
@@ -51,24 +54,21 @@ export class RectTransform extends BaseUIComponent implements IRectTransform, IR
51
54
  this._anchoredPosition = value;
52
55
  }
53
56
 
54
- @serializable(Rect)
55
- private rect?: Rect; // TODO: should we use the rect or sizeDelta?
56
-
57
57
  @serializable(Vector2)
58
- sizeDelta!: Vector2;
58
+ sizeDelta: Vector2 = new Vector2(100, 100);
59
59
 
60
60
  @serializable(Vector2)
61
- pivot?: Vector2;
61
+ pivot: Vector2 = new Vector2(.5, .5);
62
62
 
63
63
  @serializable(Vector2)
64
- anchorMin!: Vector2;
64
+ anchorMin: Vector2 = new Vector2(0, 0);
65
65
  @serializable(Vector2)
66
- anchorMax!: Vector2;
66
+ anchorMax: Vector2 = new Vector2(1, 1);
67
67
 
68
68
  @serializable(Vector2)
69
- offsetMin!: Vector2;
69
+ offsetMin: Vector2 = new Vector2(0, 0);
70
70
  @serializable(Vector2)
71
- offsetMax!: Vector2;
71
+ offsetMax: Vector2 = new Vector2(0, 0);
72
72
 
73
73
  get width() {
74
74
  if (this.anchorMin.x !== this.anchorMax.x) {
@@ -301,8 +301,8 @@ export class RectTransform extends BaseUIComponent implements IRectTransform, IR
301
301
  // })
302
302
 
303
303
  const opts = {
304
- width: this.rect!.width,
305
- height: this.rect!.height,// * this.context.mainCameraComponent!.aspect,
304
+ width: this.sizeDelta!.x,
305
+ height: this.sizeDelta!.y,// * this.context.mainCameraComponent!.aspect,
306
306
  offset: this.offset,
307
307
  backgroundOpacity: 0,
308
308
  borderWidth: 0, // if we dont specify width here a border will automatically propagated to child blocks
@@ -40,8 +40,6 @@ export enum FontStyle {
40
40
 
41
41
  export class Text extends Graphic {
42
42
 
43
- @serializable(Canvas)
44
- canvas?: Canvas;
45
43
  @serializable()
46
44
  alignment: TextAnchor = TextAnchor.UpperLeft;
47
45
  @serializable()
@@ -334,7 +332,7 @@ export class Text extends Graphic {
334
332
  private * renderOnTopCoroutine() {
335
333
  if (!this.canvas) return;
336
334
  const updatedRendering: boolean[] = [];
337
- const canvas = this.canvas;
335
+ const canvas = this.canvas as Canvas;
338
336
  const settings = {
339
337
  renderOnTop: canvas.renderOnTop,
340
338
  depthWrite: canvas.depthWrite,