@shapediver/viewer.data-engine.gltf-converter 3.16.7 → 3.16.9

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,1070 +1,1070 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.GLTF_EXTENSIONS = exports.GLTFConverter = void 0;
13
- const viewer_data_engine_shared_types_1 = require("@shapediver/viewer.data-engine.shared-types");
14
- const viewer_shared_build_data_1 = require("@shapediver/viewer.shared.build-data");
15
- const viewer_shared_global_access_objects_1 = require("@shapediver/viewer.shared.global-access-objects");
16
- const viewer_shared_node_tree_1 = require("@shapediver/viewer.shared.node-tree");
17
- const viewer_shared_services_1 = require("@shapediver/viewer.shared.services");
18
- const viewer_shared_types_1 = require("@shapediver/viewer.shared.types");
19
- const gl_matrix_1 = require("gl-matrix");
20
- // #region Classes (1)
21
- class GLTFConverter {
22
- constructor() {
23
- // #region Properties (23)
24
- this._converter = viewer_shared_services_1.Converter.instance;
25
- this._eventEngine = viewer_shared_services_1.EventEngine.instance;
26
- this._globalAccessObjects = viewer_shared_global_access_objects_1.GlobalAccessObjects.instance;
27
- this._globalTransformationInverse = gl_matrix_1.mat4.fromValues(1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1);
28
- this._progressUpdateLimit = 500;
29
- this._uuidGenerator = viewer_shared_services_1.UuidGenerator.instance;
30
- this._animations = [];
31
- this._buffers = [];
32
- this._byteOffset = 0;
33
- this._content = {
34
- asset: {
35
- copyright: "2025 (c) ShapeDiver",
36
- generator: "ShapeDiverViewer@" + viewer_shared_build_data_1.build_data.build_version,
37
- version: "2.0",
38
- extensions: {},
39
- },
40
- };
41
- this._convertForAR = false;
42
- this._eventId = "";
43
- this._extensionsRequired = [];
44
- this._extensionsUsed = [];
45
- this._imageCache = {};
46
- this._materialCache = {};
47
- this._meshCache = {};
48
- this._nodes = [];
49
- this._numberOfNodes = 0;
50
- this._progressTimer = 0;
51
- this._promises = [];
52
- // #endregion Private Methods (17)
53
- }
54
- // #endregion Properties (23)
55
- // #region Public Static Getters And Setters (1)
56
- static get instance() {
57
- return this._instance || (this._instance = new this());
58
- }
59
- // #endregion Public Static Getters And Setters (1)
60
- // #region Public Methods (1)
61
- convert(node, convertForAR = false, viewport) {
62
- var _a, _b, _c;
63
- return __awaiter(this, void 0, void 0, function* () {
64
- this._eventId = this._uuidGenerator.create();
65
- const eventStart = {
66
- type: viewer_shared_types_1.TASK_TYPE.GLTF_CREATION,
67
- id: this._eventId,
68
- progress: 0,
69
- status: "Starting glTF conversion.",
70
- };
71
- this._eventEngine.emitEvent(viewer_shared_services_1.EVENTTYPE.TASK.TASK_START, eventStart);
72
- this._numberOfNodes = 0;
73
- node.traverse(() => this._numberOfNodes++);
74
- this._progressTimer = performance.now();
75
- this.reset();
76
- this._convertForAR = convertForAR;
77
- this._viewport = viewport;
78
- const originalParent = node.parent;
79
- const sceneNode = new viewer_shared_node_tree_1.TreeNode("ShapeDiverRootNode");
80
- sceneNode.addChild(node);
81
- const sceneDef = {
82
- name: (_a = sceneNode.displayName) !== null && _a !== void 0 ? _a : sceneNode.name,
83
- };
84
- const globalTransformationInverseId = this._uuidGenerator.create();
85
- node.addTransformation({
86
- id: globalTransformationInverseId,
87
- matrix: this._globalTransformationInverse,
88
- });
89
- const translationMatrixId = this._uuidGenerator.create();
90
- if (convertForAR) {
91
- // add translation matrix to scene tree node
92
- const center = node.boundingBox.boundingSphere.center;
93
- const translationMatrix = gl_matrix_1.mat4.fromTranslation(gl_matrix_1.mat4.create(), gl_matrix_1.vec3.multiply(gl_matrix_1.vec3.create(), gl_matrix_1.vec3.fromValues(center[0], center[1], center[2]), gl_matrix_1.vec3.fromValues(-1, -1, -1)));
94
- node.addTransformation({
95
- id: translationMatrixId,
96
- matrix: translationMatrix,
97
- });
98
- }
99
- if (this._viewport) {
100
- if (this._viewport &&
101
- node.excludeViewports.includes(this._viewport) === false &&
102
- (node.restrictViewports.length > 0 &&
103
- !node.restrictViewports.includes(this._viewport)) === false) {
104
- const nodeId = yield this.convertNode(node);
105
- if (nodeId !== -1) {
106
- sceneDef.nodes = [];
107
- (_b = sceneDef.nodes) === null || _b === void 0 ? void 0 : _b.push(nodeId);
108
- }
109
- }
110
- }
111
- else {
112
- const nodeId = yield this.convertNode(node);
113
- if (nodeId !== -1) {
114
- sceneDef.nodes = [];
115
- (_c = sceneDef.nodes) === null || _c === void 0 ? void 0 : _c.push(nodeId);
116
- }
117
- }
118
- for (let i = 0; i < node.transformations.length; i++)
119
- if (node.transformations[i].id === globalTransformationInverseId)
120
- node.removeTransformation(node.transformations[i]);
121
- if (convertForAR) {
122
- // remove translation the matrix
123
- for (let i = 0; i < node.transformations.length; i++)
124
- if (node.transformations[i].id === translationMatrixId)
125
- node.removeTransformation(node.transformations[i]);
126
- }
127
- this._content.scenes = [];
128
- this._content.scenes.push(sceneDef);
129
- this.convertAnimations();
130
- // Declare extensions.
131
- if (this._extensionsUsed.length > 0)
132
- this._content.extensionsUsed = this._extensionsUsed;
133
- if (this._extensionsRequired.length > 0)
134
- this._content.extensionsRequired = this._extensionsRequired;
135
- let promisesLength = 0;
136
- while (promisesLength !== this._promises.length) {
137
- promisesLength = this._promises.length;
138
- yield Promise.all(this._promises);
139
- yield new Promise((resolve) => setTimeout(resolve, 0));
140
- }
141
- const eventProgressImagePromises = {
142
- type: viewer_shared_types_1.TASK_TYPE.GLTF_CREATION,
143
- id: this._eventId,
144
- progress: 0.75,
145
- status: "GlTF images resolved.",
146
- };
147
- this._eventEngine.emitEvent(viewer_shared_services_1.EVENTTYPE.TASK.TASK_PROCESS, eventProgressImagePromises);
148
- // Merge buffers.
149
- const blob = new Blob(this._buffers, {
150
- type: "application/octet-stream",
151
- });
152
- if (originalParent)
153
- originalParent.addChild(node);
154
- // Update byte length of the single buffer.
155
- if (this._content.buffers && this._content.buffers.length > 0)
156
- this._content.buffers[0].byteLength = blob.size;
157
- return new Promise((resolve, reject) => {
158
- // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#glb-file-format-specification
159
- try {
160
- if (typeof window !== "undefined" && window.FileReader) {
161
- const reader = new window.FileReader();
162
- reader.readAsArrayBuffer(blob);
163
- reader.onloadend = () => {
164
- // Binary chunk.
165
- const binaryChunk = this.getPaddedArrayBuffer(reader.result);
166
- const binaryChunkPrefix = new DataView(new ArrayBuffer(8));
167
- binaryChunkPrefix.setUint32(0, binaryChunk.byteLength, true);
168
- binaryChunkPrefix.setUint32(4, 0x004e4942, true);
169
- // JSON chunk.
170
- const jsonChunk = this.getPaddedArrayBuffer(this.stringToArrayBuffer(JSON.stringify(this._content)), 0x20);
171
- const jsonChunkPrefix = new DataView(new ArrayBuffer(8));
172
- jsonChunkPrefix.setUint32(0, jsonChunk.byteLength, true);
173
- jsonChunkPrefix.setUint32(4, 0x4e4f534a, true);
174
- // GLB header.
175
- const header = new ArrayBuffer(12);
176
- const headerView = new DataView(header);
177
- headerView.setUint32(0, 0x46546c67, true);
178
- headerView.setUint32(4, 2, true);
179
- const totalByteLength = 12 +
180
- jsonChunkPrefix.byteLength +
181
- jsonChunk.byteLength +
182
- binaryChunkPrefix.byteLength +
183
- binaryChunk.byteLength;
184
- headerView.setUint32(8, totalByteLength, true);
185
- const glbBlob = new Blob([
186
- header,
187
- jsonChunkPrefix,
188
- jsonChunk,
189
- binaryChunkPrefix,
190
- binaryChunk,
191
- ], { type: "application/octet-stream" });
192
- const glbReader = new window.FileReader();
193
- glbReader.readAsArrayBuffer(glbBlob);
194
- glbReader.onloadend = () => {
195
- const eventEnd = {
196
- type: viewer_shared_types_1.TASK_TYPE.GLTF_CREATION,
197
- id: this._eventId,
198
- progress: 1,
199
- status: "GlTF creation complete.",
200
- };
201
- this._eventEngine.emitEvent(viewer_shared_services_1.EVENTTYPE.TASK.TASK_END, eventEnd);
202
- resolve(glbReader.result);
203
- };
204
- glbReader.onerror = reject;
205
- };
206
- reader.onerror = reject;
207
- }
208
- else {
209
- reject("FileReader not available.");
210
- }
211
- }
212
- catch (e) {
213
- reject(e);
214
- }
215
- });
216
- });
217
- }
218
- // #endregion Public Methods (1)
219
- // #region Private Methods (17)
220
- convertAccessor(data) {
221
- if (!this._content.accessors)
222
- this._content.accessors = [];
223
- const bufferView = this.convertBufferView(data);
224
- const minMax = this.getMinMax(data);
225
- const accessorDef = {
226
- bufferView: bufferView,
227
- byteOffset: 0,
228
- componentType: this.getComponentType(data.array),
229
- normalized: data.normalized,
230
- count: +data.count,
231
- max: minMax.max,
232
- min: minMax.min,
233
- type: this.getType(data.itemSize),
234
- // sparse: { // TODO
235
- // count: number,
236
- // indices: {
237
- // bufferView: number,
238
- // byteOffset?: number,
239
- // componentType: number,
240
- // extensions?: { [id: string]: any },
241
- // extras?: any
242
- // },
243
- // values: {
244
- // bufferView: number,
245
- // byteOffset?: number,
246
- // extensions?: { [id: string]: any },
247
- // extras?: any
248
- // },
249
- // extensions?: { [id: string]: any },
250
- // extras?: any
251
- // },
252
- };
253
- this._content.accessors.push(accessorDef);
254
- return this._content.accessors.length - 1;
255
- }
256
- convertAnimations() {
257
- var _a;
258
- if (!this._content.animations && this._animations.length > 0)
259
- this._content.animations = [];
260
- for (let i = 0; i < this._animations.length; i++) {
261
- const animation = this._animations[i];
262
- const animationDef = {
263
- name: animation.name || "animation_" + i,
264
- channels: [],
265
- samplers: [],
266
- };
267
- for (let j = 0; j < animation.tracks.length; j++) {
268
- const track = animation.tracks[j];
269
- const value = this._nodes.find((a) => a.node === track.node);
270
- if (!value)
271
- continue;
272
- const inputMin = Math.min(...track.times);
273
- const inputMax = Math.max(...track.times);
274
- const inputData = new viewer_shared_node_tree_1.AttributeData(new Float32Array(track.times), 1, 4, 0, 4, false, track.times.length, [inputMin], [inputMax]);
275
- const outputMin = [];
276
- outputMin.push(Math.min(...track.values.filter((s, i) => i % (track.path === "rotation" ? 4 : 3) === 0)));
277
- outputMin.push(Math.min(...track.values.filter((s, i) => i % (track.path === "rotation" ? 4 : 3) === 1)));
278
- outputMin.push(Math.min(...track.values.filter((s, i) => i % (track.path === "rotation" ? 4 : 3) === 2)));
279
- if (track.path === "rotation") {
280
- outputMin.push(Math.min(...track.values.filter((s, i) => i % 4 === 3)));
281
- }
282
- const outputMax = [];
283
- outputMax.push(Math.max(...track.values.filter((s, i) => i % (track.path === "rotation" ? 4 : 3) === 0)));
284
- outputMax.push(Math.max(...track.values.filter((s, i) => i % (track.path === "rotation" ? 4 : 3) === 1)));
285
- outputMax.push(Math.max(...track.values.filter((s, i) => i % (track.path === "rotation" ? 4 : 3) === 2)));
286
- if (track.path === "rotation") {
287
- outputMax.push(Math.max(...track.values.filter((s, i) => i % 4 === 3)));
288
- }
289
- const outputData = new viewer_shared_node_tree_1.AttributeData(new Float32Array(track.values), track.path === "rotation" ? 4 : 3, //itemSize
290
- track.path === "rotation" ? 16 : 12, //itemBytes
291
- 0, 4, false, track.times.length, outputMin, outputMax, track.path === "rotation" ? 16 : 12);
292
- const samplerDef = {
293
- input: this.convertAccessor(inputData),
294
- output: this.convertAccessor(outputData),
295
- interpolation: typeof track.interpolation === "string"
296
- ? track.interpolation.toUpperCase()
297
- : "LINEAR",
298
- };
299
- animationDef.samplers.push(samplerDef);
300
- const channelDef = {
301
- sampler: animationDef.samplers.length - 1,
302
- target: {
303
- node: value.id,
304
- path: track.path,
305
- },
306
- };
307
- animationDef.channels.push(channelDef);
308
- }
309
- (_a = this._content.animations) === null || _a === void 0 ? void 0 : _a.push(animationDef);
310
- }
311
- }
312
- convertBuffer(buffer) {
313
- if (!this._content.buffers)
314
- this._content.buffers = [];
315
- if (this._content.buffers.length === 0)
316
- this._content.buffers = [{ byteLength: 0 }];
317
- this._buffers.push(buffer);
318
- return 0;
319
- }
320
- convertBufferView(data) {
321
- if (!this._content.bufferViews)
322
- this._content.bufferViews = [];
323
- const componentTypeNumber = this.getComponentType(data.array);
324
- const componentSize = viewer_data_engine_shared_types_1.ACCESSORCOMPONENTSIZE_V2[componentTypeNumber];
325
- const byteLength = Math.ceil((data.count * data.itemSize * componentSize) / 4) * 4;
326
- const dataView = new DataView(new ArrayBuffer(byteLength));
327
- let offset = 0;
328
- for (let i = 0; i < data.count; i++) {
329
- for (let a = 0; a < data.itemSize; a++) {
330
- let value = 0;
331
- if (data.itemSize > 4) {
332
- // no support for interleaved data for itemSize > 4
333
- value = data.array[i * data.itemSize + a];
334
- }
335
- else {
336
- if (a === 0)
337
- value = data.array[i * data.itemSize];
338
- else if (a === 1)
339
- value = data.array[i * data.itemSize + 1];
340
- else if (a === 2)
341
- value = data.array[i * data.itemSize + 2];
342
- else if (a === 3)
343
- value = data.array[i * data.itemSize + 3];
344
- }
345
- if (data.array instanceof Float32Array) {
346
- dataView.setFloat32(offset, value, true);
347
- }
348
- else if (data.array instanceof Uint32Array) {
349
- dataView.setUint32(offset, value, true);
350
- }
351
- else if (data.array instanceof Uint16Array) {
352
- dataView.setUint16(offset, value, true);
353
- }
354
- else if (data.array instanceof Int16Array) {
355
- dataView.setInt16(offset, value, true);
356
- }
357
- else if (data.array instanceof Uint8Array) {
358
- dataView.setUint8(offset, value);
359
- }
360
- else if (data.array instanceof Int8Array) {
361
- dataView.setInt8(offset, value);
362
- }
363
- offset += componentSize;
364
- }
365
- }
366
- const bufferViewDef = {
367
- buffer: this.convertBuffer(dataView.buffer),
368
- byteOffset: this._byteOffset,
369
- byteLength: byteLength,
370
- target: data.target,
371
- };
372
- this._byteOffset += byteLength;
373
- this._content.bufferViews.push(bufferViewDef);
374
- return this._content.bufferViews.length - 1;
375
- }
376
- convertBufferViewImage(blob) {
377
- return __awaiter(this, void 0, void 0, function* () {
378
- if (!this._content.bufferViews)
379
- this._content.bufferViews = [];
380
- return new Promise((resolve, reject) => {
381
- try {
382
- if (typeof window !== "undefined" && window.FileReader) {
383
- const reader = new window.FileReader();
384
- reader.readAsArrayBuffer(blob);
385
- reader.onloadend = () => {
386
- const buffer = this.getPaddedArrayBuffer(reader.result);
387
- const bufferViewDef = {
388
- buffer: this.convertBuffer(buffer),
389
- byteOffset: this._byteOffset,
390
- byteLength: buffer.byteLength,
391
- };
392
- this._byteOffset += buffer.byteLength;
393
- this._content.bufferViews.push(bufferViewDef);
394
- resolve(this._content.bufferViews.length - 1);
395
- };
396
- reader.onerror = reject;
397
- }
398
- else {
399
- reject("FileReader not available.");
400
- }
401
- }
402
- catch (e) {
403
- reject(e);
404
- }
405
- });
406
- });
407
- }
408
- convertImage(data) {
409
- if (!this._content.images)
410
- this._content.images = [];
411
- if (data.image instanceof ArrayBuffer)
412
- return;
413
- const cacheKey = (data.image instanceof HTMLImageElement
414
- ? data.image.src
415
- : data.image.id) || data.image.id;
416
- if (this._imageCache[cacheKey] !== undefined)
417
- return this._imageCache[cacheKey];
418
- const imageDef = {};
419
- const canvas = document.createElement("canvas");
420
- canvas.width = data.image.width;
421
- canvas.height = data.image.height;
422
- const ctx = canvas.getContext("2d");
423
- if (data.flipY) {
424
- ctx.translate(0, canvas.height);
425
- ctx.scale(1, -1);
426
- }
427
- if (data.blob) {
428
- imageDef.mimeType = data.blob.type;
429
- this._promises.push(new Promise((resolve, reject) => {
430
- try {
431
- this.convertBufferViewImage(data.blob).then((bufferViewIndex) => {
432
- imageDef.bufferView = bufferViewIndex;
433
- resolve();
434
- });
435
- }
436
- catch (e) {
437
- reject(e);
438
- }
439
- }));
440
- }
441
- else if (data.image instanceof HTMLImageElement) {
442
- let mimeType = "image/png";
443
- if (data.image.src.endsWith(".jpg") ||
444
- data.image.src.includes("image/jpeg"))
445
- mimeType = "image/jpeg";
446
- imageDef.mimeType = mimeType;
447
- const DATA_URI_REGEX = /^data:(.*?)(;base64)?,(.*)$/;
448
- if (DATA_URI_REGEX.test(data.image.src)) {
449
- const byteString = (0, viewer_shared_services_1.atobCustom)(data.image.src.split(",")[1]);
450
- const mimeType = data.image.src
451
- .split(",")[0]
452
- .split(":")[1]
453
- .split(";")[0];
454
- const ab = new ArrayBuffer(byteString.length);
455
- const ia = new Uint8Array(ab);
456
- for (let i = 0; i < byteString.length; i++)
457
- ia[i] = byteString.charCodeAt(i);
458
- const blob = new Blob([ab], { type: mimeType });
459
- this._promises.push(new Promise((resolve, reject) => {
460
- try {
461
- this.convertBufferViewImage(blob)
462
- .then((bufferViewIndex) => {
463
- imageDef.bufferView = bufferViewIndex;
464
- resolve();
465
- })
466
- .catch(reject);
467
- }
468
- catch (e) {
469
- reject(e);
470
- }
471
- }));
472
- }
473
- else {
474
- ctx.drawImage(data.image, 0, 0, canvas.width, canvas.height);
475
- this._promises.push(new Promise((resolve, reject) => {
476
- try {
477
- canvas.toBlob((blob) => __awaiter(this, void 0, void 0, function* () {
478
- try {
479
- const bufferViewIndex = yield this.convertBufferViewImage(blob);
480
- imageDef.bufferView = bufferViewIndex;
481
- resolve();
482
- }
483
- catch (e) {
484
- reject(e);
485
- }
486
- }), mimeType);
487
- }
488
- catch (e) {
489
- reject(e);
490
- }
491
- }));
492
- }
493
- }
494
- else {
495
- // convert to blob
496
- const bitmapCanvas = document.createElement("canvas");
497
- bitmapCanvas.width = data.image.width;
498
- bitmapCanvas.height = data.image.height;
499
- const bitmapCtx = bitmapCanvas.getContext("2d");
500
- bitmapCtx.drawImage(data.image, 0, 0);
501
- this._promises.push(new Promise((resolve, reject) => {
502
- try {
503
- bitmapCanvas.toBlob((blob) => __awaiter(this, void 0, void 0, function* () {
504
- try {
505
- const bufferViewIndex = yield this.convertBufferViewImage(blob);
506
- imageDef.bufferView = bufferViewIndex;
507
- resolve();
508
- }
509
- catch (e) {
510
- reject(e);
511
- }
512
- }), "image/png");
513
- }
514
- catch (e) {
515
- reject(e);
516
- }
517
- }));
518
- }
519
- this._content.images.push(imageDef);
520
- this._imageCache[cacheKey] = this._content.images.length - 1;
521
- return this._content.images.length - 1;
522
- }
523
- convertMaterial(data, includeMaps = true) {
524
- if (!this._content.materials)
525
- this._content.materials = [];
526
- if (this._materialCache[data.id + "_" + data.version] !== undefined)
527
- return this._materialCache[data.id + "_" + data.version];
528
- const materialDef = {
529
- name: this._convertForAR ? this._uuidGenerator.create() : data.name,
530
- pbrMetallicRoughness: {},
531
- };
532
- if (data instanceof viewer_shared_node_tree_1.MaterialSpecularGlossinessData) {
533
- if (!this._extensionsUsed.includes("KHR_materials_pbrSpecularGlossiness"))
534
- this._extensionsUsed.push("KHR_materials_pbrSpecularGlossiness");
535
- if (!this._extensionsRequired.includes("KHR_materials_pbrSpecularGlossiness"))
536
- this._extensionsRequired.push("KHR_materials_pbrSpecularGlossiness");
537
- const ext = {};
538
- ext.diffuseFactor = this._converter.toColorArray(data.color);
539
- ext.diffuseFactor[3] = data.opacity;
540
- if (data.map && includeMaps) {
541
- const textureIndex = this.convertTexture(data.map);
542
- if (textureIndex !== undefined)
543
- ext.diffuseTexture = { index: textureIndex };
544
- }
545
- ext.specularFactor = this._converter.toColorArray(data.specular);
546
- ext.glossinessFactor = data.glossiness;
547
- if (data.specularGlossinessMap && includeMaps) {
548
- const textureIndex = this.convertTexture(data.specularGlossinessMap);
549
- if (textureIndex !== undefined)
550
- ext.specularGlossinessTexture = { index: textureIndex };
551
- }
552
- materialDef.extensions = {
553
- KHR_materials_pbrSpecularGlossiness: ext,
554
- };
555
- }
556
- else if (data instanceof viewer_shared_node_tree_1.MaterialUnlitData) {
557
- if (!this._extensionsUsed.includes("KHR_materials_unlit"))
558
- this._extensionsUsed.push("KHR_materials_unlit");
559
- if (!this._extensionsRequired.includes("KHR_materials_unlit"))
560
- this._extensionsRequired.push("KHR_materials_unlit");
561
- materialDef.pbrMetallicRoughness.baseColorFactor =
562
- this._converter.toColorArray(data.color);
563
- materialDef.pbrMetallicRoughness.baseColorFactor[3] = data.opacity;
564
- if (data.map && includeMaps) {
565
- const textureIndex = this.convertTexture(data.map);
566
- if (textureIndex !== undefined)
567
- materialDef.pbrMetallicRoughness.baseColorTexture = {
568
- index: textureIndex,
569
- };
570
- }
571
- materialDef.extensions = {
572
- KHR_materials_unlit: {},
573
- };
574
- }
575
- else {
576
- const standardMaterialData = data;
577
- materialDef.pbrMetallicRoughness.baseColorFactor =
578
- this._converter.toColorArray(standardMaterialData.color);
579
- materialDef.pbrMetallicRoughness.baseColorFactor[3] =
580
- standardMaterialData.opacity;
581
- if (standardMaterialData.map && includeMaps) {
582
- const textureIndex = this.convertTexture(standardMaterialData.map);
583
- if (textureIndex !== undefined)
584
- materialDef.pbrMetallicRoughness.baseColorTexture = {
585
- index: textureIndex,
586
- };
587
- }
588
- materialDef.pbrMetallicRoughness.metallicFactor =
589
- standardMaterialData.metalness;
590
- materialDef.pbrMetallicRoughness.roughnessFactor =
591
- standardMaterialData.roughness;
592
- if (standardMaterialData.metalnessRoughnessMap && includeMaps) {
593
- const textureIndex = this.convertTexture(standardMaterialData.metalnessRoughnessMap);
594
- if (textureIndex !== undefined)
595
- materialDef.pbrMetallicRoughness.metallicRoughnessTexture =
596
- { index: textureIndex };
597
- }
598
- else if (standardMaterialData.metalnessMap &&
599
- standardMaterialData.roughnessMap &&
600
- includeMaps) {
601
- if (this._globalAccessObjects.combineTextures) {
602
- this._promises.push(new Promise((resolve, reject) => {
603
- try {
604
- // no support for combining textures
605
- if (!this._globalAccessObjects.combineTextures)
606
- return standardMaterialData.roughnessMap;
607
- this._globalAccessObjects
608
- .combineTextures(undefined, standardMaterialData.roughnessMap &&
609
- standardMaterialData.roughnessMap
610
- .image instanceof
611
- HTMLImageElement
612
- ? standardMaterialData.roughnessMap
613
- .image
614
- : undefined, standardMaterialData.metalnessMap &&
615
- standardMaterialData.metalnessMap
616
- .image instanceof
617
- HTMLImageElement
618
- ? standardMaterialData.metalnessMap
619
- .image
620
- : undefined)
621
- .then((imageData) => {
622
- const m = (standardMaterialData.roughnessMap ||
623
- standardMaterialData.metalnessMap);
624
- const mapData = new viewer_shared_node_tree_1.MapData(imageData.image, {
625
- blob: imageData.blob,
626
- wrapS: m.wrapS,
627
- wrapT: m.wrapT,
628
- minFilter: m.minFilter,
629
- magFilter: m.magFilter,
630
- center: m.center,
631
- color: m.color,
632
- offset: m.offset,
633
- repeat: m.repeat,
634
- rotation: m.rotation,
635
- texCoord: m.texCoord,
636
- flipY: m.flipY,
637
- });
638
- const textureIndex = this.convertTexture(mapData);
639
- if (textureIndex !== undefined)
640
- materialDef.pbrMetallicRoughness.metallicRoughnessTexture =
641
- { index: textureIndex };
642
- resolve();
643
- })
644
- .catch(reject);
645
- }
646
- catch (e) {
647
- reject(e);
648
- }
649
- }));
650
- }
651
- else {
652
- // no support for combining textures
653
- const textureIndex = this.convertTexture(standardMaterialData.roughnessMap);
654
- if (textureIndex !== undefined)
655
- materialDef.pbrMetallicRoughness.metallicRoughnessTexture =
656
- { index: textureIndex };
657
- }
658
- }
659
- else if (standardMaterialData.metalnessMap && includeMaps) {
660
- const textureIndex = this.convertTexture(standardMaterialData.metalnessMap);
661
- if (textureIndex !== undefined)
662
- materialDef.pbrMetallicRoughness.metallicRoughnessTexture =
663
- { index: textureIndex };
664
- }
665
- else if (standardMaterialData.roughnessMap && includeMaps) {
666
- const textureIndex = this.convertTexture(standardMaterialData.roughnessMap);
667
- if (textureIndex !== undefined)
668
- materialDef.pbrMetallicRoughness.metallicRoughnessTexture =
669
- { index: textureIndex };
670
- }
671
- }
672
- if (data.normalMap && includeMaps) {
673
- const textureIndex = this.convertTexture(data.normalMap);
674
- if (textureIndex !== undefined)
675
- materialDef.normalTexture = { index: textureIndex };
676
- }
677
- if (data.aoMap && includeMaps) {
678
- const textureIndex = this.convertTexture(data.aoMap);
679
- if (textureIndex !== undefined)
680
- materialDef.occlusionTexture = { index: textureIndex };
681
- }
682
- if (data.emissiveMap && includeMaps) {
683
- const textureIndex = this.convertTexture(data.emissiveMap);
684
- if (textureIndex !== undefined)
685
- materialDef.emissiveTexture = { index: textureIndex };
686
- }
687
- if (data.emissiveness)
688
- materialDef.emissiveFactor = this._converter.toColorArray(data.emissiveness);
689
- materialDef.alphaMode = data.alphaMode.toUpperCase();
690
- if (data.alphaMode === viewer_shared_types_1.MATERIAL_ALPHA.MASK)
691
- materialDef.alphaCutoff = data.alphaCutoff;
692
- materialDef.doubleSided = data.side === viewer_shared_types_1.MATERIAL_SIDE.DOUBLE;
693
- this._content.materials.push(materialDef);
694
- this._materialCache[data.id + "_" + data.version] =
695
- this._content.materials.length - 1;
696
- return this._materialCache[data.id + "_" + data.version];
697
- }
698
- convertMesh(data) {
699
- var _a;
700
- if (!this._content.meshes)
701
- this._content.meshes = [];
702
- if (this._meshCache[data.id + "_" + data.version] !== undefined)
703
- return this._meshCache[data.id + "_" + data.version];
704
- const meshDef = {
705
- primitives: [],
706
- };
707
- (_a = meshDef.primitives) === null || _a === void 0 ? void 0 : _a.push(this.convertPrimitive(data, data.primitive));
708
- this._content.meshes.push(meshDef);
709
- this._meshCache[data.id + "_" + data.version] =
710
- this._content.meshes.length - 1;
711
- return this._meshCache[data.id + "_" + data.version];
712
- }
713
- convertNode(node) {
714
- var _a, _b, _c, _d, _e;
715
- return __awaiter(this, void 0, void 0, function* () {
716
- if (!this._content.nodes)
717
- this._content.nodes = [];
718
- const nodeDef = {
719
- name: this._convertForAR
720
- ? this._uuidGenerator.create()
721
- : ((_a = node.displayName) !== null && _a !== void 0 ? _a : node.name),
722
- };
723
- if (node.transformations.length > 0) {
724
- let matrix = node.nodeMatrix;
725
- if (node.nodeMatrix.filter((v) => isNaN(v) || v === Infinity || v === -Infinity).length > 0)
726
- matrix = gl_matrix_1.mat4.create();
727
- const inv = gl_matrix_1.mat4.invert(gl_matrix_1.mat4.create(), matrix);
728
- if (inv) {
729
- nodeDef.matrix = [];
730
- for (let i = 0; i < matrix.length; i++)
731
- nodeDef.matrix[i] = matrix[i];
732
- }
733
- else {
734
- viewer_shared_services_1.Logger.instance.warn(`GLTFConverter.convertNode: The matrix of node ${(_b = node.displayName) !== null && _b !== void 0 ? _b : node.name} is not invertible and will be ignored.`);
735
- }
736
- }
737
- for (let i = 0; i < node.data.length; i++) {
738
- if (node.data[i] instanceof viewer_shared_node_tree_1.GeometryData) {
739
- const geometryData = node.data[i];
740
- let instanceMatrices;
741
- // as this is a node that contains a mesh
742
- // we check the parent node for instance matrices
743
- if (node.parent) {
744
- const instanceMatricesData = node.parent.data.find((d) => d instanceof viewer_shared_node_tree_1.InstanceData);
745
- if (instanceMatricesData &&
746
- instanceMatricesData.instanceMatrices &&
747
- instanceMatricesData.instanceMatrices.length > 0) {
748
- instanceMatrices =
749
- instanceMatricesData.instanceMatrices;
750
- }
751
- }
752
- if (this._convertForAR) {
753
- if (geometryData.mode !== viewer_shared_types_1.PRIMITIVE_MODE.POINTS &&
754
- geometryData.mode !== viewer_shared_types_1.PRIMITIVE_MODE.LINES &&
755
- geometryData.mode !== viewer_shared_types_1.PRIMITIVE_MODE.LINE_LOOP &&
756
- geometryData.mode !== viewer_shared_types_1.PRIMITIVE_MODE.LINE_STRIP) {
757
- if (instanceMatrices) {
758
- if (!nodeDef.children)
759
- nodeDef.children = [];
760
- const meshDef = this.convertMesh(geometryData);
761
- // create intermediate nodes for each instance
762
- for (let j = 0; j < instanceMatrices.length; j++) {
763
- this._content.nodes.push({
764
- name: ((_c = node.displayName) !== null && _c !== void 0 ? _c : node.name) +
765
- "_instance_" +
766
- j,
767
- matrix: Array.from(instanceMatrices[j]),
768
- mesh: meshDef,
769
- });
770
- (_d = nodeDef.children) === null || _d === void 0 ? void 0 : _d.push(this._content.nodes.length - 1);
771
- }
772
- }
773
- else {
774
- nodeDef.mesh = this.convertMesh(geometryData);
775
- }
776
- }
777
- }
778
- else {
779
- nodeDef.mesh = this.convertMesh(geometryData);
780
- if (instanceMatrices) {
781
- if (!nodeDef.extensions)
782
- nodeDef.extensions = {};
783
- nodeDef.extensions[GLTF_EXTENSIONS.EXT_MESH_GPU_INSTANCING] = this.convertInstances(instanceMatrices);
784
- if (!this._extensionsUsed.includes(GLTF_EXTENSIONS.EXT_MESH_GPU_INSTANCING))
785
- this._extensionsUsed.push(GLTF_EXTENSIONS.EXT_MESH_GPU_INSTANCING);
786
- if (!this._extensionsRequired.includes(GLTF_EXTENSIONS.EXT_MESH_GPU_INSTANCING))
787
- this._extensionsRequired.push(GLTF_EXTENSIONS.EXT_MESH_GPU_INSTANCING);
788
- }
789
- }
790
- }
791
- if (node.data[i] instanceof viewer_shared_node_tree_1.AnimationData)
792
- this._animations.push(node.data[i]);
793
- }
794
- if (node.children.length > 0)
795
- nodeDef.children = [];
796
- for (let i = 0; i < node.children.length; i++) {
797
- if (node.children[i].visible === true) {
798
- if (this._viewport) {
799
- if (node.children[i].excludeViewports.includes(this._viewport))
800
- continue;
801
- if (node.children[i].restrictViewports.length > 0 &&
802
- !node.children[i].restrictViewports.includes(this._viewport))
803
- continue;
804
- }
805
- const nodeId = yield this.convertNode(node.children[i]);
806
- if (nodeId !== -1)
807
- (_e = nodeDef.children) === null || _e === void 0 ? void 0 : _e.push(nodeId);
808
- }
809
- }
810
- // remove children array if it is empty
811
- if (nodeDef.children !== undefined && nodeDef.children.length === 0)
812
- nodeDef.children = undefined;
813
- if (performance.now() - this._progressTimer >
814
- this._progressUpdateLimit) {
815
- this._progressTimer = performance.now();
816
- const eventProgress = {
817
- type: viewer_shared_types_1.TASK_TYPE.GLTF_CREATION,
818
- id: this._eventId,
819
- progress: this._content.nodes.length / this._numberOfNodes / 2,
820
- status: `GlTF conversion progress: ${this._content.nodes.length}/${this._numberOfNodes} nodes.`,
821
- };
822
- this._eventEngine.emitEvent(viewer_shared_services_1.EVENTTYPE.TASK.TASK_PROCESS, eventProgress);
823
- yield new Promise((resolve) => setTimeout(resolve, 0));
824
- }
825
- // if the node is empty, don't add it
826
- if (nodeDef.camera === undefined &&
827
- nodeDef.children === undefined &&
828
- nodeDef.mesh === undefined &&
829
- nodeDef.extensions === undefined &&
830
- nodeDef.extras === undefined)
831
- return -1;
832
- this._content.nodes.push(nodeDef);
833
- this._nodes.push({
834
- node,
835
- id: this._content.nodes.length - 1,
836
- });
837
- return this._content.nodes.length - 1;
838
- });
839
- }
840
- convertInstances(matrices) {
841
- const translations = matrices.map((m) => gl_matrix_1.mat4.getTranslation(gl_matrix_1.vec3.create(), m));
842
- const translationMap = translations.flatMap((t) => Array.from(t));
843
- const { minimum: minTranslation, maximum: maxTranslation } = translations.reduce((acc, t) => {
844
- for (let i = 0; i < 3; i++) {
845
- acc.minimum[i] = Math.min(acc.minimum[i], t[i]);
846
- acc.maximum[i] = Math.max(acc.maximum[i], t[i]);
847
- }
848
- return acc;
849
- }, {
850
- minimum: [Infinity, Infinity, Infinity],
851
- maximum: [-Infinity, -Infinity, -Infinity],
852
- });
853
- const rotations = matrices.map((m) => gl_matrix_1.mat4.getRotation(gl_matrix_1.quat.create(), m));
854
- const rotationsMap = rotations.flatMap((r) => Array.from(r));
855
- const { minimum: minRotation, maximum: maxRotation } = rotations.reduce((acc, r) => {
856
- for (let i = 0; i < 4; i++) {
857
- acc.minimum[i] = Math.min(acc.minimum[i], r[i]);
858
- acc.maximum[i] = Math.max(acc.maximum[i], r[i]);
859
- }
860
- return acc;
861
- }, {
862
- minimum: [Infinity, Infinity, Infinity, Infinity],
863
- maximum: [-Infinity, -Infinity, -Infinity, -Infinity],
864
- });
865
- const scales = matrices.map((m) => gl_matrix_1.mat4.getScaling(gl_matrix_1.vec3.create(), m));
866
- const scalesMap = scales.flatMap((s) => Array.from(s));
867
- const { minimum: minScale, maximum: maxScale } = scales.reduce((acc, s) => {
868
- for (let i = 0; i < 3; i++) {
869
- acc.minimum[i] = Math.min(acc.minimum[i], s[i]);
870
- acc.maximum[i] = Math.max(acc.maximum[i], s[i]);
871
- }
872
- return acc;
873
- }, {
874
- minimum: [Infinity, Infinity, Infinity],
875
- maximum: [-Infinity, -Infinity, -Infinity],
876
- });
877
- return {
878
- attributes: {
879
- TRANSLATION: this.convertAccessor(new viewer_shared_node_tree_1.AttributeData(new Float32Array(translationMap), 3, 3 * 4, 0, 4, false, translations.length, minTranslation, maxTranslation)),
880
- ROTATION: this.convertAccessor(new viewer_shared_node_tree_1.AttributeData(new Float32Array(rotationsMap), 4, 4 * 4, 0, 4, false, rotations.length, minRotation, maxRotation)),
881
- SCALE: this.convertAccessor(new viewer_shared_node_tree_1.AttributeData(new Float32Array(scalesMap), 3, 3 * 4, 0, 4, false, scales.length, minScale, maxScale)),
882
- },
883
- };
884
- }
885
- convertPrimitive(geometryData, data) {
886
- const primitiveDef = {
887
- attributes: {},
888
- mode: geometryData.mode,
889
- };
890
- for (const a in data.attributes) {
891
- if (data.attributes[a].array.length > 0) {
892
- if (a.includes("COLOR")) {
893
- if (data.attributes[a].itemSize % 4 === 0) {
894
- primitiveDef.attributes[a] = this.convertAccessor(data.attributes[a]);
895
- }
896
- else if (data.attributes[a].itemSize % 3 === 0) {
897
- const oldAttributeData = data.attributes[a];
898
- const newArray = new Float32Array((oldAttributeData.array.length / 3) * 4);
899
- let counter = 0;
900
- for (let i = 0; i < newArray.length; i += 4) {
901
- newArray[i] =
902
- oldAttributeData.array[counter] /
903
- (oldAttributeData.elementBytes === 1
904
- ? 255.0
905
- : 1.0);
906
- newArray[i + 1] =
907
- oldAttributeData.array[counter + 1] /
908
- (oldAttributeData.elementBytes === 1
909
- ? 255.0
910
- : 1.0);
911
- newArray[i + 2] =
912
- oldAttributeData.array[counter + 2] /
913
- (oldAttributeData.elementBytes === 1
914
- ? 255.0
915
- : 1.0);
916
- newArray[i + 3] = 1.0;
917
- counter += 3;
918
- }
919
- primitiveDef.attributes[a] = this.convertAccessor(new viewer_shared_node_tree_1.AttributeData(newArray, 4, 4 * 4, oldAttributeData.byteOffset, 4, oldAttributeData.normalized, oldAttributeData.count, oldAttributeData.min, oldAttributeData.max, oldAttributeData.byteStride));
920
- }
921
- }
922
- else {
923
- primitiveDef.attributes[a] = this.convertAccessor(data.attributes[a]);
924
- }
925
- }
926
- }
927
- if (data.indices && data.indices.array.length > 0)
928
- primitiveDef.indices = this.convertAccessor(data.indices);
929
- if (geometryData.material) {
930
- const k = Object.keys(primitiveDef.attributes).find((k) => k.includes("TEXCOORD"));
931
- primitiveDef.material = this.convertMaterial(geometryData.material, !!k);
932
- }
933
- return primitiveDef;
934
- }
935
- convertTexture(data) {
936
- if (!this._content.textures)
937
- this._content.textures = [];
938
- const imageIndex = this.convertImage(data);
939
- if (imageIndex === undefined)
940
- return;
941
- const textureDef = {
942
- source: imageIndex,
943
- };
944
- // TODO samplers
945
- this._content.textures.push(textureDef);
946
- return this._content.textures.length - 1;
947
- }
948
- getComponentType(array) {
949
- switch (true) {
950
- case array instanceof Int8Array:
951
- return 5120;
952
- case array instanceof Uint8Array:
953
- return 5121;
954
- case array instanceof Int16Array:
955
- return 5122;
956
- case array instanceof Uint16Array:
957
- return 5123;
958
- case array instanceof Uint32Array:
959
- return 5125;
960
- default:
961
- return 5126;
962
- }
963
- }
964
- getMinMax(data) {
965
- const output = {
966
- min: new Array(data.itemSize).fill(Number.POSITIVE_INFINITY),
967
- max: new Array(data.itemSize).fill(Number.NEGATIVE_INFINITY),
968
- };
969
- for (let i = 0; i < data.count; i++) {
970
- for (let a = 0; a < data.itemSize; a++) {
971
- let value = 0;
972
- if (data.itemSize > 4) {
973
- // no support for interleaved data for itemSize > 4
974
- value = data.array[i * data.itemSize + a];
975
- }
976
- else {
977
- if (a === 0)
978
- value = data.array[i * data.itemSize];
979
- else if (a === 1)
980
- value = data.array[i * data.itemSize + 1];
981
- else if (a === 2)
982
- value = data.array[i * data.itemSize + 2];
983
- else if (a === 3)
984
- value = data.array[i * data.itemSize + 3];
985
- }
986
- output.min[a] = Math.min(output.min[a], value);
987
- output.max[a] = Math.max(output.max[a], value);
988
- }
989
- }
990
- return output;
991
- }
992
- getPaddedArrayBuffer(arrayBuffer, paddingByte = 0) {
993
- const paddedLength = Math.ceil(arrayBuffer.byteLength / 4) * 4;
994
- if (paddedLength !== arrayBuffer.byteLength) {
995
- const array = new Uint8Array(paddedLength);
996
- array.set(new Uint8Array(arrayBuffer));
997
- if (paddingByte !== 0) {
998
- for (let i = arrayBuffer.byteLength; i < paddedLength; i++) {
999
- array[i] = paddingByte;
1000
- }
1001
- }
1002
- return array.buffer;
1003
- }
1004
- return arrayBuffer;
1005
- }
1006
- getType(itemSize) {
1007
- switch (itemSize) {
1008
- case 1:
1009
- return "SCALAR";
1010
- case 2:
1011
- return "VEC2";
1012
- case 3:
1013
- return "VEC3";
1014
- case 4:
1015
- return "VEC4";
1016
- case 9:
1017
- return "MAT3";
1018
- case 18:
1019
- return "MAT4";
1020
- default:
1021
- return "VEC3";
1022
- }
1023
- }
1024
- reset() {
1025
- this._animations = [];
1026
- this._buffers = [];
1027
- this._byteOffset = 0;
1028
- this._content = {
1029
- asset: {
1030
- copyright: "2025 (c) ShapeDiver",
1031
- generator: "ShapeDiverViewer@" + viewer_shared_build_data_1.build_data.build_version,
1032
- version: "2.0",
1033
- extensions: {},
1034
- },
1035
- };
1036
- this._extensionsRequired = [];
1037
- this._extensionsUsed = [];
1038
- this._imageCache = {};
1039
- this._materialCache = {};
1040
- this._meshCache = {};
1041
- this._nodes = [];
1042
- this._promises = [];
1043
- this._convertForAR = false;
1044
- this._viewport = undefined;
1045
- }
1046
- stringToArrayBuffer(text) {
1047
- if (window.TextEncoder !== undefined) {
1048
- return new TextEncoder().encode(text).buffer;
1049
- }
1050
- const array = new Uint8Array(new ArrayBuffer(text.length));
1051
- for (let i = 0, il = text.length; i < il; i++) {
1052
- const value = text.charCodeAt(i);
1053
- // Replacing multi-byte character with space(0x20).
1054
- array[i] = value > 0xff ? 0x20 : value;
1055
- }
1056
- return array.buffer;
1057
- }
1058
- }
1059
- exports.GLTFConverter = GLTFConverter;
1060
- // #endregion Classes (1)
1061
- // #region Enums (1)
1062
- var GLTF_EXTENSIONS;
1063
- (function (GLTF_EXTENSIONS) {
1064
- GLTF_EXTENSIONS["KHR_BINARY_GLTF"] = "KHR_binary_glTF";
1065
- GLTF_EXTENSIONS["KHR_MATERIALS_PBRSPECULARGLOSSINESS"] = "KHR_materials_pbrSpecularGlossiness";
1066
- GLTF_EXTENSIONS["KHR_MATERIALS_UNLIT"] = "KHR_materials_unlit";
1067
- GLTF_EXTENSIONS["EXT_MESH_GPU_INSTANCING"] = "EXT_mesh_gpu_instancing";
1068
- })(GLTF_EXTENSIONS = exports.GLTF_EXTENSIONS || (exports.GLTF_EXTENSIONS = {}));
1069
- // #endregion Enums (1)
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.GLTF_EXTENSIONS = exports.GLTFConverter = void 0;
13
+ const viewer_data_engine_shared_types_1 = require("@shapediver/viewer.data-engine.shared-types");
14
+ const viewer_shared_build_data_1 = require("@shapediver/viewer.shared.build-data");
15
+ const viewer_shared_global_access_objects_1 = require("@shapediver/viewer.shared.global-access-objects");
16
+ const viewer_shared_node_tree_1 = require("@shapediver/viewer.shared.node-tree");
17
+ const viewer_shared_services_1 = require("@shapediver/viewer.shared.services");
18
+ const viewer_shared_types_1 = require("@shapediver/viewer.shared.types");
19
+ const gl_matrix_1 = require("gl-matrix");
20
+ // #region Classes (1)
21
+ class GLTFConverter {
22
+ constructor() {
23
+ // #region Properties (23)
24
+ this._converter = viewer_shared_services_1.Converter.instance;
25
+ this._eventEngine = viewer_shared_services_1.EventEngine.instance;
26
+ this._globalAccessObjects = viewer_shared_global_access_objects_1.GlobalAccessObjects.instance;
27
+ this._globalTransformationInverse = gl_matrix_1.mat4.fromValues(1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1);
28
+ this._progressUpdateLimit = 500;
29
+ this._uuidGenerator = viewer_shared_services_1.UuidGenerator.instance;
30
+ this._animations = [];
31
+ this._buffers = [];
32
+ this._byteOffset = 0;
33
+ this._content = {
34
+ asset: {
35
+ copyright: "2025 (c) ShapeDiver",
36
+ generator: "ShapeDiverViewer@" + viewer_shared_build_data_1.build_data.build_version,
37
+ version: "2.0",
38
+ extensions: {},
39
+ },
40
+ };
41
+ this._convertForAR = false;
42
+ this._eventId = "";
43
+ this._extensionsRequired = [];
44
+ this._extensionsUsed = [];
45
+ this._imageCache = {};
46
+ this._materialCache = {};
47
+ this._meshCache = {};
48
+ this._nodes = [];
49
+ this._numberOfNodes = 0;
50
+ this._progressTimer = 0;
51
+ this._promises = [];
52
+ // #endregion Private Methods (17)
53
+ }
54
+ // #endregion Properties (23)
55
+ // #region Public Static Getters And Setters (1)
56
+ static get instance() {
57
+ return this._instance || (this._instance = new this());
58
+ }
59
+ // #endregion Public Static Getters And Setters (1)
60
+ // #region Public Methods (1)
61
+ convert(node_1) {
62
+ return __awaiter(this, arguments, void 0, function* (node, convertForAR = false, viewport) {
63
+ var _a, _b, _c;
64
+ this._eventId = this._uuidGenerator.create();
65
+ const eventStart = {
66
+ type: viewer_shared_types_1.TASK_TYPE.GLTF_CREATION,
67
+ id: this._eventId,
68
+ progress: 0,
69
+ status: "Starting glTF conversion.",
70
+ };
71
+ this._eventEngine.emitEvent(viewer_shared_services_1.EVENTTYPE.TASK.TASK_START, eventStart);
72
+ this._numberOfNodes = 0;
73
+ node.traverse(() => this._numberOfNodes++);
74
+ this._progressTimer = performance.now();
75
+ this.reset();
76
+ this._convertForAR = convertForAR;
77
+ this._viewport = viewport;
78
+ const originalParent = node.parent;
79
+ const sceneNode = new viewer_shared_node_tree_1.TreeNode("ShapeDiverRootNode");
80
+ sceneNode.addChild(node);
81
+ const sceneDef = {
82
+ name: (_a = sceneNode.displayName) !== null && _a !== void 0 ? _a : sceneNode.name,
83
+ };
84
+ const globalTransformationInverseId = this._uuidGenerator.create();
85
+ node.addTransformation({
86
+ id: globalTransformationInverseId,
87
+ matrix: this._globalTransformationInverse,
88
+ });
89
+ const translationMatrixId = this._uuidGenerator.create();
90
+ if (convertForAR) {
91
+ // add translation matrix to scene tree node
92
+ const center = node.boundingBox.boundingSphere.center;
93
+ const translationMatrix = gl_matrix_1.mat4.fromTranslation(gl_matrix_1.mat4.create(), gl_matrix_1.vec3.multiply(gl_matrix_1.vec3.create(), gl_matrix_1.vec3.fromValues(center[0], center[1], center[2]), gl_matrix_1.vec3.fromValues(-1, -1, -1)));
94
+ node.addTransformation({
95
+ id: translationMatrixId,
96
+ matrix: translationMatrix,
97
+ });
98
+ }
99
+ if (this._viewport) {
100
+ if (this._viewport &&
101
+ node.excludeViewports.includes(this._viewport) === false &&
102
+ (node.restrictViewports.length > 0 &&
103
+ !node.restrictViewports.includes(this._viewport)) === false) {
104
+ const nodeId = yield this.convertNode(node);
105
+ if (nodeId !== -1) {
106
+ sceneDef.nodes = [];
107
+ (_b = sceneDef.nodes) === null || _b === void 0 ? void 0 : _b.push(nodeId);
108
+ }
109
+ }
110
+ }
111
+ else {
112
+ const nodeId = yield this.convertNode(node);
113
+ if (nodeId !== -1) {
114
+ sceneDef.nodes = [];
115
+ (_c = sceneDef.nodes) === null || _c === void 0 ? void 0 : _c.push(nodeId);
116
+ }
117
+ }
118
+ for (let i = 0; i < node.transformations.length; i++)
119
+ if (node.transformations[i].id === globalTransformationInverseId)
120
+ node.removeTransformation(node.transformations[i]);
121
+ if (convertForAR) {
122
+ // remove translation the matrix
123
+ for (let i = 0; i < node.transformations.length; i++)
124
+ if (node.transformations[i].id === translationMatrixId)
125
+ node.removeTransformation(node.transformations[i]);
126
+ }
127
+ this._content.scenes = [];
128
+ this._content.scenes.push(sceneDef);
129
+ this.convertAnimations();
130
+ // Declare extensions.
131
+ if (this._extensionsUsed.length > 0)
132
+ this._content.extensionsUsed = this._extensionsUsed;
133
+ if (this._extensionsRequired.length > 0)
134
+ this._content.extensionsRequired = this._extensionsRequired;
135
+ let promisesLength = 0;
136
+ while (promisesLength !== this._promises.length) {
137
+ promisesLength = this._promises.length;
138
+ yield Promise.all(this._promises);
139
+ yield new Promise((resolve) => setTimeout(resolve, 0));
140
+ }
141
+ const eventProgressImagePromises = {
142
+ type: viewer_shared_types_1.TASK_TYPE.GLTF_CREATION,
143
+ id: this._eventId,
144
+ progress: 0.75,
145
+ status: "GlTF images resolved.",
146
+ };
147
+ this._eventEngine.emitEvent(viewer_shared_services_1.EVENTTYPE.TASK.TASK_PROCESS, eventProgressImagePromises);
148
+ // Merge buffers.
149
+ const blob = new Blob(this._buffers, {
150
+ type: "application/octet-stream",
151
+ });
152
+ if (originalParent)
153
+ originalParent.addChild(node);
154
+ // Update byte length of the single buffer.
155
+ if (this._content.buffers && this._content.buffers.length > 0)
156
+ this._content.buffers[0].byteLength = blob.size;
157
+ return new Promise((resolve, reject) => {
158
+ // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#glb-file-format-specification
159
+ try {
160
+ if (typeof window !== "undefined" && window.FileReader) {
161
+ const reader = new window.FileReader();
162
+ reader.readAsArrayBuffer(blob);
163
+ reader.onloadend = () => {
164
+ // Binary chunk.
165
+ const binaryChunk = this.getPaddedArrayBuffer(reader.result);
166
+ const binaryChunkPrefix = new DataView(new ArrayBuffer(8));
167
+ binaryChunkPrefix.setUint32(0, binaryChunk.byteLength, true);
168
+ binaryChunkPrefix.setUint32(4, 0x004e4942, true);
169
+ // JSON chunk.
170
+ const jsonChunk = this.getPaddedArrayBuffer(this.stringToArrayBuffer(JSON.stringify(this._content)), 0x20);
171
+ const jsonChunkPrefix = new DataView(new ArrayBuffer(8));
172
+ jsonChunkPrefix.setUint32(0, jsonChunk.byteLength, true);
173
+ jsonChunkPrefix.setUint32(4, 0x4e4f534a, true);
174
+ // GLB header.
175
+ const header = new ArrayBuffer(12);
176
+ const headerView = new DataView(header);
177
+ headerView.setUint32(0, 0x46546c67, true);
178
+ headerView.setUint32(4, 2, true);
179
+ const totalByteLength = 12 +
180
+ jsonChunkPrefix.byteLength +
181
+ jsonChunk.byteLength +
182
+ binaryChunkPrefix.byteLength +
183
+ binaryChunk.byteLength;
184
+ headerView.setUint32(8, totalByteLength, true);
185
+ const glbBlob = new Blob([
186
+ header,
187
+ jsonChunkPrefix,
188
+ jsonChunk,
189
+ binaryChunkPrefix,
190
+ binaryChunk,
191
+ ], { type: "application/octet-stream" });
192
+ const glbReader = new window.FileReader();
193
+ glbReader.readAsArrayBuffer(glbBlob);
194
+ glbReader.onloadend = () => {
195
+ const eventEnd = {
196
+ type: viewer_shared_types_1.TASK_TYPE.GLTF_CREATION,
197
+ id: this._eventId,
198
+ progress: 1,
199
+ status: "GlTF creation complete.",
200
+ };
201
+ this._eventEngine.emitEvent(viewer_shared_services_1.EVENTTYPE.TASK.TASK_END, eventEnd);
202
+ resolve(glbReader.result);
203
+ };
204
+ glbReader.onerror = reject;
205
+ };
206
+ reader.onerror = reject;
207
+ }
208
+ else {
209
+ reject("FileReader not available.");
210
+ }
211
+ }
212
+ catch (e) {
213
+ reject(e);
214
+ }
215
+ });
216
+ });
217
+ }
218
+ // #endregion Public Methods (1)
219
+ // #region Private Methods (17)
220
+ convertAccessor(data) {
221
+ if (!this._content.accessors)
222
+ this._content.accessors = [];
223
+ const bufferView = this.convertBufferView(data);
224
+ const minMax = this.getMinMax(data);
225
+ const accessorDef = {
226
+ bufferView: bufferView,
227
+ byteOffset: 0,
228
+ componentType: this.getComponentType(data.array),
229
+ normalized: data.normalized,
230
+ count: +data.count,
231
+ max: minMax.max,
232
+ min: minMax.min,
233
+ type: this.getType(data.itemSize),
234
+ // sparse: { // TODO
235
+ // count: number,
236
+ // indices: {
237
+ // bufferView: number,
238
+ // byteOffset?: number,
239
+ // componentType: number,
240
+ // extensions?: { [id: string]: any },
241
+ // extras?: any
242
+ // },
243
+ // values: {
244
+ // bufferView: number,
245
+ // byteOffset?: number,
246
+ // extensions?: { [id: string]: any },
247
+ // extras?: any
248
+ // },
249
+ // extensions?: { [id: string]: any },
250
+ // extras?: any
251
+ // },
252
+ };
253
+ this._content.accessors.push(accessorDef);
254
+ return this._content.accessors.length - 1;
255
+ }
256
+ convertAnimations() {
257
+ var _a;
258
+ if (!this._content.animations && this._animations.length > 0)
259
+ this._content.animations = [];
260
+ for (let i = 0; i < this._animations.length; i++) {
261
+ const animation = this._animations[i];
262
+ const animationDef = {
263
+ name: animation.name || "animation_" + i,
264
+ channels: [],
265
+ samplers: [],
266
+ };
267
+ for (let j = 0; j < animation.tracks.length; j++) {
268
+ const track = animation.tracks[j];
269
+ const value = this._nodes.find((a) => a.node === track.node);
270
+ if (!value)
271
+ continue;
272
+ const inputMin = Math.min(...track.times);
273
+ const inputMax = Math.max(...track.times);
274
+ const inputData = new viewer_shared_node_tree_1.AttributeData(new Float32Array(track.times), 1, 4, 0, 4, false, track.times.length, [inputMin], [inputMax]);
275
+ const outputMin = [];
276
+ outputMin.push(Math.min(...track.values.filter((s, i) => i % (track.path === "rotation" ? 4 : 3) === 0)));
277
+ outputMin.push(Math.min(...track.values.filter((s, i) => i % (track.path === "rotation" ? 4 : 3) === 1)));
278
+ outputMin.push(Math.min(...track.values.filter((s, i) => i % (track.path === "rotation" ? 4 : 3) === 2)));
279
+ if (track.path === "rotation") {
280
+ outputMin.push(Math.min(...track.values.filter((s, i) => i % 4 === 3)));
281
+ }
282
+ const outputMax = [];
283
+ outputMax.push(Math.max(...track.values.filter((s, i) => i % (track.path === "rotation" ? 4 : 3) === 0)));
284
+ outputMax.push(Math.max(...track.values.filter((s, i) => i % (track.path === "rotation" ? 4 : 3) === 1)));
285
+ outputMax.push(Math.max(...track.values.filter((s, i) => i % (track.path === "rotation" ? 4 : 3) === 2)));
286
+ if (track.path === "rotation") {
287
+ outputMax.push(Math.max(...track.values.filter((s, i) => i % 4 === 3)));
288
+ }
289
+ const outputData = new viewer_shared_node_tree_1.AttributeData(new Float32Array(track.values), track.path === "rotation" ? 4 : 3, //itemSize
290
+ track.path === "rotation" ? 16 : 12, //itemBytes
291
+ 0, 4, false, track.times.length, outputMin, outputMax, track.path === "rotation" ? 16 : 12);
292
+ const samplerDef = {
293
+ input: this.convertAccessor(inputData),
294
+ output: this.convertAccessor(outputData),
295
+ interpolation: typeof track.interpolation === "string"
296
+ ? track.interpolation.toUpperCase()
297
+ : "LINEAR",
298
+ };
299
+ animationDef.samplers.push(samplerDef);
300
+ const channelDef = {
301
+ sampler: animationDef.samplers.length - 1,
302
+ target: {
303
+ node: value.id,
304
+ path: track.path,
305
+ },
306
+ };
307
+ animationDef.channels.push(channelDef);
308
+ }
309
+ (_a = this._content.animations) === null || _a === void 0 ? void 0 : _a.push(animationDef);
310
+ }
311
+ }
312
+ convertBuffer(buffer) {
313
+ if (!this._content.buffers)
314
+ this._content.buffers = [];
315
+ if (this._content.buffers.length === 0)
316
+ this._content.buffers = [{ byteLength: 0 }];
317
+ this._buffers.push(buffer);
318
+ return 0;
319
+ }
320
+ convertBufferView(data) {
321
+ if (!this._content.bufferViews)
322
+ this._content.bufferViews = [];
323
+ const componentTypeNumber = this.getComponentType(data.array);
324
+ const componentSize = viewer_data_engine_shared_types_1.ACCESSORCOMPONENTSIZE_V2[componentTypeNumber];
325
+ const byteLength = Math.ceil((data.count * data.itemSize * componentSize) / 4) * 4;
326
+ const dataView = new DataView(new ArrayBuffer(byteLength));
327
+ let offset = 0;
328
+ for (let i = 0; i < data.count; i++) {
329
+ for (let a = 0; a < data.itemSize; a++) {
330
+ let value = 0;
331
+ if (data.itemSize > 4) {
332
+ // no support for interleaved data for itemSize > 4
333
+ value = data.array[i * data.itemSize + a];
334
+ }
335
+ else {
336
+ if (a === 0)
337
+ value = data.array[i * data.itemSize];
338
+ else if (a === 1)
339
+ value = data.array[i * data.itemSize + 1];
340
+ else if (a === 2)
341
+ value = data.array[i * data.itemSize + 2];
342
+ else if (a === 3)
343
+ value = data.array[i * data.itemSize + 3];
344
+ }
345
+ if (data.array instanceof Float32Array) {
346
+ dataView.setFloat32(offset, value, true);
347
+ }
348
+ else if (data.array instanceof Uint32Array) {
349
+ dataView.setUint32(offset, value, true);
350
+ }
351
+ else if (data.array instanceof Uint16Array) {
352
+ dataView.setUint16(offset, value, true);
353
+ }
354
+ else if (data.array instanceof Int16Array) {
355
+ dataView.setInt16(offset, value, true);
356
+ }
357
+ else if (data.array instanceof Uint8Array) {
358
+ dataView.setUint8(offset, value);
359
+ }
360
+ else if (data.array instanceof Int8Array) {
361
+ dataView.setInt8(offset, value);
362
+ }
363
+ offset += componentSize;
364
+ }
365
+ }
366
+ const bufferViewDef = {
367
+ buffer: this.convertBuffer(dataView.buffer),
368
+ byteOffset: this._byteOffset,
369
+ byteLength: byteLength,
370
+ target: data.target,
371
+ };
372
+ this._byteOffset += byteLength;
373
+ this._content.bufferViews.push(bufferViewDef);
374
+ return this._content.bufferViews.length - 1;
375
+ }
376
+ convertBufferViewImage(blob) {
377
+ return __awaiter(this, void 0, void 0, function* () {
378
+ if (!this._content.bufferViews)
379
+ this._content.bufferViews = [];
380
+ return new Promise((resolve, reject) => {
381
+ try {
382
+ if (typeof window !== "undefined" && window.FileReader) {
383
+ const reader = new window.FileReader();
384
+ reader.readAsArrayBuffer(blob);
385
+ reader.onloadend = () => {
386
+ const buffer = this.getPaddedArrayBuffer(reader.result);
387
+ const bufferViewDef = {
388
+ buffer: this.convertBuffer(buffer),
389
+ byteOffset: this._byteOffset,
390
+ byteLength: buffer.byteLength,
391
+ };
392
+ this._byteOffset += buffer.byteLength;
393
+ this._content.bufferViews.push(bufferViewDef);
394
+ resolve(this._content.bufferViews.length - 1);
395
+ };
396
+ reader.onerror = reject;
397
+ }
398
+ else {
399
+ reject("FileReader not available.");
400
+ }
401
+ }
402
+ catch (e) {
403
+ reject(e);
404
+ }
405
+ });
406
+ });
407
+ }
408
+ convertImage(data) {
409
+ if (!this._content.images)
410
+ this._content.images = [];
411
+ if (data.image instanceof ArrayBuffer)
412
+ return;
413
+ const cacheKey = (data.image instanceof HTMLImageElement
414
+ ? data.image.src
415
+ : data.image.id) || data.image.id;
416
+ if (this._imageCache[cacheKey] !== undefined)
417
+ return this._imageCache[cacheKey];
418
+ const imageDef = {};
419
+ const canvas = document.createElement("canvas");
420
+ canvas.width = data.image.width;
421
+ canvas.height = data.image.height;
422
+ const ctx = canvas.getContext("2d");
423
+ if (data.flipY) {
424
+ ctx.translate(0, canvas.height);
425
+ ctx.scale(1, -1);
426
+ }
427
+ if (data.blob) {
428
+ imageDef.mimeType = data.blob.type;
429
+ this._promises.push(new Promise((resolve, reject) => {
430
+ try {
431
+ this.convertBufferViewImage(data.blob).then((bufferViewIndex) => {
432
+ imageDef.bufferView = bufferViewIndex;
433
+ resolve();
434
+ });
435
+ }
436
+ catch (e) {
437
+ reject(e);
438
+ }
439
+ }));
440
+ }
441
+ else if (data.image instanceof HTMLImageElement) {
442
+ let mimeType = "image/png";
443
+ if (data.image.src.endsWith(".jpg") ||
444
+ data.image.src.includes("image/jpeg"))
445
+ mimeType = "image/jpeg";
446
+ imageDef.mimeType = mimeType;
447
+ const DATA_URI_REGEX = /^data:(.*?)(;base64)?,(.*)$/;
448
+ if (DATA_URI_REGEX.test(data.image.src)) {
449
+ const byteString = (0, viewer_shared_services_1.atobCustom)(data.image.src.split(",")[1]);
450
+ const mimeType = data.image.src
451
+ .split(",")[0]
452
+ .split(":")[1]
453
+ .split(";")[0];
454
+ const ab = new ArrayBuffer(byteString.length);
455
+ const ia = new Uint8Array(ab);
456
+ for (let i = 0; i < byteString.length; i++)
457
+ ia[i] = byteString.charCodeAt(i);
458
+ const blob = new Blob([ab], { type: mimeType });
459
+ this._promises.push(new Promise((resolve, reject) => {
460
+ try {
461
+ this.convertBufferViewImage(blob)
462
+ .then((bufferViewIndex) => {
463
+ imageDef.bufferView = bufferViewIndex;
464
+ resolve();
465
+ })
466
+ .catch(reject);
467
+ }
468
+ catch (e) {
469
+ reject(e);
470
+ }
471
+ }));
472
+ }
473
+ else {
474
+ ctx.drawImage(data.image, 0, 0, canvas.width, canvas.height);
475
+ this._promises.push(new Promise((resolve, reject) => {
476
+ try {
477
+ canvas.toBlob((blob) => __awaiter(this, void 0, void 0, function* () {
478
+ try {
479
+ const bufferViewIndex = yield this.convertBufferViewImage(blob);
480
+ imageDef.bufferView = bufferViewIndex;
481
+ resolve();
482
+ }
483
+ catch (e) {
484
+ reject(e);
485
+ }
486
+ }), mimeType);
487
+ }
488
+ catch (e) {
489
+ reject(e);
490
+ }
491
+ }));
492
+ }
493
+ }
494
+ else {
495
+ // convert to blob
496
+ const bitmapCanvas = document.createElement("canvas");
497
+ bitmapCanvas.width = data.image.width;
498
+ bitmapCanvas.height = data.image.height;
499
+ const bitmapCtx = bitmapCanvas.getContext("2d");
500
+ bitmapCtx.drawImage(data.image, 0, 0);
501
+ this._promises.push(new Promise((resolve, reject) => {
502
+ try {
503
+ bitmapCanvas.toBlob((blob) => __awaiter(this, void 0, void 0, function* () {
504
+ try {
505
+ const bufferViewIndex = yield this.convertBufferViewImage(blob);
506
+ imageDef.bufferView = bufferViewIndex;
507
+ resolve();
508
+ }
509
+ catch (e) {
510
+ reject(e);
511
+ }
512
+ }), "image/png");
513
+ }
514
+ catch (e) {
515
+ reject(e);
516
+ }
517
+ }));
518
+ }
519
+ this._content.images.push(imageDef);
520
+ this._imageCache[cacheKey] = this._content.images.length - 1;
521
+ return this._content.images.length - 1;
522
+ }
523
+ convertMaterial(data, includeMaps = true) {
524
+ if (!this._content.materials)
525
+ this._content.materials = [];
526
+ if (this._materialCache[data.id + "_" + data.version] !== undefined)
527
+ return this._materialCache[data.id + "_" + data.version];
528
+ const materialDef = {
529
+ name: this._convertForAR ? this._uuidGenerator.create() : data.name,
530
+ pbrMetallicRoughness: {},
531
+ };
532
+ if (data instanceof viewer_shared_node_tree_1.MaterialSpecularGlossinessData) {
533
+ if (!this._extensionsUsed.includes("KHR_materials_pbrSpecularGlossiness"))
534
+ this._extensionsUsed.push("KHR_materials_pbrSpecularGlossiness");
535
+ if (!this._extensionsRequired.includes("KHR_materials_pbrSpecularGlossiness"))
536
+ this._extensionsRequired.push("KHR_materials_pbrSpecularGlossiness");
537
+ const ext = {};
538
+ ext.diffuseFactor = this._converter.toColorArray(data.color);
539
+ ext.diffuseFactor[3] = data.opacity;
540
+ if (data.map && includeMaps) {
541
+ const textureIndex = this.convertTexture(data.map);
542
+ if (textureIndex !== undefined)
543
+ ext.diffuseTexture = { index: textureIndex };
544
+ }
545
+ ext.specularFactor = this._converter.toColorArray(data.specular);
546
+ ext.glossinessFactor = data.glossiness;
547
+ if (data.specularGlossinessMap && includeMaps) {
548
+ const textureIndex = this.convertTexture(data.specularGlossinessMap);
549
+ if (textureIndex !== undefined)
550
+ ext.specularGlossinessTexture = { index: textureIndex };
551
+ }
552
+ materialDef.extensions = {
553
+ KHR_materials_pbrSpecularGlossiness: ext,
554
+ };
555
+ }
556
+ else if (data instanceof viewer_shared_node_tree_1.MaterialUnlitData) {
557
+ if (!this._extensionsUsed.includes("KHR_materials_unlit"))
558
+ this._extensionsUsed.push("KHR_materials_unlit");
559
+ if (!this._extensionsRequired.includes("KHR_materials_unlit"))
560
+ this._extensionsRequired.push("KHR_materials_unlit");
561
+ materialDef.pbrMetallicRoughness.baseColorFactor =
562
+ this._converter.toColorArray(data.color);
563
+ materialDef.pbrMetallicRoughness.baseColorFactor[3] = data.opacity;
564
+ if (data.map && includeMaps) {
565
+ const textureIndex = this.convertTexture(data.map);
566
+ if (textureIndex !== undefined)
567
+ materialDef.pbrMetallicRoughness.baseColorTexture = {
568
+ index: textureIndex,
569
+ };
570
+ }
571
+ materialDef.extensions = {
572
+ KHR_materials_unlit: {},
573
+ };
574
+ }
575
+ else {
576
+ const standardMaterialData = data;
577
+ materialDef.pbrMetallicRoughness.baseColorFactor =
578
+ this._converter.toColorArray(standardMaterialData.color);
579
+ materialDef.pbrMetallicRoughness.baseColorFactor[3] =
580
+ standardMaterialData.opacity;
581
+ if (standardMaterialData.map && includeMaps) {
582
+ const textureIndex = this.convertTexture(standardMaterialData.map);
583
+ if (textureIndex !== undefined)
584
+ materialDef.pbrMetallicRoughness.baseColorTexture = {
585
+ index: textureIndex,
586
+ };
587
+ }
588
+ materialDef.pbrMetallicRoughness.metallicFactor =
589
+ standardMaterialData.metalness;
590
+ materialDef.pbrMetallicRoughness.roughnessFactor =
591
+ standardMaterialData.roughness;
592
+ if (standardMaterialData.metalnessRoughnessMap && includeMaps) {
593
+ const textureIndex = this.convertTexture(standardMaterialData.metalnessRoughnessMap);
594
+ if (textureIndex !== undefined)
595
+ materialDef.pbrMetallicRoughness.metallicRoughnessTexture =
596
+ { index: textureIndex };
597
+ }
598
+ else if (standardMaterialData.metalnessMap &&
599
+ standardMaterialData.roughnessMap &&
600
+ includeMaps) {
601
+ if (this._globalAccessObjects.combineTextures) {
602
+ this._promises.push(new Promise((resolve, reject) => {
603
+ try {
604
+ // no support for combining textures
605
+ if (!this._globalAccessObjects.combineTextures)
606
+ return standardMaterialData.roughnessMap;
607
+ this._globalAccessObjects
608
+ .combineTextures(undefined, standardMaterialData.roughnessMap &&
609
+ standardMaterialData.roughnessMap
610
+ .image instanceof
611
+ HTMLImageElement
612
+ ? standardMaterialData.roughnessMap
613
+ .image
614
+ : undefined, standardMaterialData.metalnessMap &&
615
+ standardMaterialData.metalnessMap
616
+ .image instanceof
617
+ HTMLImageElement
618
+ ? standardMaterialData.metalnessMap
619
+ .image
620
+ : undefined)
621
+ .then((imageData) => {
622
+ const m = (standardMaterialData.roughnessMap ||
623
+ standardMaterialData.metalnessMap);
624
+ const mapData = new viewer_shared_node_tree_1.MapData(imageData.image, {
625
+ blob: imageData.blob,
626
+ wrapS: m.wrapS,
627
+ wrapT: m.wrapT,
628
+ minFilter: m.minFilter,
629
+ magFilter: m.magFilter,
630
+ center: m.center,
631
+ color: m.color,
632
+ offset: m.offset,
633
+ repeat: m.repeat,
634
+ rotation: m.rotation,
635
+ texCoord: m.texCoord,
636
+ flipY: m.flipY,
637
+ });
638
+ const textureIndex = this.convertTexture(mapData);
639
+ if (textureIndex !== undefined)
640
+ materialDef.pbrMetallicRoughness.metallicRoughnessTexture =
641
+ { index: textureIndex };
642
+ resolve();
643
+ })
644
+ .catch(reject);
645
+ }
646
+ catch (e) {
647
+ reject(e);
648
+ }
649
+ }));
650
+ }
651
+ else {
652
+ // no support for combining textures
653
+ const textureIndex = this.convertTexture(standardMaterialData.roughnessMap);
654
+ if (textureIndex !== undefined)
655
+ materialDef.pbrMetallicRoughness.metallicRoughnessTexture =
656
+ { index: textureIndex };
657
+ }
658
+ }
659
+ else if (standardMaterialData.metalnessMap && includeMaps) {
660
+ const textureIndex = this.convertTexture(standardMaterialData.metalnessMap);
661
+ if (textureIndex !== undefined)
662
+ materialDef.pbrMetallicRoughness.metallicRoughnessTexture =
663
+ { index: textureIndex };
664
+ }
665
+ else if (standardMaterialData.roughnessMap && includeMaps) {
666
+ const textureIndex = this.convertTexture(standardMaterialData.roughnessMap);
667
+ if (textureIndex !== undefined)
668
+ materialDef.pbrMetallicRoughness.metallicRoughnessTexture =
669
+ { index: textureIndex };
670
+ }
671
+ }
672
+ if (data.normalMap && includeMaps) {
673
+ const textureIndex = this.convertTexture(data.normalMap);
674
+ if (textureIndex !== undefined)
675
+ materialDef.normalTexture = { index: textureIndex };
676
+ }
677
+ if (data.aoMap && includeMaps) {
678
+ const textureIndex = this.convertTexture(data.aoMap);
679
+ if (textureIndex !== undefined)
680
+ materialDef.occlusionTexture = { index: textureIndex };
681
+ }
682
+ if (data.emissiveMap && includeMaps) {
683
+ const textureIndex = this.convertTexture(data.emissiveMap);
684
+ if (textureIndex !== undefined)
685
+ materialDef.emissiveTexture = { index: textureIndex };
686
+ }
687
+ if (data.emissiveness)
688
+ materialDef.emissiveFactor = this._converter.toColorArray(data.emissiveness);
689
+ materialDef.alphaMode = data.alphaMode.toUpperCase();
690
+ if (data.alphaMode === viewer_shared_types_1.MATERIAL_ALPHA.MASK)
691
+ materialDef.alphaCutoff = data.alphaCutoff;
692
+ materialDef.doubleSided = data.side === viewer_shared_types_1.MATERIAL_SIDE.DOUBLE;
693
+ this._content.materials.push(materialDef);
694
+ this._materialCache[data.id + "_" + data.version] =
695
+ this._content.materials.length - 1;
696
+ return this._materialCache[data.id + "_" + data.version];
697
+ }
698
+ convertMesh(data) {
699
+ var _a;
700
+ if (!this._content.meshes)
701
+ this._content.meshes = [];
702
+ if (this._meshCache[data.id + "_" + data.version] !== undefined)
703
+ return this._meshCache[data.id + "_" + data.version];
704
+ const meshDef = {
705
+ primitives: [],
706
+ };
707
+ (_a = meshDef.primitives) === null || _a === void 0 ? void 0 : _a.push(this.convertPrimitive(data, data.primitive));
708
+ this._content.meshes.push(meshDef);
709
+ this._meshCache[data.id + "_" + data.version] =
710
+ this._content.meshes.length - 1;
711
+ return this._meshCache[data.id + "_" + data.version];
712
+ }
713
+ convertNode(node) {
714
+ return __awaiter(this, void 0, void 0, function* () {
715
+ var _a, _b, _c, _d, _e;
716
+ if (!this._content.nodes)
717
+ this._content.nodes = [];
718
+ const nodeDef = {
719
+ name: this._convertForAR
720
+ ? this._uuidGenerator.create()
721
+ : ((_a = node.displayName) !== null && _a !== void 0 ? _a : node.name),
722
+ };
723
+ if (node.transformations.length > 0) {
724
+ let matrix = node.nodeMatrix;
725
+ if (Array.from(node.nodeMatrix).filter((v) => isNaN(v) || v === Infinity || v === -Infinity).length > 0)
726
+ matrix = gl_matrix_1.mat4.create();
727
+ const inv = gl_matrix_1.mat4.invert(gl_matrix_1.mat4.create(), matrix);
728
+ if (inv) {
729
+ nodeDef.matrix = [];
730
+ for (let i = 0; i < matrix.length; i++)
731
+ nodeDef.matrix[i] = matrix[i];
732
+ }
733
+ else {
734
+ viewer_shared_services_1.Logger.instance.warn(`GLTFConverter.convertNode: The matrix of node ${(_b = node.displayName) !== null && _b !== void 0 ? _b : node.name} is not invertible and will be ignored.`);
735
+ }
736
+ }
737
+ for (let i = 0; i < node.data.length; i++) {
738
+ if (node.data[i] instanceof viewer_shared_node_tree_1.GeometryData) {
739
+ const geometryData = node.data[i];
740
+ let instanceMatrices;
741
+ // as this is a node that contains a mesh
742
+ // we check the parent node for instance matrices
743
+ if (node.parent) {
744
+ const instanceMatricesData = node.parent.data.find((d) => d instanceof viewer_shared_node_tree_1.InstanceData);
745
+ if (instanceMatricesData &&
746
+ instanceMatricesData.instanceMatrices &&
747
+ instanceMatricesData.instanceMatrices.length > 0) {
748
+ instanceMatrices =
749
+ instanceMatricesData.instanceMatrices;
750
+ }
751
+ }
752
+ if (this._convertForAR) {
753
+ if (geometryData.mode !== viewer_shared_types_1.PRIMITIVE_MODE.POINTS &&
754
+ geometryData.mode !== viewer_shared_types_1.PRIMITIVE_MODE.LINES &&
755
+ geometryData.mode !== viewer_shared_types_1.PRIMITIVE_MODE.LINE_LOOP &&
756
+ geometryData.mode !== viewer_shared_types_1.PRIMITIVE_MODE.LINE_STRIP) {
757
+ if (instanceMatrices) {
758
+ if (!nodeDef.children)
759
+ nodeDef.children = [];
760
+ const meshDef = this.convertMesh(geometryData);
761
+ // create intermediate nodes for each instance
762
+ for (let j = 0; j < instanceMatrices.length; j++) {
763
+ this._content.nodes.push({
764
+ name: ((_c = node.displayName) !== null && _c !== void 0 ? _c : node.name) +
765
+ "_instance_" +
766
+ j,
767
+ matrix: Array.from(instanceMatrices[j]),
768
+ mesh: meshDef,
769
+ });
770
+ (_d = nodeDef.children) === null || _d === void 0 ? void 0 : _d.push(this._content.nodes.length - 1);
771
+ }
772
+ }
773
+ else {
774
+ nodeDef.mesh = this.convertMesh(geometryData);
775
+ }
776
+ }
777
+ }
778
+ else {
779
+ nodeDef.mesh = this.convertMesh(geometryData);
780
+ if (instanceMatrices) {
781
+ if (!nodeDef.extensions)
782
+ nodeDef.extensions = {};
783
+ nodeDef.extensions[GLTF_EXTENSIONS.EXT_MESH_GPU_INSTANCING] = this.convertInstances(instanceMatrices);
784
+ if (!this._extensionsUsed.includes(GLTF_EXTENSIONS.EXT_MESH_GPU_INSTANCING))
785
+ this._extensionsUsed.push(GLTF_EXTENSIONS.EXT_MESH_GPU_INSTANCING);
786
+ if (!this._extensionsRequired.includes(GLTF_EXTENSIONS.EXT_MESH_GPU_INSTANCING))
787
+ this._extensionsRequired.push(GLTF_EXTENSIONS.EXT_MESH_GPU_INSTANCING);
788
+ }
789
+ }
790
+ }
791
+ if (node.data[i] instanceof viewer_shared_node_tree_1.AnimationData)
792
+ this._animations.push(node.data[i]);
793
+ }
794
+ if (node.children.length > 0)
795
+ nodeDef.children = [];
796
+ for (let i = 0; i < node.children.length; i++) {
797
+ if (node.children[i].visible === true) {
798
+ if (this._viewport) {
799
+ if (node.children[i].excludeViewports.includes(this._viewport))
800
+ continue;
801
+ if (node.children[i].restrictViewports.length > 0 &&
802
+ !node.children[i].restrictViewports.includes(this._viewport))
803
+ continue;
804
+ }
805
+ const nodeId = yield this.convertNode(node.children[i]);
806
+ if (nodeId !== -1)
807
+ (_e = nodeDef.children) === null || _e === void 0 ? void 0 : _e.push(nodeId);
808
+ }
809
+ }
810
+ // remove children array if it is empty
811
+ if (nodeDef.children !== undefined && nodeDef.children.length === 0)
812
+ nodeDef.children = undefined;
813
+ if (performance.now() - this._progressTimer >
814
+ this._progressUpdateLimit) {
815
+ this._progressTimer = performance.now();
816
+ const eventProgress = {
817
+ type: viewer_shared_types_1.TASK_TYPE.GLTF_CREATION,
818
+ id: this._eventId,
819
+ progress: this._content.nodes.length / this._numberOfNodes / 2,
820
+ status: `GlTF conversion progress: ${this._content.nodes.length}/${this._numberOfNodes} nodes.`,
821
+ };
822
+ this._eventEngine.emitEvent(viewer_shared_services_1.EVENTTYPE.TASK.TASK_PROCESS, eventProgress);
823
+ yield new Promise((resolve) => setTimeout(resolve, 0));
824
+ }
825
+ // if the node is empty, don't add it
826
+ if (nodeDef.camera === undefined &&
827
+ nodeDef.children === undefined &&
828
+ nodeDef.mesh === undefined &&
829
+ nodeDef.extensions === undefined &&
830
+ nodeDef.extras === undefined)
831
+ return -1;
832
+ this._content.nodes.push(nodeDef);
833
+ this._nodes.push({
834
+ node,
835
+ id: this._content.nodes.length - 1,
836
+ });
837
+ return this._content.nodes.length - 1;
838
+ });
839
+ }
840
+ convertInstances(matrices) {
841
+ const translations = matrices.map((m) => gl_matrix_1.mat4.getTranslation(gl_matrix_1.vec3.create(), m));
842
+ const translationMap = translations.flatMap((t) => Array.from(t));
843
+ const { minimum: minTranslation, maximum: maxTranslation } = translations.reduce((acc, t) => {
844
+ for (let i = 0; i < 3; i++) {
845
+ acc.minimum[i] = Math.min(acc.minimum[i], t[i]);
846
+ acc.maximum[i] = Math.max(acc.maximum[i], t[i]);
847
+ }
848
+ return acc;
849
+ }, {
850
+ minimum: [Infinity, Infinity, Infinity],
851
+ maximum: [-Infinity, -Infinity, -Infinity],
852
+ });
853
+ const rotations = matrices.map((m) => gl_matrix_1.mat4.getRotation(gl_matrix_1.quat.create(), m));
854
+ const rotationsMap = rotations.flatMap((r) => Array.from(r));
855
+ const { minimum: minRotation, maximum: maxRotation } = rotations.reduce((acc, r) => {
856
+ for (let i = 0; i < 4; i++) {
857
+ acc.minimum[i] = Math.min(acc.minimum[i], r[i]);
858
+ acc.maximum[i] = Math.max(acc.maximum[i], r[i]);
859
+ }
860
+ return acc;
861
+ }, {
862
+ minimum: [Infinity, Infinity, Infinity, Infinity],
863
+ maximum: [-Infinity, -Infinity, -Infinity, -Infinity],
864
+ });
865
+ const scales = matrices.map((m) => gl_matrix_1.mat4.getScaling(gl_matrix_1.vec3.create(), m));
866
+ const scalesMap = scales.flatMap((s) => Array.from(s));
867
+ const { minimum: minScale, maximum: maxScale } = scales.reduce((acc, s) => {
868
+ for (let i = 0; i < 3; i++) {
869
+ acc.minimum[i] = Math.min(acc.minimum[i], s[i]);
870
+ acc.maximum[i] = Math.max(acc.maximum[i], s[i]);
871
+ }
872
+ return acc;
873
+ }, {
874
+ minimum: [Infinity, Infinity, Infinity],
875
+ maximum: [-Infinity, -Infinity, -Infinity],
876
+ });
877
+ return {
878
+ attributes: {
879
+ TRANSLATION: this.convertAccessor(new viewer_shared_node_tree_1.AttributeData(new Float32Array(translationMap), 3, 3 * 4, 0, 4, false, translations.length, minTranslation, maxTranslation)),
880
+ ROTATION: this.convertAccessor(new viewer_shared_node_tree_1.AttributeData(new Float32Array(rotationsMap), 4, 4 * 4, 0, 4, false, rotations.length, minRotation, maxRotation)),
881
+ SCALE: this.convertAccessor(new viewer_shared_node_tree_1.AttributeData(new Float32Array(scalesMap), 3, 3 * 4, 0, 4, false, scales.length, minScale, maxScale)),
882
+ },
883
+ };
884
+ }
885
+ convertPrimitive(geometryData, data) {
886
+ const primitiveDef = {
887
+ attributes: {},
888
+ mode: geometryData.mode,
889
+ };
890
+ for (const a in data.attributes) {
891
+ if (data.attributes[a].array.length > 0) {
892
+ if (a.includes("COLOR")) {
893
+ if (data.attributes[a].itemSize % 4 === 0) {
894
+ primitiveDef.attributes[a] = this.convertAccessor(data.attributes[a]);
895
+ }
896
+ else if (data.attributes[a].itemSize % 3 === 0) {
897
+ const oldAttributeData = data.attributes[a];
898
+ const newArray = new Float32Array((oldAttributeData.array.length / 3) * 4);
899
+ let counter = 0;
900
+ for (let i = 0; i < newArray.length; i += 4) {
901
+ newArray[i] =
902
+ oldAttributeData.array[counter] /
903
+ (oldAttributeData.elementBytes === 1
904
+ ? 255.0
905
+ : 1.0);
906
+ newArray[i + 1] =
907
+ oldAttributeData.array[counter + 1] /
908
+ (oldAttributeData.elementBytes === 1
909
+ ? 255.0
910
+ : 1.0);
911
+ newArray[i + 2] =
912
+ oldAttributeData.array[counter + 2] /
913
+ (oldAttributeData.elementBytes === 1
914
+ ? 255.0
915
+ : 1.0);
916
+ newArray[i + 3] = 1.0;
917
+ counter += 3;
918
+ }
919
+ primitiveDef.attributes[a] = this.convertAccessor(new viewer_shared_node_tree_1.AttributeData(newArray, 4, 4 * 4, oldAttributeData.byteOffset, 4, oldAttributeData.normalized, oldAttributeData.count, oldAttributeData.min, oldAttributeData.max, oldAttributeData.byteStride));
920
+ }
921
+ }
922
+ else {
923
+ primitiveDef.attributes[a] = this.convertAccessor(data.attributes[a]);
924
+ }
925
+ }
926
+ }
927
+ if (data.indices && data.indices.array.length > 0)
928
+ primitiveDef.indices = this.convertAccessor(data.indices);
929
+ if (geometryData.material) {
930
+ const k = Object.keys(primitiveDef.attributes).find((k) => k.includes("TEXCOORD"));
931
+ primitiveDef.material = this.convertMaterial(geometryData.material, !!k);
932
+ }
933
+ return primitiveDef;
934
+ }
935
+ convertTexture(data) {
936
+ if (!this._content.textures)
937
+ this._content.textures = [];
938
+ const imageIndex = this.convertImage(data);
939
+ if (imageIndex === undefined)
940
+ return;
941
+ const textureDef = {
942
+ source: imageIndex,
943
+ };
944
+ // TODO samplers
945
+ this._content.textures.push(textureDef);
946
+ return this._content.textures.length - 1;
947
+ }
948
+ getComponentType(array) {
949
+ switch (true) {
950
+ case array instanceof Int8Array:
951
+ return 5120;
952
+ case array instanceof Uint8Array:
953
+ return 5121;
954
+ case array instanceof Int16Array:
955
+ return 5122;
956
+ case array instanceof Uint16Array:
957
+ return 5123;
958
+ case array instanceof Uint32Array:
959
+ return 5125;
960
+ default:
961
+ return 5126;
962
+ }
963
+ }
964
+ getMinMax(data) {
965
+ const output = {
966
+ min: new Array(data.itemSize).fill(Number.POSITIVE_INFINITY),
967
+ max: new Array(data.itemSize).fill(Number.NEGATIVE_INFINITY),
968
+ };
969
+ for (let i = 0; i < data.count; i++) {
970
+ for (let a = 0; a < data.itemSize; a++) {
971
+ let value = 0;
972
+ if (data.itemSize > 4) {
973
+ // no support for interleaved data for itemSize > 4
974
+ value = data.array[i * data.itemSize + a];
975
+ }
976
+ else {
977
+ if (a === 0)
978
+ value = data.array[i * data.itemSize];
979
+ else if (a === 1)
980
+ value = data.array[i * data.itemSize + 1];
981
+ else if (a === 2)
982
+ value = data.array[i * data.itemSize + 2];
983
+ else if (a === 3)
984
+ value = data.array[i * data.itemSize + 3];
985
+ }
986
+ output.min[a] = Math.min(output.min[a], value);
987
+ output.max[a] = Math.max(output.max[a], value);
988
+ }
989
+ }
990
+ return output;
991
+ }
992
+ getPaddedArrayBuffer(arrayBuffer, paddingByte = 0) {
993
+ const paddedLength = Math.ceil(arrayBuffer.byteLength / 4) * 4;
994
+ if (paddedLength !== arrayBuffer.byteLength) {
995
+ const array = new Uint8Array(paddedLength);
996
+ array.set(new Uint8Array(arrayBuffer));
997
+ if (paddingByte !== 0) {
998
+ for (let i = arrayBuffer.byteLength; i < paddedLength; i++) {
999
+ array[i] = paddingByte;
1000
+ }
1001
+ }
1002
+ return array.buffer;
1003
+ }
1004
+ return arrayBuffer;
1005
+ }
1006
+ getType(itemSize) {
1007
+ switch (itemSize) {
1008
+ case 1:
1009
+ return "SCALAR";
1010
+ case 2:
1011
+ return "VEC2";
1012
+ case 3:
1013
+ return "VEC3";
1014
+ case 4:
1015
+ return "VEC4";
1016
+ case 9:
1017
+ return "MAT3";
1018
+ case 18:
1019
+ return "MAT4";
1020
+ default:
1021
+ return "VEC3";
1022
+ }
1023
+ }
1024
+ reset() {
1025
+ this._animations = [];
1026
+ this._buffers = [];
1027
+ this._byteOffset = 0;
1028
+ this._content = {
1029
+ asset: {
1030
+ copyright: "2025 (c) ShapeDiver",
1031
+ generator: "ShapeDiverViewer@" + viewer_shared_build_data_1.build_data.build_version,
1032
+ version: "2.0",
1033
+ extensions: {},
1034
+ },
1035
+ };
1036
+ this._extensionsRequired = [];
1037
+ this._extensionsUsed = [];
1038
+ this._imageCache = {};
1039
+ this._materialCache = {};
1040
+ this._meshCache = {};
1041
+ this._nodes = [];
1042
+ this._promises = [];
1043
+ this._convertForAR = false;
1044
+ this._viewport = undefined;
1045
+ }
1046
+ stringToArrayBuffer(text) {
1047
+ if (window.TextEncoder !== undefined) {
1048
+ return new TextEncoder().encode(text).buffer;
1049
+ }
1050
+ const array = new Uint8Array(new ArrayBuffer(text.length));
1051
+ for (let i = 0, il = text.length; i < il; i++) {
1052
+ const value = text.charCodeAt(i);
1053
+ // Replacing multi-byte character with space(0x20).
1054
+ array[i] = value > 0xff ? 0x20 : value;
1055
+ }
1056
+ return array.buffer;
1057
+ }
1058
+ }
1059
+ exports.GLTFConverter = GLTFConverter;
1060
+ // #endregion Classes (1)
1061
+ // #region Enums (1)
1062
+ var GLTF_EXTENSIONS;
1063
+ (function (GLTF_EXTENSIONS) {
1064
+ GLTF_EXTENSIONS["KHR_BINARY_GLTF"] = "KHR_binary_glTF";
1065
+ GLTF_EXTENSIONS["KHR_MATERIALS_PBRSPECULARGLOSSINESS"] = "KHR_materials_pbrSpecularGlossiness";
1066
+ GLTF_EXTENSIONS["KHR_MATERIALS_UNLIT"] = "KHR_materials_unlit";
1067
+ GLTF_EXTENSIONS["EXT_MESH_GPU_INSTANCING"] = "EXT_mesh_gpu_instancing";
1068
+ })(GLTF_EXTENSIONS || (exports.GLTF_EXTENSIONS = GLTF_EXTENSIONS = {}));
1069
+ // #endregion Enums (1)
1070
1070
  //# sourceMappingURL=GLTFConverter.js.map