@shapediver/viewer.data-engine.geometry-engine 3.3.3 → 3.3.6

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.
@@ -1,527 +0,0 @@
1
- import { ITreeNode, TreeNode } from '@shapediver/viewer.shared.node-tree';
2
- import {
3
- EventEngine,
4
- EVENTTYPE,
5
- HttpClient,
6
- HttpResponse,
7
- Logger,
8
- PerformanceEvaluator,
9
- ShapeDiverViewerDataProcessingError,
10
- UuidGenerator,
11
- } from '@shapediver/viewer.shared.services';
12
- import { IGLTF_v2 } from '@shapediver/viewer.data-engine.shared-types';
13
- import { mat4, vec3, vec4 } from 'gl-matrix';
14
- import {
15
- AnimationData,
16
- IAnimationTrack,
17
- AttributeData,
18
- BoneData,
19
- Color,
20
- ITaskEvent,
21
- TASK_TYPE
22
- } from '@shapediver/viewer.shared.types';
23
- import { OrthographicCamera, PerspectiveCamera } from '@shapediver/viewer.rendering-engine.camera-engine';
24
- import {
25
- AbstractLight,
26
- DirectionalLight,
27
- PointLight,
28
- SpotLight,
29
- } from '@shapediver/viewer.rendering-engine.light-engine';
30
-
31
- import { BufferLoader } from './loaders/BufferLoader';
32
- import { BufferViewLoader } from './loaders/BufferViewLoader';
33
- import { AccessorLoader } from './loaders/AccessorLoader';
34
- import { TextureLoader } from './loaders/TextureLoader';
35
- import { MaterialLoader } from './loaders/MaterialLoader';
36
- import { GeometryLoader } from './loaders/GeometryLoader';
37
-
38
- export enum GLTF_EXTENSIONS {
39
- KHR_BINARY_GLTF = 'KHR_binary_glTF',
40
- KHR_DRACO_MESH_COMPRESSION = 'KHR_draco_mesh_compression',
41
- KHR_LIGHTS_PUNCTUAL = 'KHR_lights_punctual',
42
- KHR_MATERIALS_CLEARCOAT = 'KHR_materials_clearcoat',
43
- KHR_MATERIALS_IOR = 'KHR_materials_ior',
44
- KHR_MATERIALS_PBRSPECULARGLOSSINESS = 'KHR_materials_pbrSpecularGlossiness',
45
- KHR_MATERIALS_SHEEN = 'KHR_materials_sheen',
46
- KHR_MATERIALS_SPECULAR = 'KHR_materials_specular',
47
- KHR_MATERIALS_TRANSMISSION = 'KHR_materials_transmission',
48
- KHR_MATERIALS_UNLIT = 'KHR_materials_unlit',
49
- KHR_MATERIALS_VARIANTS = 'KHR_materials_variants',
50
- KHR_MATERIALS_VOLUME = 'KHR_materials_volume',
51
- KHR_MESH_QUANTIZATION = 'KHR_mesh_quantization',
52
- KHR_TEXTURE_TRANSFORM = 'KHR_texture_transform',
53
- SHAPEDIVER_MATERIALS_PRESET = 'SHAPEDIVER_materials_preset'
54
- }
55
-
56
- // eslint-disable-next-line @typescript-eslint/no-var-requires
57
- const DRACO = require('./draco/draco_decoder.js');
58
-
59
- export class GLTFLoader {
60
- // #region Properties (22)
61
-
62
- private readonly BINARY_EXTENSION_HEADER_LENGTH = 20;
63
- private readonly _eventEngine: EventEngine = EventEngine.instance;
64
- private readonly _globalTransformation = mat4.fromValues(1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1);
65
- private readonly _httpClient: HttpClient = HttpClient.instance;
66
- private readonly _logger: Logger = Logger.instance;
67
- private readonly _performanceEvaluator = PerformanceEvaluator.instance;
68
- private readonly _progressUpdateLimit = 500;
69
- private readonly _uuidGenerator: UuidGenerator = UuidGenerator.instance;
70
-
71
- private _accessorLoader!: AccessorLoader;
72
- private _baseUri: string | undefined;
73
- private _body: ArrayBuffer | undefined;
74
- private _bufferLoader!: BufferLoader;
75
- private _bufferViewLoader!: BufferViewLoader;
76
- private _content!: IGLTF_v2;
77
- private _eventId: string = '';
78
- private _geometryLoader!: GeometryLoader;
79
- private _materialLoader!: MaterialLoader;
80
- private _nodes: {
81
- [key: number]: ITreeNode
82
- } = {};
83
- private _numberOfConvertedNodes = 0;
84
- private _numberOfNodes = 0;
85
- private _progressTimer = 0;
86
- private _textureLoader!: TextureLoader;
87
-
88
- // #endregion Properties (22)
89
-
90
- // #region Public Methods (2)
91
-
92
- public async load(content: IGLTF_v2, gltfBinary?: ArrayBuffer, gltfHeader?: { magic: string, version: number, length: number, contentLength: number, contentFormat: number }, baseUri?: string, taskEventId?: string): Promise<ITreeNode> {
93
- this._eventId = taskEventId || this._uuidGenerator.create();
94
- const eventStart: ITaskEvent = { type: TASK_TYPE.GLTF_CONTENT_LOADING, id: this._eventId, progress: 0, status: 'Starting glTF 2.0 loading.' };
95
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_START, eventStart);
96
-
97
- this._numberOfConvertedNodes = 0;
98
- this._numberOfNodes = content.nodes ? content.nodes.length : 0;
99
- this._progressTimer = performance.now();
100
-
101
- this._baseUri = baseUri;
102
- if (gltfBinary && gltfHeader)
103
- this._body = gltfBinary.slice(this.BINARY_EXTENSION_HEADER_LENGTH + gltfHeader.contentLength + 8, gltfHeader.length);
104
- this._content = content;
105
-
106
- this.validateVersionAndExtensions();
107
-
108
- const dracoModule = await new DRACO();
109
-
110
- this._bufferLoader = new BufferLoader(this._content, this._body, this._baseUri);
111
- await this._bufferLoader.load();
112
- this._bufferViewLoader = new BufferViewLoader(this._content, this._bufferLoader);
113
- this._bufferViewLoader.load();
114
- this._accessorLoader = new AccessorLoader(this._content, this._bufferViewLoader);
115
- this._accessorLoader.load();
116
- this._textureLoader = new TextureLoader(this._content, this._bufferViewLoader, this._baseUri);
117
- await this._textureLoader.load();
118
- this._materialLoader = new MaterialLoader(this._content, this._textureLoader);
119
- await this._materialLoader.load();
120
- this._geometryLoader = new GeometryLoader(this._content, this._accessorLoader, this._bufferViewLoader, this._materialLoader, dracoModule);
121
-
122
- const eventProgressInit: ITaskEvent = { type: TASK_TYPE.GLTF_CONTENT_LOADING, id: this._eventId, progress: 0.1, status: 'Initial logic of glTF loading.' };
123
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_PROCESS, eventProgressInit);
124
-
125
- const node = await this.loadScene();
126
-
127
- if (this._content.extensions && this._content.extensions[GLTF_EXTENSIONS.KHR_MATERIALS_VARIANTS]) {
128
- const variants = this._content.extensions[GLTF_EXTENSIONS.KHR_MATERIALS_VARIANTS].variants;
129
- for (let i = 0; i < variants.length; i++)
130
- this._geometryLoader.materialVariantsData.variants.push(variants[i].name);
131
- this._geometryLoader.materialVariantsData.variantIndex = 0;
132
- node.data.push(this._geometryLoader.materialVariantsData);
133
- }
134
-
135
- if (this._content.skins !== undefined && this._content.nodes !== undefined) {
136
- for (let i = 0; i < this._content.nodes?.length; i++) {
137
- if (this._content.nodes[i].skin !== undefined) {
138
- const skinDef = this.loadSkin(this._content.nodes[i].skin!);
139
-
140
- const skinNode = this._nodes[i];
141
-
142
- const bones: ITreeNode[] = [];
143
- const boneInverses: mat4[] = [];
144
-
145
- for (let j = 0; j < skinDef.joints.length; j++) {
146
- this._nodes[skinDef.joints[j]].data.push(new BoneData());
147
- bones.push(this._nodes[skinDef.joints[j]]);
148
-
149
- let mat = mat4.create();
150
- if (skinDef.inverseBindMatrices !== undefined) {
151
- const matricesArray = skinDef.inverseBindMatrices!.array;
152
- mat = mat4.fromValues(matricesArray[j * 16 + 0], matricesArray[j * 16 + 1], matricesArray[j * 16 + 2], matricesArray[j * 16 + 3],
153
- matricesArray[j * 16 + 4], matricesArray[j * 16 + 5], matricesArray[j * 16 + 6], matricesArray[j * 16 + 7],
154
- matricesArray[j * 16 + 8], matricesArray[j * 16 + 9], matricesArray[j * 16 + 10], matricesArray[j * 16 + 11],
155
- matricesArray[j * 16 + 12], matricesArray[j * 16 + 13], matricesArray[j * 16 + 14], matricesArray[j * 16 + 15]);
156
- }
157
- boneInverses.push(mat);
158
- }
159
-
160
- skinNode.skinNode = true;
161
- skinNode.bones = bones;
162
- skinNode.boneInverses = boneInverses;
163
- }
164
- }
165
- }
166
-
167
- if (this._content.animations)
168
- for (let i = 0; i < this._content.animations?.length; i++)
169
- node.data.push(this.loadAnimation(i));
170
-
171
- const eventEnd: ITaskEvent = { type: TASK_TYPE.GLTF_CONTENT_LOADING, id: this._eventId, progress: 1, status: 'GlTF loading complete.' };
172
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_END, eventEnd);
173
- return node;
174
- }
175
-
176
- public async loadWithUrl(url?: string | undefined): Promise<ITreeNode> {
177
- this._performanceEvaluator.startSection('gltfProcessing.' + url);
178
-
179
- this._performanceEvaluator.startSection('loadGltf.' + url);
180
- const axiosResponse = await this._httpClient.get(url!, {
181
- responseType: 'arraybuffer'
182
- }) as HttpResponse<ArrayBuffer>;
183
- this._performanceEvaluator.endSection('loadGltf.' + url);
184
-
185
- let gltfContent, gltfBinary, gltfBaseUrl, gltfHeader;
186
-
187
- const magic = new TextDecoder().decode(new Uint8Array(axiosResponse.data, 0, 4));
188
- const isBinary = magic === 'glTF' || (axiosResponse.headers['content-type'] &&
189
- (axiosResponse.headers['content-type'] === 'model/gltf-binary' ||
190
- axiosResponse.headers['content-type'] === 'application/octet-stream' ||
191
- axiosResponse.headers['content-type'] === 'model/gltf.binary'));
192
-
193
- if (isBinary) {
194
- gltfBinary = axiosResponse.data;
195
- // create header data
196
- const headerDataView = new DataView(gltfBinary, 0, this.BINARY_EXTENSION_HEADER_LENGTH);
197
- gltfHeader = {
198
- magic: magic,
199
- version: headerDataView.getUint32(4, true),
200
- length: headerDataView.getUint32(8, true),
201
- contentLength: headerDataView.getUint32(12, true),
202
- contentFormat: headerDataView.getUint32(16, true)
203
- };
204
- if (gltfHeader.magic != 'glTF')
205
- throw new ShapeDiverViewerDataProcessingError('GLTFLoader.load: Invalid data: sdgTF magic wrong.');
206
-
207
- // create content
208
- const contentDataView = new DataView(gltfBinary, this.BINARY_EXTENSION_HEADER_LENGTH, gltfHeader.contentLength);
209
- const contentDecoded = new TextDecoder().decode(contentDataView);
210
- gltfContent = JSON.parse(contentDecoded);
211
-
212
- // create body
213
- this._body = gltfBinary.slice(this.BINARY_EXTENSION_HEADER_LENGTH + gltfHeader.contentLength + 8, gltfHeader.length);
214
- } else {
215
- gltfContent = JSON.parse(new TextDecoder().decode(axiosResponse.data));
216
-
217
- const removeLastDirectoryPartOf = (the_url: string): string => {
218
- const dir_char = the_url.includes('/') ? '/' : '\\';
219
- const the_arr = the_url.split(dir_char);
220
- the_arr.pop();
221
- return the_arr.join(dir_char);
222
- };
223
-
224
- gltfBaseUrl = removeLastDirectoryPartOf(url!);
225
- if (!gltfBaseUrl && window && window.location && window.location.href)
226
- gltfBaseUrl = removeLastDirectoryPartOf(window.location.href);
227
- }
228
-
229
- return await this.load(gltfContent, gltfBinary, gltfHeader, gltfBaseUrl);
230
- }
231
-
232
- // #endregion Public Methods (2)
233
-
234
- // #region Private Methods (7)
235
-
236
- /**
237
- * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations
238
- * @param {number} animationIndex
239
- * @return {Promise<AnimationClip>}
240
- */
241
- private loadAnimation(animationId: number): AnimationData {
242
- if (!this._content.animations) throw new Error('Animations not available.');
243
- if (!this._content.animations[animationId]) throw new Error('Animations not available.');
244
- const animationDef = this._content.animations[animationId];
245
- const animationTracks: IAnimationTrack[] = [];
246
- let min = Infinity, max = -Infinity;
247
-
248
- for (let i = 0; i < animationDef.channels.length; i++) {
249
- const channel = animationDef.channels[i];
250
- const sampler = animationDef.samplers[channel.sampler];
251
-
252
- const target = channel.target;
253
- const path = target.path;
254
- const node = this._nodes[target.node];
255
- if (node === undefined) throw new Error('Animation node not available.');
256
-
257
- const input = this._accessorLoader.getAccessor(sampler.input);
258
- min = Math.min(min, input!.min[0]);
259
- max = Math.max(max, input!.max[0]);
260
- const output = this._accessorLoader.getAccessor(sampler.output);
261
- let interpolation = sampler.interpolation;
262
- if (interpolation === 'CUBICSPLINE') {
263
- this._logger.warn('Animation with CUBICSPLINE interpolation is currently not supported. Assigning linear interpolation instead.');
264
- interpolation = 'linear';
265
- }
266
-
267
- animationTracks.push({
268
- node,
269
- times: input!.array,
270
- values: output!.array,
271
- path: <'scale' | 'translation' | 'rotation'>path,
272
- interpolation: <'linear' | 'step'>interpolation?.toLowerCase()
273
- });
274
- }
275
-
276
- return new AnimationData(animationDef.name || 'gltf_animation_' + animationId, animationTracks, min, max - min);
277
- }
278
-
279
- private loadCamera(cameraId: number): ITreeNode {
280
- if (!this._content.cameras) throw new Error('Cameras not available.');
281
- if (!this._content.cameras[cameraId]) throw new Error('Cameras not available.');
282
- const cameraDef = this._content.cameras[cameraId];
283
- const cameraNode = new TreeNode(cameraDef.name || 'camera_' + cameraId);
284
- cameraNode.originalName = cameraDef.name;
285
-
286
- let cameraData: PerspectiveCamera | OrthographicCamera;
287
- if (cameraDef.type === 'perspective') {
288
- const perspectiveCameraDef = cameraDef.perspective!;
289
- cameraData = new PerspectiveCamera(cameraNode.id);
290
- cameraNode.data.push(cameraData);
291
- cameraData.fov = perspectiveCameraDef.yfov * (180 / Math.PI);
292
- cameraData.aspect = perspectiveCameraDef.aspectRatio || 1;
293
- cameraData.near = perspectiveCameraDef.znear || 1;
294
- cameraData.far = perspectiveCameraDef.zfar || 2e6;
295
- } else {
296
- const orthographicCameraDef = cameraDef.orthographic!;
297
- cameraData = new OrthographicCamera(cameraNode.id);
298
- cameraNode.data.push(cameraData);
299
- cameraData.left = -orthographicCameraDef.xmag;
300
- cameraData.right = orthographicCameraDef.xmag;
301
- cameraData.top = -orthographicCameraDef.ymag;
302
- cameraData.bottom = orthographicCameraDef.ymag;
303
- cameraData.near = orthographicCameraDef.znear || 1;
304
- cameraData.far = orthographicCameraDef.zfar || 2e6;
305
- }
306
-
307
- cameraData.useNodeData = true;
308
- cameraData.node = cameraNode;
309
-
310
- return cameraNode;
311
- }
312
-
313
- private loadLights(lightId: number): ITreeNode {
314
- if (!this._content.extensions || !this._content.extensions[GLTF_EXTENSIONS.KHR_LIGHTS_PUNCTUAL] || !this._content.extensions[GLTF_EXTENSIONS.KHR_LIGHTS_PUNCTUAL].lights) throw new Error(`Extension ${GLTF_EXTENSIONS.KHR_LIGHTS_PUNCTUAL} not available.`);
315
- if (!this._content.extensions[GLTF_EXTENSIONS.KHR_LIGHTS_PUNCTUAL].lights[lightId]) throw new Error('Light not available.');
316
- const lightDef = this._content.extensions[GLTF_EXTENSIONS.KHR_LIGHTS_PUNCTUAL].lights[lightId];
317
- const lightNode = new TreeNode(lightDef.name || 'light_' + lightId);
318
- lightNode.originalName = lightDef.name;
319
-
320
- let color: Color = '#ffffffff';
321
- if (lightDef.color !== undefined)
322
- color = [lightDef.color[0] * 255, lightDef.color[1] * 255, lightDef.color[2] * 255];
323
-
324
- const range = lightDef.range !== undefined ? lightDef.range : 0;
325
-
326
- let lightData: AbstractLight;
327
- if (lightDef.type === 'directional') {
328
- lightData = new DirectionalLight({ color });
329
- lightNode.data.push(lightData);
330
-
331
- const directionalLightData = <DirectionalLight>lightData;
332
-
333
- if (lightDef.intensity !== undefined) directionalLightData.intensity = lightDef.intensity;
334
- } else if (lightDef.type === 'point') {
335
- lightData = new PointLight({ color });
336
- lightNode.data.push(lightData);
337
-
338
- const pointLightData = <PointLight>lightData;
339
-
340
- pointLightData.distance = range;
341
- pointLightData.decay = 2;
342
- if (lightDef.intensity !== undefined) lightData.intensity = lightDef.intensity;
343
-
344
- pointLightData.position = [0, 0, 0];
345
- } else if (lightDef.type === 'spot') {
346
- lightData = new SpotLight({ color });
347
- lightNode.data.push(lightData);
348
-
349
- lightDef.spot = lightDef.spot || {};
350
- lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0;
351
- lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0;
352
-
353
- const spotLightData = <SpotLight>lightData;
354
- spotLightData.distance = range;
355
- spotLightData.angle = lightDef.spot.outerConeAngle;
356
- spotLightData.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle;
357
- spotLightData.decay = 2;
358
- if (lightDef.intensity !== undefined) lightData.intensity = lightDef.intensity;
359
-
360
- spotLightData.position = [0, 0, 0];
361
- spotLightData.target = [0, 0, -1];
362
- } else {
363
- throw new Error('Unexpected light type: ' + lightDef.type);
364
- }
365
-
366
- lightData.useNodeData = true;
367
- return lightNode;
368
- }
369
-
370
- private async loadNode(nodeId: number): Promise<ITreeNode> {
371
- if (!this._content.nodes) throw new Error('Nodes not available.');
372
- if (!this._content.nodes[nodeId]) throw new Error('Node not available.');
373
- const node = this._content.nodes[nodeId];
374
- const nodeDef = new TreeNode(node.name || 'node_' + nodeId);
375
- nodeDef.originalName = node.name;
376
- this._nodes[nodeId] = nodeDef;
377
-
378
- if (node.matrix) {
379
- nodeDef.addTransformation({
380
- id: 'gltf_matrix',
381
- matrix: mat4.fromValues(node.matrix[0], node.matrix[1], node.matrix[2], node.matrix[3],
382
- node.matrix[4], node.matrix[5], node.matrix[6], node.matrix[7],
383
- node.matrix[8], node.matrix[9], node.matrix[10], node.matrix[11],
384
- node.matrix[12], node.matrix[13], node.matrix[14], node.matrix[15])
385
- });
386
-
387
- nodeDef.addTransformation({
388
- id: 'gltf_matrix_translation',
389
- matrix: mat4.create()
390
- });
391
- nodeDef.addTransformation({
392
- id: 'gltf_matrix_rotation',
393
- matrix: mat4.create()
394
- });
395
- nodeDef.addTransformation({
396
- id: 'gltf_matrix_scale',
397
- matrix: mat4.create()
398
- });
399
- } else if (node.translation || node.scale || node.rotation) {
400
- const matT = node.translation ? mat4.fromTranslation(mat4.create(), vec3.fromValues(node.translation[0], node.translation[1], node.translation[2])) : mat4.create();
401
- const matS = node.scale ? mat4.fromScaling(mat4.create(), vec3.fromValues(node.scale[0], node.scale[1], node.scale[2])) : mat4.create();
402
- const matR = node.rotation ? mat4.fromQuat(mat4.create(), vec4.fromValues(node.rotation[0], node.rotation[1], node.rotation[2], node.rotation[3])) : mat4.create();
403
-
404
- nodeDef.addTransformation({
405
- id: 'gltf_matrix_translation',
406
- matrix: matT
407
- });
408
- nodeDef.addTransformation({
409
- id: 'gltf_matrix_rotation',
410
- matrix: matR
411
- });
412
- nodeDef.addTransformation({
413
- id: 'gltf_matrix_scale',
414
- matrix: matS
415
- });
416
- }
417
-
418
- if (node.mesh !== undefined)
419
- nodeDef.addChild(this._geometryLoader.loadMesh(node.mesh, node.weights));
420
-
421
- if (node.camera !== undefined)
422
- nodeDef.addChild(this.loadCamera(node.camera));
423
-
424
- if (node.extensions && node.extensions[GLTF_EXTENSIONS.KHR_LIGHTS_PUNCTUAL])
425
- nodeDef.addChild(this.loadLights(node.extensions[GLTF_EXTENSIONS.KHR_LIGHTS_PUNCTUAL].light));
426
-
427
- if (node.children) {
428
- for (let i = 0, len = node.children.length; i < len; i++) {
429
- // got through all children
430
- nodeDef.addChild(await this.loadNode(node.children[i]));
431
- }
432
- }
433
-
434
- this._numberOfConvertedNodes++;
435
- if (performance.now() - this._progressTimer > this._progressUpdateLimit) {
436
- this._progressTimer = performance.now();
437
- const eventProgress: ITaskEvent = { type: TASK_TYPE.GLTF_CONTENT_LOADING, id: this._eventId, progress: (this._numberOfConvertedNodes / this._numberOfNodes) / 2 + 0.1, status: `GlTF conversion progress: ${this._numberOfConvertedNodes}/${this._numberOfNodes} nodes.` };
438
- this._eventEngine.emitEvent(EVENTTYPE.TASK.TASK_PROCESS, eventProgress);
439
- await new Promise(resolve => setTimeout(resolve, 0));
440
- }
441
-
442
- return nodeDef;
443
- }
444
-
445
- private async loadScene(): Promise<ITreeNode> {
446
- if (!this._content.scenes) throw new Error('Scenes not available.');
447
- const sceneId = this._content.scene || 0;
448
- if (!this._content.scenes[sceneId]) throw new Error('Scene not available.');
449
- const scene = this._content.scenes[sceneId];
450
- const sceneDef = new TreeNode(scene.name || 'scene_' + sceneId + '');
451
- sceneDef.originalName = scene.name;
452
- sceneDef.addTransformation({
453
- id: this._uuidGenerator.create(),
454
- matrix: this._globalTransformation
455
- });
456
- if (scene.nodes)
457
- for (let i = 0, len = scene.nodes.length; i < len; i++)
458
- sceneDef.addChild(await this.loadNode(scene.nodes[i]));
459
- return sceneDef;
460
- }
461
-
462
- private loadSkin(skinId: number): {
463
- joints: number[],
464
- inverseBindMatrices: AttributeData | null
465
- } {
466
- if (!this._content.skins) throw new Error('Skins not available.');
467
- if (!this._content.skins[skinId]) throw new Error('Skin not available.');
468
- const skinDef = this._content.skins![skinId];
469
-
470
- const skinEntry: {
471
- joints: number[],
472
- inverseBindMatrices: AttributeData | null
473
- } = {
474
- joints: skinDef.joints,
475
- inverseBindMatrices: null
476
- };
477
-
478
- if (skinDef.inverseBindMatrices === undefined) {
479
- return skinEntry;
480
- }
481
-
482
- skinEntry.inverseBindMatrices = this._accessorLoader.getAccessor(skinDef.inverseBindMatrices);
483
- return skinEntry;
484
- }
485
-
486
- private validateVersionAndExtensions(): void {
487
- if (!this._content.asset) throw new Error('Asset not available.');
488
- const asset = this._content.asset;
489
- if (!asset.version) throw new Error('Asset does not have a version.');
490
- const version: string = asset.minVersion ? asset.minVersion : asset.version;
491
- if (!version.startsWith('2')) throw new Error('Version of the glTF not supported.');
492
-
493
- if (this._content.extensionsUsed) {
494
- const notSupported = [];
495
- for (let i = 0; i < this._content.extensionsUsed.length; i++) {
496
- if (!(<string[]>Object.values(GLTF_EXTENSIONS)).includes(this._content.extensionsUsed[i]))
497
- notSupported.push(this._content.extensionsUsed[i]);
498
- }
499
- if (notSupported.length > 0) {
500
- let message = 'Extension' + (notSupported.length === 1 ? ' ' : 's ');
501
- notSupported.forEach((element, index) => {
502
- message += '"' + element + '"' + (index === notSupported.length - 1 ? '' : index === notSupported.length - 2 ? ' and ' : ', ');
503
- });
504
- message += (notSupported.length === 1 ? ' is' : ' are') + ' not supported, but used. Loading glTF regardless.';
505
- this._logger.info('GLTFLoader.validateVersionAndExtensions: ' + message);
506
- }
507
- }
508
-
509
- if (this._content.extensionsRequired) {
510
- const notSupported = [];
511
- for (let i = 0; i < this._content.extensionsRequired.length; i++) {
512
- if (!(<string[]>Object.values(GLTF_EXTENSIONS)).includes(this._content.extensionsRequired[i]))
513
- notSupported.push(this._content.extensionsRequired[i]);
514
- }
515
- if (notSupported.length > 0) {
516
- let message = 'Extension' + (notSupported.length === 1 ? ' ' : 's ');
517
- notSupported.forEach((element, index) => {
518
- message += '"' + element + '"' + (index === notSupported.length - 1 ? '' : index === notSupported.length - 2 ? ' and ' : ', ');
519
- });
520
- message += (notSupported.length === 1 ? ' is' : ' are') + ' not supported, but required. Aborting glTF loading.';
521
- throw new Error(message);
522
- }
523
- }
524
- }
525
-
526
- // #endregion Private Methods (7)
527
- }