@shopware-ag/dive 1.18.5 → 1.19.1-beta.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 +220 -215
- package/build/dive.cjs.map +1 -1
- package/build/dive.js +233 -21102
- package/build/dive.js.map +1 -1
- package/build/dive.mjs +26111 -0
- package/build/dive.mjs.map +1 -0
- package/build/src/ar/AR.d.ts +37 -14
- package/build/src/ar/arquicklook/ARQuickLook.d.ts +5 -6
- package/build/src/ar/sceneviewer/SceneViewer.d.ts +42 -6
- package/build/src/com/actions/index.d.ts +2 -0
- package/build/src/com/actions/renderer/startrender.d.ts +5 -0
- package/build/src/com/actions/scene/launchar.d.ts +5 -2
- package/build/src/converter/Converter.d.ts +21 -0
- package/build/src/dive.d.ts +3 -2
- package/build/src/exporter/Exporter.d.ts +11 -0
- package/build/src/helper/applyMixins/applyMixins.d.ts +22 -6
- package/build/src/info/Info.d.ts +37 -13
- package/build/src/interface/Movable.d.ts +5 -5
- package/build/src/interface/Selectable.d.ts +4 -4
- package/build/src/loader/Loader.d.ts +11 -0
- package/build/src/model/Model.d.ts +2 -2
- package/build/src/node/Node.d.ts +3 -3
- package/build/src/scene/root/Root.d.ts +1 -1
- package/build/src/types/ExporterOptions.d.ts +15 -0
- package/build/src/types/FileTypes.d.ts +27 -0
- package/build/src/types/index.d.ts +4 -0
- package/build/src/types/info/index.d.ts +66 -0
- package/package.json +16 -1
- package/src/ar/AR.ts +72 -69
- package/src/ar/__test__/AR.test.ts +194 -105
- package/src/ar/arquicklook/ARQuickLook.ts +32 -72
- package/src/ar/arquicklook/__test__/ARQuickLook.test.ts +89 -38
- package/src/ar/sceneviewer/SceneViewer.ts +96 -51
- package/src/ar/sceneviewer/__test__/SceneViewer.test.ts +144 -47
- package/src/ar/webxr/WebXR.ts +5 -4
- package/src/com/Communication.ts +10 -7
- package/src/com/__test__/Communication.test.ts +16 -3
- package/src/com/actions/index.ts +2 -0
- package/src/com/actions/renderer/startrender.ts +5 -0
- package/src/com/actions/scene/launchar.ts +2 -2
- package/src/converter/Converter.ts +117 -0
- package/src/dive.ts +10 -6
- package/src/exporter/Exporter.ts +75 -0
- package/src/helper/applyMixins/applyMixins.ts +59 -7
- package/src/info/Info.ts +99 -75
- package/src/info/__test__/Info.test.ts +162 -154
- package/src/interface/Movable.ts +5 -5
- package/src/interface/Selectable.ts +4 -4
- package/src/loader/Loader.ts +48 -0
- package/src/model/Model.ts +10 -5
- package/src/model/__test__/Model.test.ts +4 -11
- package/src/node/Node.ts +7 -5
- package/src/scene/root/Root.ts +4 -4
- package/src/scene/root/__test__/Root.test.ts +4 -4
- package/src/toolbox/Toolbox.ts +1 -3
- package/src/types/ExporterOptions.ts +14 -0
- package/src/types/FileTypes.ts +37 -0
- package/src/types/index.ts +26 -0
- package/src/types/info/index.ts +76 -0
- package/build/src/exporters/usdz/USDZExporter.d.ts +0 -15
- package/build/src/loadingmanager/LoadingManager.d.ts +0 -14
- package/src/exporters/usdz/USDZExporter.ts +0 -21
- package/src/exporters/usdz/__test__/USDZExporter.test.ts +0 -57
- package/src/loadingmanager/LoadingManager.ts +0 -50
- package/src/loadingmanager/__test__/LoadingManager.test.ts +0 -27
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SystemInfo } from '../Info';
|
|
2
|
+
import { ESystem, EWebXRUnsupportedReason } from '../../types/info';
|
|
2
3
|
|
|
3
4
|
const mockNavigator = (navigator: any) => {
|
|
4
5
|
Object.defineProperty(global, 'navigator', {
|
|
@@ -9,54 +10,62 @@ const mockNavigator = (navigator: any) => {
|
|
|
9
10
|
|
|
10
11
|
describe('dive/info/DIVEInfo', () => {
|
|
11
12
|
beforeEach(() => {
|
|
12
|
-
|
|
13
|
+
SystemInfo['_supportsWebXR'] = false;
|
|
13
14
|
jest.clearAllMocks();
|
|
14
15
|
});
|
|
15
16
|
|
|
16
17
|
it('should get system: Windows', () => {
|
|
17
18
|
mockNavigator({
|
|
18
|
-
|
|
19
|
+
userAgent:
|
|
20
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.153 Safari/537.36',
|
|
19
21
|
});
|
|
20
|
-
expect(
|
|
22
|
+
expect(SystemInfo.GetSystem()).toBe(ESystem.WINDOWS);
|
|
21
23
|
});
|
|
22
24
|
|
|
23
25
|
it('should get system: MacOS', () => {
|
|
24
26
|
mockNavigator({
|
|
25
|
-
|
|
27
|
+
userAgent:
|
|
28
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.153 Safari/537.36',
|
|
26
29
|
});
|
|
27
|
-
expect(
|
|
30
|
+
expect(SystemInfo.GetSystem()).toBe(ESystem.MACOS);
|
|
28
31
|
});
|
|
29
32
|
|
|
30
33
|
it('should get system: Linux', () => {
|
|
31
34
|
mockNavigator({
|
|
32
|
-
|
|
35
|
+
userAgent:
|
|
36
|
+
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.153 Safari/537.36',
|
|
33
37
|
});
|
|
34
|
-
expect(
|
|
38
|
+
expect(SystemInfo.GetSystem()).toBe(ESystem.LINUX);
|
|
35
39
|
});
|
|
36
40
|
|
|
37
41
|
it('should get system: Android', () => {
|
|
38
42
|
mockNavigator({
|
|
39
43
|
userAgent:
|
|
40
|
-
'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
41
|
-
platform: 'Linux',
|
|
44
|
+
'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.153 Mobile Safari/537.36',
|
|
42
45
|
});
|
|
43
|
-
expect(
|
|
46
|
+
expect(SystemInfo.GetSystem()).toBe(ESystem.ANDROID);
|
|
44
47
|
});
|
|
45
48
|
|
|
46
49
|
it('should get system: iOS', () => {
|
|
47
50
|
mockNavigator({
|
|
48
51
|
userAgent:
|
|
49
|
-
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0_1 like Mac OS X) AppleWebKit/
|
|
50
|
-
platform: 'iPhone',
|
|
52
|
+
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
|
|
51
53
|
});
|
|
52
|
-
expect(
|
|
54
|
+
expect(SystemInfo.GetSystem()).toBe(ESystem.IOS);
|
|
53
55
|
});
|
|
54
56
|
|
|
55
57
|
it('should get system: Unknown', () => {
|
|
56
58
|
mockNavigator({
|
|
57
|
-
|
|
59
|
+
userAgent: 'Unknown Browser',
|
|
58
60
|
});
|
|
59
|
-
expect(
|
|
61
|
+
expect(SystemInfo.GetSystem()).toBe(ESystem.UNKNOWN);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should get system: Unknown when window is undefined', () => {
|
|
65
|
+
const originalNavigator = window.navigator;
|
|
66
|
+
window.navigator = undefined as any;
|
|
67
|
+
expect(SystemInfo.GetSystem()).toBe(ESystem.UNKNOWN);
|
|
68
|
+
window.navigator = originalNavigator;
|
|
60
69
|
});
|
|
61
70
|
|
|
62
71
|
it('should support webXR', async () => {
|
|
@@ -65,19 +74,29 @@ describe('dive/info/DIVEInfo', () => {
|
|
|
65
74
|
isSessionSupported: jest.fn().mockResolvedValue(true),
|
|
66
75
|
},
|
|
67
76
|
});
|
|
68
|
-
const
|
|
77
|
+
const restoreSecureContext = window.isSecureContext;
|
|
78
|
+
window.isSecureContext = true;
|
|
79
|
+
|
|
80
|
+
const supports = await SystemInfo.GetSupportsWebXR();
|
|
69
81
|
expect(supports).toBe(true);
|
|
82
|
+
|
|
83
|
+
window.isSecureContext = restoreSecureContext;
|
|
70
84
|
});
|
|
71
85
|
|
|
72
86
|
it('should not support webXR (xr undefined)', async () => {
|
|
73
87
|
mockNavigator({
|
|
74
88
|
xr: undefined,
|
|
75
89
|
});
|
|
76
|
-
const
|
|
90
|
+
const restoreSecureContext = window.isSecureContext;
|
|
91
|
+
window.isSecureContext = true;
|
|
92
|
+
|
|
93
|
+
const supports = await SystemInfo.GetSupportsWebXR();
|
|
77
94
|
expect(supports).toBe(false);
|
|
78
95
|
|
|
79
|
-
const reason =
|
|
80
|
-
expect(reason).toBe(
|
|
96
|
+
const reason = SystemInfo.GetWebXRUnsupportedReason();
|
|
97
|
+
expect(reason).toBe(EWebXRUnsupportedReason.NO_WEBXR_API);
|
|
98
|
+
|
|
99
|
+
window.isSecureContext = restoreSecureContext;
|
|
81
100
|
});
|
|
82
101
|
|
|
83
102
|
it('should not support webXR (xr undefined & isSecureContext false)', async () => {
|
|
@@ -85,11 +104,11 @@ describe('dive/info/DIVEInfo', () => {
|
|
|
85
104
|
mockNavigator({
|
|
86
105
|
xr: undefined,
|
|
87
106
|
});
|
|
88
|
-
const supports = await
|
|
107
|
+
const supports = await SystemInfo.GetSupportsWebXR();
|
|
89
108
|
expect(supports).toBe(false);
|
|
90
109
|
|
|
91
|
-
const reason =
|
|
92
|
-
expect(reason).toBe(
|
|
110
|
+
const reason = SystemInfo.GetWebXRUnsupportedReason();
|
|
111
|
+
expect(reason).toBe(EWebXRUnsupportedReason.NO_HTTPS);
|
|
93
112
|
});
|
|
94
113
|
|
|
95
114
|
it('should get empty reason (not checked)', async () => {
|
|
@@ -98,9 +117,15 @@ describe('dive/info/DIVEInfo', () => {
|
|
|
98
117
|
isSessionSupported: jest.fn().mockResolvedValue(true),
|
|
99
118
|
},
|
|
100
119
|
});
|
|
120
|
+
const restoreSecureContext = window.isSecureContext;
|
|
121
|
+
window.isSecureContext = true;
|
|
122
|
+
|
|
123
|
+
await SystemInfo.GetSupportsWebXR();
|
|
101
124
|
console.log = jest.fn();
|
|
102
|
-
const reason =
|
|
125
|
+
const reason = SystemInfo.GetWebXRUnsupportedReason();
|
|
103
126
|
expect(reason).toBe(null);
|
|
127
|
+
|
|
128
|
+
window.isSecureContext = restoreSecureContext;
|
|
104
129
|
});
|
|
105
130
|
|
|
106
131
|
it('should get empty reason (webXR supported)', async () => {
|
|
@@ -109,8 +134,14 @@ describe('dive/info/DIVEInfo', () => {
|
|
|
109
134
|
isSessionSupported: jest.fn().mockResolvedValue(true),
|
|
110
135
|
},
|
|
111
136
|
});
|
|
112
|
-
const
|
|
137
|
+
const restoreSecureContext = window.isSecureContext;
|
|
138
|
+
window.isSecureContext = true;
|
|
139
|
+
|
|
140
|
+
await SystemInfo.GetSupportsWebXR();
|
|
141
|
+
const reason = SystemInfo.GetWebXRUnsupportedReason();
|
|
113
142
|
expect(reason).toBe(null);
|
|
143
|
+
|
|
144
|
+
window.isSecureContext = restoreSecureContext;
|
|
114
145
|
});
|
|
115
146
|
|
|
116
147
|
it('should not support webXR', async () => {
|
|
@@ -119,13 +150,18 @@ describe('dive/info/DIVEInfo', () => {
|
|
|
119
150
|
isSessionSupported: jest.fn().mockResolvedValue(false),
|
|
120
151
|
},
|
|
121
152
|
});
|
|
122
|
-
const
|
|
153
|
+
const restoreSecureContext = window.isSecureContext;
|
|
154
|
+
window.isSecureContext = true;
|
|
155
|
+
|
|
156
|
+
const supports = await SystemInfo.GetSupportsWebXR();
|
|
123
157
|
expect(supports).toBe(false);
|
|
124
158
|
|
|
125
|
-
const reason =
|
|
159
|
+
const reason = SystemInfo.GetWebXRUnsupportedReason();
|
|
126
160
|
expect(reason).toBe(
|
|
127
|
-
|
|
161
|
+
EWebXRUnsupportedReason.IMMERSIVE_AR_NOT_SUPPORTED_BY_DEVICE,
|
|
128
162
|
);
|
|
163
|
+
|
|
164
|
+
window.isSecureContext = restoreSecureContext;
|
|
129
165
|
});
|
|
130
166
|
|
|
131
167
|
it('should not support webXR on error', async () => {
|
|
@@ -134,205 +170,177 @@ describe('dive/info/DIVEInfo', () => {
|
|
|
134
170
|
isSessionSupported: jest.fn().mockRejectedValue('error'),
|
|
135
171
|
},
|
|
136
172
|
});
|
|
137
|
-
const
|
|
173
|
+
const restoreSecureContext = window.isSecureContext;
|
|
174
|
+
window.isSecureContext = true;
|
|
175
|
+
|
|
176
|
+
const supports = await SystemInfo.GetSupportsWebXR();
|
|
138
177
|
expect(supports).toBe(false);
|
|
139
178
|
|
|
140
|
-
const reason =
|
|
141
|
-
expect(reason).toBe(
|
|
179
|
+
const reason = SystemInfo.GetWebXRUnsupportedReason();
|
|
180
|
+
expect(reason).toBe(EWebXRUnsupportedReason.AR_PERMISSION_DENIED);
|
|
181
|
+
|
|
182
|
+
window.isSecureContext = restoreSecureContext;
|
|
142
183
|
});
|
|
143
184
|
|
|
144
185
|
it('should return cached value', async () => {
|
|
145
|
-
|
|
186
|
+
SystemInfo['_supportsWebXR'] = true;
|
|
146
187
|
mockNavigator({
|
|
147
188
|
xr: {
|
|
148
189
|
isSessionSupported: jest.fn().mockRejectedValue('error'),
|
|
149
190
|
},
|
|
150
191
|
});
|
|
151
|
-
const supports = await
|
|
192
|
+
const supports = await SystemInfo.GetSupportsWebXR();
|
|
152
193
|
expect(supports).toBe(true);
|
|
153
194
|
});
|
|
154
195
|
|
|
155
196
|
it('should return cached value (false)', async () => {
|
|
156
|
-
|
|
197
|
+
SystemInfo['_supportsWebXR'] = false;
|
|
157
198
|
mockNavigator({
|
|
158
199
|
xr: {
|
|
159
200
|
isSessionSupported: jest.fn().mockRejectedValue('error'),
|
|
160
201
|
},
|
|
161
202
|
});
|
|
162
|
-
const supports = await
|
|
203
|
+
const supports = await SystemInfo.GetSupportsWebXR();
|
|
163
204
|
expect(supports).toBe(false);
|
|
164
205
|
});
|
|
165
206
|
|
|
166
|
-
it('should support ARQuickLook
|
|
167
|
-
mockNavigator({
|
|
168
|
-
userAgent:
|
|
169
|
-
'Mozilla/5.0 (iPhone; CPU iPhone OS 15_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Mobile/15E148 Safari/604.',
|
|
170
|
-
});
|
|
207
|
+
it('should support ARQuickLook when relList supports AR', () => {
|
|
171
208
|
jest.spyOn(document, 'createElement').mockReturnValue({
|
|
172
209
|
relList: { supports: () => true },
|
|
173
210
|
} as unknown as HTMLAnchorElement);
|
|
174
|
-
const supports =
|
|
211
|
+
const supports = SystemInfo.GetSupportsARQuickLook();
|
|
175
212
|
expect(supports).toBe(true);
|
|
176
213
|
});
|
|
177
214
|
|
|
178
|
-
it('should support ARQuickLook
|
|
179
|
-
mockNavigator({
|
|
180
|
-
userAgent:
|
|
181
|
-
'Mozilla/5.0 (iPhone; CPU iPhone OS 15_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Mobile/15E148 Safari/604.',
|
|
182
|
-
});
|
|
215
|
+
it('should not support ARQuickLook when relList does not support AR', () => {
|
|
183
216
|
jest.spyOn(document, 'createElement').mockReturnValue({
|
|
184
217
|
relList: { supports: () => false },
|
|
185
218
|
} as unknown as HTMLAnchorElement);
|
|
186
|
-
const supports =
|
|
187
|
-
expect(supports).toBe(
|
|
219
|
+
const supports = SystemInfo.GetSupportsARQuickLook();
|
|
220
|
+
expect(supports).toBe(false);
|
|
188
221
|
});
|
|
189
222
|
|
|
190
|
-
it('should
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
});
|
|
195
|
-
jest.spyOn(document, 'createElement').mockReturnValue({
|
|
196
|
-
relList: { supports: () => false },
|
|
197
|
-
} as unknown as HTMLAnchorElement);
|
|
198
|
-
const supports = DIVEInfo.GetSupportsARQuickLook();
|
|
199
|
-
expect(supports).toBe(true);
|
|
223
|
+
it('should be mobile (iOS)', () => {
|
|
224
|
+
jest.spyOn(SystemInfo, 'GetSystem').mockReturnValue(ESystem.IOS);
|
|
225
|
+
expect(SystemInfo.isMobile).toBe(true);
|
|
226
|
+
expect(SystemInfo.isDesktop).toBe(false);
|
|
200
227
|
});
|
|
201
228
|
|
|
202
|
-
it('should
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
expect(
|
|
229
|
+
it('should be mobile (Android)', () => {
|
|
230
|
+
jest.spyOn(SystemInfo, 'GetSystem').mockReturnValue(ESystem.ANDROID);
|
|
231
|
+
expect(SystemInfo.isMobile).toBe(true);
|
|
232
|
+
expect(SystemInfo.isDesktop).toBe(false);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('should be desktop (Windows)', () => {
|
|
236
|
+
jest.spyOn(SystemInfo, 'GetSystem').mockReturnValue(ESystem.WINDOWS);
|
|
237
|
+
expect(SystemInfo.isMobile).toBe(false);
|
|
238
|
+
expect(SystemInfo.isDesktop).toBe(true);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should be desktop (MacOS)', () => {
|
|
242
|
+
jest.spyOn(SystemInfo, 'GetSystem').mockReturnValue(ESystem.MACOS);
|
|
243
|
+
expect(SystemInfo.isMobile).toBe(false);
|
|
244
|
+
expect(SystemInfo.isDesktop).toBe(true);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('should be desktop (Linux)', () => {
|
|
248
|
+
jest.spyOn(SystemInfo, 'GetSystem').mockReturnValue(ESystem.LINUX);
|
|
249
|
+
expect(SystemInfo.isMobile).toBe(false);
|
|
250
|
+
expect(SystemInfo.isDesktop).toBe(true);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('should be desktop (Unknown)', () => {
|
|
254
|
+
jest.spyOn(SystemInfo, 'GetSystem').mockReturnValue(ESystem.UNKNOWN);
|
|
255
|
+
expect(SystemInfo.isMobile).toBe(false);
|
|
256
|
+
expect(SystemInfo.isDesktop).toBe(true);
|
|
212
257
|
});
|
|
213
258
|
|
|
214
|
-
it('should
|
|
259
|
+
it('should be capable of AR (ARQuickLook)', async () => {
|
|
260
|
+
jest.spyOn(SystemInfo, 'GetSupportsARQuickLook').mockReturnValue(true);
|
|
261
|
+
jest.spyOn(SystemInfo, 'GetSupportsWebXR').mockResolvedValue(false);
|
|
262
|
+
expect(await SystemInfo.GetIsARCapable()).toBe(true);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('should be capable of AR (WebXR)', async () => {
|
|
266
|
+
jest.spyOn(SystemInfo, 'GetSupportsARQuickLook').mockReturnValue(false);
|
|
267
|
+
jest.spyOn(SystemInfo, 'GetSupportsWebXR').mockResolvedValue(true);
|
|
268
|
+
expect(await SystemInfo.GetIsARCapable()).toBe(true);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('should not be capable of AR', async () => {
|
|
272
|
+
jest.spyOn(SystemInfo, 'GetSupportsARQuickLook').mockReturnValue(false);
|
|
273
|
+
jest.spyOn(SystemInfo, 'GetSupportsWebXR').mockResolvedValue(false);
|
|
274
|
+
expect(await SystemInfo.GetIsARCapable()).toBe(false);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('should support SceneViewer (Android, Chrome 89+)', () => {
|
|
215
278
|
mockNavigator({
|
|
216
279
|
userAgent:
|
|
217
|
-
'Mozilla/5.0 (
|
|
280
|
+
'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Mobile Safari/537.36',
|
|
218
281
|
});
|
|
219
|
-
|
|
220
|
-
relList: { supports: () => false },
|
|
221
|
-
} as unknown as HTMLAnchorElement);
|
|
222
|
-
const supports = DIVEInfo.GetSupportsARQuickLook();
|
|
282
|
+
const supports = SystemInfo.GetSupportsSceneViewer();
|
|
223
283
|
expect(supports).toBe(true);
|
|
224
284
|
});
|
|
225
285
|
|
|
226
|
-
it('should support
|
|
286
|
+
it('should support SceneViewer (Android, Chrome 126+)', () => {
|
|
227
287
|
mockNavigator({
|
|
228
288
|
userAgent:
|
|
229
|
-
'Mozilla/5.0 (
|
|
289
|
+
'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.153 Mobile Safari/537.36',
|
|
230
290
|
});
|
|
231
|
-
|
|
232
|
-
relList: { supports: () => false },
|
|
233
|
-
} as unknown as HTMLAnchorElement);
|
|
234
|
-
const supports = DIVEInfo.GetSupportsARQuickLook();
|
|
291
|
+
const supports = SystemInfo.GetSupportsSceneViewer();
|
|
235
292
|
expect(supports).toBe(true);
|
|
236
293
|
});
|
|
237
294
|
|
|
238
|
-
it('should not support
|
|
295
|
+
it('should not support SceneViewer (Android, Chrome <89)', () => {
|
|
239
296
|
mockNavigator({
|
|
240
297
|
userAgent:
|
|
241
|
-
'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
298
|
+
'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4389.72 Mobile Safari/537.36',
|
|
242
299
|
});
|
|
243
|
-
|
|
244
|
-
relList: { supports: () => false },
|
|
245
|
-
} as unknown as HTMLAnchorElement);
|
|
246
|
-
const supports = DIVEInfo.GetSupportsARQuickLook();
|
|
300
|
+
const supports = SystemInfo.GetSupportsSceneViewer();
|
|
247
301
|
expect(supports).toBe(false);
|
|
248
302
|
});
|
|
249
303
|
|
|
250
|
-
it('should not support
|
|
304
|
+
it('should not support SceneViewer (Android, no Chrome)', () => {
|
|
251
305
|
mockNavigator({
|
|
252
306
|
userAgent:
|
|
253
|
-
'Mozilla/5.0 (
|
|
307
|
+
'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Mobile Safari/537.36',
|
|
254
308
|
});
|
|
255
|
-
|
|
256
|
-
relList: { supports: () => false },
|
|
257
|
-
} as unknown as HTMLAnchorElement);
|
|
258
|
-
const supports = DIVEInfo.GetSupportsARQuickLook();
|
|
309
|
+
const supports = SystemInfo.GetSupportsSceneViewer();
|
|
259
310
|
expect(supports).toBe(false);
|
|
260
311
|
});
|
|
261
312
|
|
|
262
|
-
it('should not support
|
|
313
|
+
it('should not support SceneViewer (iOS)', () => {
|
|
263
314
|
mockNavigator({
|
|
264
|
-
userAgent:
|
|
315
|
+
userAgent:
|
|
316
|
+
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
|
|
265
317
|
});
|
|
266
|
-
|
|
267
|
-
relList: { supports: () => false },
|
|
268
|
-
} as unknown as HTMLAnchorElement);
|
|
269
|
-
const supports = DIVEInfo.GetSupportsARQuickLook();
|
|
318
|
+
const supports = SystemInfo.GetSupportsSceneViewer();
|
|
270
319
|
expect(supports).toBe(false);
|
|
271
320
|
});
|
|
272
321
|
|
|
273
|
-
it('should not support
|
|
322
|
+
it('should not support SceneViewer (Windows)', () => {
|
|
274
323
|
mockNavigator({
|
|
275
324
|
userAgent:
|
|
276
|
-
'Mozilla/5.0 (
|
|
325
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.153 Safari/537.36',
|
|
277
326
|
});
|
|
278
|
-
|
|
279
|
-
relList: { supports: () => false },
|
|
280
|
-
} as unknown as HTMLAnchorElement);
|
|
281
|
-
const supports = DIVEInfo.GetSupportsARQuickLook();
|
|
327
|
+
const supports = SystemInfo.GetSupportsSceneViewer();
|
|
282
328
|
expect(supports).toBe(false);
|
|
283
329
|
});
|
|
284
330
|
|
|
285
|
-
it('should
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
it('should be mobile (Android)', () => {
|
|
292
|
-
jest.spyOn(DIVEInfo, 'GetSystem').mockReturnValue('Android');
|
|
293
|
-
expect(DIVEInfo.isMobile).toBe(true);
|
|
294
|
-
expect(DIVEInfo.isDesktop).toBe(false);
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
it('should be desktop (Windows)', () => {
|
|
298
|
-
jest.spyOn(DIVEInfo, 'GetSystem').mockReturnValue('Windows');
|
|
299
|
-
expect(DIVEInfo.isMobile).toBe(false);
|
|
300
|
-
expect(DIVEInfo.isDesktop).toBe(true);
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
it('should be desktop (MacOS)', () => {
|
|
304
|
-
jest.spyOn(DIVEInfo, 'GetSystem').mockReturnValue('MacOS');
|
|
305
|
-
expect(DIVEInfo.isMobile).toBe(false);
|
|
306
|
-
expect(DIVEInfo.isDesktop).toBe(true);
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
it('should be desktop (Linux)', () => {
|
|
310
|
-
jest.spyOn(DIVEInfo, 'GetSystem').mockReturnValue('Linux');
|
|
311
|
-
expect(DIVEInfo.isMobile).toBe(false);
|
|
312
|
-
expect(DIVEInfo.isDesktop).toBe(true);
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
it('should be desktop (Unknown)', () => {
|
|
316
|
-
jest.spyOn(DIVEInfo, 'GetSystem').mockReturnValue('Unknown');
|
|
317
|
-
expect(DIVEInfo.isMobile).toBe(false);
|
|
318
|
-
expect(DIVEInfo.isDesktop).toBe(true);
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
it('should be capable of AR (ARQuickLook)', async () => {
|
|
322
|
-
jest.spyOn(DIVEInfo, 'GetSupportsARQuickLook').mockReturnValue(true);
|
|
323
|
-
jest.spyOn(DIVEInfo, 'GetSupportsWebXR').mockResolvedValue(false);
|
|
324
|
-
expect(await DIVEInfo.GetIsARCapable()).toBe(true);
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
it('should be capable of AR (WebXR)', async () => {
|
|
328
|
-
jest.spyOn(DIVEInfo, 'GetSupportsARQuickLook').mockReturnValue(false);
|
|
329
|
-
jest.spyOn(DIVEInfo, 'GetSupportsWebXR').mockResolvedValue(true);
|
|
330
|
-
expect(await DIVEInfo.GetIsARCapable()).toBe(true);
|
|
331
|
+
it('should not support SceneViewer (no window)', () => {
|
|
332
|
+
const originalWindow = global.window;
|
|
333
|
+
global.window = undefined as any;
|
|
334
|
+
const supports = SystemInfo.GetSupportsSceneViewer();
|
|
335
|
+
expect(supports).toBe(false);
|
|
336
|
+
global.window = originalWindow;
|
|
331
337
|
});
|
|
332
338
|
|
|
333
|
-
it('should not
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
339
|
+
it('should not support SceneViewer (no navigator)', () => {
|
|
340
|
+
const originalNavigator = global.navigator;
|
|
341
|
+
global.navigator = undefined as any;
|
|
342
|
+
const supports = SystemInfo.GetSupportsSceneViewer();
|
|
343
|
+
expect(supports).toBe(false);
|
|
344
|
+
global.navigator = originalNavigator;
|
|
337
345
|
});
|
|
338
346
|
});
|
package/src/interface/Movable.ts
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export
|
|
8
|
-
isMovable: true;
|
|
9
|
-
onMoveStart
|
|
10
|
-
onMove
|
|
11
|
-
onMoveEnd
|
|
7
|
+
export class DIVEMovable {
|
|
8
|
+
readonly isMovable: true = true;
|
|
9
|
+
public onMoveStart?(): void;
|
|
10
|
+
public onMove?(): void;
|
|
11
|
+
public onMoveEnd?(): void;
|
|
12
12
|
}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export
|
|
8
|
-
isSelectable: true;
|
|
9
|
-
onSelect
|
|
10
|
-
onDeselect
|
|
7
|
+
export class DIVESelectable {
|
|
8
|
+
readonly isSelectable: true = true;
|
|
9
|
+
public onSelect?(): void;
|
|
10
|
+
public onDeselect?(): void;
|
|
11
11
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
|
|
2
|
+
import { USDZLoader } from 'three/examples/jsm/loaders/USDZLoader';
|
|
3
|
+
import { Object3D } from 'three';
|
|
4
|
+
import { type FileType, SUPPORTED_FILE_TYPES } from '../types';
|
|
5
|
+
|
|
6
|
+
export class Loader {
|
|
7
|
+
private _gltfLoader: GLTFLoader;
|
|
8
|
+
private _usdzLoader: USDZLoader;
|
|
9
|
+
|
|
10
|
+
constructor() {
|
|
11
|
+
this._gltfLoader = new GLTFLoader();
|
|
12
|
+
this._usdzLoader = new USDZLoader();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public async load(uri: string): Promise<Object3D> {
|
|
16
|
+
const type = this._getFileTypeFromUri(uri);
|
|
17
|
+
return this._load(uri, type);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private _getFileTypeFromUri(uri: string): FileType {
|
|
21
|
+
const extension = uri.split('.').pop()?.toLowerCase() as FileType;
|
|
22
|
+
if (!extension || !SUPPORTED_FILE_TYPES.includes(extension)) {
|
|
23
|
+
throw new Error(`Unsupported file type: ${extension}`);
|
|
24
|
+
}
|
|
25
|
+
return extension;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private async _load(uri: string, type: FileType): Promise<Object3D> {
|
|
29
|
+
switch (type) {
|
|
30
|
+
case 'glb':
|
|
31
|
+
case 'gltf': {
|
|
32
|
+
return this._loadGltf(uri);
|
|
33
|
+
}
|
|
34
|
+
case 'usdz': {
|
|
35
|
+
return this._loadUsdz(uri);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private async _loadGltf(uri: string): Promise<Object3D> {
|
|
41
|
+
const gltf = await this._gltfLoader.loadAsync(uri);
|
|
42
|
+
return gltf.scene;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private async _loadUsdz(uri: string): Promise<Object3D> {
|
|
46
|
+
return this._usdzLoader.loadAsync(uri);
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/model/Model.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Mesh,
|
|
3
|
+
MeshStandardMaterial,
|
|
4
|
+
type Object3D,
|
|
5
|
+
Raycaster,
|
|
6
|
+
Vector3,
|
|
7
|
+
} from 'three';
|
|
2
8
|
import { PRODUCT_LAYER_MASK } from '../constant/VisibilityLayerMask';
|
|
3
|
-
import type { GLTF } from 'three/examples/jsm/loaders/GLTFLoader';
|
|
4
9
|
import { findSceneRecursive } from '../helper/findSceneRecursive/findSceneRecursive';
|
|
5
10
|
import { type COMMaterial } from '../com/types';
|
|
6
11
|
import { DIVENode } from '../node/Node';
|
|
@@ -22,11 +27,11 @@ export class DIVEModel extends DIVENode {
|
|
|
22
27
|
private _mesh: Mesh | null = null;
|
|
23
28
|
private _material: MeshStandardMaterial | null = null;
|
|
24
29
|
|
|
25
|
-
public SetModel(gltf:
|
|
30
|
+
public SetModel(gltf: Object3D): void {
|
|
26
31
|
this.clear();
|
|
27
32
|
this._boundingBox.makeEmpty();
|
|
28
33
|
|
|
29
|
-
gltf.
|
|
34
|
+
gltf.traverse((child) => {
|
|
30
35
|
child.castShadow = true;
|
|
31
36
|
child.receiveShadow = true;
|
|
32
37
|
|
|
@@ -47,7 +52,7 @@ export class DIVEModel extends DIVENode {
|
|
|
47
52
|
}
|
|
48
53
|
});
|
|
49
54
|
|
|
50
|
-
this.add(gltf
|
|
55
|
+
this.add(gltf);
|
|
51
56
|
}
|
|
52
57
|
|
|
53
58
|
public SetMaterial(material: Partial<COMMaterial>): void {
|
|
@@ -2,7 +2,6 @@ import { RaycasterIntersectObjectMock } from '../../../__mocks__/three';
|
|
|
2
2
|
|
|
3
3
|
import { DIVEModel } from '../Model';
|
|
4
4
|
import { DIVECommunication } from '../../com/Communication';
|
|
5
|
-
import { GLTF } from 'three/examples/jsm/loaders/GLTFLoader';
|
|
6
5
|
import { DIVEScene } from '../../scene/Scene';
|
|
7
6
|
import {
|
|
8
7
|
Vector3,
|
|
@@ -29,12 +28,6 @@ jest.mock('../../com/Communication.ts', () => {
|
|
|
29
28
|
const object = new Object3D();
|
|
30
29
|
object.children.push(new Mesh());
|
|
31
30
|
|
|
32
|
-
const gltf = {
|
|
33
|
-
scene: {
|
|
34
|
-
...object,
|
|
35
|
-
},
|
|
36
|
-
} as unknown as GLTF;
|
|
37
|
-
|
|
38
31
|
jest.spyOn(DIVECommunication, 'get').mockReturnValue({
|
|
39
32
|
PerformAction: jest.fn(),
|
|
40
33
|
} as unknown as DIVECommunication);
|
|
@@ -57,11 +50,11 @@ describe('dive/model/DIVEModel', () => {
|
|
|
57
50
|
});
|
|
58
51
|
|
|
59
52
|
it('should set model', () => {
|
|
60
|
-
expect(() => model.SetModel(
|
|
53
|
+
expect(() => model.SetModel(object)).not.toThrow();
|
|
61
54
|
});
|
|
62
55
|
|
|
63
56
|
it('should place on floor', () => {
|
|
64
|
-
model.SetModel(
|
|
57
|
+
model.SetModel(object);
|
|
65
58
|
|
|
66
59
|
const com = DIVECommunication.get('id')!;
|
|
67
60
|
const spyPerformAction = jest.spyOn(com, 'PerformAction');
|
|
@@ -204,14 +197,14 @@ describe('dive/model/DIVEModel', () => {
|
|
|
204
197
|
|
|
205
198
|
it('should set model material when material already set before', () => {
|
|
206
199
|
model.SetMaterial({ roughness: 0.5 } as COMMaterial);
|
|
207
|
-
expect(() => model.SetModel(
|
|
200
|
+
expect(() => model.SetModel(object)).not.toThrow();
|
|
208
201
|
expect(
|
|
209
202
|
(model['_mesh']?.material as MeshStandardMaterial).roughness,
|
|
210
203
|
).toBe(0.5);
|
|
211
204
|
});
|
|
212
205
|
|
|
213
206
|
it('should set material to model when model already set before', () => {
|
|
214
|
-
model.SetModel(
|
|
207
|
+
model.SetModel(object);
|
|
215
208
|
expect(() =>
|
|
216
209
|
model.SetMaterial({ roughness: 0.5 } as COMMaterial),
|
|
217
210
|
).not.toThrow();
|