@needle-tools/engine 2.25.1-pre → 2.26.1-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 (66) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/needle-engine.d.ts +10 -15
  3. package/dist/needle-engine.js +342 -342
  4. package/dist/needle-engine.js.map +4 -4
  5. package/dist/needle-engine.min.js +15 -15
  6. package/dist/needle-engine.min.js.map +4 -4
  7. package/lib/engine/engine_setup.d.ts +1 -0
  8. package/lib/engine/engine_setup.js +2 -1
  9. package/lib/engine/engine_setup.js.map +1 -1
  10. package/lib/engine-components/AlignmentConstraint.js +16 -3
  11. package/lib/engine-components/AlignmentConstraint.js.map +1 -1
  12. package/lib/engine-components/AvatarLoader.d.ts +0 -3
  13. package/lib/engine-components/AvatarLoader.js +23 -52
  14. package/lib/engine-components/AvatarLoader.js.map +1 -1
  15. package/lib/engine-components/Camera.d.ts +1 -0
  16. package/lib/engine-components/Camera.js +15 -26
  17. package/lib/engine-components/Camera.js.map +1 -1
  18. package/lib/engine-components/Collider.js +6 -0
  19. package/lib/engine-components/Collider.js.map +1 -1
  20. package/lib/engine-components/Component.js +10 -0
  21. package/lib/engine-components/Component.js.map +1 -1
  22. package/lib/engine-components/OffsetConstraint.d.ts +3 -0
  23. package/lib/engine-components/OffsetConstraint.js +47 -12
  24. package/lib/engine-components/OffsetConstraint.js.map +1 -1
  25. package/lib/engine-components/Renderer.d.ts +0 -2
  26. package/lib/engine-components/Renderer.js +0 -9
  27. package/lib/engine-components/Renderer.js.map +1 -1
  28. package/lib/engine-components/SmoothFollow.js +1 -1
  29. package/lib/engine-components/SmoothFollow.js.map +1 -1
  30. package/lib/engine-components/SpectatorCamera.d.ts +2 -3
  31. package/lib/engine-components/SpectatorCamera.js +50 -43
  32. package/lib/engine-components/SpectatorCamera.js.map +1 -1
  33. package/lib/engine-components/WebXR.d.ts +2 -0
  34. package/lib/engine-components/WebXR.js +2 -0
  35. package/lib/engine-components/WebXR.js.map +1 -1
  36. package/lib/engine-components/WebXRController.js +1 -0
  37. package/lib/engine-components/WebXRController.js.map +1 -1
  38. package/lib/engine-components/avatar/Avatar_MouthShapes.js +9 -7
  39. package/lib/engine-components/avatar/Avatar_MouthShapes.js.map +1 -1
  40. package/lib/engine-components/codegen/exports.d.ts +0 -1
  41. package/lib/engine-components/codegen/exports.js +0 -1
  42. package/lib/engine-components/codegen/exports.js.map +1 -1
  43. package/lib/engine-components/js-extensions/RGBAColor.d.ts +1 -0
  44. package/lib/engine-components/js-extensions/RGBAColor.js +4 -2
  45. package/lib/engine-components/js-extensions/RGBAColor.js.map +1 -1
  46. package/package.json +1 -1
  47. package/src/engine/codegen/register_types.js +0 -2
  48. package/src/engine/engine_setup.ts +2 -1
  49. package/src/engine-components/AlignmentConstraint.ts +9 -6
  50. package/src/engine-components/AvatarLoader.ts +23 -58
  51. package/src/engine-components/Camera.ts +16 -31
  52. package/src/engine-components/Collider.ts +10 -1
  53. package/src/engine-components/Component.ts +14 -3
  54. package/src/engine-components/OffsetConstraint.ts +41 -13
  55. package/src/engine-components/Renderer.ts +0 -11
  56. package/src/engine-components/SmoothFollow.ts +1 -1
  57. package/src/engine-components/SpectatorCamera.ts +62 -48
  58. package/src/engine-components/WebXR.ts +3 -0
  59. package/src/engine-components/WebXRController.ts +1 -0
  60. package/src/engine-components/avatar/Avatar_MouthShapes.ts +8 -7
  61. package/src/engine-components/codegen/exports.ts +0 -1
  62. package/src/engine-components/js-extensions/RGBAColor.ts +6 -2
  63. package/lib/engine-components/MeshCollider.d.ts +0 -4
  64. package/lib/engine-components/MeshCollider.js +0 -8
  65. package/lib/engine-components/MeshCollider.js.map +0 -1
  66. package/src/engine-components/MeshCollider.ts +0 -12
@@ -31,26 +31,13 @@ export class AvatarModel {
31
31
  // this.leftHand?.traverse(h => h.layers.set(2));
32
32
  // this.rigthHand?.traverse(h => h.layers.set(2));
33
33
  }
34
-
35
- // createNewInstance(): AvatarModel {
36
- // const head = GameObject.instantiate(this.head);
37
- // const lh = GameObject.instantiate(this.leftHand);
38
- // const rh = GameObject.instantiate(this.rigthHand);
39
- // const model = new AvatarModel(this.root, head, lh, rh);
40
- // this.assignRandomColors();
41
- // return model;
42
- // }
43
-
44
- assignRandomColors() {
45
- AvatarLoader.assignRandomPlayerColors(this);
46
- }
47
34
  }
48
35
 
49
36
  export class AvatarLoader {
50
37
 
51
38
  private readonly avatarRegistryUrl: string | null = null;// = "https://needle-storage-castle-demo.glitch.me";//"https://smol-worlds.glitch.me/files/";
52
39
  // private loader: GLTFLoader | null;
53
- private avatarModelCache: Map<string, AvatarModel | null> = new Map<string, AvatarModel | null>();
40
+ // private avatarModelCache: Map<string, AvatarModel | null> = new Map<string, AvatarModel | null>();
54
41
 
55
42
  public async getOrCreateNewAvatarInstance(context: Context, avatarId: string | THREE.Object3D): Promise<AvatarModel | null> {
56
43
 
@@ -74,8 +61,7 @@ export class AvatarLoader {
74
61
  return null;
75
62
  }
76
63
  const model = this.findAvatar(root);
77
- // console.log(model);
78
- model.assignRandomColors();
64
+ // model.assignRandomColors();
79
65
  // this.cacheModel(avatarId, model);
80
66
 
81
67
  if (model.isValid) {
@@ -166,19 +152,19 @@ export class AvatarLoader {
166
152
  // some GLTFs have a "scene" root it seems, others don't, we skip the root here if there's only one child
167
153
  if (searchIn.children.length == 1)
168
154
  searchIn = obj.children[0];
169
- let head = this.findAvatarPart(searchIn, "head");
155
+ let head = this.findAvatarPart(searchIn, ["head"]);
170
156
 
171
- const leftHand = this.findAvatarPart(searchIn, "left");
172
- const rightHand = this.findAvatarPart(searchIn, "right");
157
+ const leftHand = this.findAvatarPart(searchIn, ["left", "hand"]);
158
+ const rightHand = this.findAvatarPart(searchIn, ["right", "hand"]);
173
159
 
174
160
  if (!head) {
175
161
  // very last fallback, entire root is used as head
176
162
  head = root;
177
163
 
178
164
  // normalize size, if the object isn't properly setup the scale might be totally off
179
- let boundsSize = new THREE.Vector3();
165
+ const boundsSize = new THREE.Vector3();
180
166
  new THREE.Box3().setFromObject(head).getSize(boundsSize);
181
- let maxAxis = Math.max(boundsSize.x, boundsSize.y, boundsSize.z);
167
+ const maxAxis = Math.max(boundsSize.x, boundsSize.y, boundsSize.z);
182
168
  console.warn("[Custom Avatar] " + "Normalizing head scale, it's too big: " + maxAxis + " meters! Should be < 0.3m");
183
169
  if (maxAxis > 0.3) {
184
170
  head.scale.multiplyScalar(1.0 / maxAxis * 0.3);
@@ -190,10 +176,22 @@ export class AvatarLoader {
190
176
  }
191
177
 
192
178
 
193
- private findAvatarPart(obj: THREE.Object3D, searchString: string): THREE.Object3D | null {
194
- for (let child of obj.children) {
195
- if (child.name.toLowerCase().indexOf(searchString) > -1)
196
- return child;
179
+ private findAvatarPart(obj: THREE.Object3D, searchString: string[]): THREE.Object3D | null {
180
+
181
+ const name = obj.name.toLowerCase();
182
+ let matchesAll = true;
183
+ for (const str of searchString) {
184
+ if (!matchesAll) break;
185
+ if (name.indexOf(str) === -1)
186
+ matchesAll = false;
187
+ }
188
+ if (matchesAll) return obj;
189
+
190
+ if (obj.children) {
191
+ for (const child of obj.children) {
192
+ const found = this.findAvatarPart(child, searchString);
193
+ if (found) return found;
194
+ }
197
195
  }
198
196
  return null;
199
197
  }
@@ -204,37 +202,4 @@ export class AvatarLoader {
204
202
  }
205
203
  return response;
206
204
  }
207
-
208
- public static assignRandomPlayerColors(model: AvatarModel) {
209
-
210
- const foundMaterials: Map<string, THREE.Material> = new Map<string, THREE.Material>();
211
-
212
- function findPlayerColorMeshesAndAssignColors(o) {
213
- // console.log(o);
214
- if (o.type === "Mesh") {
215
- const mesh = o as THREE.Mesh;
216
- const mat: THREE.Material = mesh.material as THREE.Material;
217
- if (mat && mat.name) {
218
- if (foundMaterials.has(mat.uuid)) {
219
- const found = foundMaterials.get(mat.uuid);
220
- if (found)
221
- mesh.material = found;
222
- return;
223
- }
224
- if (mat.name.endsWith("_playercolor") || mat.name.endsWith("_player_color2")) {
225
- const uuid = mat.uuid;
226
- mesh.material = mat.clone();
227
- mesh.material["color"] = new THREE.Color(Math.random(), Math.random(), Math.random());
228
- foundMaterials.set(uuid, mesh.material);
229
- }
230
- }
231
- }
232
- }
233
- if (model.head)
234
- model.head.traverse(findPlayerColorMeshesAndAssignColors);
235
- if (model.leftHand)
236
- model.leftHand.traverse(findPlayerColorMeshesAndAssignColors);
237
- if (model.rigthHand)
238
- model.rigthHand.traverse(findPlayerColorMeshesAndAssignColors);
239
- }
240
205
  }
@@ -16,8 +16,8 @@ const debug = getParam("debugcam");
16
16
 
17
17
  export class Camera extends Behaviour {
18
18
 
19
- get aspect() : number {
20
- if(this._cam instanceof PerspectiveCamera) return this._cam.aspect;
19
+ get aspect(): number {
20
+ if (this._cam instanceof PerspectiveCamera) return this._cam.aspect;
21
21
  return (this.context.domWidth / this.context.domHeight);
22
22
  }
23
23
 
@@ -61,6 +61,7 @@ export class Camera extends Behaviour {
61
61
  return this._clearFlags;
62
62
  }
63
63
  public set clearFlags(val: ClearFlags) {
64
+ if (val === this._clearFlags) return;
64
65
  this._clearFlags = val;
65
66
  this.applyClearFlagsIfIsActiveCamera();
66
67
  }
@@ -155,30 +156,6 @@ export class Camera extends Behaviour {
155
156
  if (this.tag == "MainCamera") {
156
157
  this.context.setCurrentCamera(this);
157
158
  }
158
- // if (!cameraAlreadyCreated)
159
- // this.gameObject.add(cam);
160
-
161
- // if (!cameraAlreadyCreated) {
162
- // const notInGltf = this["__notInGltf"];
163
- // if ((notInGltf !== undefined)) {
164
- // this.gameObject.getWorldPosition(cam.position);
165
- // this.gameObject.getWorldQuaternion(cam.quaternion);
166
- // const t = new THREE.Euler();
167
- // t.setFromQuaternion(cam.quaternion);
168
- // t.y += Math.PI;
169
- // t.z *= -1;
170
- // cam.rotation.copy(t);
171
- // }
172
- // else {
173
- // cam.position.copy(this.gameObject.position);
174
- // cam.rotation.copy(this.gameObject.rotation);
175
- // }
176
-
177
- // // cam.rotateX(Math.PI / -2);
178
-
179
- // this.gameObject.position.set(0, 0, 0);
180
- // this.gameObject.rotation.set(0, 0, 0);
181
- // }
182
159
  }
183
160
 
184
161
  applyClearFlagsIfIsActiveCamera() {
@@ -186,7 +163,7 @@ export class Camera extends Behaviour {
186
163
  if (this._cam && this.context.mainCameraComponent === this) {
187
164
  switch (this._clearFlags) {
188
165
  case ClearFlags.Skybox:
189
- if (this.context.isInXR) {
166
+ if (this.environmentIsTransparent()) {
190
167
  if (!this.ARBackgroundAlpha || this.ARBackgroundAlpha < 0.001) {
191
168
  this.context.scene.background = null;
192
169
  this.context.renderer.setClearColor(0x000000, 0);
@@ -196,14 +173,14 @@ export class Camera extends Behaviour {
196
173
  this.enableSkybox();
197
174
  break;
198
175
  case ClearFlags.SolidColor:
199
- if (this.backgroundColor) {
200
- let alpha = this.backgroundColor.alpha;
176
+ if (this._backgroundColor) {
177
+ let alpha = this._backgroundColor.alpha;
201
178
  // when in WebXR use ar background alpha override or set to 0
202
- if (this.context.isInXR) {
179
+ if (this.environmentIsTransparent()) {
203
180
  alpha = this.ARBackgroundAlpha ?? 0;
204
181
  }
205
182
  this.context.scene.background = null;
206
- this.context.renderer.setClearColor(this.backgroundColor, alpha);
183
+ this.context.renderer.setClearColor(this._backgroundColor, alpha);
207
184
  }
208
185
  break;
209
186
  case ClearFlags.Uninitialized:
@@ -214,6 +191,14 @@ export class Camera extends Behaviour {
214
191
  }
215
192
  }
216
193
 
194
+ private environmentIsTransparent(): boolean {
195
+ const session = this.context.renderer.xr?.getSession()
196
+ if (!session) return false;
197
+ const environmentBlendMode = session.environmentBlendMode;
198
+ const transparent = environmentBlendMode === 'additive' || environmentBlendMode === 'alpha-blend';
199
+ return transparent;
200
+ }
201
+
217
202
  private enableSkybox() {
218
203
  if (!this._skybox)
219
204
  this._skybox = new CameraSkybox(this);
@@ -69,4 +69,13 @@ export class BoxCollider extends Collider {
69
69
  // // this.context.physics.removeShape(this.gameObject, this._shape);
70
70
  // // }
71
71
  // }
72
- }
72
+ }
73
+
74
+
75
+ // export class MeshCollider extends Collider {
76
+
77
+ // onEnable() {
78
+ // if (this.enabled)
79
+ // this.context.physics.addMeshCollider(this.gameObject);
80
+ // }
81
+ // }
@@ -680,6 +680,17 @@ abstract class GameObject extends THREE.Object3D implements THREE.Object3D {
680
680
  }
681
681
 
682
682
 
683
+ // this is a fix to allow gameObject active animation be applied to a three object
684
+ Object.defineProperty(THREE.Object3D.prototype, "activeSelf", {
685
+ get: function () {
686
+ return this.visible;
687
+ },
688
+ set: function (val: boolean | number) {
689
+ const state = typeof val === "number" ? val > 0.5 : val;
690
+ this.visible = state;
691
+ }
692
+ });
693
+
683
694
 
684
695
  THREE.Object3D.prototype["addNewComponent"] = function <T extends Behaviour>(type: ConstructorConcrete<T>) {
685
696
  return GameObject.addNewComponent(this, type);
@@ -981,7 +992,7 @@ class Component implements EventTarget {
981
992
  }
982
993
 
983
994
 
984
-
995
+
985
996
  // EventTarget implementation:
986
997
 
987
998
  private _eventListeners = new Map<string, EventListener[]>();
@@ -1021,12 +1032,12 @@ class Behaviour extends Component {
1021
1032
  // when called from animationclip we receive numbers
1022
1033
  // due to interpolation they can be anything between 0 and 1
1023
1034
  if (typeof val === "number") {
1024
- if(val >= 0.5) val = true;
1035
+ if (val >= 0.5) val = true;
1025
1036
  else val = false;
1026
1037
  }
1027
1038
 
1028
1039
  if (val === this.__isEnabled) return;
1029
-
1040
+
1030
1041
  this.__isEnabled = val;
1031
1042
  // console.log(val);
1032
1043
  // need to check here because codegen is calling this before everything is setup
@@ -1,30 +1,58 @@
1
1
  import { Behaviour, GameObject } from "./Component";
2
2
  import * as utils from "./../engine/engine_three_utils";
3
- import * as THREE from "three";
3
+ import { Quaternion, Euler, Vector3, Plane } from "three";
4
+ import { serializeable } from "../engine/engine_serialization_decorator";
4
5
 
5
6
  export class OffsetConstraint extends Behaviour {
6
7
 
7
- private from!: GameObject;
8
- private affectPosition!: boolean;
9
- private affectRotation!: boolean;
10
- private alignLookDirection!: boolean;
11
- private levelLookDirection!: boolean;
12
- private positionOffset!: THREE.Vector3;
13
- private rotationOffset!: THREE.Vector3;
8
+ @serializeable(GameObject)
9
+ private referenceSpace: GameObject | undefined;
10
+
11
+ @serializeable(GameObject)
12
+ private from: GameObject | undefined;
13
+
14
+ private affectPosition: boolean = false;
15
+ private affectRotation: boolean = false;
16
+ private alignLookDirection: boolean = false;
17
+ private levelLookDirection: boolean = false;
18
+ private levelPosition: boolean = false;
19
+
20
+ @serializeable(Vector3)
21
+ private positionOffset: Vector3 = new Vector3(0,0,0);
22
+ @serializeable(Vector3)
23
+ private rotationOffset: Vector3 = new Vector3(0,0,0);
24
+
25
+ private offset: Vector3 = new Vector3(0,0,0);
14
26
 
15
27
  update() {
16
28
  if (!this.from) return;
17
29
 
18
30
  var pos = utils.getWorldPosition(this.from);
19
- var rot: THREE.Quaternion = utils.getWorldQuaternion(this.from);
31
+ var rot: Quaternion = utils.getWorldQuaternion(this.from);
20
32
 
21
- if(this.affectPosition) utils.setWorldPosition(this.gameObject, pos.add(this.positionOffset));
33
+ this.offset.copy(this.positionOffset);
34
+ const l = this.offset.length();
35
+ if (this.referenceSpace)
36
+ this.offset.transformDirection(this.referenceSpace.matrixWorld).multiplyScalar(l);
37
+
38
+ pos.add(this.offset);
39
+
40
+ if (this.levelPosition && this.referenceSpace) {
41
+ const plane = new Plane(this.gameObject.up, 0);
42
+ const refSpacePoint = utils.getWorldPosition(this.referenceSpace);
43
+ plane.setFromNormalAndCoplanarPoint(this.gameObject.up, refSpacePoint);
44
+ const v2 = new Vector3(0,0,0);
45
+ plane.projectPoint(pos, v2);
46
+ pos.copy(v2);
47
+ }
48
+
49
+ if (this.affectPosition) utils.setWorldPosition(this.gameObject, pos);
22
50
 
23
- const euler = new THREE.Euler(this.rotationOffset.x, this.rotationOffset.y, this.rotationOffset.z);
24
- const quat = new THREE.Quaternion().setFromEuler(euler);
51
+ const euler = new Euler(this.rotationOffset.x, this.rotationOffset.y, this.rotationOffset.z);
52
+ const quat = new Quaternion().setFromEuler(euler);
25
53
  if(this.affectRotation) utils.setWorldQuaternion(this.gameObject, rot.multiply(quat));
26
54
 
27
- let lookDirection = new THREE.Vector3();
55
+ let lookDirection = new Vector3();
28
56
  this.from.getWorldDirection(lookDirection).multiplyScalar(50);
29
57
  if (this.levelLookDirection) lookDirection.y = 0;
30
58
  if (this.alignLookDirection) this.gameObject.lookAt(lookDirection);
@@ -171,17 +171,6 @@ export class Renderer extends Behaviour {
171
171
  return lm !== null && lm !== undefined;
172
172
  }
173
173
 
174
-
175
- get activeSelf(): boolean {
176
- return this.enabled;
177
- }
178
-
179
- set activeSelf(val: boolean | number) {
180
- const target = typeof val === "number" ? val > 0.5 : val;
181
- this.enabled = target;
182
- if (target) this.gameObject.visible = true;
183
- }
184
-
185
174
  awake() {
186
175
  const type = this.gameObject.type;
187
176
  if (type === "Group") {
@@ -45,7 +45,7 @@ export class SmoothFollow extends Behaviour {
45
45
  if (this.rotateFactor > 0) {
46
46
  const wr = utils.getWorldQuaternion(this.target);
47
47
  if(this.flipForward){
48
- wr.multiply(SmoothFollow._invertForward);
48
+ wr.premultiply(SmoothFollow._invertForward);
49
49
  }
50
50
  const frot = this._firstUpdate || hard ? 1 : Mathf.clamp01(this.context.time.deltaTime * this.rotateFactor);
51
51
  this.worldQuaternion = this.worldQuaternion.slerp(wr, frot);
@@ -4,21 +4,22 @@ import * as THREE from "three";
4
4
  import { OrbitControls } from "./OrbitControls";
5
5
  import { WebXR, WebXREvent } from "./WebXR";
6
6
  import { AvatarMarker } from "./WebXRAvatar";
7
- import { XRFlag, XRStateFlag } from "./XRFlag";
7
+ import { XRStateFlag } from "./XRFlag";
8
8
  import { SmoothFollow } from "./SmoothFollow";
9
- import { setWorldPosition, setWorldQuaternion, getWorldPosition, getWorldQuaternion } from "../engine/engine_three_utils";
9
+ import { setWorldPosition, setWorldQuaternion, getWorldPosition, getWorldQuaternion, lookAtInverse } from "../engine/engine_three_utils";
10
+ import { ArrayCamera } from "three";
11
+ import { KeyCode } from "../engine/engine_input";
10
12
 
11
13
  export class SpectatorCamera extends Behaviour {
12
14
 
13
15
  cam: Camera | null = null;
14
16
 
15
- private _firstPersonMode: boolean | undefined = false;
17
+ private _firstPersonMode: boolean | undefined = true;
16
18
  get firstPersonMode(): boolean {
17
- return true;
18
19
  return this._firstPersonMode ?? false;
19
20
  }
20
- set firstPersonMode(_val: boolean) {
21
- // this._firstPersonMode = val;
21
+ set firstPersonMode(val: boolean) {
22
+ this._firstPersonMode = val;
22
23
  // if (this._firstPersonMode) this.enableFirstPersonMode();
23
24
  // else this.enableThirdPersonMode();
24
25
  }
@@ -62,7 +63,6 @@ export class SpectatorCamera extends Behaviour {
62
63
  private orbit: OrbitControls | null = null;
63
64
  private firstPersonFollow: SmoothFollow | null = null;
64
65
  private spectatorUIDomElement: HTMLElement | null = null;
65
- private _avatar: AvatarMarker | null = null;
66
66
 
67
67
  private eventSub_WebXRRequestStartEvent: Function | null = null;
68
68
  private eventSub_WebXRStartEvent: Function | null = null;
@@ -72,14 +72,6 @@ export class SpectatorCamera extends Behaviour {
72
72
 
73
73
  GameObject.setActive(this.gameObject, false);
74
74
 
75
- const uiQuery = "#spectator-camera-ui";
76
- this.spectatorUIDomElement = this.context.domElement.querySelector(uiQuery);
77
- if (!this.spectatorUIDomElement) {
78
- console.warn("Could not find spectator camera UI element", uiQuery);
79
- }
80
- this.spectatorUIDomElement?.classList.add("hidden");
81
-
82
-
83
75
  if (!this.isSupportedPlatform()) {
84
76
  console.log("Disable spectator cam", window.navigator.userAgent, this);
85
77
  return;
@@ -93,6 +85,22 @@ export class SpectatorCamera extends Behaviour {
93
85
  // this.cam = GameObject.addNewComponent(this.gameObject, Camera) as Camera;
94
86
  }
95
87
 
88
+ const uiQuery = "#spectator-camera-ui";
89
+ this.spectatorUIDomElement = this.context.domElement.querySelector(uiQuery);
90
+ if (!this.spectatorUIDomElement) {
91
+ console.warn("Could not find spectator camera UI element", uiQuery);
92
+ // this.spectatorUIDomElement = document.createElement("div");
93
+ // this.spectatorUIDomElement.id = "spectator-camera-ui";
94
+ // this.spectatorUIDomElement.classList.add("desktop");
95
+ // this.context.domElement.appendChild(this.spectatorUIDomElement);
96
+
97
+ // const toggle = document.createElement("button");
98
+ // toggle.id = "toggle-spectator-view";
99
+ // this.spectatorUIDomElement.appendChild(toggle);
100
+ }
101
+ this.spectatorUIDomElement?.classList.add("hidden");
102
+
103
+
96
104
  if (this.cam) {
97
105
  this.cam.enabled = true;
98
106
  this._orbitStartPos.copy(getWorldPosition(this.cam.cam));
@@ -189,53 +197,37 @@ export class SpectatorCamera extends Behaviour {
189
197
  private onXRSessionStart(_evt) {
190
198
  this._sessionHasStarted = true;
191
199
  this.updateUI();
200
+
201
+ if (this.context.mainCamera) {
202
+ const cam = this.context.renderer.xr.getCamera(this.context.mainCamera) as ArrayCamera;
203
+ this.setupFollowMode(cam);
204
+ }
192
205
  }
193
206
 
194
207
  private onXRSessionEnded(_evt) {
195
208
  this._sessionHasStarted = false;
209
+ this._firstPersonIsSetup = false;
196
210
  this.spectatorUIDomElement?.classList.add("hidden");
197
211
  GameObject.setActive(this.gameObject, false);
198
212
  }
199
213
 
200
214
  private _sessionHasStarted: boolean = false;
201
- private _isFirstStart = true;
202
215
  private _firstPersonIsSetup: boolean = false;
203
216
  private _orbitStartPos: THREE.Vector3 = new THREE.Vector3();
204
217
  private _orbitStartRot: THREE.Quaternion = new THREE.Quaternion();
205
218
  private _orbitStartPos2: THREE.Vector3 = new THREE.Vector3();
206
219
  private _orbitStartRot2: THREE.Quaternion = new THREE.Quaternion();
207
220
 
208
-
209
221
  // TODO: only show Spectator cam for DesktopVR;
210
222
  // don't show for AR, don't show on Quest
211
223
  // TODO: properly align cameras on enter/exit VR, seems currently spectator cam breaks alignment
212
224
  onAfterRender(): void {
213
225
  if (!this.cam) return;
214
226
 
215
- // if (this.context.time.frameCount % 120 === 0) {
216
- // this.firstPersonMode = !this.firstPersonMode;
217
- // }
218
-
219
- if (this.firstPersonMode) {
220
- if (!this._firstPersonIsSetup) {
221
- if (!this._avatar || this._avatar?.destroyed) {
222
- for (const av of AvatarMarker.instances) {
223
- if (av.avatar && "isLocalAvatar" in av.avatar && av.avatar?.isLocalAvatar) {
224
- this._avatar = av;
225
- const head = av.avatar.head;
226
- if (!head) continue;
227
- this.setupFollowMode(head);
228
- }
229
- }
230
- }
231
- // else {
232
- // if (this.context.mainCamera) {
233
- // this.setupFollowMode(this.context.mainCamera, true);
234
- // }
235
- // }
236
- }
237
- }
238
-
227
+ if(this.context.input.isKeyDown(KeyCode.KEY_S))
228
+ this.firstPersonMode = !this.firstPersonMode;
229
+
230
+ this.updateFollowSettings();
239
231
 
240
232
  const renderer = this.context.renderer;
241
233
  const xrWasEnabled = renderer.xr.enabled;
@@ -295,20 +287,22 @@ export class SpectatorCamera extends Behaviour {
295
287
  this.resetAvatarFlags();
296
288
  }
297
289
 
298
- private setupFollowMode(object: THREE.Object3D, flipForward: boolean = true) {
290
+ private setupFollowMode(object: THREE.Object3D) {
299
291
  if (!object) return;
300
292
  if (!this.cam) return;
301
293
  if (this._firstPersonIsSetup) return;
302
294
  this._firstPersonIsSetup = true;
303
295
 
304
- const target = object.add(new THREE.Object3D());
305
- target.add(new THREE.AxesHelper());
296
+
306
297
  this.firstPersonFollow = GameObject.addNewComponent(this.cam.gameObject, SmoothFollow);
298
+
299
+ const target = new THREE.Object3D();
300
+ object.add(target);
301
+ target.add(new THREE.AxesHelper(.2))
307
302
  this.firstPersonFollow.target = target;
308
- this.firstPersonFollow.followFactor = 12;
309
- this.firstPersonFollow.rotateFactor = 5;
310
- // TODO: figure out why this needs flipping when we use the spectator cam without an Avatar
311
- if (flipForward) this.firstPersonFollow.flipForward = true;
303
+
304
+ this.updateFollowSettings();
305
+
312
306
  const perspectiveCamera = this.context.mainCamera as THREE.PerspectiveCamera;
313
307
  if (perspectiveCamera) {
314
308
  this.cam.cam.near = perspectiveCamera.near;
@@ -318,6 +312,26 @@ export class SpectatorCamera extends Behaviour {
318
312
  if (this.orbit) this.orbit.enabled = false;
319
313
  }
320
314
 
315
+ private updateFollowSettings() {
316
+ const target = this.firstPersonFollow?.target;
317
+ if (!target || !this.firstPersonFollow) return;
318
+ if (this.firstPersonMode === false) {
319
+ this.firstPersonFollow.followFactor = 3;
320
+ this.firstPersonFollow.rotateFactor = 2;
321
+ this.firstPersonFollow.flipForward = false;
322
+ target.position.set(0, .5, 1.5);
323
+ target.quaternion.identity();
324
+ // lookAtInverse(target, new THREE.Vector3(0, 0, 0));
325
+ }
326
+ else {
327
+ target.position.set(0, 0, 0);
328
+ target.quaternion.identity();
329
+ this.firstPersonFollow.followFactor = 12;
330
+ this.firstPersonFollow.rotateFactor = 5;
331
+ this.firstPersonFollow.flipForward = false;
332
+ }
333
+ }
334
+
321
335
  private setAvatarFlagsBeforeRender() {
322
336
  for (const av of AvatarMarker.instances) {
323
337
  if (av.avatar && "isLocalAvatar" in av.avatar) {
@@ -149,6 +149,9 @@ export class WebXR extends Behaviour {
149
149
  return this._vrButton;
150
150
  }
151
151
 
152
+ public get IsInVR() { return this._isInVR; }
153
+ public get IsInAR() { return this._isInAR; }
154
+
152
155
  private rig!: THREE.Object3D;
153
156
  private isInit: boolean = false;
154
157
 
@@ -249,6 +249,7 @@ export class WebXRController extends Behaviour {
249
249
  this.webXR.Rig?.add(this.controller);
250
250
  this.webXR.Rig?.add(this.raycastLine);
251
251
  this.raycastLine?.add(this._raycastHitPoint);
252
+ this._raycastHitPoint.visible = false;
252
253
  this.hand.add(this.handPointerModel);
253
254
  console.log("ADDED TO RIG", this.webXR.Rig);
254
255
 
@@ -19,8 +19,10 @@ export class Avatar_MouthShapes extends Behaviour {
19
19
  private mouthChangeLength = 0;
20
20
 
21
21
  awake(): void {
22
- this.voip = GameObject.findObjectOfType(Voip, this.context);
23
- console.log(this);
22
+ setTimeout(()=>{
23
+ this.voip = GameObject.findObjectOfType(Voip, this.context);
24
+ if (!this.marker) this.marker = GameObject.getComponentInParent(this.gameObject, AvatarMarker);
25
+ }, 3000)
24
26
  }
25
27
 
26
28
  update(): void {
@@ -29,16 +31,16 @@ export class Avatar_MouthShapes extends Behaviour {
29
31
  let id = this.marker?.connectionId ?? null;
30
32
  if (!id) {
31
33
  if (debug) id = null;
34
+ return;
32
35
  }
33
- const freq = this.voip.getFrequency(id);
34
- if (freq == null) return;
36
+ const freq = this.voip.getFrequency(id) ?? 0;
35
37
  this.updateLips(freq);
36
38
  }
37
39
 
38
40
  private updateLips(frequency: number) {
39
41
  if (this.context.time.time - this.lastMouthChangeTime > this.mouthChangeLength) {
40
42
  this.mouthChangeLength = .05 + Math.random() * .1;
41
- if (this.talking && this.talking.length > 0 && frequency > 50) {
43
+ if (this.talking && this.talking.length > 0 && frequency > 30) {
42
44
  this.lastMouthChangeTime = this.context.time.time;
43
45
  const index = Math.floor(Math.random() * this.talking.length);
44
46
  this.setMouthShapeActive(this.talking, index);
@@ -55,14 +57,13 @@ export class Avatar_MouthShapes extends Behaviour {
55
57
  if (!arr) return;
56
58
 
57
59
  // hide other
58
- if(arr != this.idle) this.idle.map(i => i.visible = false);
60
+ if (arr != this.idle) this.idle.map(i => i.visible = false);
59
61
  else this.talking.map(i => i.visible = false);
60
62
 
61
63
  for (let i = 0; i < arr.length; i++) {
62
64
  const shape = arr[i];
63
65
  if (shape) {
64
66
  shape.visible = i === index;
65
- shape.scale.set(1,1,1);
66
67
  }
67
68
  }
68
69
  }
@@ -39,7 +39,6 @@ export { Light } from "../Light";
39
39
  export { LODModel } from "../LODGroup";
40
40
  export { LODGroup } from "../LODGroup";
41
41
  export { LookAtConstraint } from "../LookAtConstraint";
42
- export { MeshCollider } from "../MeshCollider";
43
42
  export { NavMesh } from "../NavMesh";
44
43
  export { NavMeshAgent } from "../NavMesh";
45
44
  export { NestedGltf } from "../NestedGltf";
@@ -4,6 +4,8 @@ import { Color } from "three";
4
4
  export class RGBAColor extends Color {
5
5
  alpha: number = 1;
6
6
 
7
+ get isRGBAColor() { return true; }
8
+
7
9
  constructor(r: number, g: number, b: number, a: number) {
8
10
  super(r, g, b);
9
11
  this.alpha = a;
@@ -17,8 +19,10 @@ export class RGBAColor extends Color {
17
19
 
18
20
  copy(col : RGBAColor | Color){
19
21
  super.copy(col);
20
- if("alpha" in col && typeof col.alpha === "number") this.alpha = col.alpha;
21
- if(typeof col["a"] === "number") this.alpha = col["a"];
22
+ if("alpha" in col && typeof col.alpha === "number") {
23
+ this.alpha = col.alpha;
24
+ }
25
+ else if(typeof col["a"] === "number") this.alpha = col["a"];
22
26
  return this;
23
27
  }
24
28