@needle-tools/engine 2.29.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.
- package/CHANGELOG.md +9 -0
- package/dist/needle-engine.d.ts +44 -8
- package/dist/needle-engine.js +347 -343
- package/dist/needle-engine.js.map +4 -4
- package/dist/needle-engine.min.js +20 -16
- package/dist/needle-engine.min.js.map +4 -4
- package/lib/engine/engine.d.ts +1 -0
- package/lib/engine/engine_serialization.d.ts +1 -0
- package/lib/engine/engine_serialization.js +1 -0
- package/lib/engine/engine_serialization.js.map +1 -1
- package/lib/engine/engine_setup.d.ts +5 -0
- package/lib/engine/engine_setup.js +6 -0
- package/lib/engine/engine_setup.js.map +1 -1
- package/lib/engine/engine_utils.d.ts +1 -1
- package/lib/engine/engine_utils.js +25 -8
- package/lib/engine/engine_utils.js.map +1 -1
- package/lib/engine/extensions/NEEDLE_deferred_texture.d.ts +1 -1
- package/lib/engine/extensions/NEEDLE_deferred_texture.js +26 -14
- package/lib/engine/extensions/NEEDLE_deferred_texture.js.map +1 -1
- package/lib/engine/extensions/extension_utils.js +24 -13
- package/lib/engine/extensions/extension_utils.js.map +1 -1
- package/lib/engine/extensions/extensions.js +3 -1
- package/lib/engine/extensions/extensions.js.map +1 -1
- package/lib/engine-components/Camera.js +7 -0
- package/lib/engine-components/Camera.js.map +1 -1
- package/lib/engine-components/OrbitControls.js +2 -1
- package/lib/engine-components/OrbitControls.js.map +1 -1
- package/lib/engine-components/Renderer.d.ts +1 -0
- package/lib/engine-components/Renderer.js +10 -3
- package/lib/engine-components/Renderer.js.map +1 -1
- package/lib/engine-components/ScreenCapture.d.ts +1 -0
- package/lib/engine-components/ScreenCapture.js +265 -1
- package/lib/engine-components/ScreenCapture.js.map +1 -1
- package/lib/engine-components/SpectatorCamera.js +29 -5
- package/lib/engine-components/SpectatorCamera.js.map +1 -1
- package/lib/engine-components/SyncedRoom.js +2 -0
- package/lib/engine-components/SyncedRoom.js.map +1 -1
- package/lib/engine-components/VideoPlayer.d.ts +10 -1
- package/lib/engine-components/VideoPlayer.js +64 -15
- package/lib/engine-components/VideoPlayer.js.map +1 -1
- package/lib/engine-components/Volume.d.ts +4 -0
- package/lib/engine-components/Volume.js +44 -3
- package/lib/engine-components/Volume.js.map +1 -1
- package/lib/engine-components/WebARSessionRoot.d.ts +9 -2
- package/lib/engine-components/WebARSessionRoot.js +69 -24
- package/lib/engine-components/WebARSessionRoot.js.map +1 -1
- package/lib/engine-components/WebXR.d.ts +6 -3
- package/lib/engine-components/WebXR.js +42 -7
- package/lib/engine-components/WebXR.js.map +1 -1
- package/lib/engine-components/WebXRAvatar.js +4 -0
- package/lib/engine-components/WebXRAvatar.js.map +1 -1
- package/lib/engine-components/WebXRController.js +13 -7
- package/lib/engine-components/WebXRController.js.map +1 -1
- package/lib/engine-components/ui/CanvasGroup.d.ts +1 -0
- package/lib/engine-components/ui/CanvasGroup.js +1 -0
- package/lib/engine-components/ui/CanvasGroup.js.map +1 -1
- package/lib/engine-components/ui/EventSystem.js +13 -4
- package/lib/engine-components/ui/EventSystem.js.map +1 -1
- package/lib/engine-components/ui/Graphic.d.ts +1 -0
- package/lib/engine-components/ui/Graphic.js +2 -0
- package/lib/engine-components/ui/Graphic.js.map +1 -1
- package/lib/engine-components/ui/Interfaces.d.ts +2 -0
- package/package.json +2 -2
- package/src/engine/engine_serialization.ts +3 -1
- package/src/engine/engine_setup.ts +6 -0
- package/src/engine/engine_utils.ts +34 -8
- package/src/engine/extensions/NEEDLE_deferred_texture.ts +25 -19
- package/src/engine/extensions/extension_utils.ts +24 -12
- package/src/engine/extensions/extensions.ts +3 -2
- package/src/engine-components/Camera.ts +9 -1
- package/src/engine-components/OrbitControls.ts +2 -1
- package/src/engine-components/Renderer.ts +11 -3
- package/src/engine-components/ScreenCapture.ts +312 -2
- package/src/engine-components/SpectatorCamera.ts +28 -5
- package/src/engine-components/SyncedRoom.ts +1 -0
- package/src/engine-components/VideoPlayer.ts +97 -21
- package/src/engine-components/Volume.ts +47 -4
- package/src/engine-components/WebARSessionRoot.ts +78 -28
- package/src/engine-components/WebXR.ts +50 -15
- package/src/engine-components/WebXRAvatar.ts +5 -0
- package/src/engine-components/WebXRController.ts +20 -14
- package/src/engine-components/ui/CanvasGroup.ts +2 -0
- package/src/engine-components/ui/EventSystem.ts +21 -15
- package/src/engine-components/ui/Graphic.ts +3 -0
- package/src/engine-components/ui/Interfaces.ts +2 -0
|
@@ -2,9 +2,10 @@ import { Behaviour } from "./Component";
|
|
|
2
2
|
import * as THREE from "three";
|
|
3
3
|
import { Material } from "material/Material";
|
|
4
4
|
import { serializeable } from "../engine/engine_serialization_decorator";
|
|
5
|
-
import { Object3D } from "three";
|
|
5
|
+
import { LinearFilter, Object3D, Texture } from "three";
|
|
6
6
|
import { awaitInput } from "../engine/engine_input_utils";
|
|
7
7
|
import { getParam } from "../engine/engine_utils";
|
|
8
|
+
import { Renderer } from "./Renderer";
|
|
8
9
|
|
|
9
10
|
const debug = getParam("debugvideo");
|
|
10
11
|
|
|
@@ -20,22 +21,17 @@ export enum VideoSource {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export enum VideoAudioOutputMode {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
Direct,
|
|
35
|
-
/// <summary>
|
|
36
|
-
/// <para>Send the embedded audio to the associated AudioSampleProvider.</para>
|
|
37
|
-
/// </summary>
|
|
38
|
-
APIOnly,
|
|
24
|
+
None = 0,
|
|
25
|
+
AudioSource = 1,
|
|
26
|
+
Direct = 2,
|
|
27
|
+
APIOnly = 3,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export enum VideoRenderMode {
|
|
31
|
+
CameraFarPlane = 0,
|
|
32
|
+
CameraNearPlane = 1,
|
|
33
|
+
RenderTexture = 2,
|
|
34
|
+
MaterialOverride = 3,
|
|
39
35
|
}
|
|
40
36
|
|
|
41
37
|
export class VideoPlayer extends Behaviour {
|
|
@@ -48,7 +44,16 @@ export class VideoPlayer extends Behaviour {
|
|
|
48
44
|
playOnEnable?: boolean;
|
|
49
45
|
|
|
50
46
|
@serializeable()
|
|
51
|
-
|
|
47
|
+
private renderMode?: VideoRenderMode;
|
|
48
|
+
|
|
49
|
+
@serializeable()
|
|
50
|
+
private targetMaterialProperty?: string;
|
|
51
|
+
|
|
52
|
+
@serializeable(Renderer)
|
|
53
|
+
private targetMaterialRenderer?: Renderer;
|
|
54
|
+
|
|
55
|
+
@serializeable(Texture)
|
|
56
|
+
private targetTexture?: Texture;
|
|
52
57
|
|
|
53
58
|
@serializeable()
|
|
54
59
|
private time: number = 0;
|
|
@@ -250,11 +255,26 @@ export class VideoPlayer extends Behaviour {
|
|
|
250
255
|
if (!this.enabled) return;
|
|
251
256
|
if (!this.videoElement) return;
|
|
252
257
|
|
|
253
|
-
|
|
258
|
+
let target: Object3D | undefined = this.gameObject;
|
|
259
|
+
|
|
260
|
+
switch (this.renderMode) {
|
|
261
|
+
case VideoRenderMode.MaterialOverride:
|
|
262
|
+
target = this.targetMaterialRenderer?.gameObject;
|
|
263
|
+
break;
|
|
264
|
+
case VideoRenderMode.RenderTexture:
|
|
265
|
+
console.error("VideoPlayer renderTexture not implemented yet. Please use material override instead");
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (!target) {
|
|
270
|
+
console.error("Missing target for video material renderer", this);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const mat = target["material"];
|
|
254
274
|
if (mat) {
|
|
255
275
|
if (mat !== this.videoMaterial) {
|
|
256
276
|
this.videoMaterial = mat.clone();
|
|
257
|
-
|
|
277
|
+
target["material"] = this.videoMaterial;
|
|
258
278
|
}
|
|
259
279
|
|
|
260
280
|
if (!this.targetMaterialProperty) {
|
|
@@ -305,4 +325,60 @@ export class VideoPlayer extends Behaviour {
|
|
|
305
325
|
this.videoElement.style.visibility = "hidden";
|
|
306
326
|
this.videoElement.style.display = "none";
|
|
307
327
|
}
|
|
308
|
-
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
// class VideoTexture extends Texture {
|
|
334
|
+
|
|
335
|
+
// get isVideoTexture() { return true; }
|
|
336
|
+
|
|
337
|
+
// constructor( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {
|
|
338
|
+
|
|
339
|
+
// super( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );
|
|
340
|
+
|
|
341
|
+
// this.minFilter = minFilter !== undefined ? minFilter : LinearFilter;
|
|
342
|
+
// this.magFilter = magFilter !== undefined ? magFilter : LinearFilter;
|
|
343
|
+
|
|
344
|
+
// this.generateMipmaps = false;
|
|
345
|
+
|
|
346
|
+
// const scope = this;
|
|
347
|
+
|
|
348
|
+
// function updateVideo() {
|
|
349
|
+
|
|
350
|
+
// scope.needsUpdate = true;
|
|
351
|
+
// video.requestVideoFrameCallback( updateVideo );
|
|
352
|
+
|
|
353
|
+
// }
|
|
354
|
+
|
|
355
|
+
// if ( 'requestVideoFrameCallback' in video ) {
|
|
356
|
+
|
|
357
|
+
// video.requestVideoFrameCallback( updateVideo );
|
|
358
|
+
|
|
359
|
+
// }
|
|
360
|
+
|
|
361
|
+
// }
|
|
362
|
+
|
|
363
|
+
// // clone() {
|
|
364
|
+
|
|
365
|
+
// // return new this.constructor( this.image ).copy( this );
|
|
366
|
+
|
|
367
|
+
// // }
|
|
368
|
+
|
|
369
|
+
// update() {
|
|
370
|
+
|
|
371
|
+
// const video = this.image;
|
|
372
|
+
// const hasVideoFrameCallback = 'requestVideoFrameCallback' in video;
|
|
373
|
+
|
|
374
|
+
// if ( hasVideoFrameCallback === false && video.readyState >= video.HAVE_CURRENT_DATA ) {
|
|
375
|
+
|
|
376
|
+
// this.needsUpdate = true;
|
|
377
|
+
|
|
378
|
+
// }
|
|
379
|
+
|
|
380
|
+
// }
|
|
381
|
+
|
|
382
|
+
// }
|
|
383
|
+
|
|
384
|
+
// export { VideoTexture };
|
|
@@ -2,6 +2,9 @@ import { Behaviour } from "./Component";
|
|
|
2
2
|
import { NoToneMapping, LinearToneMapping, ACESFilmicToneMapping, ReinhardToneMapping } from "three";
|
|
3
3
|
import { serializeable } from "../engine/engine_serialization_decorator";
|
|
4
4
|
import { Context } from "../engine/engine_setup";
|
|
5
|
+
import { getParam } from "../engine/engine_utils";
|
|
6
|
+
|
|
7
|
+
const debug = getParam("debugvolume");
|
|
5
8
|
|
|
6
9
|
export enum TonemappingMode {
|
|
7
10
|
None = 0,
|
|
@@ -35,21 +38,41 @@ function resolveComponentType(data) {
|
|
|
35
38
|
return VolumeComponent;
|
|
36
39
|
}
|
|
37
40
|
|
|
41
|
+
const volumeKey = Symbol("volumeprofile");
|
|
42
|
+
|
|
38
43
|
export class VolumeProfile {
|
|
39
44
|
@serializeable([d => resolveComponentType(d), VolumeComponent])
|
|
40
45
|
components?: VolumeComponent[];
|
|
41
46
|
|
|
42
|
-
|
|
43
47
|
apply(context: Context) {
|
|
48
|
+
this.onUpdate(context, false);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
unapply(context: Context) {
|
|
52
|
+
this.onUpdate(context, true);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private onUpdate(context: Context, remove: boolean) {
|
|
44
56
|
if (!this.components) return;
|
|
57
|
+
const renderer = context.renderer;
|
|
58
|
+
const currentProfile = renderer[volumeKey];
|
|
59
|
+
const isActive = currentProfile !== undefined;
|
|
60
|
+
if (remove) {
|
|
61
|
+
// can not remove volume profile that is not active
|
|
62
|
+
if (!isActive) return;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
renderer[volumeKey] = this;
|
|
66
|
+
}
|
|
45
67
|
for (const component of this.components) {
|
|
46
68
|
|
|
47
69
|
if (component instanceof ToneMapping) {
|
|
48
70
|
const tonemapping = component as ToneMapping;
|
|
49
|
-
if (!component.active) {
|
|
71
|
+
if (!component.active || remove) {
|
|
50
72
|
context.renderer.toneMapping = LinearToneMapping;
|
|
51
73
|
continue;
|
|
52
74
|
}
|
|
75
|
+
if (debug) console.log("VOLUME:", TonemappingMode[tonemapping.mode?.value ?? 0]);
|
|
53
76
|
switch (tonemapping.mode?.value ?? 0) {
|
|
54
77
|
case TonemappingMode.None:
|
|
55
78
|
context.renderer.toneMapping = LinearToneMapping;
|
|
@@ -66,9 +89,11 @@ export class VolumeProfile {
|
|
|
66
89
|
const colorAdjustments = component as ColorAdjustments;
|
|
67
90
|
// unity range goes from -15..15
|
|
68
91
|
// three.js range goes from 0..inf
|
|
69
|
-
|
|
92
|
+
if (debug)
|
|
93
|
+
console.log(colorAdjustments.postExposure);
|
|
70
94
|
const exposure = Math.pow(2, colorAdjustments.postExposure?.value ?? 0);
|
|
71
|
-
|
|
95
|
+
const useExposure = colorAdjustments.postExposure?.overrideState && !remove;
|
|
96
|
+
context.renderer.toneMappingExposure = useExposure ? exposure : 1;
|
|
72
97
|
}
|
|
73
98
|
}
|
|
74
99
|
}
|
|
@@ -80,7 +105,25 @@ export class Volume extends Behaviour {
|
|
|
80
105
|
@serializeable(VolumeProfile)
|
|
81
106
|
sharedProfile?: VolumeProfile;
|
|
82
107
|
|
|
108
|
+
awake() {
|
|
109
|
+
if (debug) {
|
|
110
|
+
console.log(this);
|
|
111
|
+
console.log("Press P to toggle post processing");
|
|
112
|
+
window.addEventListener("keydown", (e) => {
|
|
113
|
+
if (e.key === "p") {
|
|
114
|
+
console.log("Toggle volume: " + this.name, !this.enabled);
|
|
115
|
+
this.enabled = !this.enabled;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
83
121
|
onEnable() {
|
|
122
|
+
if (debug) console.log("APPLY VOLUME", this)
|
|
84
123
|
this.sharedProfile?.apply(this.context);
|
|
85
124
|
}
|
|
125
|
+
|
|
126
|
+
onDisable() {
|
|
127
|
+
this.sharedProfile?.unapply(this.context);
|
|
128
|
+
}
|
|
86
129
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Behaviour } from "./Component";
|
|
1
|
+
import { Behaviour, GameObject } from "./Component";
|
|
2
2
|
import * as THREE from 'three'
|
|
3
|
-
import { Object3D, XRPose } from "three";
|
|
4
|
-
import { WebAR } from "./WebXR";
|
|
3
|
+
import { Matrix4, Object3D, XRPose } from "three";
|
|
4
|
+
import { WebAR, WebXR } from "./WebXR";
|
|
5
5
|
import { InstancingUtil } from "./Renderer";
|
|
6
6
|
import { serializeable } from "../engine/engine_serialization_decorator";
|
|
7
7
|
|
|
@@ -11,6 +11,10 @@ export class WebARSessionRoot extends Behaviour {
|
|
|
11
11
|
|
|
12
12
|
webAR: WebAR | null = null;
|
|
13
13
|
|
|
14
|
+
get rig(): THREE.Object3D | undefined {
|
|
15
|
+
return this.webAR?.webxr.Rig;
|
|
16
|
+
}
|
|
17
|
+
|
|
14
18
|
@serializeable()
|
|
15
19
|
invertForward: boolean = false;
|
|
16
20
|
|
|
@@ -24,46 +28,57 @@ export class WebARSessionRoot extends Behaviour {
|
|
|
24
28
|
this.setScale(val);
|
|
25
29
|
}
|
|
26
30
|
|
|
31
|
+
private readonly _initalMatrix = new THREE.Matrix4();
|
|
32
|
+
private readonly _selectStartFn = this.onSelectStart.bind(this);
|
|
33
|
+
private readonly _selectEndFn = this.onSelectEnd.bind(this);
|
|
34
|
+
|
|
35
|
+
start() {
|
|
36
|
+
const xr = GameObject.findObjectOfType(WebXR);
|
|
37
|
+
if (xr) {
|
|
38
|
+
xr.Rig.updateMatrix();
|
|
39
|
+
this._initalMatrix.copy(xr.Rig.matrix);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
27
43
|
private _arScale: number = 5;
|
|
28
44
|
private _rig: THREE.Object3D | null = null;
|
|
29
45
|
private _startPose: THREE.Matrix4 | null = null;
|
|
30
46
|
private _placementPose: THREE.Matrix4 | null = null;
|
|
31
47
|
private _isTouching: boolean = false;
|
|
48
|
+
private _rigStartPose: THREE.Matrix4 | undefined | null = null;
|
|
32
49
|
|
|
33
50
|
onBegin(session: THREE.XRSession) {
|
|
34
51
|
this._placementPose = null;
|
|
35
52
|
this.gameObject.visible = false;
|
|
36
53
|
this.gameObject.matrixAutoUpdate = false;
|
|
37
54
|
this._startPose = this.gameObject.matrix.clone();
|
|
38
|
-
|
|
39
|
-
session.addEventListener('
|
|
40
|
-
|
|
55
|
+
this._rigStartPose = this.rig?.matrix.clone();
|
|
56
|
+
session.addEventListener('selectstart', this._selectStartFn);
|
|
57
|
+
session.addEventListener('selectend', this._selectEndFn);
|
|
58
|
+
// setTimeout(() => this.gameObject.visible = false, 1000); // TODO test on phone AR and Hololens if this was still needed
|
|
59
|
+
|
|
60
|
+
// console.log(this.rig?.position, this.rig?.quaternion, this.rig?.scale);
|
|
61
|
+
this.gameObject.visible = false;
|
|
62
|
+
|
|
63
|
+
if (this.rig) {
|
|
64
|
+
// reset rig to initial pose, this is helping the mix of immersive AR and immersive VR that we now have on quest
|
|
65
|
+
// where the rig can be moved and scaled by the user in VR mode and we use the rig position when entering
|
|
66
|
+
// immersive Ar right now to place the user/offset the session
|
|
67
|
+
this.rig.matrixAutoUpdate = true;
|
|
68
|
+
this._initalMatrix.decompose(this.rig.position, this.rig.quaternion, this.rig.scale);
|
|
69
|
+
}
|
|
41
70
|
}
|
|
42
71
|
|
|
43
|
-
onUpdate(rig: THREE.Object3D | null, _session: THREE.XRSession, pose: XRPose | null | undefined) {
|
|
72
|
+
onUpdate(rig: THREE.Object3D | null, _session: THREE.XRSession, pose: XRPose | null | undefined): boolean {
|
|
44
73
|
|
|
45
74
|
if (pose && !this._placementPose) {
|
|
46
75
|
if (this._isTouching) {
|
|
47
76
|
if (this.webAR) this.webAR.setReticleActive(false);
|
|
48
|
-
this.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (this.invertForward) {
|
|
52
|
-
const rot = new THREE.Matrix4().makeRotationY(Math.PI);
|
|
53
|
-
this._placementPose.premultiply(rot);
|
|
54
|
-
}
|
|
55
|
-
this._rig = rig;
|
|
56
|
-
|
|
57
|
-
this.setScale(this.arScale);
|
|
58
|
-
}
|
|
59
|
-
else this._rig = null;
|
|
60
|
-
// this.gameObject.matrix.copy(this._placementPose);
|
|
61
|
-
// if (rig) {
|
|
62
|
-
// this.gameObject.matrix.premultiply(rig.matrixWorld)
|
|
63
|
-
// }
|
|
64
|
-
this.gameObject.visible = true;
|
|
77
|
+
this.placeAt(rig, new THREE.Matrix4().fromArray(pose.transform.matrix).invert());
|
|
78
|
+
return true;
|
|
65
79
|
}
|
|
66
80
|
}
|
|
81
|
+
return false;
|
|
67
82
|
|
|
68
83
|
// if (this._placementPose) {
|
|
69
84
|
// this.gameObject.matrixAutoUpdate = false;
|
|
@@ -75,16 +90,47 @@ export class WebARSessionRoot extends Behaviour {
|
|
|
75
90
|
// }
|
|
76
91
|
}
|
|
77
92
|
|
|
93
|
+
placeAt(rig: THREE.Object3D | null, mat: Matrix4) {
|
|
94
|
+
if (!this._placementPose) this._placementPose = new THREE.Matrix4();
|
|
95
|
+
this._placementPose.copy(mat);
|
|
96
|
+
if (rig) {
|
|
97
|
+
|
|
98
|
+
if (this.invertForward) {
|
|
99
|
+
const rot = new THREE.Matrix4().makeRotationY(Math.PI);
|
|
100
|
+
this._placementPose.premultiply(rot);
|
|
101
|
+
}
|
|
102
|
+
this._rig = rig;
|
|
103
|
+
|
|
104
|
+
this.setScale(this.arScale);
|
|
105
|
+
}
|
|
106
|
+
else this._rig = null;
|
|
107
|
+
// this.gameObject.matrix.copy(this._placementPose);
|
|
108
|
+
// if (rig) {
|
|
109
|
+
// this.gameObject.matrix.premultiply(rig.matrixWorld)
|
|
110
|
+
// }
|
|
111
|
+
this.gameObject.visible = true;
|
|
112
|
+
}
|
|
113
|
+
|
|
78
114
|
onEnd(rig: THREE.Object3D | null, _session: THREE.XRSession) {
|
|
79
115
|
this._placementPose = null;
|
|
80
116
|
this.gameObject.visible = false;
|
|
81
|
-
|
|
117
|
+
this.gameObject.matrixAutoUpdate = false;
|
|
118
|
+
if (this._startPose) {
|
|
82
119
|
this.gameObject.matrix.copy(this._startPose);
|
|
83
|
-
|
|
84
|
-
if (rig)
|
|
120
|
+
}
|
|
121
|
+
if (rig) {
|
|
122
|
+
rig.matrixAutoUpdate = true;
|
|
123
|
+
if (this._rigStartPose) {
|
|
124
|
+
this._rigStartPose.decompose(rig.position, rig.quaternion, rig.scale);
|
|
125
|
+
// console.log(rig.position, rig.quaternion, rig.scale);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
85
128
|
InstancingUtil.markDirty(this.gameObject, true);
|
|
86
129
|
// HACK to fix physics being not in correct place after exiting AR
|
|
87
|
-
setTimeout(() =>
|
|
130
|
+
setTimeout(() => {
|
|
131
|
+
this.gameObject.matrixAutoUpdate = true;
|
|
132
|
+
this.gameObject.visible = true;
|
|
133
|
+
}, 100);
|
|
88
134
|
}
|
|
89
135
|
|
|
90
136
|
|
|
@@ -101,9 +147,13 @@ export class WebARSessionRoot extends Behaviour {
|
|
|
101
147
|
if (!rig || !this._placementPose) {
|
|
102
148
|
return;
|
|
103
149
|
}
|
|
150
|
+
if (!this._rigStartPose) {
|
|
151
|
+
this._rigStartPose = rig.matrix.clone();
|
|
152
|
+
}
|
|
104
153
|
rig.matrixAutoUpdate = false;
|
|
105
154
|
rig.matrix.multiplyMatrices(new THREE.Matrix4().makeScale(scale, scale, scale), this._placementPose);
|
|
106
155
|
rig.matrix.decompose(rig.position, rig.quaternion, rig.scale);
|
|
107
156
|
rig.updateMatrixWorld();
|
|
157
|
+
console.log("Place", rig.position);
|
|
108
158
|
}
|
|
109
159
|
}
|
|
@@ -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)
|
|
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 = {
|
|
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
|
|
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;
|
|
@@ -361,10 +361,12 @@ export class WebXR extends Behaviour {
|
|
|
361
361
|
|
|
362
362
|
switch (flag) {
|
|
363
363
|
case XRStateFlag.AR:
|
|
364
|
+
this.context.xrSessionMode = XRSessionMode.ImmersiveAR;
|
|
364
365
|
this._isInAR = true;
|
|
365
366
|
this.webAR?.onBegin(session);
|
|
366
367
|
break;
|
|
367
368
|
case XRStateFlag.VR:
|
|
369
|
+
this.context.xrSessionMode = XRSessionMode.ImmersiveVR;
|
|
368
370
|
this._isInVR = true;
|
|
369
371
|
this.onEnterVR(session);
|
|
370
372
|
break;
|
|
@@ -391,6 +393,7 @@ export class WebXR extends Behaviour {
|
|
|
391
393
|
this._isInVR = false;
|
|
392
394
|
this._requestedAR = false;
|
|
393
395
|
this._requestedVR = false;
|
|
396
|
+
this.context.xrSessionMode = undefined;
|
|
394
397
|
|
|
395
398
|
if (this.xrMirrorWindow) {
|
|
396
399
|
this.xrMirrorWindow.close();
|
|
@@ -448,7 +451,10 @@ export class WebXR extends Behaviour {
|
|
|
448
451
|
// not sure if this should be a behaviour.
|
|
449
452
|
// for now we dont really need it to go through the usual update loop
|
|
450
453
|
export class WebAR {
|
|
451
|
-
|
|
454
|
+
|
|
455
|
+
get webxr(): WebXR { return this._webxr; }
|
|
456
|
+
|
|
457
|
+
private _webxr: WebXR;
|
|
452
458
|
|
|
453
459
|
private reticle: THREE.Object3D | null = null;
|
|
454
460
|
private hitTestSource: XRHitTestSource | null = null;
|
|
@@ -468,11 +474,13 @@ export class WebAR {
|
|
|
468
474
|
private get context() { return this.webxr.context; }
|
|
469
475
|
|
|
470
476
|
constructor(webxr: WebXR) {
|
|
471
|
-
this.
|
|
477
|
+
this._webxr = webxr;
|
|
472
478
|
}
|
|
473
479
|
|
|
474
480
|
private arDomOverlay: EngineElement | null = null;
|
|
475
481
|
private arOverlayElement: HTMLElement | null = null;
|
|
482
|
+
private noHitTestAvailable: boolean = false;
|
|
483
|
+
private didPlaceARSessionRoot: boolean = false;
|
|
476
484
|
|
|
477
485
|
getAROverlayContainer(): HTMLElement | null {
|
|
478
486
|
this.arDomOverlay = this.webxr.context.domElement as EngineElement;
|
|
@@ -484,12 +492,14 @@ export class WebAR {
|
|
|
484
492
|
this.reticleActive = active;
|
|
485
493
|
}
|
|
486
494
|
|
|
487
|
-
onBegin(session: THREE.XRSession) {
|
|
495
|
+
async onBegin(session: THREE.XRSession) {
|
|
488
496
|
const context = this.webxr.context;
|
|
489
497
|
this.reticleActive = true;
|
|
498
|
+
this.didPlaceARSessionRoot = false;
|
|
490
499
|
|
|
500
|
+
const deviceType = navigator.userAgent?.includes("OculusBrowser") ? ControllerType.PhysicalDevice : ControllerType.Touch;
|
|
491
501
|
for (let i = 0; i < 4; i++) {
|
|
492
|
-
WebXRController.Create(this.webxr, i, this.webxr.gameObject,
|
|
502
|
+
WebXRController.Create(this.webxr, i, this.webxr.gameObject, deviceType)
|
|
493
503
|
}
|
|
494
504
|
|
|
495
505
|
if (!this.sessionRoot || this.sessionRoot.destroyed || !this.sessionRoot.activeAndEnabled)
|
|
@@ -502,6 +512,9 @@ export class WebAR {
|
|
|
502
512
|
session.requestReferenceSpace('viewer').then((referenceSpace) => {
|
|
503
513
|
session.requestHitTestSource({ space: referenceSpace }).then((source) => {
|
|
504
514
|
this.hitTestSource = source;
|
|
515
|
+
}).catch((_) => {
|
|
516
|
+
this.noHitTestAvailable = true;
|
|
517
|
+
console.warn("WebXR: Hit test not supported");
|
|
505
518
|
});
|
|
506
519
|
});
|
|
507
520
|
|
|
@@ -534,7 +547,7 @@ export class WebAR {
|
|
|
534
547
|
if (this.arDomOverlay && this.arOverlayElement) {
|
|
535
548
|
this.arDomOverlay.onEnterAR(session, this.arOverlayElement);
|
|
536
549
|
}
|
|
537
|
-
|
|
550
|
+
|
|
538
551
|
this.context.mainCameraComponent?.applyClearFlagsIfIsActiveCamera();
|
|
539
552
|
}
|
|
540
553
|
|
|
@@ -552,11 +565,29 @@ export class WebAR {
|
|
|
552
565
|
this.sessionRoot.onEnd(this.webxr.Rig, _session);
|
|
553
566
|
}
|
|
554
567
|
if (this.arDomOverlay) this.arDomOverlay.onExitAR(_session);
|
|
555
|
-
|
|
568
|
+
|
|
556
569
|
this.context.mainCameraComponent?.applyClearFlagsIfIsActiveCamera();
|
|
557
570
|
}
|
|
558
571
|
|
|
559
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
|
+
|
|
560
591
|
if (!this.hitTestSource) return;
|
|
561
592
|
const hitTestResults = frame.getHitTestResults(this.hitTestSource);
|
|
562
593
|
if (hitTestResults.length) {
|
|
@@ -564,7 +595,11 @@ export class WebAR {
|
|
|
564
595
|
const referenceSpace = this.webxr.context.renderer.xr.getReferenceSpace();
|
|
565
596
|
if (referenceSpace) {
|
|
566
597
|
const pose = hit.getPose(referenceSpace);
|
|
567
|
-
|
|
598
|
+
|
|
599
|
+
if (this.sessionRoot) {
|
|
600
|
+
const didPlace = this.sessionRoot.onUpdate(this.webxr.Rig, session, pose);
|
|
601
|
+
this.didPlaceARSessionRoot = didPlace;
|
|
602
|
+
}
|
|
568
603
|
|
|
569
604
|
if (this.reticle) {
|
|
570
605
|
this.reticle.visible = this.reticleActive;
|
|
@@ -587,4 +622,4 @@ export class WebAR {
|
|
|
587
622
|
}
|
|
588
623
|
}
|
|
589
624
|
|
|
590
|
-
|
|
625
|
+
const arPlacementWithoutHitTestMatrix = new THREE.Matrix4().identity().makeTranslation(0, -0.5, 0);
|
|
@@ -11,6 +11,7 @@ import { VRUserState } from "./WebXRSync";
|
|
|
11
11
|
import { getParam } from "../engine/engine_utils";
|
|
12
12
|
import { serializeable } from "../engine/engine_serialization_decorator";
|
|
13
13
|
import { ViewDevice } from "../engine/engine_playerview";
|
|
14
|
+
import { InstancingUtil } from "./Renderer";
|
|
14
15
|
|
|
15
16
|
export const debug = getParam("debugavatar");
|
|
16
17
|
|
|
@@ -168,6 +169,8 @@ export class WebXRAvatar {
|
|
|
168
169
|
// }
|
|
169
170
|
this.context.players.setPlayerView(state.guid, viewObj, device);
|
|
170
171
|
|
|
172
|
+
InstancingUtil.markDirty(this.head);
|
|
173
|
+
|
|
171
174
|
this._canInterpolate = true;
|
|
172
175
|
const ht = this.isLocalAvatar ? this.head : this._headTarget;
|
|
173
176
|
ht.position.set(state.position.x, state.position.y, state.position.z);
|
|
@@ -183,6 +186,7 @@ export class WebXRAvatar {
|
|
|
183
186
|
ht.quaternion.multiply(WebXRAvatar.invertRotation);
|
|
184
187
|
ht.scale.set(state.scale, state.scale, state.scale);
|
|
185
188
|
ht.scale.multiply(this.handLeftScale);
|
|
189
|
+
InstancingUtil.markDirty(this.handLeft);
|
|
186
190
|
}
|
|
187
191
|
|
|
188
192
|
if (this.handRight) {
|
|
@@ -192,6 +196,7 @@ export class WebXRAvatar {
|
|
|
192
196
|
ht.quaternion.multiply(WebXRAvatar.invertRotation);
|
|
193
197
|
ht.scale.set(state.scale, state.scale, state.scale);
|
|
194
198
|
ht.scale.multiply(this.handRightScale);
|
|
199
|
+
InstancingUtil.markDirty(this.handRight);
|
|
195
200
|
}
|
|
196
201
|
}
|
|
197
202
|
}
|