@needle-tools/engine 4.1.0-beta.1 → 4.1.0-beta.3

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 (38) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/needle-engine.bundle.js +1878 -1875
  3. package/dist/needle-engine.bundle.light.js +1853 -1850
  4. package/dist/needle-engine.bundle.light.min.js +92 -93
  5. package/dist/needle-engine.bundle.light.umd.cjs +97 -98
  6. package/dist/needle-engine.bundle.min.js +92 -93
  7. package/dist/needle-engine.bundle.umd.cjs +97 -98
  8. package/lib/engine/engine_networking_auto.js +6 -11
  9. package/lib/engine/engine_networking_auto.js.map +1 -1
  10. package/lib/engine/xr/NeedleXRSession.js +3 -0
  11. package/lib/engine/xr/NeedleXRSession.js.map +1 -1
  12. package/lib/engine-components/GroundProjection.d.ts +1 -1
  13. package/lib/engine-components/GroundProjection.js +39 -32
  14. package/lib/engine-components/GroundProjection.js.map +1 -1
  15. package/lib/engine-components/SpatialTrigger.d.ts +2 -2
  16. package/lib/engine-components/SpatialTrigger.js +4 -4
  17. package/lib/engine-components/SpatialTrigger.js.map +1 -1
  18. package/lib/engine-components/TransformGizmo.d.ts +7 -1
  19. package/lib/engine-components/TransformGizmo.js +39 -32
  20. package/lib/engine-components/TransformGizmo.js.map +1 -1
  21. package/lib/engine-components/VideoPlayer.js +0 -1
  22. package/lib/engine-components/VideoPlayer.js.map +1 -1
  23. package/lib/engine-components/webxr/WebXRRig.d.ts +11 -1
  24. package/lib/engine-components/webxr/WebXRRig.js +13 -1
  25. package/lib/engine-components/webxr/WebXRRig.js.map +1 -1
  26. package/lib/engine-components-experimental/networking/PlayerSync.d.ts +4 -1
  27. package/lib/engine-components-experimental/networking/PlayerSync.js +16 -29
  28. package/lib/engine-components-experimental/networking/PlayerSync.js.map +1 -1
  29. package/package.json +1 -1
  30. package/plugins/vite/transform.js +3 -3
  31. package/src/engine/engine_networking_auto.ts +7 -22
  32. package/src/engine/xr/NeedleXRSession.ts +3 -0
  33. package/src/engine-components/GroundProjection.ts +46 -38
  34. package/src/engine-components/SpatialTrigger.ts +6 -6
  35. package/src/engine-components/TransformGizmo.ts +41 -33
  36. package/src/engine-components/VideoPlayer.ts +0 -1
  37. package/src/engine-components/webxr/WebXRRig.ts +15 -2
  38. package/src/engine-components-experimental/networking/PlayerSync.ts +17 -36
@@ -32,15 +32,12 @@ class ComponentPropertiesSyncer {
32
32
  comp: IComponent;
33
33
 
34
34
  constructor(comp: IComponent) {
35
- // console.log("CREATE NEW SYNC", comp.name, comp.guid);
36
35
  this.comp = comp;
37
36
  }
38
37
 
39
38
  // private getters: { [key: string]: Function } = {};
40
39
  private hasChanges: boolean = false;
41
40
  private changedProperties: { [key: string]: any } = {};
42
- // private data = {};
43
-
44
41
 
45
42
  get networkingKey(): string {
46
43
  return this.comp.guid;
@@ -89,27 +86,24 @@ class ComponentPropertiesSyncer {
89
86
  return;
90
87
  }
91
88
  for (const name in this.changedProperties) {
92
- const guid = this.comp.guid + "/" + name;
93
89
  const value = this.changedProperties[name];
94
- if (debug) console.log("SEND", this.comp.name, this.networkingKey);
95
- net.send(this.networkingKey, { guid, data: value }, SendQueue.Queued);
90
+ if (debug) console.log("SEND", this.comp.guid, this.networkingKey);
91
+ net.send(this.networkingKey, { guid: this.comp.guid, property: name, data: value }, SendQueue.Queued);
92
+ delete this.changedProperties[name];
96
93
  }
97
94
  }
98
95
 
99
- private onHandleReceiving = (val: { guid: string, data: any }) => {
100
- if (debug)
101
- console.log("RECEIVE", this.comp.name, this.comp.guid, val);
96
+ private onHandleReceiving = (val: { guid: string, property: string, data: any }) => {
97
+ if (debug) console.log("SYNCFIELD RECEIVE", this.comp.name, this.comp.guid, val);
102
98
  if (!this._isInit) return;
103
99
  if (!this.comp) return;
104
100
  // check if this change belongs to this component
105
- if (val.guid.startsWith(this.comp.guid) === false) {
101
+ if (val.guid !== this.comp.guid) {
106
102
  return;
107
103
  }
108
- const [_guid, key] = val.guid.split("/");
109
- if (debug) console.log("RECEIVED", this.comp.name, this.comp.guid, val);
110
104
  try {
111
105
  this._isReceiving = true;
112
- this.comp[key] = val.data;
106
+ this.comp[val.property] = val.data;
113
107
  }
114
108
  catch (err) {
115
109
  console.error(err);
@@ -118,15 +112,6 @@ class ComponentPropertiesSyncer {
118
112
  this._isReceiving = false;
119
113
  }
120
114
  }
121
-
122
- // private _seen: Set<any> = new Set();
123
- // private uniqBy(a, key) {
124
- // this._seen.clear();
125
- // return a.filter(item => {
126
- // let k = key(item);
127
- // return this._seen.has(k) ? false : this._seen.add(k);
128
- // });
129
- // }
130
115
  }
131
116
 
132
117
  function testValueChanged(newValue, previousValue): boolean {
@@ -712,8 +712,11 @@ export class NeedleXRSession implements INeedleXRSession {
712
712
  /** Sets a XRRig to be active which will parent the camera to this rig */
713
713
  setRigActive(rig: IXRRig) {
714
714
  const i = this._rigs.indexOf(rig);
715
+ const currentlyActive = this._rigs[0];
715
716
  this._rigs.splice(i, 1);
716
717
  this._rigs.unshift(rig);
718
+ // if there's another rig currently active we need to make sure we have at least the same priority
719
+ rig.priority = currentlyActive?.priority ?? 0;
717
720
  this.updateActiveXRRig();
718
721
  }
719
722
  /**
@@ -3,7 +3,7 @@ import { GroundedSkybox as GroundProjection } from 'three/examples/jsm/objects/G
3
3
 
4
4
  import { Gizmos } from "../engine/engine_gizmos.js";
5
5
  import { serializable } from "../engine/engine_serialization_decorator.js";
6
- import { getBoundingBox, getTempVector, getWorldScale, Graphics, setVisibleInCustomShadowRendering,setWorldPosition } from "../engine/engine_three_utils.js";
6
+ import { getBoundingBox, getTempVector, getWorldScale, Graphics, setVisibleInCustomShadowRendering, setWorldPosition } from "../engine/engine_three_utils.js";
7
7
  import { delayForFrames, getParam, Watch as Watch } from "../engine/engine_utils.js";
8
8
  import { Behaviour } from "./Component.js";
9
9
 
@@ -37,7 +37,7 @@ export class GroundProjectedEnv extends Behaviour {
37
37
  @serializable()
38
38
  set radius(val: number) {
39
39
  this._radius = val;
40
- this.updateProjection();
40
+ if (this._projection) this.updateProjection();
41
41
  }
42
42
  get radius(): number { return this._radius; }
43
43
  private _radius: number = 50;
@@ -49,7 +49,7 @@ export class GroundProjectedEnv extends Behaviour {
49
49
  @serializable()
50
50
  set height(val: number) {
51
51
  this._height = val;
52
- this.updateProjection();
52
+ if (this._projection) this.updateProjection();
53
53
  }
54
54
  get height(): number { return this._height; }
55
55
  private _height: number = 3;
@@ -68,7 +68,7 @@ export class GroundProjectedEnv extends Behaviour {
68
68
  get arBlending(): number { return this._arblending; }
69
69
  private _arblending = 0;
70
70
 
71
- private _lastEnvironment?: Texture;
71
+ private _lastBackground?: Texture;
72
72
  private _lastRadius?: number;
73
73
  private _lastHeight?: number;
74
74
  private _projection?: GroundProjection;
@@ -88,9 +88,10 @@ export class GroundProjectedEnv extends Behaviour {
88
88
  this.updateAndCreate();
89
89
  }
90
90
  if (!this._watcher) {
91
- this._watcher = new Watch(this.context.scene, "environment");
91
+ this._watcher = new Watch(this.context.scene, "background");
92
92
  this._watcher.subscribeWrite(_ => {
93
- this.updateProjection();
93
+ if (debug) console.log("Background changed", this.context.scene.background);
94
+ this._needsTextureUpdate = true;
94
95
  });
95
96
  }
96
97
  }
@@ -101,13 +102,13 @@ export class GroundProjectedEnv extends Behaviour {
101
102
  }
102
103
  /** @internal */
103
104
  onEnterXR(): void {
104
- if(!this.activeAndEnabled) return;
105
+ if (!this.activeAndEnabled) return;
105
106
  this._needsTextureUpdate = true;
106
107
  this.updateProjection();
107
108
  }
108
109
  /** @internal */
109
110
  async onLeaveXR() {
110
- if(!this.activeAndEnabled) return;
111
+ if (!this.activeAndEnabled) return;
111
112
  await delayForFrames(1);
112
113
  this.updateProjection();
113
114
  }
@@ -121,8 +122,8 @@ export class GroundProjectedEnv extends Behaviour {
121
122
  if (blurrinessChanged) {
122
123
  this.updateProjection();
123
124
  }
124
- else if (this._needsTextureUpdate) {
125
- this.updateBlurriness();
125
+ else if (this._needsTextureUpdate && this.context.scene.background instanceof Texture) {
126
+ this.updateBlurriness(this.context.scene.background, this.context.scene.backgroundBlurriness);
126
127
  }
127
128
  }
128
129
 
@@ -138,10 +139,16 @@ export class GroundProjectedEnv extends Behaviour {
138
139
  * Updates the ground projection. This is called automatically when the environment or settings change.
139
140
  */
140
141
  updateProjection() {
141
- if (!this.context.scene.environment) {
142
+ if (!this.context.scene.background) {
143
+ this._projection?.removeFromParent();
144
+ return;
145
+ }
146
+ const backgroundTexture = this.context.scene.background;
147
+ if (!(backgroundTexture instanceof Texture)) {
142
148
  this._projection?.removeFromParent();
143
149
  return;
144
150
  }
151
+
145
152
  if (this.context.xr?.isPassThrough || this.context.xr?.isAR) {
146
153
  if (this.arBlending === 0) {
147
154
  this._projection?.removeFromParent();
@@ -152,25 +159,25 @@ export class GroundProjectedEnv extends Behaviour {
152
159
  if (!this.gameObject || this.destroyed) {
153
160
  return;
154
161
  }
155
-
162
+
156
163
  let needsNewAutoFit = true;
157
164
  // offset here must be zero (and not .01) because the plane occlusion (when mesh tracking is active) is otherwise not correct
158
165
  const offset = 0;
159
- if (!this._projection || this.context.scene.environment !== this._lastEnvironment || this._height !== this._lastHeight || this._radius !== this._lastRadius) {
160
- if (debug)
161
- console.log("Create/Update Ground Projection", this.context.scene.environment.name);
166
+
167
+ const hasChanged = backgroundTexture !== this._lastBackground || this._height !== this._lastHeight || this._radius !== this._lastRadius;
168
+
169
+ if (!this._projection || hasChanged) {
170
+ if (debug) console.log("Create/Update Ground Projection", backgroundTexture.name);
171
+
162
172
  this._projection?.removeFromParent();
163
- if (!this._projection || (this.context.scene.environment !== this._lastEnvironment || this._lastHeight !== this._height || this._lastRadius !== this._radius)) {
164
- try {
165
- this._projection = new GroundProjection(this.context.scene.environment, this._height, this.radius, 64);
166
- }
167
- catch (e) {
168
- console.error("Failed to enable GroundProjection for environment", e);
169
- return;
170
- }
173
+
174
+ try {
175
+ this._projection = new GroundProjection(backgroundTexture, this._height, this._radius, 64);
176
+ }
177
+ catch (e) {
178
+ console.error("Error creating three GroundProjection", e);
179
+ return;
171
180
  }
172
- else
173
- needsNewAutoFit = false;
174
181
  this._projection.position.y = this._height - offset;
175
182
  this._projection.name = "GroundProjection";
176
183
  setVisibleInCustomShadowRendering(this._projection, false);
@@ -205,35 +212,35 @@ export class GroundProjectedEnv extends Behaviour {
205
212
  this.env.radius = this._radius;
206
213
  this.env.height = this._height;
207
214
  */
208
-
215
+
209
216
  if (this.context.scene.backgroundBlurriness > 0.001 && this._needsTextureUpdate) {
210
- this.updateBlurriness();
217
+ this.updateBlurriness(backgroundTexture, this.context.scene.backgroundBlurriness);
211
218
  }
212
219
 
213
- this._lastEnvironment = this.context.scene.environment;
220
+ this._lastBackground = backgroundTexture;
214
221
  this._lastHeight = this._height;
215
222
  this._lastRadius = this._radius;
216
-
217
223
  this._needsTextureUpdate = false;
218
224
  }
219
225
 
220
226
  private _blurrynessShader: ShaderMaterial | null = null;
221
227
  private _lastBlurriness: number = -1;
222
228
 
223
- private updateBlurriness() {
229
+ private updateBlurriness(texture: Texture, blurriness: number) {
224
230
  if (!this._projection) {
225
231
  return;
226
232
  }
227
- else if (!this.context.scene.environment) {
233
+ else if (!texture) {
228
234
  return;
229
235
  }
236
+
230
237
  this._needsTextureUpdate = false;
231
- if (debug) console.log("Update Blurriness", this.context.scene.backgroundBlurriness);
238
+ if (debug) console.log("Update Blurriness", blurriness);
232
239
  this._blurrynessShader ??= new ShaderMaterial({
233
240
  name: "GroundProjectionBlurriness",
234
241
  uniforms: {
235
- map: { value: this.context.scene.environment },
236
- blurriness: { value: this.context.scene.backgroundBlurriness },
242
+ map: { value: texture },
243
+ blurriness: { value: blurriness },
237
244
  blending: { value: 0 },
238
245
  alphaFactor: { value: 1 }
239
246
  },
@@ -241,9 +248,10 @@ export class GroundProjectedEnv extends Behaviour {
241
248
  fragmentShader: blurFragmentShader
242
249
  });
243
250
  this._blurrynessShader.depthWrite = false;
244
- this._blurrynessShader.uniforms.blurriness.value = this.context.scene.backgroundBlurriness;
245
- this._lastBlurriness = this.context.scene.backgroundBlurriness;
246
- this.context.scene.environment.needsPMREMUpdate = true;
251
+ this._blurrynessShader.uniforms.map.value = texture;
252
+ this._blurrynessShader.uniforms.blurriness.value = 1;//blurriness;
253
+ this._lastBlurriness = blurriness;
254
+ texture.needsUpdate = true;
247
255
 
248
256
  const wasTransparent = this._projection.material.transparent;
249
257
  this._projection.material.transparent = (this.context.xr?.isAR === true && this.arBlending > 0.000001) ?? false;
@@ -266,7 +274,7 @@ export class GroundProjectedEnv extends Behaviour {
266
274
  }
267
275
 
268
276
  // Update the texture
269
- this._projection.material.map = Graphics.copyTexture(this.context.scene.environment, this._blurrynessShader);
277
+ this._projection.material.map = Graphics.copyTexture(texture, this._blurrynessShader);
270
278
  this._projection.material.depthTest = true;
271
279
  this._projection.material.depthWrite = false;
272
280
  }
@@ -62,23 +62,23 @@ export class SpatialTriggerReceiver extends Behaviour {
62
62
 
63
63
  }
64
64
 
65
- currentIntersected: SpatialTrigger[] = [];
66
- lastIntersected: SpatialTrigger[] = [];
65
+ readonly currentIntersected: SpatialTrigger[] = [];
66
+ readonly lastIntersected: SpatialTrigger[] = [];
67
67
 
68
68
  onEnterTrigger(trigger: SpatialTrigger): void {
69
69
  if(debug) console.log("ENTER TRIGGER", this.name, trigger.name, this, trigger);
70
70
  trigger.raiseOnEnterEvent(this);
71
- this.onEnter?.invoke(this, trigger);
71
+ this.onEnter?.invoke();
72
72
  }
73
73
  onExitTrigger(trigger: SpatialTrigger): void {
74
- if(debug) console.log("EXIT TRIGGER", this.name, trigger.name, this, trigger);
74
+ if(debug) console.log("EXIT TRIGGER", this.name, trigger.name, );
75
75
  trigger.raiseOnExitEvent(this);
76
- this.onExit?.invoke(this, trigger);
76
+ this.onExit?.invoke();
77
77
  }
78
78
 
79
79
  onStayTrigger(trigger: SpatialTrigger): void {
80
80
  trigger.raiseOnStayEvent(this);
81
- this.onStay?.invoke(this, trigger);
81
+ this.onStay?.invoke();
82
82
  }
83
83
  }
84
84
 
@@ -1,4 +1,4 @@
1
- import { MathUtils,Mesh, MeshBasicMaterial, Object3D } from "three";
1
+ import { MathUtils, Mesh, MeshBasicMaterial, Object3D } from "three";
2
2
  import { TransformControls } from "three/examples/jsm/controls/TransformControls.js";
3
3
 
4
4
  import * as params from "../engine/engine_default_parameters.js";
@@ -26,7 +26,15 @@ export class TransformGizmo extends Behaviour {
26
26
  @serializable()
27
27
  public scaleSnap: number = .25;
28
28
 
29
- private control?: TransformControls;
29
+ /**
30
+ * Get the underlying three.js TransformControls instance.
31
+ * @returns The TransformControls instance.
32
+ */
33
+ get control() {
34
+ return this._control;
35
+ }
36
+
37
+ private _control?: TransformControls;
30
38
  private orbit?: OrbitControls;
31
39
 
32
40
  /** @internal */
@@ -35,12 +43,12 @@ export class TransformGizmo extends Behaviour {
35
43
 
36
44
  if (!this.context.mainCamera) return;
37
45
 
38
- if (!this.control) {
39
- this.control = new TransformControls(this.context.mainCamera, this.context.renderer.domElement);
40
- this.control.enabled = true;
41
- this.control.getRaycaster().layers.set(2);
42
- this.control.size = 1;
43
- const obj = ("_root" in this.control ? this.control._root : this.control) as Object3D;
46
+ if (!this._control) {
47
+ this._control = new TransformControls(this.context.mainCamera, this.context.renderer.domElement);
48
+ this._control.enabled = true;
49
+ this._control.getRaycaster().layers.set(2);
50
+ this._control.size = 1;
51
+ const obj = ("_root" in this._control ? this._control._root : this._control) as Object3D;
44
52
  obj.traverse(x => {
45
53
  const mesh = x as Mesh;
46
54
  mesh.layers.set(2);
@@ -54,12 +62,12 @@ export class TransformGizmo extends Behaviour {
54
62
  this.orbit = GameObject.getComponentInParent(this.context.mainCamera, OrbitControls) ?? undefined;
55
63
  }
56
64
 
57
- if (this.control) {
58
- const obj = this.control.getHelper();
65
+ if (this._control) {
66
+ const obj = this._control.getHelper();
59
67
  this.context.scene.add(obj);
60
- this.control.attach(this.gameObject);
68
+ this._control.attach(this.gameObject);
61
69
 
62
- this.control?.addEventListener('dragging-changed', this.onControlChangedEvent);
70
+ this._control?.addEventListener('dragging-changed', this.onControlChangedEvent);
63
71
  window.addEventListener('keydown', this.windowKeyDownListener);
64
72
  window.addEventListener('keyup', this.windowKeyUpListener);
65
73
  }
@@ -67,25 +75,25 @@ export class TransformGizmo extends Behaviour {
67
75
 
68
76
  /** @internal */
69
77
  onDisable() {
70
- this.control?.getHelper()?.removeFromParent();
71
- this.control?.removeEventListener('dragging-changed', this.onControlChangedEvent);
78
+ this._control?.getHelper()?.removeFromParent();
79
+ this._control?.removeEventListener('dragging-changed', this.onControlChangedEvent);
72
80
  window.removeEventListener('keydown', this.windowKeyDownListener);
73
81
  window.removeEventListener('keyup', this.windowKeyUpListener);
74
82
  }
75
83
 
76
84
  enableSnapping() {
77
- if (this.control) {
78
- this.control.setTranslationSnap(this.translationSnap);
79
- this.control.setRotationSnap(MathUtils.degToRad(this.rotationSnapAngle));
80
- this.control.setScaleSnap(this.scaleSnap);
85
+ if (this._control) {
86
+ this._control.setTranslationSnap(this.translationSnap);
87
+ this._control.setRotationSnap(MathUtils.degToRad(this.rotationSnapAngle));
88
+ this._control.setScaleSnap(this.scaleSnap);
81
89
  }
82
90
  }
83
91
 
84
92
  disableSnapping() {
85
- if (this.control) {
86
- this.control.setTranslationSnap(null);
87
- this.control.setRotationSnap(null);
88
- this.control.setScaleSnap(null);
93
+ if (this._control) {
94
+ this._control.setTranslationSnap(null);
95
+ this._control.setRotationSnap(null);
96
+ this._control.setScaleSnap(null);
89
97
  }
90
98
  }
91
99
 
@@ -104,11 +112,11 @@ export class TransformGizmo extends Behaviour {
104
112
 
105
113
  private windowKeyDownListener = (event) => {
106
114
  if (!this.enabled) return;
107
- if (!this.control) return;
115
+ if (!this._control) return;
108
116
  switch (event.keyCode) {
109
117
 
110
118
  case 81: // Q
111
- this.control.setSpace(this.control.space === 'local' ? 'world' : 'local');
119
+ this._control.setSpace(this._control.space === 'local' ? 'world' : 'local');
112
120
  break;
113
121
 
114
122
  case 16: // Shift
@@ -116,40 +124,40 @@ export class TransformGizmo extends Behaviour {
116
124
  break;
117
125
 
118
126
  case 87: // W
119
- this.control.setMode('translate');
127
+ this._control.setMode('translate');
120
128
  break;
121
129
 
122
130
  case 69: // E
123
- this.control.setMode('rotate');
131
+ this._control.setMode('rotate');
124
132
  break;
125
133
 
126
134
  case 82: // R
127
- this.control.setMode('scale');
135
+ this._control.setMode('scale');
128
136
  break;
129
137
  case 187:
130
138
  case 107: // +, =, num+
131
- this.control.setSize(this.control.size + 0.1);
139
+ this._control.setSize(this._control.size + 0.1);
132
140
  break;
133
141
 
134
142
  case 189:
135
143
  case 109: // -, _, num-
136
- this.control.setSize(Math.max(this.control.size - 0.1, 0.1));
144
+ this._control.setSize(Math.max(this._control.size - 0.1, 0.1));
137
145
  break;
138
146
 
139
147
  case 88: // X
140
- this.control.showX = !this.control.showX;
148
+ this._control.showX = !this._control.showX;
141
149
  break;
142
150
 
143
151
  case 89: // Y
144
- this.control.showY = !this.control.showY;
152
+ this._control.showY = !this._control.showY;
145
153
  break;
146
154
 
147
155
  case 90: // Z
148
- this.control.showZ = !this.control.showZ;
156
+ this._control.showZ = !this._control.showZ;
149
157
  break;
150
158
 
151
159
  case 32: // Spacebar
152
- this.control.enabled = !this.control.enabled;
160
+ this._control.enabled = !this._control.enabled;
153
161
  break;
154
162
  }
155
163
  }
@@ -1017,7 +1017,6 @@ class ScreenspaceTexture extends ShaderMaterial {
1017
1017
  else
1018
1018
  {
1019
1019
  vec4 texcolor = texture2D(map, vUv);
1020
- texcolor = sRGBTransferOETF(texcolor);
1021
1020
  gl_FragColor = texcolor;
1022
1021
  }
1023
1022
  }
@@ -23,11 +23,21 @@ export class XRRig extends Behaviour implements IXRRig {
23
23
 
24
24
  get isActive() { return this.activeAndEnabled && this.gameObject.visible; }
25
25
 
26
- /** Sets this rig to be the active XR rig (needs to be called during an active XR session) */
26
+ /**
27
+ * Sets this rig to be the active XR rig (needs to be called during an active XR session)
28
+ * Note that this might modify the priority of this rig to be the highest.
29
+ */
27
30
  setAsActiveXRRig() {
28
31
  NeedleXRSession.active?.setRigActive(this);
29
32
  }
33
+ /**
34
+ * Sets the priority of the rig.
35
+ */
36
+ setPriority(value: number) {
37
+ this.priority = value;
38
+ }
30
39
 
40
+ /** @internal */
31
41
  awake(): void {
32
42
  if (debug) {
33
43
  const gizmoObj = new Object3D() as IGameObject;
@@ -51,15 +61,18 @@ export class XRRig extends Behaviour implements IXRRig {
51
61
 
52
62
  private _startScale?: Vector3;
53
63
 
64
+ /** @internal */
54
65
  onEnterXR(args: NeedleXREventArgs): void {
55
66
  this._startScale = this.gameObject.scale.clone();
56
67
  args.xr.addRig(this);
57
- if(debug) console.log("WebXR: add Rig", this.name, this.priority);
68
+ if (debug) console.log("WebXR: add Rig", this.name, this.priority);
58
69
  }
70
+ /** @internal */
59
71
  onLeaveXR(args: NeedleXREventArgs): void {
60
72
  args.xr.removeRig(this);
61
73
  if (this._startScale && this.gameObject)
62
74
  this.gameObject.scale.copy(this._startScale);
63
75
  }
64
76
 
77
+
65
78
  }
@@ -20,7 +20,7 @@ declare type PlayerSyncWithAsset = PlayerSync & Required<Pick<PlayerSync, "asset
20
20
  * @category Networking
21
21
  */
22
22
  export class PlayerSync extends Behaviour {
23
-
23
+
24
24
  /**
25
25
  * This API is experimental and may change or be removed in the future.
26
26
  * Create a PlayerSync instance at runtime from a given URL
@@ -103,6 +103,7 @@ export class PlayerSync extends Behaviour {
103
103
  const instance = await this._localInstance;
104
104
  if (instance) {
105
105
  const pl = GameObject.getComponentsInChildren(instance, PlayerState);
106
+ if (debug) console.log(`PlayerSync.createInstance: found ${pl?.length} PlayerState components. Owner: ${this.context.connection.connectionId}`);
106
107
  if (pl?.length) {
107
108
  for (const state of pl)
108
109
  state.owner = this.context.connection.connectionId!;
@@ -226,7 +227,7 @@ export class PlayerState extends Behaviour {
226
227
  }
227
228
 
228
229
  private onOwnerChange(newOwner: string, oldOwner: string) {
229
- if (debug) console.log("PlayerSync.onOwnerChange", this, "newOwner", newOwner, "oldOwner", oldOwner);
230
+ if (debug) console.log(`PlayerSync.onOwnerChange: ${oldOwner} ${newOwner} (me: ${this.context.connection.connectionId})`);
230
231
 
231
232
  // Remove from local owner array if it was local before
232
233
  const index = PlayerState._local.indexOf(this);
@@ -259,6 +260,7 @@ export class PlayerState extends Behaviour {
259
260
  PlayerState.dispatchEvent(PlayerStateEvent.OwnerChanged, evt);
260
261
  }
261
262
 
263
+ /** @internal */
262
264
  awake(): void {
263
265
  PlayerState.all.push(this);
264
266
  if (debug) console.log("Registered new PlayerState", this.guid, PlayerState.all.length - 1, PlayerState.all)
@@ -266,31 +268,10 @@ export class PlayerState extends Behaviour {
266
268
  this.context.connection.beginListen(RoomEvents.UserLeftRoom, this.onUserLeftRoom);
267
269
  }
268
270
 
269
- private onUserLeftRoom = (model: { userId: string }) => {
270
- if (model.userId === this.owner) {
271
- if (debug)
272
- console.log("PLAYERSYNC LEFT", this.owner)
273
- this.doDestroy();
274
- return;
275
- }
276
- }
277
-
278
-
271
+ /** @internal */
279
272
  async start() {
280
273
  if (debug) console.log("PLAYERSTATE.START, owner: " + this.owner, this.context.connection.usersInRoom([]))
281
274
 
282
- // generate number from owner
283
- // if (this.owner) {
284
- // // string to number
285
- // let num = 0;
286
- // for (let i = 0; i < this.owner.length; i++) {
287
- // num += this.owner.charCodeAt(i);
288
- // }
289
- // console.log(num)
290
- // num = num / 1000
291
- // this.gameObject.position.y = num;
292
- // }
293
-
294
275
  // If a player is spawned but not in the room anymore we want to destroy it
295
276
  // this might happen in a case where all users get disconnected at once and the server
296
277
  // still has the syncInstantiate messages that are sent to all clients
@@ -314,29 +295,20 @@ export class PlayerState extends Behaviour {
314
295
  }
315
296
  else if (debug) console.warn("PlayerState.start → owner is still undefined but dontDestroy is set to true", this.name);
316
297
  }
317
- else if(debug) console.log("PlayerState.start → owner is assigned", this.owner);
298
+ else if (debug) console.log("PlayerState.start → owner is assigned", this.owner);
318
299
  }, 2000);
319
300
  }
320
301
  }
321
302
 
322
- // onEnable() {
323
- // if (debug) this.startCoroutine(this.debugRoutine());
324
- // }
325
-
326
- // *debugRoutine() {
327
- // while (!this.destroyed && this.activeAndEnabled) {
328
- // Gizmos.DrawLabel(this.gameObject.worldPosition, this.owner ?? "no owner");
329
- // yield;
330
- // }
331
- // }
332
-
333
303
  /** this tells the server that this client has been destroyed and the networking message for the instantiate will be removed */
334
304
  doDestroy() {
335
305
  if (debug) console.log("PlayerSync.doDestroy → syncDestroy", this.name);
336
306
  syncDestroy(this.gameObject, this.context.connection, true, { saveInRoom: false });
337
307
  }
338
308
 
309
+ /** @internal */
339
310
  onDestroy() {
311
+ if(debug) console.warn("PlayerState.onDestroy", this.owner);
340
312
  this.context.connection.stopListen(RoomEvents.UserLeftRoom, this.onUserLeftRoom);
341
313
  PlayerState.all.splice(PlayerState.all.indexOf(this), 1);
342
314
 
@@ -346,4 +318,13 @@ export class PlayerState extends Behaviour {
346
318
  PlayerState._local.splice(index, 1);
347
319
  }
348
320
  }
321
+
322
+ private onUserLeftRoom = (model: { userId: string }) => {
323
+ if (model.userId === this.owner) {
324
+ if (debug)
325
+ console.log("PLAYERSYNC LEFT", this.owner)
326
+ this.doDestroy();
327
+ return;
328
+ }
329
+ }
349
330
  }