@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.
- package/CHANGELOG.md +13 -0
- package/dist/needle-engine.d.ts +10 -15
- package/dist/needle-engine.js +342 -342
- package/dist/needle-engine.js.map +4 -4
- package/dist/needle-engine.min.js +15 -15
- package/dist/needle-engine.min.js.map +4 -4
- package/lib/engine/engine_setup.d.ts +1 -0
- package/lib/engine/engine_setup.js +2 -1
- package/lib/engine/engine_setup.js.map +1 -1
- package/lib/engine-components/AlignmentConstraint.js +16 -3
- package/lib/engine-components/AlignmentConstraint.js.map +1 -1
- package/lib/engine-components/AvatarLoader.d.ts +0 -3
- package/lib/engine-components/AvatarLoader.js +23 -52
- package/lib/engine-components/AvatarLoader.js.map +1 -1
- package/lib/engine-components/Camera.d.ts +1 -0
- package/lib/engine-components/Camera.js +15 -26
- package/lib/engine-components/Camera.js.map +1 -1
- package/lib/engine-components/Collider.js +6 -0
- package/lib/engine-components/Collider.js.map +1 -1
- package/lib/engine-components/Component.js +10 -0
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/OffsetConstraint.d.ts +3 -0
- package/lib/engine-components/OffsetConstraint.js +47 -12
- package/lib/engine-components/OffsetConstraint.js.map +1 -1
- package/lib/engine-components/Renderer.d.ts +0 -2
- package/lib/engine-components/Renderer.js +0 -9
- package/lib/engine-components/Renderer.js.map +1 -1
- package/lib/engine-components/SmoothFollow.js +1 -1
- package/lib/engine-components/SmoothFollow.js.map +1 -1
- package/lib/engine-components/SpectatorCamera.d.ts +2 -3
- package/lib/engine-components/SpectatorCamera.js +50 -43
- package/lib/engine-components/SpectatorCamera.js.map +1 -1
- package/lib/engine-components/WebXR.d.ts +2 -0
- package/lib/engine-components/WebXR.js +2 -0
- package/lib/engine-components/WebXR.js.map +1 -1
- package/lib/engine-components/WebXRController.js +1 -0
- package/lib/engine-components/WebXRController.js.map +1 -1
- package/lib/engine-components/avatar/Avatar_MouthShapes.js +9 -7
- package/lib/engine-components/avatar/Avatar_MouthShapes.js.map +1 -1
- package/lib/engine-components/codegen/exports.d.ts +0 -1
- package/lib/engine-components/codegen/exports.js +0 -1
- package/lib/engine-components/codegen/exports.js.map +1 -1
- package/lib/engine-components/js-extensions/RGBAColor.d.ts +1 -0
- package/lib/engine-components/js-extensions/RGBAColor.js +4 -2
- package/lib/engine-components/js-extensions/RGBAColor.js.map +1 -1
- package/package.json +1 -1
- package/src/engine/codegen/register_types.js +0 -2
- package/src/engine/engine_setup.ts +2 -1
- package/src/engine-components/AlignmentConstraint.ts +9 -6
- package/src/engine-components/AvatarLoader.ts +23 -58
- package/src/engine-components/Camera.ts +16 -31
- package/src/engine-components/Collider.ts +10 -1
- package/src/engine-components/Component.ts +14 -3
- package/src/engine-components/OffsetConstraint.ts +41 -13
- package/src/engine-components/Renderer.ts +0 -11
- package/src/engine-components/SmoothFollow.ts +1 -1
- package/src/engine-components/SpectatorCamera.ts +62 -48
- package/src/engine-components/WebXR.ts +3 -0
- package/src/engine-components/WebXRController.ts +1 -0
- package/src/engine-components/avatar/Avatar_MouthShapes.ts +8 -7
- package/src/engine-components/codegen/exports.ts +0 -1
- package/src/engine-components/js-extensions/RGBAColor.ts +6 -2
- package/lib/engine-components/MeshCollider.d.ts +0 -4
- package/lib/engine-components/MeshCollider.js +0 -8
- package/lib/engine-components/MeshCollider.js.map +0 -1
- 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
|
-
//
|
|
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
|
-
|
|
165
|
+
const boundsSize = new THREE.Vector3();
|
|
180
166
|
new THREE.Box3().setFromObject(head).getSize(boundsSize);
|
|
181
|
-
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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()
|
|
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.
|
|
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.
|
|
200
|
-
let alpha = this.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
8
|
-
private
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
private
|
|
12
|
-
|
|
13
|
-
private
|
|
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:
|
|
31
|
+
var rot: Quaternion = utils.getWorldQuaternion(this.from);
|
|
20
32
|
|
|
21
|
-
|
|
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
|
|
24
|
-
const quat = new
|
|
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
|
|
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.
|
|
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 {
|
|
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 =
|
|
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(
|
|
21
|
-
|
|
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
|
-
|
|
216
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
309
|
-
this.
|
|
310
|
-
|
|
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
|
-
|
|
23
|
-
|
|
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 >
|
|
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")
|
|
21
|
-
|
|
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
|
|