@shopware-ag/dive 1.16.25 → 1.16.26-beta.1
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/build/dive.cjs +1656 -203
- package/build/dive.cjs.map +1 -1
- package/build/dive.d.cts +50 -8
- package/build/dive.d.ts +50 -8
- package/build/dive.js +1623 -159
- package/build/dive.js.map +1 -1
- package/package.json +1 -1
- package/src/ar/AR.ts +164 -0
- package/src/ar/arquicklook/ARQuickLook.ts +42 -0
- package/src/ar/webxr/WebXR.ts +176 -0
- package/src/ar/webxr/controller/WebXRController.ts +334 -0
- package/src/ar/webxr/crosshair/WebXRCrosshair.ts +35 -0
- package/src/ar/webxr/origin/WebXROrigin.ts +191 -0
- package/src/ar/webxr/overlay/Overlay.ts +50 -0
- package/src/ar/webxr/raycaster/WebXRRaycaster.ts +131 -0
- package/src/ar/webxr/raycaster/ar/WebXRRaycasterAR.ts +102 -0
- package/src/ar/webxr/raycaster/three/WebXRRaycasterTHREE.ts +49 -0
- package/src/ar/webxr/touchscreencontrols/WebXRTouchscreenControls.ts +356 -0
- package/src/axiscamera/AxisCamera.ts +4 -4
- package/src/axiscamera/__test__/AxisCamera.test.ts +4 -0
- package/src/com/Communication.ts +17 -0
- package/src/com/__test__/Communication.test.ts +1 -1
- package/src/com/actions/index.ts +2 -0
- package/src/dive.ts +55 -9
- package/src/events/EventExecutor.ts +35 -0
- package/src/helper/findSceneRecursive/findSceneRecursive.ts +2 -2
- package/src/info/Info.ts +37 -1
- package/src/info/__test__/Info.test.ts +45 -5
- package/src/mediacreator/MediaCreator.ts +4 -4
- package/src/mediacreator/__test__/MediaCreator.test.ts +7 -2
- package/src/renderer/Renderer.ts +21 -11
- package/src/renderer/__test__/Renderer.test.ts +19 -1
- package/src/scene/Scene.ts +35 -12
- package/src/scene/__test__/Scene.test.ts +39 -5
- package/src/scene/root/Root.ts +1 -0
- package/src/scene/xrroot/XRRoot.ts +56 -0
- package/src/scene/xrroot/xrlightroot/XRLightRoot.ts +80 -0
- package/src/toolbox/BaseTool.ts +9 -3
- package/src/toolbox/Toolbox.ts +1 -1
- package/src/toolbox/__test__/Toolbox.test.ts +1 -1
- package/src/toolbox/select/SelectTool.ts +1 -1
- package/src/toolbox/select/__test__/SelectTool.test.ts +1 -1
- package/src/toolbox/transform/TransformTool.ts +4 -4
- package/src/toolbox/transform/__test__/TransformTool.test.ts +2 -4
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Matrix4,
|
|
3
|
+
Mesh,
|
|
4
|
+
Object3D,
|
|
5
|
+
Quaternion,
|
|
6
|
+
Vector3,
|
|
7
|
+
WebXRArrayCamera,
|
|
8
|
+
} from 'three';
|
|
9
|
+
import { DIVERenderer } from '../../../renderer/Renderer';
|
|
10
|
+
import { DIVEScene } from '../../../scene/Scene';
|
|
11
|
+
import { DIVEWebXRCrosshair } from '../crosshair/WebXRCrosshair';
|
|
12
|
+
import { DIVEWebXRRaycaster } from '../raycaster/WebXRRaycaster';
|
|
13
|
+
import { DIVEWebXROrigin } from '../origin/WebXROrigin';
|
|
14
|
+
import {
|
|
15
|
+
DIVETouchscreenEvents,
|
|
16
|
+
DIVEWebXRTouchscreenControls,
|
|
17
|
+
} from '../touchscreencontrols/WebXRTouchscreenControls';
|
|
18
|
+
import { type DIVEMovable } from '../.././../interface/Movable';
|
|
19
|
+
import { findInterface } from '../../../helper/findInterface/findInterface';
|
|
20
|
+
|
|
21
|
+
export class DIVEWebXRController extends Object3D {
|
|
22
|
+
// general members
|
|
23
|
+
private _renderer: DIVERenderer;
|
|
24
|
+
private _scene: DIVEScene;
|
|
25
|
+
private _session: XRSession;
|
|
26
|
+
|
|
27
|
+
private _frameBuffer: XRFrame | null = null;
|
|
28
|
+
|
|
29
|
+
// raycaster members
|
|
30
|
+
private _xrRaycaster: DIVEWebXRRaycaster;
|
|
31
|
+
private _origin: DIVEWebXROrigin;
|
|
32
|
+
|
|
33
|
+
// crosshair
|
|
34
|
+
private _crosshair: DIVEWebXRCrosshair;
|
|
35
|
+
|
|
36
|
+
// controller members
|
|
37
|
+
private _touchscreenControls: DIVEWebXRTouchscreenControls;
|
|
38
|
+
private _handNodeInitialPosition = new Vector3();
|
|
39
|
+
private _xrCamera: WebXRArrayCamera;
|
|
40
|
+
private _placed: boolean = false;
|
|
41
|
+
|
|
42
|
+
// grabbing
|
|
43
|
+
private _grabbedObject: Object3D | null = null;
|
|
44
|
+
private _arHitPosition: Vector3 = new Vector3();
|
|
45
|
+
private _arHitQuaternion: Quaternion = new Quaternion();
|
|
46
|
+
private _arHitScale: Vector3 = new Vector3(1, 1, 1);
|
|
47
|
+
|
|
48
|
+
// grabbing position
|
|
49
|
+
private _initialObjectPosition: Vector3 | null = null;
|
|
50
|
+
private _initialRaycastHit: Vector3 | null = null;
|
|
51
|
+
private _deltaRaycastHit: Vector3 = new Vector3();
|
|
52
|
+
|
|
53
|
+
// grabbing rotation
|
|
54
|
+
private _touchQuaterion: Quaternion = new Quaternion();
|
|
55
|
+
|
|
56
|
+
// grabbing scale
|
|
57
|
+
private _touchScale: number = 1;
|
|
58
|
+
private _scaleThreshold: number = 0.1;
|
|
59
|
+
|
|
60
|
+
constructor(session: XRSession, renderer: DIVERenderer, scene: DIVEScene) {
|
|
61
|
+
super();
|
|
62
|
+
|
|
63
|
+
this._renderer = renderer;
|
|
64
|
+
this._scene = scene;
|
|
65
|
+
this._session = session;
|
|
66
|
+
|
|
67
|
+
this._xrRaycaster = new DIVEWebXRRaycaster(session, renderer, scene);
|
|
68
|
+
this._origin = new DIVEWebXROrigin(this._session, this._renderer, [
|
|
69
|
+
'plane',
|
|
70
|
+
]);
|
|
71
|
+
|
|
72
|
+
this._crosshair = new DIVEWebXRCrosshair();
|
|
73
|
+
this._crosshair.visible = false;
|
|
74
|
+
|
|
75
|
+
this._xrCamera = this._renderer.xr.getCamera();
|
|
76
|
+
|
|
77
|
+
this._scene.XRRoot.XRHandNode.position.set(0, -0.05, -0.25);
|
|
78
|
+
this._handNodeInitialPosition =
|
|
79
|
+
this._scene.XRRoot.XRHandNode.position.clone();
|
|
80
|
+
|
|
81
|
+
this._touchscreenControls = new DIVEWebXRTouchscreenControls(
|
|
82
|
+
this._session,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// translating
|
|
86
|
+
this._touchscreenControls.Subscribe('TOUCH_START', () =>
|
|
87
|
+
this.onTouchStart(),
|
|
88
|
+
);
|
|
89
|
+
this._touchscreenControls.Subscribe('TOUCH_MOVE', () =>
|
|
90
|
+
this.onTouchMove(),
|
|
91
|
+
);
|
|
92
|
+
this._touchscreenControls.Subscribe('TOUCH_END', (p) =>
|
|
93
|
+
this.onTouchEnd(p),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// rotating
|
|
97
|
+
this._touchscreenControls.Subscribe('ROTATE_START', () =>
|
|
98
|
+
this.onRotateStart(),
|
|
99
|
+
);
|
|
100
|
+
this._touchscreenControls.Subscribe('ROTATE_MOVE', (p) =>
|
|
101
|
+
this.onRotateMove(p),
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// scaling
|
|
105
|
+
this._touchscreenControls.Subscribe('PINCH_START', () =>
|
|
106
|
+
this.onPinchStart(),
|
|
107
|
+
);
|
|
108
|
+
this._touchscreenControls.Subscribe('PINCH_MOVE', (p) =>
|
|
109
|
+
this.onPinchMove(p),
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public async Init(): Promise<this> {
|
|
114
|
+
this.prepareScene();
|
|
115
|
+
|
|
116
|
+
await this.initOrigin();
|
|
117
|
+
await this.initRaycaster();
|
|
118
|
+
|
|
119
|
+
return Promise.resolve(this);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
public Dispose(): void {
|
|
123
|
+
this.restoreScene();
|
|
124
|
+
|
|
125
|
+
this._origin.Dispose();
|
|
126
|
+
this._xrRaycaster.Dispose();
|
|
127
|
+
|
|
128
|
+
// reset placement members
|
|
129
|
+
this._placed = false;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public Update(frame: XRFrame): void {
|
|
133
|
+
this._frameBuffer = frame;
|
|
134
|
+
|
|
135
|
+
if (!this._placed) {
|
|
136
|
+
this._xrCamera.updateMatrixWorld();
|
|
137
|
+
this._scene.XRRoot.XRHandNode.position.copy(
|
|
138
|
+
this._handNodeInitialPosition
|
|
139
|
+
.clone()
|
|
140
|
+
.applyMatrix4(this._xrCamera.matrixWorld),
|
|
141
|
+
);
|
|
142
|
+
this._scene.XRRoot.XRHandNode.quaternion.setFromRotationMatrix(
|
|
143
|
+
this._xrCamera.matrixWorld,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
if (this._origin) {
|
|
147
|
+
this._origin.Update(frame);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// placement
|
|
153
|
+
private async initOrigin(): Promise<void> {
|
|
154
|
+
// initialize origin
|
|
155
|
+
this._origin = await this._origin.Init();
|
|
156
|
+
|
|
157
|
+
// set resolve callback: place objects at origin when it is set
|
|
158
|
+
this._origin.originSet.then(() => {
|
|
159
|
+
this.placeObjects(this._origin.matrix);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private placeObjects(matrix: Matrix4): void {
|
|
164
|
+
this._scene.XRRoot.XRModelRoot.matrix.copy(matrix);
|
|
165
|
+
[...this._scene.XRRoot.XRHandNode.children].forEach((child) => {
|
|
166
|
+
this._scene.XRRoot.XRModelRoot.add(child);
|
|
167
|
+
});
|
|
168
|
+
this._placed = true;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// grabbing
|
|
172
|
+
private updateObject(): void {
|
|
173
|
+
if (!this._grabbedObject) return;
|
|
174
|
+
|
|
175
|
+
this._grabbedObject.position.copy(this._arHitPosition);
|
|
176
|
+
this._grabbedObject.quaternion.copy(
|
|
177
|
+
this._arHitQuaternion.clone().multiply(this._touchQuaterion),
|
|
178
|
+
);
|
|
179
|
+
this._grabbedObject.scale.copy(
|
|
180
|
+
new Vector3(
|
|
181
|
+
this._touchScale,
|
|
182
|
+
this._touchScale,
|
|
183
|
+
this._touchScale,
|
|
184
|
+
).multiply(this._arHitScale),
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private onTouchStart(): void {
|
|
189
|
+
const sceneHits = this._xrRaycaster.GetSceneIntersections();
|
|
190
|
+
console.log('sceneHits', sceneHits);
|
|
191
|
+
if (sceneHits.length === 0) return;
|
|
192
|
+
if (!sceneHits[0].object) return;
|
|
193
|
+
|
|
194
|
+
const moveable = findInterface<DIVEMovable>(
|
|
195
|
+
sceneHits[0].object,
|
|
196
|
+
'isMovable',
|
|
197
|
+
);
|
|
198
|
+
if (!moveable) return;
|
|
199
|
+
|
|
200
|
+
this._grabbedObject = moveable;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private onTouchMove(): void {
|
|
204
|
+
// raycast ar
|
|
205
|
+
if (!this._frameBuffer) return;
|
|
206
|
+
if (!this._grabbedObject) return;
|
|
207
|
+
|
|
208
|
+
const intersections = this._xrRaycaster.GetARIntersections(
|
|
209
|
+
this._frameBuffer,
|
|
210
|
+
);
|
|
211
|
+
if (intersections.length === 0) {
|
|
212
|
+
this._crosshair.visible = false;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const hit = intersections[0];
|
|
217
|
+
|
|
218
|
+
this._crosshair.visible = true;
|
|
219
|
+
this._crosshair.matrix.copy(hit.matrix);
|
|
220
|
+
|
|
221
|
+
if (!this._grabbedObject) return;
|
|
222
|
+
|
|
223
|
+
// if initial values have been reset by TOUCH_END event then set them again
|
|
224
|
+
if (!this._initialObjectPosition || !this._initialRaycastHit) {
|
|
225
|
+
this._initialObjectPosition = this._grabbedObject.position.clone();
|
|
226
|
+
this._initialRaycastHit = hit.point.clone();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// decompose hit matrix to apply hit matrix to object
|
|
230
|
+
hit.matrix.decompose(
|
|
231
|
+
this._arHitPosition,
|
|
232
|
+
this._arHitQuaternion,
|
|
233
|
+
this._arHitScale,
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
// calculate raycast hit delta
|
|
237
|
+
this._deltaRaycastHit.copy(
|
|
238
|
+
hit.point.clone().sub(this._initialRaycastHit),
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
// apply moved raycast delta to actual object position
|
|
242
|
+
this._arHitPosition.copy(
|
|
243
|
+
this._initialObjectPosition.clone().add(this._deltaRaycastHit),
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
console.log('arHitPosition', this._arHitPosition);
|
|
247
|
+
|
|
248
|
+
this.updateObject();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private onTouchEnd(payload: DIVETouchscreenEvents['TOUCH_END']): void {
|
|
252
|
+
if (payload.touchCount === 0) {
|
|
253
|
+
this._crosshair.visible = false;
|
|
254
|
+
|
|
255
|
+
// reset grab
|
|
256
|
+
this._initialObjectPosition = null;
|
|
257
|
+
this._initialRaycastHit = null;
|
|
258
|
+
this._grabbedObject = null;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private _startTouchQuaternion: Quaternion = new Quaternion();
|
|
263
|
+
private onRotateStart(): void {
|
|
264
|
+
this._startTouchQuaternion = this._touchQuaterion.clone();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
private onRotateMove(payload: DIVETouchscreenEvents['ROTATE_MOVE']): void {
|
|
268
|
+
this._touchQuaterion.setFromAxisAngle(
|
|
269
|
+
new Vector3(0, -1, 0),
|
|
270
|
+
payload.delta * 3,
|
|
271
|
+
);
|
|
272
|
+
this._touchQuaterion.multiply(this._startTouchQuaternion);
|
|
273
|
+
this.updateObject();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
private _startTouchScale: number = 1;
|
|
277
|
+
private onPinchStart(): void {
|
|
278
|
+
this._startTouchScale = this._touchScale;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private onPinchMove(payload: DIVETouchscreenEvents['PINCH_MOVE']): void {
|
|
282
|
+
this._touchScale = this._startTouchScale * payload.current;
|
|
283
|
+
this.updateObject();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// prepare & cleanup scene
|
|
287
|
+
private prepareScene(): void {
|
|
288
|
+
this._scene.XRRoot.XRModelRoot.matrixAutoUpdate = false;
|
|
289
|
+
|
|
290
|
+
// initialize crosshair
|
|
291
|
+
this._scene.add(this._crosshair);
|
|
292
|
+
|
|
293
|
+
// hang current scene children to hang node
|
|
294
|
+
const children: Object3D[] = [];
|
|
295
|
+
this._scene.Root.children.forEach((child) => {
|
|
296
|
+
const clone = child.clone();
|
|
297
|
+
clone.layers.enableAll();
|
|
298
|
+
clone.traverse((obj) => {
|
|
299
|
+
obj.layers.enableAll();
|
|
300
|
+
if (obj instanceof Mesh) {
|
|
301
|
+
obj.scale.set(0.1, 0.1, 0.1);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
clone.position.set(0, 0, 0);
|
|
305
|
+
children.push(clone);
|
|
306
|
+
});
|
|
307
|
+
this._scene.XRRoot.XRHandNode.add(...children);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
private restoreScene(): void {
|
|
311
|
+
this._scene.remove(this._crosshair);
|
|
312
|
+
|
|
313
|
+
// clear hang node and remove attached models
|
|
314
|
+
this._scene.XRRoot.XRHandNode.clear();
|
|
315
|
+
this._scene.XRRoot.XRModelRoot.clear();
|
|
316
|
+
|
|
317
|
+
this._scene.XRRoot.XRModelRoot.matrixAutoUpdate = true;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// raycast
|
|
321
|
+
private async initRaycaster(): Promise<void> {
|
|
322
|
+
// initialize raycaster
|
|
323
|
+
await this._xrRaycaster.Init();
|
|
324
|
+
|
|
325
|
+
// check if successful
|
|
326
|
+
if (!this._xrRaycaster) {
|
|
327
|
+
console.error(
|
|
328
|
+
'Raycaster not initialized successfully. Aborting WebXR...',
|
|
329
|
+
);
|
|
330
|
+
this.Dispose();
|
|
331
|
+
return Promise.reject();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Mesh, MeshBasicMaterial, Object3D, RingGeometry } from 'three';
|
|
2
|
+
|
|
3
|
+
export class DIVEWebXRCrosshair extends Object3D {
|
|
4
|
+
public set mesh(mesh: Mesh | undefined) {
|
|
5
|
+
this.clear();
|
|
6
|
+
|
|
7
|
+
if (mesh) {
|
|
8
|
+
this.add(mesh);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
constructor(mesh?: Mesh) {
|
|
13
|
+
super();
|
|
14
|
+
|
|
15
|
+
if (mesh) {
|
|
16
|
+
this.mesh = mesh;
|
|
17
|
+
} else {
|
|
18
|
+
this.UseDefaultMesh();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
this.matrixAutoUpdate = false;
|
|
22
|
+
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public UseDefaultMesh(): void {
|
|
27
|
+
const geometry = new RingGeometry(0.08, 0.1, 32).rotateX(-Math.PI / 2);
|
|
28
|
+
const material = new MeshBasicMaterial();
|
|
29
|
+
this.mesh = new Mesh(geometry, material);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public UpdateFromPose(pose: XRPose): void {
|
|
33
|
+
this.matrix.fromArray(pose.transform.matrix);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { Matrix4, Quaternion, Vector3 } from 'three';
|
|
2
|
+
import { DIVERenderer } from '../../../renderer/Renderer';
|
|
3
|
+
|
|
4
|
+
export class DIVEWebXROrigin {
|
|
5
|
+
private _renderer: DIVERenderer;
|
|
6
|
+
private _session: XRSession;
|
|
7
|
+
|
|
8
|
+
private _requesting: boolean;
|
|
9
|
+
private _initialized: boolean;
|
|
10
|
+
|
|
11
|
+
private _referenceSpaceBuffer: XRReferenceSpace | null;
|
|
12
|
+
private _hitTestSource: XRHitTestSource | null;
|
|
13
|
+
|
|
14
|
+
private _entityTypes: XRHitTestTrackableType[];
|
|
15
|
+
|
|
16
|
+
private _hitTestResultBuffer: XRHitTestResult[];
|
|
17
|
+
private _raycastHitCounter: number = 0;
|
|
18
|
+
|
|
19
|
+
private _originSet: Promise<void>;
|
|
20
|
+
private _originSetResolve: (value: void) => void = () => {};
|
|
21
|
+
public get originSet(): Promise<void> {
|
|
22
|
+
return this._originSet;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private _matrix: Matrix4;
|
|
26
|
+
public get matrix(): Matrix4 {
|
|
27
|
+
return this._matrix;
|
|
28
|
+
}
|
|
29
|
+
private set matrix(value: Matrix4) {
|
|
30
|
+
this._matrix = value;
|
|
31
|
+
this._matrix.decompose(this._position, this._quaternion, this._scale);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private _position: Vector3;
|
|
35
|
+
public get position(): Vector3 {
|
|
36
|
+
return this._position;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private _quaternion: Quaternion;
|
|
40
|
+
public get quaternion(): Quaternion {
|
|
41
|
+
return this._quaternion;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private _scale: Vector3;
|
|
45
|
+
public get scale(): Vector3 {
|
|
46
|
+
return this._scale;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
constructor(
|
|
50
|
+
session: XRSession,
|
|
51
|
+
renderer: DIVERenderer,
|
|
52
|
+
entityTypes?: XRHitTestTrackableType[],
|
|
53
|
+
) {
|
|
54
|
+
this._renderer = renderer;
|
|
55
|
+
this._session = session;
|
|
56
|
+
|
|
57
|
+
this._originSet = new Promise<void>((resolve) => {
|
|
58
|
+
this._originSetResolve = resolve;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
this._requesting = false;
|
|
62
|
+
this._initialized = false;
|
|
63
|
+
|
|
64
|
+
this._referenceSpaceBuffer = null;
|
|
65
|
+
this._hitTestSource = null;
|
|
66
|
+
|
|
67
|
+
this._entityTypes = entityTypes || ['plane'];
|
|
68
|
+
|
|
69
|
+
this._hitTestResultBuffer = [];
|
|
70
|
+
|
|
71
|
+
// set up promises and executors
|
|
72
|
+
this._matrix = new Matrix4();
|
|
73
|
+
this._position = new Vector3();
|
|
74
|
+
this._quaternion = new Quaternion();
|
|
75
|
+
this._scale = new Vector3();
|
|
76
|
+
|
|
77
|
+
// when origin is set, decompose matrix into position, quaternion and scale
|
|
78
|
+
this._originSet.then(() => {
|
|
79
|
+
// decompose matrix into position, quaternion and scale
|
|
80
|
+
this._matrix.decompose(
|
|
81
|
+
this._position,
|
|
82
|
+
this._quaternion,
|
|
83
|
+
this._scale,
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public async Init(): Promise<this> {
|
|
89
|
+
if (this._initialized) {
|
|
90
|
+
return Promise.resolve(this);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!this._session) {
|
|
94
|
+
console.error(
|
|
95
|
+
'DIVEWebXROrigin: No session set in Init()! Aborting initialization...',
|
|
96
|
+
);
|
|
97
|
+
return Promise.reject();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (this._requesting) {
|
|
101
|
+
console.error(
|
|
102
|
+
'DIVEWebXROrigin: Currently initializing! Aborting initialization...',
|
|
103
|
+
);
|
|
104
|
+
return Promise.reject();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this._requesting = true;
|
|
108
|
+
const referenceSpace =
|
|
109
|
+
await this._session.requestReferenceSpace('viewer');
|
|
110
|
+
this._hitTestSource =
|
|
111
|
+
(await this._session.requestHitTestSource!({
|
|
112
|
+
space: referenceSpace,
|
|
113
|
+
entityTypes: this._entityTypes,
|
|
114
|
+
})) || null;
|
|
115
|
+
this._requesting = false;
|
|
116
|
+
|
|
117
|
+
if (!this._hitTestSource) {
|
|
118
|
+
return Promise.reject();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
this._initialized = true;
|
|
122
|
+
|
|
123
|
+
return Promise.resolve(this);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public Dispose(): void {
|
|
127
|
+
this._initialized = false;
|
|
128
|
+
this._requesting = false;
|
|
129
|
+
|
|
130
|
+
this._hitTestSource?.cancel();
|
|
131
|
+
|
|
132
|
+
this._hitTestSource = null;
|
|
133
|
+
this._hitTestResultBuffer = [];
|
|
134
|
+
|
|
135
|
+
this._matrix = new Matrix4();
|
|
136
|
+
this._position = new Vector3();
|
|
137
|
+
this._quaternion = new Quaternion();
|
|
138
|
+
this._scale = new Vector3();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public Update(frame: XRFrame): void {
|
|
142
|
+
if (!this._initialized) return;
|
|
143
|
+
|
|
144
|
+
if (!this._hitTestSource) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
'DIVEWebXRRaycaster: Critical Error: HitTestSource not available but WebXROrigin is initialized!',
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// get hit test results
|
|
151
|
+
this._hitTestResultBuffer = frame.getHitTestResults(
|
|
152
|
+
this._hitTestSource,
|
|
153
|
+
);
|
|
154
|
+
if (this._hitTestResultBuffer.length > 0) {
|
|
155
|
+
// hit found
|
|
156
|
+
this._referenceSpaceBuffer = this._renderer.xr.getReferenceSpace();
|
|
157
|
+
|
|
158
|
+
// if there is no reference space, hit will be counted as lost for this frame
|
|
159
|
+
if (!this._referenceSpaceBuffer) {
|
|
160
|
+
this.onHitLost();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const pose = this._hitTestResultBuffer[0].getPose(
|
|
165
|
+
this._referenceSpaceBuffer,
|
|
166
|
+
);
|
|
167
|
+
if (!pose) {
|
|
168
|
+
this.onHitLost();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this.onHitFound(pose);
|
|
173
|
+
} else {
|
|
174
|
+
this.onHitLost();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private onHitFound(pose: XRPose): void {
|
|
179
|
+
this._raycastHitCounter++;
|
|
180
|
+
|
|
181
|
+
this.matrix.fromArray(pose.transform.matrix);
|
|
182
|
+
|
|
183
|
+
if (this._raycastHitCounter > 50) {
|
|
184
|
+
this._originSetResolve();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private onHitLost(): void {
|
|
189
|
+
this._raycastHitCounter = 0;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export class Overlay {
|
|
2
|
+
private _element: HTMLDivElement;
|
|
3
|
+
public get Element(): HTMLDivElement {
|
|
4
|
+
return this._element;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
private _closeButton: SVGSVGElement;
|
|
8
|
+
public get CloseButton(): SVGSVGElement {
|
|
9
|
+
return this._closeButton;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
// create div
|
|
14
|
+
this._element = document.createElement('div');
|
|
15
|
+
|
|
16
|
+
// create and append close button to overlay
|
|
17
|
+
this._closeButton = this.createCloseButton();
|
|
18
|
+
this._element.appendChild(this._closeButton);
|
|
19
|
+
|
|
20
|
+
// append overlay to body
|
|
21
|
+
document.body.appendChild(this._element);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private createCloseButton(): SVGSVGElement {
|
|
25
|
+
// create svg path
|
|
26
|
+
const path = document.createElementNS(
|
|
27
|
+
'http://www.w3.org/2000/svg',
|
|
28
|
+
'path',
|
|
29
|
+
);
|
|
30
|
+
path.setAttribute('d', 'M 12,12 L 28,28 M 28,12 12,28');
|
|
31
|
+
path.setAttribute('stroke', '#fff');
|
|
32
|
+
path.setAttribute('stroke-width', '2');
|
|
33
|
+
|
|
34
|
+
// create svg
|
|
35
|
+
const svg = document.createElementNS(
|
|
36
|
+
'http://www.w3.org/2000/svg',
|
|
37
|
+
'svg',
|
|
38
|
+
);
|
|
39
|
+
svg.setAttribute('width', '38');
|
|
40
|
+
svg.setAttribute('height', '38');
|
|
41
|
+
svg.style.position = 'absolute';
|
|
42
|
+
svg.style.right = '20px';
|
|
43
|
+
svg.style.top = '20px';
|
|
44
|
+
|
|
45
|
+
// append path to svg
|
|
46
|
+
svg.appendChild(path);
|
|
47
|
+
|
|
48
|
+
return svg;
|
|
49
|
+
}
|
|
50
|
+
}
|