@shapediver/viewer.data-engine.geometry-engine 1.15.5 → 2.0.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.
Files changed (42) hide show
  1. package/dist/GeometryEngine.d.ts +2 -2
  2. package/dist/GeometryEngine.d.ts.map +1 -1
  3. package/dist/GeometryEngine.js +7 -7
  4. package/dist/GeometryEngine.js.map +1 -1
  5. package/dist/gltfv1/GLTFLoader.d.ts +3 -3
  6. package/dist/gltfv1/GLTFLoader.d.ts.map +1 -1
  7. package/dist/gltfv1/GLTFLoader.js +12 -12
  8. package/dist/gltfv1/GLTFLoader.js.map +1 -1
  9. package/dist/gltfv1/SDGTFLoader.d.ts +2 -2
  10. package/dist/gltfv1/SDGTFLoader.d.ts.map +1 -1
  11. package/dist/gltfv1/SDGTFLoader.js +15 -15
  12. package/dist/gltfv1/SDGTFLoader.js.map +1 -1
  13. package/dist/gltfv2/GLTFLoader.d.ts +3 -3
  14. package/dist/gltfv2/GLTFLoader.d.ts.map +1 -1
  15. package/dist/gltfv2/GLTFLoader.js +18 -18
  16. package/dist/gltfv2/GLTFLoader.js.map +1 -1
  17. package/dist/gltfv2/loaders/AccessorLoader.js +1 -1
  18. package/dist/gltfv2/loaders/AccessorLoader.js.map +1 -1
  19. package/dist/gltfv2/loaders/GeometryLoader.d.ts +2 -2
  20. package/dist/gltfv2/loaders/GeometryLoader.d.ts.map +1 -1
  21. package/dist/gltfv2/loaders/GeometryLoader.js.map +1 -1
  22. package/dist/gltfv2/loaders/MaterialLoader.d.ts +2 -2
  23. package/dist/gltfv2/loaders/MaterialLoader.d.ts.map +1 -1
  24. package/dist/gltfv2/loaders/MaterialLoader.js.map +1 -1
  25. package/dist/gltfv2/loaders/TextureLoader.d.ts +0 -1
  26. package/dist/gltfv2/loaders/TextureLoader.d.ts.map +1 -1
  27. package/dist/gltfv2/loaders/TextureLoader.js +2 -3
  28. package/dist/gltfv2/loaders/TextureLoader.js.map +1 -1
  29. package/package.json +19 -16
  30. package/src/GeometryEngine.ts +130 -0
  31. package/src/gltfv1/GLTFLoader.ts +335 -0
  32. package/src/gltfv1/SDGTFLoader.ts +830 -0
  33. package/src/gltfv2/GLTFLoader.ts +513 -0
  34. package/src/gltfv2/draco/draco_decoder.js +36 -0
  35. package/src/gltfv2/loaders/AccessorLoader.ts +139 -0
  36. package/src/gltfv2/loaders/BufferLoader.ts +77 -0
  37. package/src/gltfv2/loaders/BufferViewLoader.ts +48 -0
  38. package/src/gltfv2/loaders/GeometryLoader.ts +215 -0
  39. package/src/gltfv2/loaders/MaterialLoader.ts +348 -0
  40. package/src/gltfv2/loaders/TextureLoader.ts +88 -0
  41. package/src/index.ts +5 -0
  42. package/tsconfig.json +19 -0
@@ -0,0 +1,335 @@
1
+ import { ITreeNode, TreeNode } from '@shapediver/viewer.shared.node-tree'
2
+ import { Converter, HttpClient, PerformanceEvaluator, UuidGenerator, Logger, LOGGING_TOPIC, ShapeDiverViewerDataProcessingError } from '@shapediver/viewer.shared.services'
3
+ import {
4
+ ACCESSORCOMPONENTTYPE_V1 as ACCESSOR_COMPONENTTYPE,
5
+ ACCESSORTYPE_V1 as ACCESSORTYPE,
6
+ IGLTF_v1,
7
+ IGLTF_v1_Material,
8
+ } from '@shapediver/viewer.data-engine.shared-types'
9
+ import { mat4, vec3, vec4 } from 'gl-matrix'
10
+ import {
11
+ AttributeData,
12
+ GeometryData,
13
+ MATERIAL_SIDE,
14
+ MaterialStandardData,
15
+ PrimitiveData,
16
+ } from '@shapediver/viewer.shared.types'
17
+ import { container } from 'tsyringe'
18
+
19
+ import { SDGTFLoader } from './SDGTFLoader'
20
+
21
+ export class GLTFLoader {
22
+ // #region Properties (5)
23
+
24
+ private readonly BINARY_EXTENSION_HEADER_LENGTH = 20;
25
+ private readonly _httpClient: HttpClient = <HttpClient>container.resolve(HttpClient);
26
+ private readonly _uuidGenerator: UuidGenerator = <UuidGenerator>container.resolve(UuidGenerator);
27
+ private readonly _logger: Logger = <Logger>container.resolve(Logger);
28
+ private readonly _implementedExtensions = ['KHR_materials_common'];
29
+ private readonly _globalTransformation = mat4.fromValues(1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1);
30
+ private readonly _converter: Converter = <Converter>container.resolve(Converter);
31
+ private readonly _performanceEvaluator = <PerformanceEvaluator>container.resolve(PerformanceEvaluator);
32
+
33
+ private _baseUri: string | undefined;
34
+ private _body: ArrayBuffer | undefined;
35
+ private _content!: IGLTF_v1;
36
+
37
+ // #endregion Properties (5)
38
+
39
+ // #region Public Methods (1)
40
+
41
+ public async load(content: IGLTF_v1, gltfBinary?: ArrayBuffer, gltfHeader?: { magic: string, version: number, length: number, contentLength: number, contentFormat: number }, baseUri?: string): Promise<ITreeNode> {
42
+ this._baseUri = baseUri;
43
+ if(gltfBinary && gltfHeader)
44
+ this._body = gltfBinary.slice(this.BINARY_EXTENSION_HEADER_LENGTH + gltfHeader.contentLength, gltfHeader.length);
45
+ this._content = content;
46
+
47
+ let sdgtfNode;
48
+ if(gltfBinary && gltfHeader)
49
+ sdgtfNode = await new SDGTFLoader().load(gltfBinary, gltfHeader.length);
50
+
51
+ try {
52
+ this.validateVersionAndExtensions();
53
+ const node = await this.loadScene();
54
+ if(sdgtfNode) node.addChild(sdgtfNode);
55
+ return node;
56
+ } catch (e) {
57
+ throw this._logger.handleError(LOGGING_TOPIC.DATA_PROCESSING, `GLTFLoader.loadContent`, e);
58
+ }
59
+ }
60
+
61
+ public async loadWithUrl(url?: string | undefined): Promise<ITreeNode> {
62
+ this._performanceEvaluator.startSection('gltfProcessing.' + url);
63
+ let binaryGeometry: ArrayBuffer;
64
+
65
+ try {
66
+ this._performanceEvaluator.startSection('loadGltf.' + url);
67
+ binaryGeometry = (await this._httpClient.get(url!, {
68
+ responseType: 'arraybuffer'
69
+ })).data;
70
+ this._performanceEvaluator.endSection('loadGltf.' + url);
71
+ } catch (e) {
72
+ throw this._logger.handleError(LOGGING_TOPIC.DATA_PROCESSING, `GLTFLoader.load`, e);
73
+ }
74
+
75
+ // create header data
76
+ const headerDataView = new DataView(binaryGeometry, 0, this.BINARY_EXTENSION_HEADER_LENGTH);
77
+ const header = {
78
+ magic: String.fromCharCode(headerDataView.getUint8(0)) + String.fromCharCode(headerDataView.getUint8(1)) + String.fromCharCode(headerDataView.getUint8(2)) + String.fromCharCode(headerDataView.getUint8(3)),
79
+ version: headerDataView.getUint32(4, true),
80
+ length: headerDataView.getUint32(8, true),
81
+ contentLength: headerDataView.getUint32(12, true),
82
+ contentFormat: headerDataView.getUint32(16, true)
83
+ }
84
+ if (header.magic != 'glTF') {
85
+ const error = new ShapeDiverViewerDataProcessingError('GLTFLoader.load: Invalid data: glTF magic wrong.');
86
+ throw this._logger.handleError(LOGGING_TOPIC.DATA_PROCESSING, `GLTFLoader.load`, error);
87
+ }
88
+
89
+ // create content
90
+ const contentDataView = new DataView(binaryGeometry, this.BINARY_EXTENSION_HEADER_LENGTH, header.contentLength);
91
+ const contentDecoded = new TextDecoder().decode(contentDataView);
92
+ this._content = JSON.parse(contentDecoded);
93
+
94
+ // create body
95
+ this._body = binaryGeometry.slice(this.BINARY_EXTENSION_HEADER_LENGTH + header.contentLength, header.length);
96
+
97
+ const sdgtfNode = await new SDGTFLoader().load(binaryGeometry, header.length);
98
+
99
+ try {
100
+ this.validateVersionAndExtensions();
101
+ const node = await this.loadScene();
102
+ node.addChild(sdgtfNode);
103
+ this._performanceEvaluator.endSection('gltfProcessing.' + url);
104
+ return node;
105
+ } catch (e) {
106
+ throw this._logger.handleError(LOGGING_TOPIC.DATA_PROCESSING, `GLTFLoader.load`, e);
107
+ }
108
+ }
109
+
110
+ // #endregion Public Methods (1)
111
+
112
+ // #region Private Methods (6)
113
+
114
+ private validateVersionAndExtensions(): void {
115
+ if(this._content.extensionsUsed) {
116
+ const notSupported = [];
117
+ for(let i = 0; i < this._content.extensionsUsed.length; i++) {
118
+ if(!this._implementedExtensions.includes(this._content.extensionsUsed[i]))
119
+ notSupported.push(this._content.extensionsUsed[i]);
120
+ }
121
+ if(notSupported.length > 0) {
122
+ let message = 'Extension' + (notSupported.length === 1 ? ' ' : 's ');
123
+ notSupported.forEach((element, index) => {
124
+ message += '"' + element + '"' + (index === notSupported.length-1 ? '' : index === notSupported.length-2 ? ' and ' : ', ');
125
+ });
126
+ message += (notSupported.length === 1 ? ' is' : ' are') + ' not supported, but used. Loading glTF regardless.';
127
+ this._logger.info(LOGGING_TOPIC.DATA_PROCESSING, 'GLTFLoader.validateVersionAndExtensions: ' + message);
128
+ }
129
+ }
130
+ }
131
+
132
+ private async loadAccessor(accessorName: string): Promise<AttributeData> {
133
+ if (!this._content.accessors![accessorName]) throw new Error('Accessor not available.')
134
+ const accessor = this._content.accessors![accessorName];
135
+ const bufferView = await this.loadBufferView(accessor.bufferView!);
136
+
137
+ const itemSize = ACCESSORTYPE[<keyof typeof ACCESSORTYPE>accessor.type];
138
+ if(accessor.componentType === 5124) this._logger.warn(LOGGING_TOPIC.DATA_PROCESSING, 'GLTFLoader.loadAccessor: The componentType for this accessor is 5124, which is not allowed. Trying to load it anyway.');
139
+ const ArrayType = ACCESSOR_COMPONENTTYPE[<keyof typeof ACCESSOR_COMPONENTTYPE>accessor.componentType];
140
+ const elementBytes = ArrayType.BYTES_PER_ELEMENT;
141
+ const itemBytes = elementBytes * itemSize;
142
+
143
+ const byteOffset = accessor.byteOffset || 0;
144
+ const byteStride = accessor.byteStride;
145
+ const normalized = false;
146
+
147
+ const min = this._content.asset && this._content.asset?.generator === "ShapeDiverGltfV1Writer" ? accessor.min || [] : [];
148
+ const max = this._content.asset && this._content.asset?.generator === "ShapeDiverGltfV1Writer" ? accessor.max || [] : [];
149
+
150
+ // The buffer is not interleaved if the stride is the item size in bytes.
151
+ return new AttributeData(new ArrayType(bufferView), itemSize, itemBytes, byteOffset, elementBytes, normalized, accessor.count, min, max, byteStride);
152
+ }
153
+
154
+ private async loadBuffer(bufferName: string): Promise<ArrayBuffer> {
155
+ if (!this._content.buffers![bufferName]) throw new Error('Buffer not available.')
156
+ const buffer = this._content.buffers![bufferName];
157
+
158
+ if (bufferName === 'binary_glTF')
159
+ return this._body!;
160
+
161
+ if (buffer.type === 'arraybuffer') {
162
+ const binaryGeometry: ArrayBuffer = (await this._httpClient.get(buffer.uri!, {
163
+ responseType: 'arraybuffer'
164
+ })).data;
165
+ return binaryGeometry;
166
+ }
167
+ if(!this._body) throw new Error('Buffer not available.');
168
+ return this._body;
169
+ }
170
+
171
+ private async loadBufferView(bufferViewName: string): Promise<ArrayBuffer> {
172
+ if (!this._content.bufferViews![bufferViewName]) throw new Error('Buffer View not available.')
173
+ const bufferView = this._content.bufferViews![bufferViewName];
174
+ const buffer: ArrayBuffer = await this.loadBuffer(bufferView.buffer!);
175
+ const byteLength = bufferView.byteLength !== undefined ? bufferView.byteLength : 0;
176
+
177
+ return buffer.slice(bufferView.byteOffset!, bufferView.byteOffset! + byteLength);
178
+ }
179
+
180
+
181
+ private async loadMaterial(materialName: string): Promise<MaterialStandardData> {
182
+ if(!this._content.materials![materialName]) throw new Error('Material not available.')
183
+ const material: IGLTF_v1_Material = this._content.materials![materialName];
184
+ const materialData = new MaterialStandardData();
185
+ if(material.name !== undefined) materialData.name = material.name;
186
+
187
+ if(material.extensions && material.extensions.KHR_materials_common) {
188
+ const technique = material.extensions.KHR_materials_common.technique;
189
+ if(technique && technique !== 'BLINN') this._logger.warn(LOGGING_TOPIC.DATA_PROCESSING, 'The technique ' + technique + ' is not supported. Trying to load the material either way.')
190
+ const values = material.extensions.KHR_materials_common.values;
191
+
192
+ if (values.hasOwnProperty('doubleSided'))
193
+ materialData.side = values.doubleSided ? MATERIAL_SIDE.DOUBLE : MATERIAL_SIDE.FRONT;
194
+
195
+ materialData.color = '#d3d3d3';
196
+ if (values.hasOwnProperty('diffuse') && Array.isArray(values.diffuse)) {
197
+ const diffuseScaled = (<number[]>values.diffuse).map(element => element *= 255.0);
198
+ materialData.color = this._converter.toColor(diffuseScaled);
199
+ materialData.opacity = Math.max(0.0, Math.min(values.diffuse[3], 1.0));
200
+ } else if(values.hasOwnProperty('diffuse')) {
201
+ this._logger.warn(LOGGING_TOPIC.DATA_PROCESSING, 'GLTFLoader.loadMaterial: The value diffuse was set for a material, but is not supported in that type.')
202
+ }
203
+
204
+ if (!values.hasOwnProperty('diffuse') && values.hasOwnProperty('ambient')) {
205
+ const ambientScaled = (<number[]>values.ambient).map(element => element *= 255.0);
206
+ materialData.color = this._converter.toColor(ambientScaled);
207
+ }
208
+
209
+ if (values.hasOwnProperty('emission') && Array.isArray(values.emission)) {
210
+ materialData.emissiveness = this._converter.toColor(values.emission);
211
+ } else if (values.hasOwnProperty('emission')) {
212
+ this._logger.warn(LOGGING_TOPIC.DATA_PROCESSING, 'GLTFLoader.loadMaterial: The value emission was set for a material, but is not supported in that type.')
213
+ }
214
+
215
+ if (values.hasOwnProperty('shininess')) {
216
+ materialData.metalness = Math.min(1, values.shininess);
217
+ materialData.roughness = 1 - Math.min(1, values.shininess);
218
+ }
219
+
220
+ if (values.hasOwnProperty('transparency'))
221
+ materialData.opacity = Math.max(0.0, Math.min(values.transparency, 1.0));
222
+
223
+ if (!values.hasOwnProperty('transparency') && values.hasOwnProperty('transparent') && (values.transparency === 'true' || values.transparency === true))
224
+ materialData.opacity = 0;
225
+
226
+ if (values.hasOwnProperty('_roughness'))
227
+ materialData.roughness = Math.min(1, Math.max(0, values.roughness));
228
+
229
+ if (values.hasOwnProperty('_metalness'))
230
+ materialData.metalness = Math.min(1, Math.max(0, values.metalness));
231
+ }
232
+ return materialData;
233
+ }
234
+
235
+ private async loadMesh(meshName: string): Promise<ITreeNode> {
236
+ if (!this._content.meshes![meshName]) throw new Error('Mesh not available.')
237
+ const mesh = this._content.meshes![meshName];
238
+ const meshNode = new TreeNode(meshName);
239
+
240
+ if(!mesh.primitives) return new TreeNode('primitive');
241
+ for (let i = 0, len = mesh.primitives!.length; i < len; i++) {
242
+ const primitiveNode = new TreeNode('primitive_' + i);
243
+ meshNode.addChild(primitiveNode);
244
+
245
+ let primitive = mesh.primitives![i];
246
+ const attributes: {
247
+ [key: string]: AttributeData
248
+ } = {};
249
+
250
+ for (let attribute in primitive.attributes) {
251
+ // attribute name conversion to be consistent witg gltf
252
+ let attributeName = attribute;
253
+ if(/\d/.test(attributeName) && !attributeName.includes('_')) {
254
+ const index = attributeName.search(/\d/)
255
+ attributeName = attributeName.substring(0, index) + '_' + attributeName.substring(index, attributeName.length);
256
+ } else if(attributeName === 'TEXCOORD' || attributeName === 'COLOR' || attributeName === 'JOINTS' || attributeName === 'WEIGHTS') {
257
+ attributeName += '_0';
258
+ } else if (attributeName === 'UV') {
259
+ attributeName = 'TEXCOORD_0';
260
+ }
261
+
262
+ attributes[attributeName] = await this.loadAccessor(primitive.attributes[attribute]);
263
+ if(attributeName.startsWith('COLOR'))
264
+ attributes[attributeName] = new AttributeData(attributes[attributeName].array, attributes[attributeName].itemSize, attributes[attributeName].itemBytes, attributes[attributeName].byteOffset, attributes[attributeName].elementBytes, true, attributes[attributeName].count, [], [], attributes[attributeName].byteStride)
265
+ }
266
+
267
+ let material: MaterialStandardData | undefined;
268
+ if(primitive.material)
269
+ material = await this.loadMaterial(primitive.material);
270
+
271
+ const geometry = new GeometryData(new PrimitiveData(attributes, 4, await this.loadAccessor(primitive.indices!), material));
272
+ primitiveNode.data.push(geometry);
273
+ }
274
+ return meshNode;
275
+ }
276
+
277
+ private async loadNode(nodeName: string): Promise<ITreeNode> {
278
+ if (!this._content.nodes![nodeName]) throw new Error('Node not available.')
279
+ const node = this._content.nodes![nodeName];
280
+ const nodeDef = new TreeNode(nodeName);
281
+
282
+ if (node.matrix) {
283
+ nodeDef.addTransformation({
284
+ id: this._uuidGenerator.create(),
285
+ matrix: mat4.fromValues(node.matrix[0], node.matrix[1], node.matrix[2], node.matrix[3],
286
+ node.matrix[4], node.matrix[5], node.matrix[6], node.matrix[7],
287
+ node.matrix[8], node.matrix[9], node.matrix[10], node.matrix[11],
288
+ node.matrix[12], node.matrix[13], node.matrix[14], node.matrix[15])
289
+ });
290
+ } else if (node.translation || node.scale || node.rotation) {
291
+ const matT = node.translation ? mat4.fromTranslation(mat4.create(), vec3.fromValues(node.translation[0], node.translation[1], node.translation[2])) : mat4.create();
292
+ const matS = node.scale ? mat4.fromScaling(mat4.create(), vec3.fromValues(node.scale[0], node.scale[1], node.scale[2])) : mat4.create();
293
+ const matR = node.rotation ? mat4.fromQuat(mat4.create(), vec4.fromValues(node.rotation[0], node.rotation[1], node.rotation[2], node.rotation[3])) : mat4.create();
294
+ const matrix = mat4.mul(mat4.create(), mat4.mul(mat4.create(), matT, matS), matR);
295
+ nodeDef.addTransformation({
296
+ id: this._uuidGenerator.create(),
297
+ matrix: matrix
298
+ });
299
+ }
300
+
301
+ if(node.meshes) {
302
+ for (let i = 0, len = node.meshes!.length; i < len; i++) {
303
+ // we create a child node as we one want to have one mesh as in the GLTF2 def
304
+ nodeDef.addChild(await this.loadMesh(node.meshes![i]));
305
+ }
306
+ }
307
+
308
+ if (node.children) {
309
+ for (let i = 0, len = node.children!.length; i < len; i++) {
310
+ // got through all children
311
+ nodeDef.addChild(await this.loadNode(node.children![i]));
312
+ }
313
+ }
314
+
315
+ return nodeDef;
316
+ }
317
+
318
+ private async loadScene(): Promise<ITreeNode> {
319
+ if (!this._content.scene) throw new Error('No scene.')
320
+ if (!this._content.scenes![this._content.scene!]) throw new Error('Scene not available.')
321
+ const scene = this._content.scenes![this._content.scene!];
322
+ const sceneDef = new TreeNode(this._content.scene!);
323
+ if(this._content.asset && this._content.asset?.generator !== "ShapeDiverGltfWriter" && this._content.asset?.generator !== "ShapeDiverGltfV1Writer") {
324
+ sceneDef.addTransformation({
325
+ id: this._uuidGenerator.create(),
326
+ matrix: this._globalTransformation
327
+ })
328
+ }
329
+ if(scene.nodes)
330
+ for (let i = 0, len = scene.nodes!.length; i < len; i++)
331
+ sceneDef.addChild(await this.loadNode(scene.nodes![i]));
332
+ return sceneDef;
333
+ }
334
+ // #endregion Private Methods (6)
335
+ }