@needle-tools/engine 2.28.0-pre → 2.30.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 (130) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/needle-engine.d.ts +232 -139
  3. package/dist/needle-engine.js +349 -345
  4. package/dist/needle-engine.js.map +4 -4
  5. package/dist/needle-engine.min.js +24 -20
  6. package/dist/needle-engine.min.js.map +4 -4
  7. package/lib/engine/engine.d.ts +1 -0
  8. package/lib/engine/engine_input.d.ts +13 -1
  9. package/lib/engine/engine_input.js +47 -16
  10. package/lib/engine/engine_input.js.map +1 -1
  11. package/lib/engine/engine_physics.d.ts +1 -0
  12. package/lib/engine/engine_physics.js +2 -1
  13. package/lib/engine/engine_physics.js.map +1 -1
  14. package/lib/engine/engine_playerview.d.ts +26 -0
  15. package/lib/engine/engine_playerview.js +65 -0
  16. package/lib/engine/engine_playerview.js.map +1 -0
  17. package/lib/engine/engine_serialization.d.ts +1 -0
  18. package/lib/engine/engine_serialization.js +1 -0
  19. package/lib/engine/engine_serialization.js.map +1 -1
  20. package/lib/engine/engine_serialization_core.js +5 -0
  21. package/lib/engine/engine_serialization_core.js.map +1 -1
  22. package/lib/engine/engine_setup.d.ts +8 -0
  23. package/lib/engine/engine_setup.js +23 -0
  24. package/lib/engine/engine_setup.js.map +1 -1
  25. package/lib/engine/engine_utils.d.ts +1 -1
  26. package/lib/engine/engine_utils.js +25 -8
  27. package/lib/engine/engine_utils.js.map +1 -1
  28. package/lib/engine/extensions/NEEDLE_deferred_texture.d.ts +1 -1
  29. package/lib/engine/extensions/NEEDLE_deferred_texture.js +26 -14
  30. package/lib/engine/extensions/NEEDLE_deferred_texture.js.map +1 -1
  31. package/lib/engine/extensions/NEEDLE_lighting_settings.js +6 -2
  32. package/lib/engine/extensions/NEEDLE_lighting_settings.js.map +1 -1
  33. package/lib/engine/extensions/extension_utils.js +24 -13
  34. package/lib/engine/extensions/extension_utils.js.map +1 -1
  35. package/lib/engine/extensions/extensions.js +3 -1
  36. package/lib/engine/extensions/extensions.js.map +1 -1
  37. package/lib/engine-components/Camera.js +7 -0
  38. package/lib/engine-components/Camera.js.map +1 -1
  39. package/lib/engine-components/Component.d.ts +1 -1
  40. package/lib/engine-components/Component.js.map +1 -1
  41. package/lib/engine-components/Light.js +1 -0
  42. package/lib/engine-components/Light.js.map +1 -1
  43. package/lib/engine-components/OrbitControls.js +3 -3
  44. package/lib/engine-components/OrbitControls.js.map +1 -1
  45. package/lib/engine-components/ParticleSystem.d.ts +0 -1
  46. package/lib/engine-components/ParticleSystem.js +24 -27
  47. package/lib/engine-components/ParticleSystem.js.map +1 -1
  48. package/lib/engine-components/PlayerColor.js +1 -2
  49. package/lib/engine-components/PlayerColor.js.map +1 -1
  50. package/lib/engine-components/Renderer.d.ts +1 -0
  51. package/lib/engine-components/Renderer.js +10 -3
  52. package/lib/engine-components/Renderer.js.map +1 -1
  53. package/lib/engine-components/ScreenCapture.d.ts +1 -0
  54. package/lib/engine-components/ScreenCapture.js +265 -1
  55. package/lib/engine-components/ScreenCapture.js.map +1 -1
  56. package/lib/engine-components/SpectatorCamera.d.ts +24 -17
  57. package/lib/engine-components/SpectatorCamera.js +435 -182
  58. package/lib/engine-components/SpectatorCamera.js.map +1 -1
  59. package/lib/engine-components/SyncedCamera.d.ts +8 -4
  60. package/lib/engine-components/SyncedCamera.js +15 -18
  61. package/lib/engine-components/SyncedCamera.js.map +1 -1
  62. package/lib/engine-components/SyncedRoom.js +2 -0
  63. package/lib/engine-components/SyncedRoom.js.map +1 -1
  64. package/lib/engine-components/VideoPlayer.d.ts +10 -1
  65. package/lib/engine-components/VideoPlayer.js +64 -15
  66. package/lib/engine-components/VideoPlayer.js.map +1 -1
  67. package/lib/engine-components/Volume.d.ts +4 -0
  68. package/lib/engine-components/Volume.js +44 -3
  69. package/lib/engine-components/Volume.js.map +1 -1
  70. package/lib/engine-components/WebARSessionRoot.d.ts +9 -2
  71. package/lib/engine-components/WebARSessionRoot.js +69 -24
  72. package/lib/engine-components/WebARSessionRoot.js.map +1 -1
  73. package/lib/engine-components/WebXR.d.ts +6 -3
  74. package/lib/engine-components/WebXR.js +43 -7
  75. package/lib/engine-components/WebXR.js.map +1 -1
  76. package/lib/engine-components/WebXRAvatar.d.ts +3 -0
  77. package/lib/engine-components/WebXRAvatar.js +20 -0
  78. package/lib/engine-components/WebXRAvatar.js.map +1 -1
  79. package/lib/engine-components/WebXRController.js +14 -8
  80. package/lib/engine-components/WebXRController.js.map +1 -1
  81. package/lib/engine-components/WebXRSync.js +3 -3
  82. package/lib/engine-components/WebXRSync.js.map +1 -1
  83. package/lib/engine-components/XRFlag.d.ts +2 -1
  84. package/lib/engine-components/XRFlag.js +1 -0
  85. package/lib/engine-components/XRFlag.js.map +1 -1
  86. package/lib/engine-components/ui/CanvasGroup.d.ts +1 -0
  87. package/lib/engine-components/ui/CanvasGroup.js +1 -0
  88. package/lib/engine-components/ui/CanvasGroup.js.map +1 -1
  89. package/lib/engine-components/ui/EventSystem.js +13 -4
  90. package/lib/engine-components/ui/EventSystem.js.map +1 -1
  91. package/lib/engine-components/ui/Graphic.d.ts +1 -0
  92. package/lib/engine-components/ui/Graphic.js +2 -0
  93. package/lib/engine-components/ui/Graphic.js.map +1 -1
  94. package/lib/engine-components/ui/Interfaces.d.ts +2 -0
  95. package/package.json +2 -2
  96. package/src/engine/engine_components.js +16 -0
  97. package/src/engine/engine_input.ts +62 -20
  98. package/src/engine/engine_physics.ts +2 -1
  99. package/src/engine/engine_playerview.ts +80 -0
  100. package/src/engine/engine_serialization.ts +3 -1
  101. package/src/engine/engine_serialization_core.ts +8 -0
  102. package/src/engine/engine_setup.ts +24 -0
  103. package/src/engine/engine_utils.ts +34 -8
  104. package/src/engine/extensions/NEEDLE_deferred_texture.ts +25 -19
  105. package/src/engine/extensions/NEEDLE_lighting_settings.ts +4 -2
  106. package/src/engine/extensions/extension_utils.ts +24 -12
  107. package/src/engine/extensions/extensions.ts +3 -2
  108. package/src/engine-components/Camera.ts +9 -1
  109. package/src/engine-components/Component.ts +1 -1
  110. package/src/engine-components/Light.ts +3 -0
  111. package/src/engine-components/OrbitControls.ts +3 -3
  112. package/src/engine-components/ParticleSystem.ts +25 -26
  113. package/src/engine-components/PlayerColor.ts +1 -1
  114. package/src/engine-components/Renderer.ts +11 -3
  115. package/src/engine-components/ScreenCapture.ts +312 -2
  116. package/src/engine-components/SpectatorCamera.ts +490 -195
  117. package/src/engine-components/SyncedCamera.ts +23 -22
  118. package/src/engine-components/SyncedRoom.ts +1 -0
  119. package/src/engine-components/VideoPlayer.ts +97 -21
  120. package/src/engine-components/Volume.ts +47 -4
  121. package/src/engine-components/WebARSessionRoot.ts +78 -28
  122. package/src/engine-components/WebXR.ts +51 -15
  123. package/src/engine-components/WebXRAvatar.ts +27 -2
  124. package/src/engine-components/WebXRController.ts +21 -15
  125. package/src/engine-components/WebXRSync.ts +3 -3
  126. package/src/engine-components/XRFlag.ts +1 -0
  127. package/src/engine-components/ui/CanvasGroup.ts +2 -0
  128. package/src/engine-components/ui/EventSystem.ts +21 -15
  129. package/src/engine-components/ui/Graphic.ts +3 -0
  130. package/src/engine-components/ui/Interfaces.ts +2 -0
@@ -4,7 +4,7 @@ import { ARButton } from '../include/three/ARButton.js';
4
4
  import { VRButton } from '../include/three/VRButton.js';
5
5
 
6
6
  import * as THREE from 'three'
7
- import { ArrayCamera, XRHitTestSource } from 'three';
7
+ import { ArrayCamera, Matrix4, Vector3, XRHitTestSource } from 'three';
8
8
 
9
9
  import * as utils from "../engine/engine_utils";
10
10
  import { noVoip } from "./Voip";
@@ -17,6 +17,7 @@ import { EngineElement } from "../engine/engine_element";
17
17
  import { AssetReference } from "../engine/engine_addressables";
18
18
  import { serializeable } from "../engine/engine_serialization_decorator";
19
19
  import { WebXRSync } from "./WebXRSync";
20
+ import { XRSessionMode } from "../engine/engine_setup";
20
21
 
21
22
 
22
23
  export async function detectARSupport() {
@@ -73,7 +74,7 @@ export class WebXR extends Behaviour {
73
74
  this.events.addEventListener(type, listener);
74
75
  return listener;
75
76
  }
76
- public static removeEventListener(type: string, listener: any) : any {
77
+ public static removeEventListener(type: string, listener: any): any {
77
78
  this.events.removeEventListener(type, listener);
78
79
  return listener;
79
80
  }
@@ -94,10 +95,10 @@ export class WebXR extends Behaviour {
94
95
 
95
96
  public static createARButton(webXR: WebXR, opts?: CreateButtonOptions): HTMLButtonElement {
96
97
  const domOverlayRoot = webXR.webAR?.getAROverlayContainer();
97
- const features: any = { requiredFeatures: ['hit-test'] };
98
+ const features: any = {};
98
99
  if (domOverlayRoot) {
99
100
  features.domOverlay = { root: domOverlayRoot };
100
- features.optionalFeatures = ['dom-overlay'];
101
+ features.optionalFeatures = ['hit-test', 'dom-overlay'];
101
102
  }
102
103
  const arButton = ARButton.createButton(webXR.context.renderer, features);
103
104
  arButton.classList.add('webxr-ar-button');
@@ -120,7 +121,8 @@ export class WebXR extends Behaviour {
120
121
  if (session) session.end();
121
122
  }
122
123
 
123
- public get Rig(): THREE.Object3D | null {
124
+ public get Rig(): THREE.Object3D {
125
+ if(!this.rig) this.ensureRig();
124
126
  return this.rig;
125
127
  }
126
128
 
@@ -256,8 +258,6 @@ export class WebXR extends Behaviour {
256
258
  WebXR.events.dispatchEvent({ type: WebXREvent.XRUpdate, frame: frame, xr: this.context.renderer.xr, rig: this.rig });
257
259
  }
258
260
 
259
-
260
-
261
261
  private onClickedARButton() {
262
262
  if (!this._isInAR) {
263
263
  this._requestedAR = true;
@@ -313,6 +313,7 @@ export class WebXR extends Behaviour {
313
313
  }
314
314
  else {
315
315
  this.rig = new THREE.Group();
316
+ this.rig.rotateY(Math.PI);
316
317
  this.rig.name = "XRRig";
317
318
  this.context.scene.add(this.rig);
318
319
  }
@@ -360,10 +361,12 @@ export class WebXR extends Behaviour {
360
361
 
361
362
  switch (flag) {
362
363
  case XRStateFlag.AR:
364
+ this.context.xrSessionMode = XRSessionMode.ImmersiveAR;
363
365
  this._isInAR = true;
364
366
  this.webAR?.onBegin(session);
365
367
  break;
366
368
  case XRStateFlag.VR:
369
+ this.context.xrSessionMode = XRSessionMode.ImmersiveVR;
367
370
  this._isInVR = true;
368
371
  this.onEnterVR(session);
369
372
  break;
@@ -390,6 +393,7 @@ export class WebXR extends Behaviour {
390
393
  this._isInVR = false;
391
394
  this._requestedAR = false;
392
395
  this._requestedVR = false;
396
+ this.context.xrSessionMode = undefined;
393
397
 
394
398
  if (this.xrMirrorWindow) {
395
399
  this.xrMirrorWindow.close();
@@ -447,7 +451,10 @@ export class WebXR extends Behaviour {
447
451
  // not sure if this should be a behaviour.
448
452
  // for now we dont really need it to go through the usual update loop
449
453
  export class WebAR {
450
- private webxr: WebXR;
454
+
455
+ get webxr(): WebXR { return this._webxr; }
456
+
457
+ private _webxr: WebXR;
451
458
 
452
459
  private reticle: THREE.Object3D | null = null;
453
460
  private hitTestSource: XRHitTestSource | null = null;
@@ -467,11 +474,13 @@ export class WebAR {
467
474
  private get context() { return this.webxr.context; }
468
475
 
469
476
  constructor(webxr: WebXR) {
470
- this.webxr = webxr;
477
+ this._webxr = webxr;
471
478
  }
472
479
 
473
480
  private arDomOverlay: EngineElement | null = null;
474
481
  private arOverlayElement: HTMLElement | null = null;
482
+ private noHitTestAvailable: boolean = false;
483
+ private didPlaceARSessionRoot: boolean = false;
475
484
 
476
485
  getAROverlayContainer(): HTMLElement | null {
477
486
  this.arDomOverlay = this.webxr.context.domElement as EngineElement;
@@ -483,12 +492,14 @@ export class WebAR {
483
492
  this.reticleActive = active;
484
493
  }
485
494
 
486
- onBegin(session: THREE.XRSession) {
495
+ async onBegin(session: THREE.XRSession) {
487
496
  const context = this.webxr.context;
488
497
  this.reticleActive = true;
498
+ this.didPlaceARSessionRoot = false;
489
499
 
500
+ const deviceType = navigator.userAgent?.includes("OculusBrowser") ? ControllerType.PhysicalDevice : ControllerType.Touch;
490
501
  for (let i = 0; i < 4; i++) {
491
- WebXRController.Create(this.webxr, i, this.webxr.gameObject, ControllerType.Touch)
502
+ WebXRController.Create(this.webxr, i, this.webxr.gameObject, deviceType)
492
503
  }
493
504
 
494
505
  if (!this.sessionRoot || this.sessionRoot.destroyed || !this.sessionRoot.activeAndEnabled)
@@ -501,6 +512,9 @@ export class WebAR {
501
512
  session.requestReferenceSpace('viewer').then((referenceSpace) => {
502
513
  session.requestHitTestSource({ space: referenceSpace }).then((source) => {
503
514
  this.hitTestSource = source;
515
+ }).catch((_) => {
516
+ this.noHitTestAvailable = true;
517
+ console.warn("WebXR: Hit test not supported");
504
518
  });
505
519
  });
506
520
 
@@ -533,7 +547,7 @@ export class WebAR {
533
547
  if (this.arDomOverlay && this.arOverlayElement) {
534
548
  this.arDomOverlay.onEnterAR(session, this.arOverlayElement);
535
549
  }
536
-
550
+
537
551
  this.context.mainCameraComponent?.applyClearFlagsIfIsActiveCamera();
538
552
  }
539
553
 
@@ -551,11 +565,29 @@ export class WebAR {
551
565
  this.sessionRoot.onEnd(this.webxr.Rig, _session);
552
566
  }
553
567
  if (this.arDomOverlay) this.arDomOverlay.onExitAR(_session);
554
-
568
+
555
569
  this.context.mainCameraComponent?.applyClearFlagsIfIsActiveCamera();
556
570
  }
557
571
 
558
572
  onUpdate(session: THREE.XRSession, frame: THREE.XRFrame) {
573
+
574
+ if (this.noHitTestAvailable === true) {
575
+ if (this.reticle)
576
+ this.reticle.visible = false;
577
+ if (!this.didPlaceARSessionRoot) {
578
+ this.didPlaceARSessionRoot = true;
579
+ const rig = this.webxr.Rig;
580
+ const placementMatrix = arPlacementWithoutHitTestMatrix.clone();
581
+ if (rig) {
582
+ const positionFromRig = new Vector3(0, 0, 0).add(rig.position).divideScalar(this.sessionRoot?.arScale ?? 1);
583
+ placementMatrix.multiply(new Matrix4().makeTranslation(positionFromRig.x, positionFromRig.y, positionFromRig.z));
584
+ // placementMatrix.setPosition(positionFromRig);
585
+ }
586
+ this.sessionRoot?.placeAt(rig, placementMatrix);
587
+ }
588
+ return;
589
+ }
590
+
559
591
  if (!this.hitTestSource) return;
560
592
  const hitTestResults = frame.getHitTestResults(this.hitTestSource);
561
593
  if (hitTestResults.length) {
@@ -563,7 +595,11 @@ export class WebAR {
563
595
  const referenceSpace = this.webxr.context.renderer.xr.getReferenceSpace();
564
596
  if (referenceSpace) {
565
597
  const pose = hit.getPose(referenceSpace);
566
- this.sessionRoot?.onUpdate(this.webxr.Rig, session, pose);
598
+
599
+ if (this.sessionRoot) {
600
+ const didPlace = this.sessionRoot.onUpdate(this.webxr.Rig, session, pose);
601
+ this.didPlaceARSessionRoot = didPlace;
602
+ }
567
603
 
568
604
  if (this.reticle) {
569
605
  this.reticle.visible = this.reticleActive;
@@ -586,4 +622,4 @@ export class WebAR {
586
622
  }
587
623
  }
588
624
 
589
-
625
+ const arPlacementWithoutHitTestMatrix = new THREE.Matrix4().identity().makeTranslation(0, -0.5, 0);
@@ -10,6 +10,8 @@ import { Object3D } from "three";
10
10
  import { VRUserState } from "./WebXRSync";
11
11
  import { getParam } from "../engine/engine_utils";
12
12
  import { serializeable } from "../engine/engine_serialization_decorator";
13
+ import { ViewDevice } from "../engine/engine_playerview";
14
+ import { InstancingUtil } from "./Renderer";
13
15
 
14
16
  export const debug = getParam("debugavatar");
15
17
 
@@ -18,8 +20,14 @@ export type AvatarMarkerEventArgs = {
18
20
  gameObject: Object3D;
19
21
  }
20
22
 
21
-
22
23
  export class AvatarMarker extends Behaviour {
24
+
25
+ public static getAvatar(index: number): AvatarMarker | null {
26
+ if (index >= 0 && index < AvatarMarker.instances.length)
27
+ return AvatarMarker.instances[index];
28
+ return null;
29
+ }
30
+
23
31
  public static instances: AvatarMarker[] = [];
24
32
 
25
33
  public static onAvatarMarkerCreated(cb: (args: AvatarMarkerEventArgs) => void): Function {
@@ -55,7 +63,7 @@ export class AvatarMarker extends Behaviour {
55
63
  cb({ avatarMarker: this, gameObject: this.gameObject });
56
64
  }
57
65
 
58
- isLocalAvatar(){
66
+ isLocalAvatar() {
59
67
  return this.connectionId === this.context.connection.connectionId;
60
68
  }
61
69
 
@@ -80,7 +88,10 @@ export class WebXRAvatar {
80
88
  this.updateVisibility();
81
89
  }
82
90
 
91
+ get isWebXRAvatar() { return true; }
92
+
83
93
  // TODO: set layers on all avatars
94
+ /** the user id */
84
95
  public guid: string;
85
96
 
86
97
  private root: THREE.Object3D | null = null;
@@ -148,6 +159,18 @@ export class WebXRAvatar {
148
159
 
149
160
  this.lastUpdate = state.time;
150
161
  if (this.head) {
162
+
163
+ const device = this.webxr.IsInAR ? ViewDevice.Handheld : ViewDevice.Headset;
164
+ let viewObj = this.head;
165
+ // if (this.isLocalAvatar) {
166
+ // if (this.context.mainCamera && this.context.isInXR) {
167
+ // viewObj = this.context.renderer.xr.getCamera(this.context.mainCamera);
168
+ // }
169
+ // }
170
+ this.context.players.setPlayerView(state.guid, viewObj, device);
171
+
172
+ InstancingUtil.markDirty(this.head);
173
+
151
174
  this._canInterpolate = true;
152
175
  const ht = this.isLocalAvatar ? this.head : this._headTarget;
153
176
  ht.position.set(state.position.x, state.position.y, state.position.z);
@@ -163,6 +186,7 @@ export class WebXRAvatar {
163
186
  ht.quaternion.multiply(WebXRAvatar.invertRotation);
164
187
  ht.scale.set(state.scale, state.scale, state.scale);
165
188
  ht.scale.multiply(this.handLeftScale);
189
+ InstancingUtil.markDirty(this.handLeft);
166
190
  }
167
191
 
168
192
  if (this.handRight) {
@@ -172,6 +196,7 @@ export class WebXRAvatar {
172
196
  ht.quaternion.multiply(WebXRAvatar.invertRotation);
173
197
  ht.scale.set(state.scale, state.scale, state.scale);
174
198
  ht.scale.multiply(this.handRightScale);
199
+ InstancingUtil.markDirty(this.handRight);
175
200
  }
176
201
  }
177
202
  }
@@ -13,6 +13,9 @@ import * as utils from "../engine/engine_three_utils"
13
13
  import { Interactable, UsageMarker } from "./Interactable";
14
14
  import { InstancingUtil } from "./Renderer";
15
15
  import { Rigidbody } from "./RigidBody";
16
+ import { getParam } from "../engine/engine_utils";
17
+
18
+ const debug = getParam("debugwebxrcontroller");
16
19
 
17
20
  export enum ControllerType {
18
21
  PhysicalDevice = 0,
@@ -103,10 +106,10 @@ export class WebXRController extends Behaviour {
103
106
  // this is a workaround to fix that temporarely
104
107
  // see https://github.com/needle-tools/needle-tiny-playground/issues/123
105
108
  const jnts = ctrl.hand["joints"];
106
- if(jnts){
107
- for(const key of Object.keys(jnts)){
109
+ if (jnts) {
110
+ for (const key of Object.keys(jnts)) {
108
111
  const joint = jnts[key];
109
- if(joint.parent) continue;
112
+ if (joint.parent) continue;
110
113
  ctrl.hand.add(joint);
111
114
  }
112
115
  }
@@ -189,7 +192,7 @@ export class WebXRController extends Behaviour {
189
192
  private _useSmoothing: boolean = true;
190
193
 
191
194
  awake(): void {
192
- if(!this.controller){
195
+ if (!this.controller) {
193
196
  console.warn("Missing Controller!!!", this);
194
197
  return;
195
198
  }
@@ -251,7 +254,8 @@ export class WebXRController extends Behaviour {
251
254
  this.raycastLine?.add(this._raycastHitPoint);
252
255
  this._raycastHitPoint.visible = false;
253
256
  this.hand.add(this.handPointerModel);
254
- console.log("ADDED TO RIG", this.webXR.Rig);
257
+ if (debug)
258
+ console.log("ADDED TO RIG", this.webXR.Rig);
255
259
 
256
260
  // // console.log("enable", this.index, this.controllerGrip.uuid)
257
261
  }
@@ -427,7 +431,8 @@ export class WebXRController extends Behaviour {
427
431
  this.movementVector.applyQuaternion(this.webXR.TransformOrientation);
428
432
  this.movementVector.y = 0;
429
433
  this.movementVector.applyQuaternion(this.worldRot);
430
- rig.position.add(this.movementVector.multiplyScalar(speedFactor * this.context.time.deltaTime));
434
+ this.movementVector.multiplyScalar(speedFactor * this.context.time.deltaTime);
435
+ rig.position.add(this.movementVector);
431
436
 
432
437
  if (this.isUsingHands)
433
438
  this.runTeleport(rig, buttons);
@@ -472,7 +477,7 @@ export class WebXRController extends Behaviour {
472
477
  }
473
478
  else this._pinchStartTime = undefined;
474
479
 
475
- let doTeleport = teleport > .5;
480
+ let doTeleport = teleport > .5 && this.webXR.IsInVR;
476
481
  let isInMiniatureMode = this.webXR.Rig ? this.webXR.Rig?.scale?.x < .999 : false;
477
482
  let newRigScale: number | null = null;
478
483
 
@@ -482,7 +487,7 @@ export class WebXRController extends Behaviour {
482
487
  // button[4] seems to be the A button if it exists. On hololens it's randomly pressed though for hands
483
488
  // see https://www.w3.org/TR/webxr-gamepads-module-1/#xr-standard-gamepad-mapping
484
489
  if (i === 4) {
485
- if (btn.pressed && !this.didChangeScale) {
490
+ if (btn.pressed && !this.didChangeScale && this.webXR.IsInVR) {
486
491
  this.didChangeScale = true;
487
492
  const rig = this.webXR.Rig;
488
493
  if (rig) {
@@ -566,7 +571,7 @@ export class WebXRController extends Behaviour {
566
571
  const scale = Mathf.clamp(this.lastHit.distance * .01 * factor, .015, .1);
567
572
  this._raycastHitPoint.scale.set(scale, scale, scale);
568
573
  }
569
- this._raycastHitPoint.visible = this.lastHit !== null;
574
+ this._raycastHitPoint.visible = this.lastHit !== null && this.lastHit !== undefined;
570
575
  }
571
576
  return hit;
572
577
  }
@@ -583,7 +588,7 @@ export class WebXRController extends Behaviour {
583
588
  }
584
589
 
585
590
  private selectStartCallback: Function | null = null;
586
- private lastSelectStartObject : THREE.Object3D | null = null;;
591
+ private lastSelectStartObject: THREE.Object3D | null = null;;
587
592
 
588
593
  private onHandleSelectStart() {
589
594
  this.selectStartCallback = null;
@@ -611,7 +616,8 @@ export class WebXRController extends Behaviour {
611
616
  for (const intersection of intersections) {
612
617
  const object = intersection.object;
613
618
  if (!this.testIsVisible(object)) {
614
- console.log("not visible");
619
+ if (debug)
620
+ console.log("not visible");
615
621
  continue;
616
622
  }
617
623
  this.lastSelectStartObject = object;
@@ -622,7 +628,7 @@ export class WebXRController extends Behaviour {
622
628
  sub(this, args);
623
629
  }
624
630
  }
625
- if (args.grab !== object)
631
+ if (args.grab !== object && debug)
626
632
  console.log("Grabbed object changed", "original", object, "new", args.grab);
627
633
  if (args.grab) {
628
634
  this.grabbed = AttachedObject.TryTake(this, args.grab, intersection, closeGrab);
@@ -630,8 +636,7 @@ export class WebXRController extends Behaviour {
630
636
  break;
631
637
  }
632
638
  }
633
- else
634
- {
639
+ else {
635
640
  const subs = WebXRController.eventSubs[ControllerEvents.SelectStart];
636
641
  const args = { selected: null, grab: null };
637
642
  if (subs && subs.length > 0) {
@@ -761,7 +766,8 @@ export class AttachedObject {
761
766
  public static TryTake(controller: WebXRController, candidate: THREE.Object3D, intersection: THREE.Intersection, closeGrab: boolean): AttachedObject | null {
762
767
  const interactable = GameObject.getComponentInParent(candidate, Interactable);
763
768
  if (!interactable) {
764
- console.warn("Prevented taking object that is not interactable", candidate);
769
+ if (debug)
770
+ console.warn("Prevented taking object that is not interactable", candidate);
765
771
  return null;
766
772
  }
767
773
 
@@ -346,8 +346,8 @@ export class WebXRSync extends Behaviour {
346
346
 
347
347
  private buildLocalAvatar() {
348
348
  if (this.localAvatar) return;
349
- const id = this.context.connection?.connectionId ?? this.k_LocalAvatarNoNetworkingGuid;
350
- this.localAvatar = new WebXRAvatar(this.context, id, this.webXR!);
349
+ const connectionId = this.context.connection?.connectionId ?? this.k_LocalAvatarNoNetworkingGuid;
350
+ this.localAvatar = new WebXRAvatar(this.context, connectionId, this.webXR!);
351
351
  this.localAvatar.isLocalAvatar = true;
352
352
  this.localAvatar.setAvatarOverride(this.getAvatarId());
353
353
  this.avatars[this.localAvatar.guid] = this.localAvatar;
@@ -452,7 +452,7 @@ export class WebXRSync extends Behaviour {
452
452
 
453
453
  private getAvatarId() {
454
454
  const urlAvatar = utils.getParam("avatar") as string;
455
- const avatarId = urlAvatar;
455
+ const avatarId = urlAvatar ?? null;
456
456
  return avatarId;
457
457
  }
458
458
  }
@@ -11,6 +11,7 @@ export enum XRStateFlag {
11
11
  VR = 1 << 2,
12
12
  FirstPerson = 1 << 3,
13
13
  ThirdPerson = 1 << 4,
14
+ All = 0xffffffff
14
15
  }
15
16
 
16
17
  // console.log(XRStateFlag);
@@ -16,6 +16,8 @@ export class CanvasGroup extends Behaviour implements ICanvasGroup {
16
16
  this.markDirty();
17
17
  }
18
18
 
19
+ get isCanvasGroup() { return true; }
20
+
19
21
  private _alpha: number = 1;
20
22
 
21
23
  @serializeable()
@@ -9,9 +9,8 @@ import { PointerEventData } from "./PointerEvents";
9
9
  import { Raycaster } from "./Raycaster";
10
10
  import { InputEvents, KeyCode } from "../../engine/engine_input";
11
11
  import { Object3D } from "three";
12
- import { ICanvasGroup } from "./Interfaces";
12
+ import { ICanvasGroup, IGraphic } from "./Interfaces";
13
13
  import { getParam } from "../../engine/engine_utils";
14
- import { off } from "process";
15
14
 
16
15
  const debug = getParam("debugeventsystem");
17
16
 
@@ -135,7 +134,7 @@ export class EventSystem extends Behaviour {
135
134
  private objectsHoveredLastFrame: THREE.Object3D[] = [];
136
135
  // todo: these should be listed by input source (e.g. per controller, mouse, touch)
137
136
  private raisedPointerDownEvents: any[] = [];
138
- private _didMove : boolean = false;
137
+ private _didMove: boolean = false;
139
138
 
140
139
  onBeforeUpdate() {
141
140
  this.objectsHoveredThisFrame.length = 0;
@@ -147,9 +146,9 @@ export class EventSystem extends Behaviour {
147
146
  // console.log("alt pressed");
148
147
  return;
149
148
  }
150
- if(!this._didMove){
149
+ if (!this._didMove) {
151
150
  const pos = this.context.input.getPointerPositionRC(0);
152
- if(pos && pos.x === 0 && pos.y === 0) return;
151
+ if (pos && pos.x === 0 && pos.y === 0) return;
153
152
  this._didMove = true;
154
153
  }
155
154
 
@@ -166,10 +165,10 @@ export class EventSystem extends Behaviour {
166
165
  // handle orbit control before handling input events
167
166
  // this is just so button events that modify OrbitControls.enabled work
168
167
  let orbitControlComponent: OrbitControls | null = null;
169
- let previousOrbitControlState : boolean | null = null;
168
+ let previousOrbitControlState: boolean | null = null;
170
169
  if (this.context.input.mouseDown && this.currentActiveMeshUIComponents.length > 0 && this.context.mainCameraComponent) {
171
170
  orbitControlComponent = GameObject.getComponent(this.context.mainCameraComponent.gameObject, OrbitControls) ?? null;
172
- if(orbitControlComponent){
171
+ if (orbitControlComponent) {
173
172
  previousOrbitControlState = orbitControlComponent.enabled;
174
173
  orbitControlComponent.enabled = false;
175
174
  }
@@ -274,16 +273,16 @@ export class EventSystem extends Behaviour {
274
273
  this._noDepthTestingResults.length = 0;
275
274
  for (let i = 0; i < hits.length; i++) {
276
275
  const hit = hits[i];
277
- const object = hit.object as THREE.Mesh;
278
- if(object.material){
279
- if(object.material["depthTest"] === false){
276
+ const object = hit.object as THREE.Mesh;
277
+ if (object.material) {
278
+ if (object.material["depthTest"] === false) {
280
279
  this._noDepthTestingResults.push(hit);
281
280
  continue;
282
281
  }
283
282
  }
284
283
  this._sortingBuffer.push(hit);
285
284
  }
286
- for(const obj of this._sortingBuffer){
285
+ for (const obj of this._sortingBuffer) {
287
286
  this._noDepthTestingResults.push(obj);
288
287
  }
289
288
  return this._noDepthTestingResults;
@@ -294,7 +293,7 @@ export class EventSystem extends Behaviour {
294
293
 
295
294
  if (!this.testIsVisible(object)) {
296
295
  if (args.isClicked && debug)
297
- console.log("not allowed", object);
296
+ console.log("not allowed", object);
298
297
  return false;
299
298
  }
300
299
 
@@ -314,10 +313,17 @@ export class EventSystem extends Behaviour {
314
313
  const actualGo = parent.shadowComponentOwner.gameObject;
315
314
  if (actualGo) {
316
315
  canvasGroup = this.tryFindCanvasGroup(actualGo);
317
- if (canvasGroup?.blocksRaycasts === false) return false;
318
- if (canvasGroup?.interactable === false) {
319
- return true;
316
+ if (canvasGroup?.isCanvasGroup === true) {
317
+ if (canvasGroup.blocksRaycasts === false) return false;
318
+ if (canvasGroup.interactable === false) return true;
320
319
  }
320
+ // handle Graphic Raycast target
321
+ const graphic = GameObject.foreachComponent(actualGo, c => {
322
+ if (typeof (c as unknown as IGraphic).raycastTarget === "boolean") return c;
323
+ return undefined;
324
+ }, false);
325
+ if (graphic?.raycastTarget === false) return false;
326
+
321
327
  const handled = this.handleMeshUIIntersection(object, pressedOrClicked);
322
328
  if (!clicked && handled) {
323
329
  // return true;
@@ -10,6 +10,8 @@ import { onChange, scheduleAction } from "./Utils"
10
10
 
11
11
  export class Graphic extends BaseUIComponent implements IGraphic {
12
12
 
13
+ get isGraphic() { return true; }
14
+
13
15
  @serializeable(RGBAColor)
14
16
  get color(): RGBAColor {
15
17
  if (!this._color) this._color = new RGBAColor(1, 1, 1, 1);
@@ -132,6 +134,7 @@ export class Graphic extends BaseUIComponent implements IGraphic {
132
134
  }
133
135
 
134
136
  protected onAfterAddedToScene(): void {
137
+ super.onAfterAddedToScene();
135
138
  if (this.shadowComponent) {
136
139
  //@ts-ignore
137
140
  this.shadowComponent.offset = this.shadowComponent.position.z;
@@ -1,8 +1,10 @@
1
1
  export interface ICanvasGroup {
2
+ get isCanvasGroup() : boolean;
2
3
  blocksRaycasts: boolean;
3
4
  interactable: boolean;
4
5
  }
5
6
 
6
7
  export interface IGraphic {
8
+ get isGraphic() : boolean;
7
9
  raycastTarget: boolean;
8
10
  }