@shopware-ag/dive 1.16.26-beta.2 → 1.17.0
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/README.md +8 -0
- package/build/dive.cjs +133 -111
- package/build/dive.cjs.map +1 -1
- package/build/dive.d.cts +16 -1
- package/build/dive.d.ts +16 -1
- package/build/dive.js +134 -110
- package/build/dive.js.map +1 -1
- package/package.json +3 -2
- package/src/__test__/DIVE.test.ts +2 -2
- package/src/ar/AR.ts +51 -125
- package/src/ar/__test__/AR.test.ts +187 -0
- package/src/ar/arquicklook/ARQuickLook.ts +29 -7
- package/src/ar/arquicklook/__test__/ARQuickLook.test.ts +268 -0
- package/src/ar/sceneviewer/SceneViewer.ts +74 -0
- package/src/ar/sceneviewer/__test__/SceneViewer.test.ts +245 -0
- package/src/ar/webxr/controller/WebXRController.ts +17 -11
- package/src/ar/webxr/origin/WebXROrigin.ts +1 -0
- package/src/com/Communication.ts +4 -3
- package/src/com/__test__/Communication.test.ts +47 -2
- package/src/com/actions/index.ts +2 -2
- package/src/com/actions/scene/launchar.ts +7 -0
- package/src/dive.ts +0 -16
- package/src/exporters/usdz/USDZExporter.ts +21 -0
- package/src/group/__test__/Group.test.ts +2 -0
- package/src/scene/__test__/Scene.test.ts +10 -0
- package/src/toolbox/transform/__test__/TransformTool.test.ts +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopware-ag/dive",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.0",
|
|
4
4
|
"description": "Shopware Spatial Framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./build/dive.cjs",
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"generate-readme": "yarn generate-readme:transpile && yarn generate-readme:write && yarn generate-readme:cleanup",
|
|
70
70
|
"generate-readme:transpile": "yarn tsc --resolveJsonModule --esModuleInterop ci/readme/generate-readme.ts && mv ci/readme/generate-readme.js ci/readme/generate-readme.cjs",
|
|
71
71
|
"generate-readme:write": "node ci/readme/generate-readme.cjs",
|
|
72
|
-
"generate-readme:cleanup": "node -e \"require('fs').unlinkSync('ci/readme/generate-readme.cjs')\""
|
|
72
|
+
"generate-readme:cleanup": "node -e \"require('fs').unlinkSync('ci/readme/generate-readme.cjs')\"",
|
|
73
|
+
"ci": "yarn lint && yarn coverage && yarn prettier:check && yarn build && bash ci/lint/lint-actions.sh"
|
|
73
74
|
}
|
|
74
75
|
}
|
|
@@ -208,8 +208,8 @@ describe('dive/DIVE', () => {
|
|
|
208
208
|
expect(() => (window as any).DIVE.PrintScene()).not.toThrow();
|
|
209
209
|
});
|
|
210
210
|
|
|
211
|
-
it('should instantiate in development
|
|
212
|
-
process.env.
|
|
211
|
+
it('should instantiate in development DIVE_NODE_ENV', () => {
|
|
212
|
+
process.env.DIVE_NODE_ENV = 'development';
|
|
213
213
|
const dive = new DIVE();
|
|
214
214
|
expect(dive).toBeDefined();
|
|
215
215
|
expect((window as any).DIVE.PrintScene).toBeDefined();
|
package/src/ar/AR.ts
CHANGED
|
@@ -4,15 +4,22 @@ import { DIVEWebXR } from './webxr/WebXR';
|
|
|
4
4
|
import { type DIVEScene } from '../scene/Scene';
|
|
5
5
|
import { type DIVERenderer } from '../renderer/Renderer';
|
|
6
6
|
import DIVEOrbitControls from '../controls/OrbitControls';
|
|
7
|
+
import { DIVESceneViewer } from './sceneviewer/SceneViewer';
|
|
8
|
+
|
|
9
|
+
export type DIVEAROptions = {
|
|
10
|
+
arPlacement: 'horizontal' | 'vertical';
|
|
11
|
+
arScale: 'auto' | 'fixed';
|
|
12
|
+
/**
|
|
13
|
+
* experimental, currently deactivated
|
|
14
|
+
*/
|
|
15
|
+
useWebXR: false;
|
|
16
|
+
};
|
|
7
17
|
|
|
8
18
|
export class DIVEAR {
|
|
9
19
|
private _renderer: DIVERenderer;
|
|
10
20
|
private _scene: DIVEScene;
|
|
11
21
|
private _controller: DIVEOrbitControls;
|
|
12
22
|
|
|
13
|
-
private arPlacement: string = 'floor';
|
|
14
|
-
private arScale: string = 'auto';
|
|
15
|
-
|
|
16
23
|
constructor(
|
|
17
24
|
renderer: DIVERenderer,
|
|
18
25
|
scene: DIVEScene,
|
|
@@ -23,150 +30,69 @@ export class DIVEAR {
|
|
|
23
30
|
this._controller = controller;
|
|
24
31
|
}
|
|
25
32
|
|
|
26
|
-
public async Launch(): Promise<void> {
|
|
33
|
+
public async Launch(options?: DIVEAROptions): Promise<void> {
|
|
27
34
|
const system = DIVEInfo.GetSystem();
|
|
28
35
|
|
|
29
36
|
if (system === 'iOS') {
|
|
30
|
-
|
|
31
|
-
if (!support) {
|
|
32
|
-
console.log('ARQuickLook not supported');
|
|
33
|
-
return Promise.reject();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
console.log('Launching AR on iOS');
|
|
37
|
-
|
|
38
|
-
// Launch ARQuickLook
|
|
39
|
-
await DIVEARQuickLook.Launch(this._scene);
|
|
40
|
-
return Promise.resolve();
|
|
37
|
+
return this.tryARQuickLook();
|
|
41
38
|
}
|
|
42
39
|
|
|
43
40
|
if (system === 'Android') {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const support = await DIVEInfo.GetSupportsWebXR();
|
|
48
|
-
if (!support) {
|
|
49
|
-
console.log(
|
|
50
|
-
'WebXR not supported. Reason: ' +
|
|
51
|
-
WebXRUnsupportedReason[
|
|
52
|
-
DIVEInfo.GetWebXRUnsupportedReason()!
|
|
53
|
-
],
|
|
54
|
-
);
|
|
55
|
-
return Promise.reject();
|
|
41
|
+
if (options?.useWebXR) {
|
|
42
|
+
console.warn('DIVE: WebXR is experimental on Android.');
|
|
43
|
+
return this.tryWebXR();
|
|
56
44
|
}
|
|
57
45
|
|
|
58
|
-
|
|
59
|
-
// Launch WebXR
|
|
60
|
-
await DIVEWebXR.Launch(
|
|
61
|
-
this._renderer,
|
|
62
|
-
this._scene,
|
|
63
|
-
this._controller,
|
|
64
|
-
);
|
|
65
|
-
return Promise.resolve();
|
|
46
|
+
return this.trySceneViewer();
|
|
66
47
|
}
|
|
67
48
|
|
|
68
49
|
console.log(
|
|
69
|
-
'AR not supported. Not a mobile system. (System is ' +
|
|
50
|
+
'DIVE: AR not supported. Not a mobile system. (System is ' +
|
|
51
|
+
system +
|
|
52
|
+
')',
|
|
70
53
|
);
|
|
71
54
|
}
|
|
72
55
|
|
|
73
|
-
private
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const location = self.location.toString();
|
|
80
|
-
const locationUrl = new URL(location);
|
|
81
|
-
const modelUrl = new URL(src, location);
|
|
82
|
-
if (modelUrl.hash) modelUrl.hash = '';
|
|
83
|
-
const params = new URLSearchParams(modelUrl.search);
|
|
84
|
-
|
|
85
|
-
locationUrl.hash = noArViewerSigil;
|
|
86
|
-
|
|
87
|
-
// modelUrl can contain title/link/sound etc.
|
|
88
|
-
params.set('mode', 'ar_preferred');
|
|
89
|
-
if (!params.has('disable_occlusion')) {
|
|
90
|
-
params.set('disable_occlusion', 'true');
|
|
91
|
-
}
|
|
92
|
-
if (this.arScale === 'fixed') {
|
|
93
|
-
params.set('resizable', 'false');
|
|
94
|
-
}
|
|
95
|
-
if (this.arPlacement === 'wall') {
|
|
96
|
-
params.set('enable_vertical_placement', 'true');
|
|
97
|
-
}
|
|
98
|
-
if (params.has('sound')) {
|
|
99
|
-
const soundUrl = new URL(params.get('sound')!, location);
|
|
100
|
-
params.set('sound', soundUrl.toString());
|
|
101
|
-
}
|
|
102
|
-
if (params.has('link')) {
|
|
103
|
-
const linkUrl = new URL(params.get('link')!, location);
|
|
104
|
-
params.set('link', linkUrl.toString());
|
|
56
|
+
private async tryARQuickLook(options?: DIVEAROptions): Promise<void> {
|
|
57
|
+
const support = DIVEInfo.GetSupportsARQuickLook();
|
|
58
|
+
if (!support) {
|
|
59
|
+
console.log('ARQuickLook not supported');
|
|
60
|
+
return Promise.reject();
|
|
105
61
|
}
|
|
106
62
|
|
|
107
|
-
console.log('
|
|
108
|
-
console.log(
|
|
109
|
-
'encodeURIComponent(modelUrl.toString())',
|
|
110
|
-
encodeURIComponent(modelUrl.toString()),
|
|
111
|
-
);
|
|
63
|
+
console.log('DIVE: Launching AR with ARQuickLook ...');
|
|
112
64
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
console.log('params:', params.toString());
|
|
118
|
-
console.log('modelUrl:', modelUrl.toString());
|
|
119
|
-
console.log('locationUrl:', locationUrl.toString());
|
|
120
|
-
|
|
121
|
-
const intent = `intent://arvr.google.com/scene-viewer/${version}?${
|
|
122
|
-
params.toString() + '&file=' + modelUrl.toString()
|
|
123
|
-
}#Intent;scheme=https;package=com.google.android.googlequicksearchbox;action=android.intent.action.VIEW;S.browser_fallback_url=${encodeURIComponent(
|
|
124
|
-
locationUrl.toString(),
|
|
125
|
-
)};end;`;
|
|
126
|
-
// intent =
|
|
127
|
-
// 'intent://arvr.google.com/scene-viewer/1.0?file=https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Avocado/glTF/Avocado.gltf#Intent;scheme=https;package=com.google.android.googlequicksearchbox;action=android.intent.action.VIEW;S.browser_fallback_url=https://developers.google.com/ar;end;';
|
|
128
|
-
console.log({ intent });
|
|
129
|
-
|
|
130
|
-
const undoHashChange = (): void => {
|
|
131
|
-
if (self.location.hash === noArViewerSigil) {
|
|
132
|
-
// isSceneViewerBlocked = true;
|
|
133
|
-
// The new history will be the current URL with a new hash.
|
|
134
|
-
// Go back one step so that we reset to the expected URL.
|
|
135
|
-
// NOTE(cdata): this should not invoke any browser-level navigation
|
|
136
|
-
// because hash-only changes modify the URL in-place without
|
|
137
|
-
// navigating:
|
|
138
|
-
self.history.back();
|
|
139
|
-
console.warn(
|
|
140
|
-
'Error while trying to present in AR with Scene Viewer',
|
|
141
|
-
);
|
|
142
|
-
console.warn('Falling back to next ar-mode');
|
|
143
|
-
// this[$selectARMode]();
|
|
144
|
-
// Would be nice to activateAR() here, but webXR fails due to not
|
|
145
|
-
// seeing a user activation.
|
|
146
|
-
}
|
|
147
|
-
};
|
|
65
|
+
// Launch ARQuickLook
|
|
66
|
+
await DIVEARQuickLook.Launch(this._scene, options);
|
|
67
|
+
return Promise.resolve();
|
|
68
|
+
}
|
|
148
69
|
|
|
149
|
-
|
|
70
|
+
private async tryWebXR(): Promise<void> {
|
|
71
|
+
const support = await DIVEInfo.GetSupportsWebXR();
|
|
72
|
+
if (!support) {
|
|
73
|
+
console.log(
|
|
74
|
+
'WebXR not supported. Reason: ' +
|
|
75
|
+
WebXRUnsupportedReason[
|
|
76
|
+
DIVEInfo.GetWebXRUnsupportedReason()!
|
|
77
|
+
],
|
|
78
|
+
);
|
|
79
|
+
return Promise.reject();
|
|
80
|
+
}
|
|
150
81
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
82
|
+
console.log('DIVE: Launching AR with WebXR ...');
|
|
83
|
+
// Launch WebXR
|
|
84
|
+
await DIVEWebXR.Launch(this._renderer, this._scene, this._controller);
|
|
85
|
+
return Promise.resolve();
|
|
154
86
|
}
|
|
155
87
|
|
|
156
|
-
private
|
|
157
|
-
|
|
88
|
+
private async trySceneViewer(options?: DIVEAROptions): Promise<void> {
|
|
89
|
+
// actually we don't have to try here, because SceneViewer is supported on all devices by now.
|
|
90
|
+
// if there are no AR services (ARCore) installed on the device, SceneViewer will only show the model in 3D.
|
|
91
|
+
// we also have no options to detect if SceneViewer is supported.
|
|
158
92
|
|
|
159
|
-
|
|
160
|
-
if (uri) return;
|
|
161
|
-
if (object.userData.uri) {
|
|
162
|
-
uri = object.userData.uri;
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
if (!uri) {
|
|
167
|
-
throw new Error('No model found in scene');
|
|
168
|
-
}
|
|
93
|
+
console.log('DIVE: Launching AR with SceneViewer ...');
|
|
169
94
|
|
|
170
|
-
|
|
95
|
+
DIVESceneViewer.Launch(this._scene, options);
|
|
96
|
+
return Promise.resolve();
|
|
171
97
|
}
|
|
172
98
|
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { DIVEAR, type DIVEAROptions } from '../AR';
|
|
2
|
+
import { DIVEInfo } from '../../info/Info';
|
|
3
|
+
import { DIVERenderer } from '../../renderer/Renderer';
|
|
4
|
+
import { DIVEScene } from '../../scene/Scene';
|
|
5
|
+
import DIVEOrbitControls from '../../controls/OrbitControls';
|
|
6
|
+
import { DIVEARQuickLook } from '../arquicklook/ARQuickLook';
|
|
7
|
+
import { DIVESceneViewer } from '../sceneviewer/SceneViewer';
|
|
8
|
+
import { DIVEWebXR } from '../webxr/WebXR';
|
|
9
|
+
|
|
10
|
+
jest.mock('../arquicklook/ARQuickLook', () => ({
|
|
11
|
+
DIVEARQuickLook: {
|
|
12
|
+
Launch: jest.fn(),
|
|
13
|
+
},
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
jest.mock('../webxr/WebXR', () => ({
|
|
17
|
+
DIVEWebXR: {
|
|
18
|
+
Launch: jest.fn(),
|
|
19
|
+
},
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
jest.mock('../sceneviewer/SceneViewer', () => ({
|
|
23
|
+
DIVESceneViewer: {
|
|
24
|
+
Launch: jest.fn(),
|
|
25
|
+
},
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
describe('DIVEAR', () => {
|
|
29
|
+
let renderer: DIVERenderer;
|
|
30
|
+
let scene: DIVEScene;
|
|
31
|
+
let controller: DIVEOrbitControls;
|
|
32
|
+
let diveAR: DIVEAR;
|
|
33
|
+
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
renderer = {} as DIVERenderer;
|
|
36
|
+
scene = {} as DIVEScene;
|
|
37
|
+
controller = {} as DIVEOrbitControls;
|
|
38
|
+
diveAR = new DIVEAR(renderer, scene, controller);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('Launch', () => {
|
|
42
|
+
describe('AR Quick Look', () => {
|
|
43
|
+
it('should launch ARQuickLook on iOS', async () => {
|
|
44
|
+
jest.spyOn(DIVEInfo, 'GetSystem').mockReturnValue('iOS');
|
|
45
|
+
jest.spyOn(DIVEInfo, 'GetSupportsARQuickLook').mockReturnValue(
|
|
46
|
+
true,
|
|
47
|
+
);
|
|
48
|
+
const arQuickLookLaunchSpy = jest.spyOn(
|
|
49
|
+
DIVEARQuickLook,
|
|
50
|
+
'Launch',
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const consoleLogSpy = jest
|
|
54
|
+
.spyOn(console, 'log')
|
|
55
|
+
.mockImplementation();
|
|
56
|
+
|
|
57
|
+
await diveAR.Launch();
|
|
58
|
+
|
|
59
|
+
expect(arQuickLookLaunchSpy).toHaveBeenCalledWith(
|
|
60
|
+
scene,
|
|
61
|
+
undefined,
|
|
62
|
+
);
|
|
63
|
+
arQuickLookLaunchSpy.mockRestore();
|
|
64
|
+
|
|
65
|
+
expect(consoleLogSpy).toHaveBeenCalled();
|
|
66
|
+
consoleLogSpy.mockRestore();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should not launch ARQuickLook on iOS if not supported', async () => {
|
|
70
|
+
jest.spyOn(DIVEInfo, 'GetSystem').mockReturnValue('iOS');
|
|
71
|
+
jest.spyOn(DIVEInfo, 'GetSupportsARQuickLook').mockReturnValue(
|
|
72
|
+
false,
|
|
73
|
+
);
|
|
74
|
+
const arQuickLookLaunchSpy = jest.spyOn(
|
|
75
|
+
DIVEARQuickLook,
|
|
76
|
+
'Launch',
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const consoleLogSpy = jest
|
|
80
|
+
.spyOn(console, 'log')
|
|
81
|
+
.mockImplementation();
|
|
82
|
+
|
|
83
|
+
await diveAR.Launch().catch(() => {});
|
|
84
|
+
|
|
85
|
+
expect(arQuickLookLaunchSpy).not.toHaveBeenCalled();
|
|
86
|
+
arQuickLookLaunchSpy.mockRestore();
|
|
87
|
+
|
|
88
|
+
expect(consoleLogSpy).toHaveBeenCalled();
|
|
89
|
+
consoleLogSpy.mockRestore();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('Scene Viewer', () => {
|
|
94
|
+
it('should launch SceneViewer on Android', async () => {
|
|
95
|
+
jest.spyOn(DIVEInfo, 'GetSystem').mockReturnValue('Android');
|
|
96
|
+
|
|
97
|
+
const consoleLogSpy = jest
|
|
98
|
+
.spyOn(console, 'log')
|
|
99
|
+
.mockImplementation();
|
|
100
|
+
|
|
101
|
+
await diveAR.Launch();
|
|
102
|
+
|
|
103
|
+
expect(DIVESceneViewer.Launch).toHaveBeenCalledWith(
|
|
104
|
+
scene,
|
|
105
|
+
undefined,
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
expect(consoleLogSpy).toHaveBeenCalled();
|
|
109
|
+
consoleLogSpy.mockRestore();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('WebXR', () => {
|
|
114
|
+
it('should launch WebXR on Android with useWebXR option', async () => {
|
|
115
|
+
jest.spyOn(DIVEInfo, 'GetSystem').mockReturnValue('Android');
|
|
116
|
+
jest.spyOn(DIVEInfo, 'GetSupportsWebXR').mockResolvedValue(
|
|
117
|
+
true,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const consoleLogSpy = jest
|
|
121
|
+
.spyOn(console, 'log')
|
|
122
|
+
.mockImplementation();
|
|
123
|
+
const consoleWarnSpy = jest
|
|
124
|
+
.spyOn(console, 'warn')
|
|
125
|
+
.mockImplementation();
|
|
126
|
+
|
|
127
|
+
await diveAR.Launch({
|
|
128
|
+
useWebXR: true,
|
|
129
|
+
} as unknown as DIVEAROptions);
|
|
130
|
+
|
|
131
|
+
expect(DIVEWebXR.Launch).toHaveBeenCalledWith(
|
|
132
|
+
renderer,
|
|
133
|
+
scene,
|
|
134
|
+
controller,
|
|
135
|
+
);
|
|
136
|
+
expect(consoleWarnSpy).toHaveBeenCalled();
|
|
137
|
+
consoleWarnSpy.mockRestore();
|
|
138
|
+
|
|
139
|
+
expect(consoleLogSpy).toHaveBeenCalled();
|
|
140
|
+
consoleLogSpy.mockRestore();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should not launch WebXR on Android with useWebXR option', async () => {
|
|
144
|
+
jest.spyOn(DIVEInfo, 'GetSystem').mockReturnValue('Android');
|
|
145
|
+
jest.spyOn(DIVEInfo, 'GetSupportsWebXR').mockResolvedValue(
|
|
146
|
+
false,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const consoleLogSpy = jest
|
|
150
|
+
.spyOn(console, 'log')
|
|
151
|
+
.mockImplementation();
|
|
152
|
+
const consoleWarnSpy = jest
|
|
153
|
+
.spyOn(console, 'warn')
|
|
154
|
+
.mockImplementation();
|
|
155
|
+
|
|
156
|
+
await diveAR
|
|
157
|
+
.Launch({
|
|
158
|
+
useWebXR: true,
|
|
159
|
+
} as unknown as DIVEAROptions)
|
|
160
|
+
.catch(() => {});
|
|
161
|
+
|
|
162
|
+
expect(DIVEWebXR.Launch).toHaveBeenCalledWith(
|
|
163
|
+
renderer,
|
|
164
|
+
scene,
|
|
165
|
+
controller,
|
|
166
|
+
);
|
|
167
|
+
expect(consoleWarnSpy).toHaveBeenCalled();
|
|
168
|
+
consoleWarnSpy.mockRestore();
|
|
169
|
+
|
|
170
|
+
expect(consoleLogSpy).toHaveBeenCalled();
|
|
171
|
+
consoleLogSpy.mockRestore();
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should log AR not supported on non-mobile systems', async () => {
|
|
176
|
+
const consoleLogSpy = jest
|
|
177
|
+
.spyOn(console, 'log')
|
|
178
|
+
.mockImplementation();
|
|
179
|
+
|
|
180
|
+
jest.spyOn(DIVEInfo, 'GetSystem').mockReturnValue('Windows');
|
|
181
|
+
|
|
182
|
+
await diveAR.Launch();
|
|
183
|
+
|
|
184
|
+
expect(consoleLogSpy).toHaveBeenCalled();
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
});
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { Object3D } from 'three';
|
|
2
|
+
import { DIVEUSDZExporter } from '../../exporters/usdz/USDZExporter';
|
|
2
3
|
import { type DIVEScene } from '../../scene/Scene';
|
|
3
|
-
import {
|
|
4
|
+
import { type DIVEAROptions } from '../AR';
|
|
4
5
|
|
|
5
6
|
export class DIVEARQuickLook {
|
|
6
|
-
private static _usdzExporter:
|
|
7
|
+
private static _usdzExporter: DIVEUSDZExporter = new DIVEUSDZExporter();
|
|
7
8
|
|
|
8
|
-
public static Launch(
|
|
9
|
+
public static Launch(
|
|
10
|
+
scene: DIVEScene,
|
|
11
|
+
options?: DIVEAROptions,
|
|
12
|
+
): Promise<void> {
|
|
9
13
|
// create node to build usdz from
|
|
10
14
|
const quickLookScene = new Object3D();
|
|
11
15
|
|
|
@@ -13,7 +17,7 @@ export class DIVEARQuickLook {
|
|
|
13
17
|
quickLookScene.add(...this.extractModels(scene));
|
|
14
18
|
|
|
15
19
|
// launch ARQuickLook
|
|
16
|
-
return this.launchARFromNode(quickLookScene);
|
|
20
|
+
return this.launchARFromNode(quickLookScene, options);
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
private static extractModels(scene: DIVEScene): Object3D[] {
|
|
@@ -21,14 +25,32 @@ export class DIVEARQuickLook {
|
|
|
21
25
|
return scene.Root.children;
|
|
22
26
|
}
|
|
23
27
|
|
|
24
|
-
private static launchARFromNode(
|
|
28
|
+
private static launchARFromNode(
|
|
29
|
+
node: Object3D,
|
|
30
|
+
options?: DIVEAROptions,
|
|
31
|
+
): Promise<void> {
|
|
25
32
|
// bundle USDZ
|
|
26
33
|
return this._usdzExporter
|
|
27
|
-
.parse(node, {
|
|
34
|
+
.parse(node, {
|
|
35
|
+
quickLookCompatible: true,
|
|
36
|
+
ar: {
|
|
37
|
+
anchoring: { type: 'plane' },
|
|
38
|
+
planeAnchoring: {
|
|
39
|
+
alignment:
|
|
40
|
+
options?.arPlacement === 'vertical'
|
|
41
|
+
? 'vertical'
|
|
42
|
+
: 'horizontal',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
})
|
|
28
46
|
.then((usdz: Uint8Array) => {
|
|
29
47
|
// create blob
|
|
30
48
|
const blob = new Blob([usdz], { type: 'model/vnd.usdz+zip' });
|
|
31
|
-
|
|
49
|
+
let url = URL.createObjectURL(blob);
|
|
50
|
+
|
|
51
|
+
if (options?.arScale === 'fixed') {
|
|
52
|
+
url = url.concat('#allowsContentScaling=0');
|
|
53
|
+
}
|
|
32
54
|
|
|
33
55
|
// launch ARQuickLook
|
|
34
56
|
const a = document.createElement('a');
|