@@ -182,6 +182,7 @@
|
|
182
182
|
});
|
183
183
|
this.plane = new Mesh(planeGeometry, planeMaterial);
|
184
184
|
this.plane.scale.y = - 1;
|
185
|
+
this.plane.layers.set(2);
|
185
186
|
this.gameObject.add(this.plane);
|
186
187
|
|
187
188
|
if (this.plane) this.plane.renderOrder = 1;
|
@@ -195,6 +196,7 @@
|
|
195
196
|
// .rotateX(Math.PI)
|
196
197
|
.translateY(-0.0001);
|
197
198
|
this.occluderMesh.renderOrder = -100;
|
199
|
+
this.occluderMesh.layers.set(2);
|
198
200
|
this.gameObject.add(this.occluderMesh);
|
199
201
|
|
200
202
|
// the plane onto which to blur the texture
|
@@ -26,7 +26,10 @@
|
|
26
26
|
}
|
27
27
|
|
28
28
|
|
29
|
-
|
29
|
+
/**
|
30
|
+
* Options for the {@link screenshot2} function.
|
31
|
+
*/
|
32
|
+
export declare type ScreenshotOptions = {
|
30
33
|
/**
|
31
34
|
* The context to take the screenshot from. If not provided, the current context will be used.
|
32
35
|
*/
|
@@ -35,6 +35,7 @@
|
|
35
35
|
private static active: InputField | null = null;
|
36
36
|
private static activeTime: number = -1;
|
37
37
|
private static htmlField: HTMLInputElement | null = null;
|
38
|
+
private static htmlFieldFocused: boolean = false;
|
38
39
|
|
39
40
|
private inputEventFn: any;
|
40
41
|
private _iosEventFn: any;
|
@@ -56,6 +57,8 @@
|
|
56
57
|
InputField.htmlField.style.caretColor = "transparent";
|
57
58
|
InputField.htmlField.style.outline = "none";
|
58
59
|
InputField.htmlField.classList.add("ar");
|
60
|
+
InputField.htmlField.onfocus = () => InputField.htmlFieldFocused = true;
|
61
|
+
InputField.htmlField.onblur = () => InputField.htmlFieldFocused = false;
|
59
62
|
// TODO: instead of this we should add it to the shadowdom?
|
60
63
|
document.body.append(InputField.htmlField);
|
61
64
|
}
|
@@ -70,7 +73,7 @@
|
|
70
73
|
}
|
71
74
|
if (isiOS()) {
|
72
75
|
this._iosEventFn = this.processInputOniOS.bind(this);
|
73
|
-
window.addEventListener("
|
76
|
+
window.addEventListener("click", this._iosEventFn);
|
74
77
|
}
|
75
78
|
}
|
76
79
|
|
@@ -80,7 +83,7 @@
|
|
80
83
|
// InputField.htmlField?.removeEventListener("change", this.inputEventFn);
|
81
84
|
this.onDeselected();
|
82
85
|
if (this._iosEventFn) {
|
83
|
-
window.removeEventListener("
|
86
|
+
window.removeEventListener("click", this._iosEventFn);
|
84
87
|
}
|
85
88
|
}
|
86
89
|
|
@@ -118,7 +121,7 @@
|
|
118
121
|
private *activeLoop() {
|
119
122
|
this.onSelected();
|
120
123
|
while (InputField.active === this) {
|
121
|
-
if (this.context.input.
|
124
|
+
if (this.context.input.getPointerClicked(0)) {
|
122
125
|
if (this.context.time.time - InputField.activeTime > 0.2) {
|
123
126
|
break;
|
124
127
|
}
|
@@ -227,8 +230,14 @@
|
|
227
230
|
|
228
231
|
private selectInputField() {
|
229
232
|
if (InputField.htmlField) {
|
233
|
+
if (debug) console.log("Focus Inputfield", InputField.htmlFieldFocused)
|
230
234
|
InputField.htmlField.setSelectionRange(InputField.htmlField.value.length, InputField.htmlField.value.length);
|
231
|
-
|
235
|
+
if (isiOS())
|
236
|
+
InputField.htmlField.focus({ preventScroll: true });
|
237
|
+
else {
|
238
|
+
// on Andoid if we don't focus in a timeout the keyboard will close the second time we click the InputField
|
239
|
+
setTimeout(() => InputField.htmlField?.focus(), 1);
|
240
|
+
}
|
232
241
|
}
|
233
242
|
}
|
234
243
|
|
@@ -363,7 +363,9 @@
|
|
363
363
|
if (this.clickBackgroundToFitScene <= 1 || dt < this.clickBackgroundToFitScene * .15) {
|
364
364
|
this._clickOnBackgroundCount += 1;
|
365
365
|
if (this._clickOnBackgroundCount >= this.clickBackgroundToFitScene - 1)
|
366
|
-
this.fitCamera(this.context.scene.children,
|
366
|
+
this.fitCamera(this.context.scene.children, {
|
367
|
+
immediate: false
|
368
|
+
});
|
367
369
|
}
|
368
370
|
else {
|
369
371
|
this._clickOnBackgroundCount = 0;
|
@@ -677,19 +679,31 @@
|
|
677
679
|
// Slower but better implementation that takes bones and exact vertex positions into account: https://github.com/google/model-viewer/blob/04e900c5027de8c5306fe1fe9627707f42811b05/packages/model-viewer/src/three-components/ModelScene.ts#L321
|
678
680
|
/** Fits the camera to show the objects provided (defaults to the scene if no objects are passed in)
|
679
681
|
* @param objects The objects to fit the camera to - defaults to the scene if not provided
|
680
|
-
* @param
|
681
|
-
* @param
|
682
|
+
* @param options Options for fitting the camera
|
683
|
+
* @param options.fitOffset A factor to multiply the distance to the objects by. Default is 1.1
|
684
|
+
* @param options.immediate If true the camera will move immediately to the new position, otherwise it will lerp
|
685
|
+
* @param options.centerCamera If true the camera will move to a point in the center of the objects in the y axis
|
682
686
|
*/
|
683
|
-
fitCamera(objects?: Array<Object3D>,
|
687
|
+
fitCamera(objects?: Array<Object3D>, options: {
|
688
|
+
fitOffset?: number,
|
689
|
+
immediate?: boolean,
|
690
|
+
centerCamera?: "none" | "y"
|
691
|
+
} = {
|
692
|
+
fitOffset: undefined,
|
693
|
+
immediate: true
|
694
|
+
}) {
|
684
695
|
|
685
696
|
if (this.context.isInXR) {
|
686
697
|
// camera fitting in XR is not supported
|
687
698
|
return;
|
688
699
|
}
|
689
700
|
|
701
|
+
let { fitOffset, immediate, centerCamera } = options;
|
702
|
+
|
690
703
|
if (fitOffset == undefined) {
|
691
704
|
fitOffset = 1.1;
|
692
705
|
}
|
706
|
+
|
693
707
|
const camera = this._cameraObject as PerspectiveCamera;
|
694
708
|
const controls = this._controls as ThreeOrbitControls | null;
|
695
709
|
if (!objects?.length) objects = this.context.scene.children;
|
@@ -760,10 +774,12 @@
|
|
760
774
|
const cameraWp = getWorldPosition(camera);
|
761
775
|
const direction = center.clone();
|
762
776
|
direction.sub(cameraWp);
|
763
|
-
|
777
|
+
if (centerCamera === "y")
|
778
|
+
direction.y = 0;
|
764
779
|
direction.normalize();
|
765
780
|
direction.multiplyScalar(distance);
|
766
|
-
|
781
|
+
if (centerCamera === "y")
|
782
|
+
direction.y += -verticalOffset * 4 * size.y;
|
767
783
|
|
768
784
|
if (camera.parent) {
|
769
785
|
const cameraLocalPosition = camera.parent!.worldToLocal(center.clone().sub(direction));
|
@@ -785,7 +801,7 @@
|
|
785
801
|
this._haveAttachedKeyboardEvents = true;
|
786
802
|
document.body.addEventListener("keydown", (e) => {
|
787
803
|
if (e.code === "KeyF") {
|
788
|
-
this.fitCamera(objects, fitOffset, immediate);
|
804
|
+
this.fitCamera(objects, { fitOffset, immediate });
|
789
805
|
}
|
790
806
|
});
|
791
807
|
}
|
@@ -69,6 +69,9 @@
|
|
69
69
|
console.warn("ShadowCatcher: no mesh to apply shadow catching to. Groups are currently not supported.");
|
70
70
|
return;
|
71
71
|
}
|
72
|
+
|
73
|
+
// Shadowcatcher mesh isnt raycastable
|
74
|
+
this.targetMesh.layers.set(2);
|
72
75
|
|
73
76
|
switch (this.mode) {
|
74
77
|
case ShadowMode.ShadowMask:
|
@@ -17,14 +17,36 @@
|
|
17
17
|
|
18
18
|
const invertForwardMatrix = new Matrix4().makeRotationY(Math.PI);
|
19
19
|
|
20
|
-
// TODO: webarsessionroot needs to place the rig (and not itself)
|
21
20
|
|
22
21
|
/**
|
23
22
|
* The WebARSessionRoot is the root object for a WebAR session and used to place the scene in AR.
|
24
23
|
* It is also responsible for scaling the user in AR and optionally creating a XR anchor for the scene placement.
|
24
|
+
* @example
|
25
|
+
* ```ts
|
26
|
+
* WebARSessionRoot.onPlaced((args) => {
|
27
|
+
* console.log("Scene has been placed in AR");
|
28
|
+
* });
|
29
|
+
* ```
|
25
30
|
*/
|
26
31
|
export class WebARSessionRoot extends Behaviour {
|
27
32
|
|
33
|
+
private static _eventListeners: { [key: string]: Array<(args: { instance: WebARSessionRoot }) => void> } = {};
|
34
|
+
/**
|
35
|
+
* Event that is called when the scene has been placed in AR.
|
36
|
+
* @param cb the callback that is called when the scene has been placed
|
37
|
+
* @returns a function to remove the event listener
|
38
|
+
*/
|
39
|
+
static onPlaced(cb: (args: { instance: WebARSessionRoot }) => void) {
|
40
|
+
const event = "placed";
|
41
|
+
if (!this._eventListeners[event]) this._eventListeners[event] = [];
|
42
|
+
this._eventListeners[event].push(cb);
|
43
|
+
return () => {
|
44
|
+
const index = this._eventListeners[event].indexOf(cb);
|
45
|
+
if (index >= 0) this._eventListeners[event].splice(index, 1);
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
|
28
50
|
/** The scale of a user in AR:
|
29
51
|
* Note: a large value makes the scene appear smaller
|
30
52
|
* @default 1
|