@shopware-ag/dive 1.15.4 → 1.16.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/build/dive.cjs +188 -20
- package/build/dive.cjs.map +1 -1
- package/build/dive.d.cts +17 -3
- package/build/dive.d.ts +17 -3
- package/build/dive.js +190 -22
- package/build/dive.js.map +1 -1
- package/package.json +3 -1
- package/src/com/Communication.ts +23 -2
- package/src/com/__test__/Communication.test.ts +27 -6
- package/src/com/actions/index.ts +2 -0
- package/src/com/actions/scene/exportscene.ts +6 -0
- package/src/com/types/COMBaseEntity.ts +1 -1
- package/src/dive.ts +2 -1
- package/src/io/IO.ts +107 -0
- package/src/io/__test__/IO.test.ts +145 -0
- package/src/io/gltf/GLTFIO.ts +53 -0
- package/src/io/gltf/__test__/GLTFIO.test.ts +175 -0
- package/src/primitive/Primitive.ts +5 -3
- package/src/primitive/__test__/Primitive.test.ts +4 -2
- package/src/scene/root/Root.ts +12 -16
- package/src/scene/root/__test__/Root.test.ts +9 -9
- package/src/types/SceneType.ts +14 -0
- package/src/types/index.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopware-ag/dive",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "Shopware Spatial Framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./build/dive.cjs",
|
|
@@ -30,12 +30,14 @@
|
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@tweenjs/tween.js": "^23.1.1",
|
|
33
|
+
"lodash": "^4.17.21",
|
|
33
34
|
"three": "^0.163.0",
|
|
34
35
|
"three-spritetext": "^1.8.2"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
38
|
"@eslint/js": "^9.1.1",
|
|
38
39
|
"@types/jest": "^29.5.12",
|
|
40
|
+
"@types/lodash": "^4.17.12",
|
|
39
41
|
"@types/node": "^20.12.7",
|
|
40
42
|
"@types/three": "^0.163.0",
|
|
41
43
|
"eslint": "^9.1.1",
|
package/src/com/Communication.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Actions } from "./actions/index.ts";
|
|
2
2
|
import { generateUUID } from 'three/src/math/MathUtils';
|
|
3
3
|
import { isSelectTool } from "../toolbox/select/SelectTool.ts";
|
|
4
|
+
import { merge } from 'lodash';
|
|
4
5
|
|
|
5
6
|
// type imports
|
|
6
7
|
import { type Color, type MeshStandardMaterial } from "three";
|
|
@@ -12,6 +13,8 @@ import { type DIVEModel } from "../model/Model.ts";
|
|
|
12
13
|
import { type DIVEMediaCreator } from "../mediacreator/MediaCreator.ts";
|
|
13
14
|
import { type DIVERenderer } from "../renderer/Renderer.ts";
|
|
14
15
|
import { type DIVESelectable } from "../interface/Selectable.ts";
|
|
16
|
+
import { type DIVEIO } from "../io/IO.ts";
|
|
17
|
+
|
|
15
18
|
|
|
16
19
|
type EventListener<Action extends keyof Actions> = (payload: Actions[Action]['PAYLOAD']) => void;
|
|
17
20
|
|
|
@@ -65,6 +68,15 @@ export class DIVECommunication {
|
|
|
65
68
|
return this._mediaGenerator;
|
|
66
69
|
}
|
|
67
70
|
|
|
71
|
+
private _io: DIVEIO | null;
|
|
72
|
+
private get io(): DIVEIO {
|
|
73
|
+
if (!this._io) {
|
|
74
|
+
const DIVEIO = require('../io/IO.ts').DIVEIO as typeof import('../io/IO.ts').DIVEIO;
|
|
75
|
+
this._io = new DIVEIO(this.scene);
|
|
76
|
+
}
|
|
77
|
+
return this._io;
|
|
78
|
+
}
|
|
79
|
+
|
|
68
80
|
private registered: Map<string, COMEntity> = new Map();
|
|
69
81
|
|
|
70
82
|
// private listeners: { [key: string]: EventListener[] } = {};
|
|
@@ -77,6 +89,7 @@ export class DIVECommunication {
|
|
|
77
89
|
this.controller = controls;
|
|
78
90
|
this.toolbox = toolbox;
|
|
79
91
|
this._mediaGenerator = null;
|
|
92
|
+
this._io = null;
|
|
80
93
|
|
|
81
94
|
DIVECommunication.__instances.push(this);
|
|
82
95
|
}
|
|
@@ -192,6 +205,10 @@ export class DIVECommunication {
|
|
|
192
205
|
returnValue = this.setParent(payload as Actions['SET_PARENT']['PAYLOAD']);
|
|
193
206
|
break;
|
|
194
207
|
}
|
|
208
|
+
case 'EXPORT_SCENE': {
|
|
209
|
+
returnValue = this.exportScene(payload as Actions['EXPORT_SCENE']['PAYLOAD']);
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
195
212
|
}
|
|
196
213
|
|
|
197
214
|
this.dispatch(action, payload);
|
|
@@ -266,7 +283,7 @@ export class DIVECommunication {
|
|
|
266
283
|
private addObject(payload: Actions['ADD_OBJECT']['PAYLOAD']): Actions['ADD_OBJECT']['RETURN'] {
|
|
267
284
|
if (this.registered.get(payload.id)) return false;
|
|
268
285
|
|
|
269
|
-
if (payload.
|
|
286
|
+
if (payload.parentId === undefined) payload.parentId = null;
|
|
270
287
|
|
|
271
288
|
this.registered.set(payload.id, payload);
|
|
272
289
|
|
|
@@ -279,7 +296,7 @@ export class DIVECommunication {
|
|
|
279
296
|
const objectToUpdate = this.registered.get(payload.id);
|
|
280
297
|
if (!objectToUpdate) return false;
|
|
281
298
|
|
|
282
|
-
this.registered.set(payload.id,
|
|
299
|
+
this.registered.set(payload.id, merge(objectToUpdate, payload));
|
|
283
300
|
|
|
284
301
|
const updatedObject = this.registered.get(payload.id)!;
|
|
285
302
|
this.scene.UpdateSceneObject({ ...payload, id: updatedObject.id, entityType: updatedObject.entityType });
|
|
@@ -522,6 +539,10 @@ export class DIVECommunication {
|
|
|
522
539
|
parentObject.attach(sceneObject);
|
|
523
540
|
return true;
|
|
524
541
|
}
|
|
542
|
+
|
|
543
|
+
private exportScene(payload: Actions['EXPORT_SCENE']['PAYLOAD']): Actions['EXPORT_SCENE']['RETURN'] {
|
|
544
|
+
return this.io.Export(payload.type);
|
|
545
|
+
}
|
|
525
546
|
}
|
|
526
547
|
|
|
527
548
|
export type { Actions } from './actions/index.ts';
|
|
@@ -44,6 +44,16 @@ jest.mock('../../mediacreator/MediaCreator', () => {
|
|
|
44
44
|
}
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
+
jest.mock('../../io/IO', () => {
|
|
48
|
+
return {
|
|
49
|
+
DIVEIO: jest.fn(function () {
|
|
50
|
+
this.Import = jest.fn();
|
|
51
|
+
this.Export = jest.fn();
|
|
52
|
+
return this;
|
|
53
|
+
}),
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
47
57
|
jest.mock('../../toolbox/select/SelectTool', () => {
|
|
48
58
|
return {
|
|
49
59
|
isSelectTool: jest.fn().mockReturnValue(true),
|
|
@@ -521,7 +531,7 @@ describe('dive/communication/DIVECommunication', () => {
|
|
|
521
531
|
id: "group1",
|
|
522
532
|
position: { x: 0, y: 0, z: 0 },
|
|
523
533
|
rotation: { x: 0, y: 0, z: 0 },
|
|
524
|
-
|
|
534
|
+
parentId: null,
|
|
525
535
|
} as COMGroup);
|
|
526
536
|
|
|
527
537
|
const success = testCom.PerformAction('GET_ALL_SCENE_DATA', {});
|
|
@@ -532,7 +542,7 @@ describe('dive/communication/DIVECommunication', () => {
|
|
|
532
542
|
id: "pov",
|
|
533
543
|
position: { x: 0, y: 0, z: 0 },
|
|
534
544
|
target: { x: 0, y: 0, z: 0 },
|
|
535
|
-
|
|
545
|
+
parentId: null,
|
|
536
546
|
}],
|
|
537
547
|
floorColor: "#ffffff",
|
|
538
548
|
floorEnabled: true,
|
|
@@ -542,7 +552,7 @@ describe('dive/communication/DIVECommunication', () => {
|
|
|
542
552
|
type: "ambient",
|
|
543
553
|
intensity: 0.5,
|
|
544
554
|
color: 'white',
|
|
545
|
-
|
|
555
|
+
parentId: null,
|
|
546
556
|
}],
|
|
547
557
|
mediaItem: null,
|
|
548
558
|
name: undefined,
|
|
@@ -552,7 +562,7 @@ describe('dive/communication/DIVECommunication', () => {
|
|
|
552
562
|
position: { x: 0, y: 0, z: 0 },
|
|
553
563
|
rotation: { x: 0, y: 0, z: 0 },
|
|
554
564
|
scale: { x: 0.01, y: 0.01, z: 0.01 },
|
|
555
|
-
|
|
565
|
+
parentId: null,
|
|
556
566
|
uri: "https://threejs.org/examples/models/gltf/LittlestTokyo.glb",
|
|
557
567
|
}],
|
|
558
568
|
primitives: [],
|
|
@@ -566,7 +576,7 @@ describe('dive/communication/DIVECommunication', () => {
|
|
|
566
576
|
id: "group1",
|
|
567
577
|
position: { x: 0, y: 0, z: 0 },
|
|
568
578
|
rotation: { x: 0, y: 0, z: 0 },
|
|
569
|
-
|
|
579
|
+
parentId: null,
|
|
570
580
|
}],
|
|
571
581
|
});
|
|
572
582
|
});
|
|
@@ -592,7 +602,7 @@ describe('dive/communication/DIVECommunication', () => {
|
|
|
592
602
|
expect(Array.from(successWithoutIds.values())).toStrictEqual([]);
|
|
593
603
|
|
|
594
604
|
const successWithIds = testCom.PerformAction('GET_OBJECTS', { ids: ['test1'] });
|
|
595
|
-
expect(Array.from(successWithIds.values())).toStrictEqual([{ entityType: "pov", id: "test1", position: { x: 0, y: 0, z: 0 }, target: { x: 0, y: 0, z: 0 },
|
|
605
|
+
expect(Array.from(successWithIds.values())).toStrictEqual([{ entityType: "pov", id: "test1", position: { x: 0, y: 0, z: 0 }, target: { x: 0, y: 0, z: 0 }, parentId: null }]);
|
|
596
606
|
});
|
|
597
607
|
|
|
598
608
|
it('should perform action SELECT_OBJECT', () => {
|
|
@@ -846,4 +856,15 @@ describe('dive/communication/DIVECommunication', () => {
|
|
|
846
856
|
});
|
|
847
857
|
expect(attachToValidParent).toBe(true);
|
|
848
858
|
});
|
|
859
|
+
|
|
860
|
+
it('should perform action EXPORT_SCENE', async () => {
|
|
861
|
+
const url = 'https://example.com';
|
|
862
|
+
|
|
863
|
+
jest.spyOn(testCom['io'], 'Export').mockResolvedValueOnce(url);
|
|
864
|
+
|
|
865
|
+
const result = await testCom.PerformAction('EXPORT_SCENE', {
|
|
866
|
+
type: 'glb',
|
|
867
|
+
});
|
|
868
|
+
expect(result).toBe(url);
|
|
869
|
+
});
|
|
849
870
|
});
|
package/src/com/actions/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ import SET_GIZMO_VISIBILITY from "./toolbox/transform/setgizmovisible.js";
|
|
|
23
23
|
import COMPUTE_ENCOMPASSING_VIEW from "./camera/computeencompassingview.ts";
|
|
24
24
|
import USE_TOOL from "./toolbox/usetool.ts";
|
|
25
25
|
import SET_PARENT from "./object/setparent.ts";
|
|
26
|
+
import EXPORT_SCENE from "./scene/exportscene.ts";
|
|
26
27
|
|
|
27
28
|
export type Actions = {
|
|
28
29
|
GET_ALL_SCENE_DATA: GET_ALL_SCENE_DATA,
|
|
@@ -50,4 +51,5 @@ export type Actions = {
|
|
|
50
51
|
UPDATE_SCENE: UPDATE_SCENE,
|
|
51
52
|
GENERATE_MEDIA: GENERATE_MEDIA,
|
|
52
53
|
SET_PARENT: SET_PARENT,
|
|
54
|
+
EXPORT_SCENE: EXPORT_SCENE,
|
|
53
55
|
};
|
package/src/dive.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { getObjectDelta } from "./helper/getObjectDelta/getObjectDelta.ts";
|
|
|
10
10
|
|
|
11
11
|
import { generateUUID } from "three/src/math/MathUtils";
|
|
12
12
|
import { DIVEInfo } from "./info/Info.ts";
|
|
13
|
+
import pkgjson from '../package.json';
|
|
13
14
|
|
|
14
15
|
export type DIVESettings = {
|
|
15
16
|
autoResize: boolean;
|
|
@@ -227,7 +228,7 @@ export default class DIVE {
|
|
|
227
228
|
},
|
|
228
229
|
}
|
|
229
230
|
|
|
230
|
-
console.log(
|
|
231
|
+
console.log(`DIVE ${pkgjson.version} initialized`);
|
|
231
232
|
}
|
|
232
233
|
|
|
233
234
|
public Dispose(): void {
|
package/src/io/IO.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { DIVEGLTFIO } from "./gltf/GLTFIO";
|
|
2
|
+
|
|
3
|
+
import { type DIVESceneFileType } from "../types";
|
|
4
|
+
import { type DIVEScene } from "../scene/Scene";
|
|
5
|
+
|
|
6
|
+
export class DIVEIO {
|
|
7
|
+
|
|
8
|
+
private _scene: DIVEScene;
|
|
9
|
+
|
|
10
|
+
private _gltfIO: DIVEGLTFIO;
|
|
11
|
+
|
|
12
|
+
constructor(scene: DIVEScene) {
|
|
13
|
+
|
|
14
|
+
this._scene = scene;
|
|
15
|
+
|
|
16
|
+
this._gltfIO = new DIVEGLTFIO();
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public Import<FileType extends keyof DIVESceneFileType>(type: FileType, url: string): Promise<DIVESceneFileType[FileType] | null> {
|
|
21
|
+
|
|
22
|
+
return this._importFromURL(type, url)
|
|
23
|
+
|
|
24
|
+
.catch((error) => {
|
|
25
|
+
|
|
26
|
+
console.error(error);
|
|
27
|
+
|
|
28
|
+
return null;
|
|
29
|
+
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public Export<FileType extends keyof DIVESceneFileType>(type: FileType): Promise<string | null> {
|
|
35
|
+
|
|
36
|
+
return this._exportToURL(type)
|
|
37
|
+
|
|
38
|
+
.catch((error) => {
|
|
39
|
+
|
|
40
|
+
console.error(error);
|
|
41
|
+
|
|
42
|
+
return null;
|
|
43
|
+
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private _importFromURL<FileType extends keyof DIVESceneFileType>(type: FileType, url: string): Promise<DIVESceneFileType[FileType]> {
|
|
49
|
+
|
|
50
|
+
switch (type) {
|
|
51
|
+
|
|
52
|
+
case 'glb': {
|
|
53
|
+
|
|
54
|
+
return this._gltfIO.Import(url);
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
default: {
|
|
59
|
+
|
|
60
|
+
return Promise.reject('Unsupported file type: ' + type);
|
|
61
|
+
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private _exportToURL<FileType extends keyof DIVESceneFileType>(type: FileType): Promise<string> {
|
|
68
|
+
|
|
69
|
+
switch (type) {
|
|
70
|
+
|
|
71
|
+
case 'glb': {
|
|
72
|
+
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
|
|
75
|
+
this._gltfIO.Export(this._scene, true, true)
|
|
76
|
+
|
|
77
|
+
.then((data) => {
|
|
78
|
+
|
|
79
|
+
resolve(this._createBlobURL(data))
|
|
80
|
+
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
.catch((error) => {
|
|
84
|
+
|
|
85
|
+
reject(error);
|
|
86
|
+
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
default: {
|
|
93
|
+
|
|
94
|
+
return Promise.reject('Unsupported file type: ' + type);
|
|
95
|
+
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private _createBlobURL(data: ArrayBuffer): string {
|
|
102
|
+
|
|
103
|
+
return URL.createObjectURL(new Blob([data]));
|
|
104
|
+
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { DIVEIO } from '../IO';
|
|
2
|
+
import { DIVEScene } from '../../scene/Scene';
|
|
3
|
+
|
|
4
|
+
import { type GLTF } from 'three/examples/jsm/loaders/GLTFLoader';
|
|
5
|
+
|
|
6
|
+
jest.mock('../gltf/GLTFIO', () => {
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
|
|
10
|
+
DIVEGLTFIO: jest.fn(function () {
|
|
11
|
+
|
|
12
|
+
this.Import = jest.fn();
|
|
13
|
+
|
|
14
|
+
this.Export = jest.fn();
|
|
15
|
+
|
|
16
|
+
return this;
|
|
17
|
+
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
jest.mock('../../scene/Scene.ts', () => {
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
|
|
28
|
+
DIVEScene: jest.fn(function () {
|
|
29
|
+
|
|
30
|
+
this.add = jest.fn();
|
|
31
|
+
|
|
32
|
+
this.isObject3D = true;
|
|
33
|
+
|
|
34
|
+
this.parent = null;
|
|
35
|
+
|
|
36
|
+
this.dispatchEvent = jest.fn();
|
|
37
|
+
|
|
38
|
+
this.position = {
|
|
39
|
+
|
|
40
|
+
set: jest.fn(),
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.SetIntensity = jest.fn();
|
|
45
|
+
|
|
46
|
+
this.SetEnabled = jest.fn();
|
|
47
|
+
|
|
48
|
+
this.SetColor = jest.fn();
|
|
49
|
+
|
|
50
|
+
this.userData = {
|
|
51
|
+
|
|
52
|
+
id: undefined,
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.removeFromParent = jest.fn();
|
|
57
|
+
|
|
58
|
+
return this;
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
let testIO: DIVEIO;
|
|
66
|
+
|
|
67
|
+
describe('dive/io/DIVEIO', () => {
|
|
68
|
+
beforeEach(() => {
|
|
69
|
+
|
|
70
|
+
testIO = new DIVEIO(new DIVEScene());
|
|
71
|
+
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
afterEach(() => {
|
|
75
|
+
|
|
76
|
+
jest.clearAllMocks();
|
|
77
|
+
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should instantiate', () => {
|
|
81
|
+
|
|
82
|
+
expect(testIO).toBeDefined();
|
|
83
|
+
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should import from URL', async () => {
|
|
87
|
+
|
|
88
|
+
const mockGLTF = {} as GLTF;
|
|
89
|
+
|
|
90
|
+
jest.spyOn(testIO['_gltfIO'], 'Import').mockResolvedValueOnce(mockGLTF);
|
|
91
|
+
|
|
92
|
+
const result = await testIO.Import('glb', 'test.glb');
|
|
93
|
+
|
|
94
|
+
expect(result).toStrictEqual(mockGLTF);
|
|
95
|
+
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should reject when importing with unsupported file type', async () => {
|
|
99
|
+
|
|
100
|
+
jest.spyOn(console, 'error').mockImplementationOnce(() => { });
|
|
101
|
+
|
|
102
|
+
const result = await testIO.Import('unsupported file type' as "glb", 'test.glb');
|
|
103
|
+
|
|
104
|
+
expect(result).toStrictEqual(null);
|
|
105
|
+
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should export to URL', async () => {
|
|
109
|
+
|
|
110
|
+
const mockObject = {};
|
|
111
|
+
|
|
112
|
+
const mockURL = 'blob://mockURL';
|
|
113
|
+
|
|
114
|
+
jest.spyOn(testIO['_gltfIO'], 'Export').mockResolvedValueOnce(mockObject);
|
|
115
|
+
|
|
116
|
+
URL.createObjectURL = jest.fn().mockReturnValueOnce(mockURL);
|
|
117
|
+
|
|
118
|
+
const result = await testIO.Export('glb');
|
|
119
|
+
|
|
120
|
+
expect(result).toBeDefined();
|
|
121
|
+
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should handle rejection from gltf io', async () => {
|
|
125
|
+
|
|
126
|
+
jest.spyOn(console, 'error').mockImplementationOnce(() => { });
|
|
127
|
+
|
|
128
|
+
jest.spyOn(testIO['_gltfIO'], 'Export').mockRejectedValueOnce('Error');
|
|
129
|
+
|
|
130
|
+
const result = await testIO.Export('glb');
|
|
131
|
+
|
|
132
|
+
expect(result).toBeDefined();
|
|
133
|
+
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should reject when exporting with unsupported file type', async () => {
|
|
137
|
+
|
|
138
|
+
jest.spyOn(console, 'error').mockImplementationOnce(() => { });
|
|
139
|
+
|
|
140
|
+
const result = await testIO.Export('unsupported file type' as "glb");
|
|
141
|
+
|
|
142
|
+
expect(result).toStrictEqual(null);
|
|
143
|
+
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { GLTFLoader, type GLTF } from "three/examples/jsm/loaders/GLTFLoader";
|
|
2
|
+
import { GLTFExporter } from "three/examples/jsm/exporters/GLTFExporter";
|
|
3
|
+
import { Object3D } from "three";
|
|
4
|
+
|
|
5
|
+
export class DIVEGLTFIO {
|
|
6
|
+
|
|
7
|
+
private _importer: GLTFLoader;
|
|
8
|
+
|
|
9
|
+
private _exporter: GLTFExporter;
|
|
10
|
+
|
|
11
|
+
constructor() {
|
|
12
|
+
|
|
13
|
+
this._importer = new GLTFLoader();
|
|
14
|
+
|
|
15
|
+
this._exporter = new GLTFExporter();
|
|
16
|
+
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public Import(url: string, onProgress?: (progress: number) => void): Promise<GLTF> {
|
|
20
|
+
|
|
21
|
+
return this._importer.loadAsync(url, (progress) => {
|
|
22
|
+
|
|
23
|
+
if (!onProgress) return;
|
|
24
|
+
|
|
25
|
+
onProgress(progress.loaded / progress.total);
|
|
26
|
+
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public Export(object: Object3D, binary: true, onlyVisible: boolean): Promise<ArrayBuffer>;
|
|
32
|
+
|
|
33
|
+
public Export(object: Object3D, binary: false, onlyVisible: boolean): Promise<{ [key: string]: unknown }>;
|
|
34
|
+
|
|
35
|
+
public Export(object: Object3D, binary: boolean, onlyVisible: boolean): Promise<ArrayBuffer | { [key: string]: unknown }> {
|
|
36
|
+
|
|
37
|
+
if (binary) {
|
|
38
|
+
|
|
39
|
+
// export as binary ArrayBuffer
|
|
40
|
+
|
|
41
|
+
return this._exporter.parseAsync(object, { binary, onlyVisible }) as unknown as Promise<ArrayBuffer>;
|
|
42
|
+
|
|
43
|
+
} else {
|
|
44
|
+
|
|
45
|
+
// export as JSON object
|
|
46
|
+
|
|
47
|
+
return this._exporter.parseAsync(object, { binary, onlyVisible }) as unknown as Promise<{ [key: string]: unknown; }>;
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
}
|