@colijnit/configuratorcore 262.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2941 @@
1
+ import * as THREE from 'three';
2
+ import { Group, BufferGeometry, Float32BufferAttribute, Line, LineBasicMaterial, Vector3, CylinderGeometry, Mesh, MeshBasicMaterial, Object3D, EquirectangularReflectionMapping, Vector2, Material as Material$1, Texture, Source, Camera, Box3, LinearSRGBColorSpace, SRGBColorSpace, Color, MaterialLoader, MeshPhongMaterial, NearestFilter, DoubleSide, MeshPhysicalMaterial, MeshStandardMaterial, Euler, Quaternion } from 'three';
3
+ import { Subject, BehaviorSubject } from 'rxjs';
4
+ import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
5
+ import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
6
+ import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
7
+ import { BusinessObjectFactory } from '@colijnit/ioneconnector/build/service/business-object-factory.js';
8
+ import { DecoNodeType } from '@colijnit/configuratorapi/build/enum/deco-node-type.enum.js';
9
+ import JSZip from 'jszip';
10
+ import axios from 'axios';
11
+ import { DecoNode } from '@colijnit/configuratorapi/build/model/deco-node.js';
12
+ import { Answer } from '@colijnit/configuratorapi/build/model/answer.js';
13
+ import { SelectorStructure } from '@colijnit/configuratorapi/build/model/selector-structure.bo.js';
14
+ import { NodeType } from '@colijnit/configuratorapi/build/enum/node-type.enum.js';
15
+ import { DecoNodeKind } from '@colijnit/configuratorapi/build/enum/deco-node-kind.enum.js';
16
+ import { Selection } from '@colijnit/configuratorapi/build/model/selection.js';
17
+ import { HdecoPositioning } from '@colijnit/configuratorapi/build/enum/hdeco-positioning.enum.js';
18
+
19
+ class ObjectUtils {
20
+ static materialProps = ['map', 'lightMap', 'bumpMap', 'normalMap', 'specularMap', 'envMap', 'aoMap', 'roughnessMap', 'metalnessMap'];
21
+ static CreateArrow(name, color, length, direction, headLength, headWidth, doubleArrow = false) {
22
+ const arrow = new Group();
23
+ const lineGeometry = new BufferGeometry();
24
+ lineGeometry.setAttribute('position', new Float32BufferAttribute([0, 0, 0, 0, 1, 0], 3));
25
+ const line = new Line(lineGeometry, new LineBasicMaterial({ color: color, toneMapped: false }));
26
+ lineGeometry.rotateX(Math.PI / 2 * direction.z);
27
+ lineGeometry.rotateY(Math.PI / 2 * direction.y);
28
+ lineGeometry.rotateZ(Math.PI / 2 * direction.x);
29
+ const translate = new Vector3((direction.x) / 2, direction.y ? -(direction.y) / 2 : 0, -(direction.z) / 2);
30
+ lineGeometry.translate(translate.x, translate.y, translate.z);
31
+ line.name = 'arrow_line';
32
+ line.matrixAutoUpdate = false;
33
+ arrow.add(line);
34
+ const coneGeometry = new CylinderGeometry(0, 0.5, 1, 5, 1);
35
+ const coneRightGeometry = coneGeometry.clone();
36
+ const cone = new Mesh(coneGeometry, new MeshBasicMaterial({ color: color, toneMapped: false }));
37
+ coneGeometry.rotateX(Math.PI / 2 * -direction.z);
38
+ if (direction.y) {
39
+ coneGeometry.rotateZ(Math.PI);
40
+ }
41
+ coneGeometry.rotateZ(Math.PI / 2 * -direction.x);
42
+ // coneGeometry.translate(translate.x * 2, translate.y * 2, translate.z * 2);
43
+ cone.name = 'arrow_cone_left';
44
+ cone.matrixAutoUpdate = false;
45
+ arrow.add(cone);
46
+ if (doubleArrow) {
47
+ const coneRight = new Mesh(coneRightGeometry, new MeshBasicMaterial({ color: color, toneMapped: false }));
48
+ coneRightGeometry.rotateX(Math.PI / 2 * direction.z);
49
+ coneRightGeometry.rotateZ(Math.PI / 2 * direction.x);
50
+ // coneRightGeometry.translate(-translate.x * 2, -translate.y * 2, -translate.z * 2);
51
+ coneRight.name = 'arrow_cone_right';
52
+ coneRight.matrixAutoUpdate = false;
53
+ arrow.add(coneRight);
54
+ }
55
+ arrow.name = name;
56
+ return ObjectUtils.UpdateArrowLength(arrow, length, direction, headLength, headWidth);
57
+ }
58
+ static UpdateArrowLength(arrow, length, direction, headLength, headWidth, doubleArrow = false) {
59
+ try {
60
+ const arrowLine = arrow.getObjectByName('arrow_line');
61
+ const scale = new Vector3(direction.x ? Math.max(0.0001, length - (doubleArrow ? headLength * 2 : headLength)) : 1, direction.y ? Math.max(0.0001, length - (doubleArrow ? headLength * 2 : headLength)) : 1, direction.z ? Math.max(0.0001, length - (doubleArrow ? headLength * 2 : headLength)) : 1);
62
+ arrowLine.scale.copy(scale);
63
+ arrowLine.updateMatrix();
64
+ const cone = arrow.getObjectByName('arrow_cone_left');
65
+ if (cone) {
66
+ cone.scale.set(headWidth, headLength, headWidth);
67
+ cone.position.x = direction.x ? length / 2 - headLength : 0;
68
+ cone.position.y = direction.y ? -length / 2 + headLength : 0;
69
+ cone.position.z = direction.z ? -length / 2 + headLength : 0;
70
+ cone.updateMatrix();
71
+ }
72
+ const coneRight = arrow.getObjectByName('arrow_cone_right');
73
+ if (coneRight) {
74
+ coneRight.scale.set(headWidth, headLength, headWidth);
75
+ coneRight.position.x = direction.x ? -length / 2 + headLength : 0;
76
+ coneRight.position.y = direction.y ? length / 2 - headLength : 0;
77
+ coneRight.position.z = direction.z ? length / 2 - headLength : 0;
78
+ coneRight.updateMatrix();
79
+ }
80
+ return arrow;
81
+ }
82
+ catch (error) {
83
+ }
84
+ }
85
+ static ChildOf(obj, parentName) {
86
+ if (obj && obj.parent && obj.parent.name === parentName) {
87
+ return true;
88
+ }
89
+ else if (obj.parent) {
90
+ return ObjectUtils.ChildOf(obj.parent, parentName);
91
+ }
92
+ else {
93
+ return false;
94
+ }
95
+ }
96
+ static GetParentItem(element) {
97
+ if (!element.parent) {
98
+ return null;
99
+ }
100
+ if (element.parent instanceof Object3D) {
101
+ return element.parent;
102
+ }
103
+ else {
104
+ return ObjectUtils.GetParentItem(element.parent);
105
+ }
106
+ }
107
+ static GetDefaultMaterial(object) {
108
+ const children = object.children.filter(c => {
109
+ if (c instanceof Mesh) {
110
+ return Array.isArray(c.material) ?
111
+ c.material.find(m => m.name.toLowerCase().startsWith('default_')) :
112
+ c.material.name.toLowerCase().startsWith('default_');
113
+ }
114
+ });
115
+ return children.map(c => c.material)[0];
116
+ }
117
+ static SameMaterial(first, second) {
118
+ if (!first && !second) {
119
+ return true;
120
+ }
121
+ return first.hasOwnProperty('map') && second.hasOwnProperty('map') && first.map && second.map &&
122
+ first.map.image.src === second.map.image.src;
123
+ }
124
+ static GetChanges(firstItems, secondItems) {
125
+ const result = [];
126
+ for (let i = 0; i < secondItems.length; i++) { // check added/changed items
127
+ const secondItem = secondItems[i];
128
+ let oldPosition;
129
+ let oldMaterial;
130
+ const newPosition = new Vector3().copy(secondItem.position);
131
+ const newMaterial = ObjectUtils.GetDefaultMaterial(secondItem);
132
+ const firstItem = firstItems.find(fi => fi.name === secondItems[i].name);
133
+ if (firstItem) {
134
+ oldPosition = new Vector3().copy(firstItem.position);
135
+ oldMaterial = ObjectUtils.GetDefaultMaterial(firstItem);
136
+ }
137
+ if ((!oldPosition || (newPosition && !oldPosition.equals(newPosition))) || !ObjectUtils.SameMaterial(oldMaterial, newMaterial)) {
138
+ result.push({
139
+ object: secondItem,
140
+ name: secondItem.name,
141
+ oldPosition: oldPosition,
142
+ newPosition: newPosition,
143
+ oldMaterial: oldMaterial,
144
+ newMaterial: newMaterial
145
+ });
146
+ }
147
+ }
148
+ for (let i = 0; i < firstItems.length; i++) { // check deleted items
149
+ const firstItem = firstItems[i];
150
+ const oldPosition = new Vector3().copy(firstItem.position);
151
+ let newPosition;
152
+ const secondItem = secondItems.find(fi => fi.name === firstItems[i].name);
153
+ if (secondItem) {
154
+ newPosition = new Vector3().copy(secondItem.position);
155
+ }
156
+ if (!newPosition || (newPosition && !oldPosition.equals(newPosition))) {
157
+ result.push({
158
+ object: firstItem,
159
+ name: firstItem.name,
160
+ oldPosition: oldPosition,
161
+ newPosition: newPosition
162
+ });
163
+ }
164
+ }
165
+ return result;
166
+ }
167
+ static GetSelectionFromSelectedObject(object) {
168
+ if (object) {
169
+ if (object.userData && object.userData.selection) {
170
+ return object.userData.selection;
171
+ }
172
+ else if (object.parent) {
173
+ return this.GetSelectionFromSelectedObject(object.parent);
174
+ }
175
+ else {
176
+ return undefined;
177
+ }
178
+ }
179
+ else {
180
+ return undefined;
181
+ }
182
+ }
183
+ static UpdateUvs(geo) {
184
+ if (geo) {
185
+ const pos = geo.attributes.position;
186
+ const b3 = new THREE.Box3().setFromBufferAttribute(pos);
187
+ const b3size = new THREE.Vector3();
188
+ b3.getSize(b3size);
189
+ const uv = [];
190
+ for (let i = 0; i < pos.count; i++) {
191
+ const x = pos.getX(i);
192
+ const y = pos.getY(i);
193
+ const u = (x - b3.min.x) / b3size.x;
194
+ const v = (y - b3.min.y) / b3size.y;
195
+ uv.push(u, v);
196
+ }
197
+ geo.setAttribute('uv', new THREE.Float32BufferAttribute(uv, 2));
198
+ }
199
+ }
200
+ static async LoadHDRI(path, scene) {
201
+ const hdri = await new RGBELoader().loadAsync(path);
202
+ hdri.mapping = EquirectangularReflectionMapping;
203
+ scene.environment = hdri;
204
+ }
205
+ static ProjectToPlane(points) {
206
+ const centroid = new Vector3();
207
+ points.forEach(p => centroid.add(p));
208
+ centroid.divideScalar(points.length);
209
+ const v1 = points[1].clone().sub(points[0]);
210
+ const v2 = points[2].clone().sub(points[0]);
211
+ const normal = v1.clone().cross(v2).normalize();
212
+ const arbitrary = Math.abs(normal.dot(new Vector3(1, 0, 0))) > 0.9
213
+ ? new Vector3(0, 1, 0)
214
+ : new Vector3(1, 0, 0);
215
+ const u = new Vector3().crossVectors(normal, arbitrary).normalize();
216
+ const v = new Vector3().crossVectors(normal, u).normalize();
217
+ return points.map(p => {
218
+ const rel = p.clone().sub(centroid);
219
+ return new Vector2(rel.dot(u), rel.dot(v));
220
+ });
221
+ }
222
+ static CloneMaterial(material, deepClone = false) {
223
+ if (material instanceof Material$1) {
224
+ const onBeforeCompile = material.onBeforeCompile;
225
+ const onBeforeRender = material.onBeforeRender;
226
+ const clone = material.clone();
227
+ clone.onBeforeCompile = onBeforeCompile;
228
+ clone.onBeforeRender = onBeforeRender;
229
+ this.materialProps
230
+ .filter(mapType => clone[mapType] && clone[mapType] instanceof Texture)
231
+ .forEach(mapType => clone[mapType] = ObjectUtils.CloneTexture(clone[mapType], deepClone));
232
+ return clone;
233
+ }
234
+ return material.map((m) => {
235
+ const onBeforeCompile = m.onBeforeCompile;
236
+ const onBeforeRender = m.onBeforeRender;
237
+ const clone = m.clone();
238
+ clone.onBeforeCompile = onBeforeCompile;
239
+ clone.onBeforeRender = onBeforeRender;
240
+ this.materialProps
241
+ .filter(mapType => clone[mapType] && clone[mapType] instanceof Texture)
242
+ .forEach(mapType => clone[mapType] = ObjectUtils.CloneTexture(clone[mapType], deepClone));
243
+ return clone;
244
+ });
245
+ }
246
+ /**
247
+ * ThreeJS does not clone image property, so it stays a reference to the original image.
248
+ * When deepClone, this method creates a new image and copies the original image data to it.
249
+ * @param texture
250
+ * @param deepClone
251
+ * @constructor
252
+ */
253
+ static CloneTexture(texture, deepClone = false) {
254
+ const clone = texture.clone();
255
+ if (!deepClone) {
256
+ clone.needsUpdate = true;
257
+ return clone;
258
+ }
259
+ if (texture.image) {
260
+ if (texture.image instanceof HTMLImageElement && texture.image.src) {
261
+ const img = texture.image;
262
+ const newImage = new Image();
263
+ newImage.src = img.src;
264
+ clone.source = new Source(newImage);
265
+ }
266
+ else if (texture.image instanceof HTMLCanvasElement) {
267
+ const canvas = texture.image;
268
+ const newCanvas = document.createElement('canvas');
269
+ newCanvas.width = canvas.width;
270
+ newCanvas.height = canvas.height;
271
+ const ctx = newCanvas.getContext('2d');
272
+ if (ctx) {
273
+ ctx.drawImage(canvas, 0, 0);
274
+ }
275
+ clone.source = new Source(newCanvas);
276
+ }
277
+ else if (texture.image.data && (texture.image.data.slice || texture.image.data instanceof ArrayBuffer)) {
278
+ const imgData = texture.image;
279
+ clone.image = {
280
+ ...imgData,
281
+ data: imgData.data.slice ? imgData.data.slice(0) : new Uint8Array(imgData.data).slice(0) // Deep copy the TypedArray
282
+ };
283
+ }
284
+ }
285
+ clone.needsUpdate = true;
286
+ return clone;
287
+ }
288
+ static CloneObject3D(source) {
289
+ const newObject = new Object3D();
290
+ source.children.forEach((child) => {
291
+ if (!child.visible) {
292
+ return;
293
+ }
294
+ if (child instanceof Mesh) {
295
+ newObject.add(child.clone());
296
+ }
297
+ else if (child instanceof Object3D) {
298
+ const clone = ObjectUtils.CloneObject3D(child);
299
+ clone.position.copy(child.position);
300
+ clone.rotation.copy(child.rotation);
301
+ newObject.add(clone);
302
+ }
303
+ });
304
+ return newObject;
305
+ }
306
+ static DeepCloneObject3D(source) {
307
+ if (!source) {
308
+ return new Object3D();
309
+ }
310
+ const clone = source.clone();
311
+ clone.traverse((obj) => {
312
+ if (obj instanceof Mesh && obj.material) {
313
+ obj.material = ObjectUtils.CloneMaterial(obj.material, true);
314
+ }
315
+ });
316
+ return clone;
317
+ }
318
+ static DisposeMaterial(material) {
319
+ if (!material) {
320
+ return;
321
+ }
322
+ if (Array.isArray(material)) {
323
+ material.forEach(mtrl => ObjectUtils.DisposeMaterial(mtrl));
324
+ return;
325
+ }
326
+ for (const key in material) {
327
+ if (material.hasOwnProperty(key)) {
328
+ const value = material[key];
329
+ if (value && value.isTexture) {
330
+ value.dispose();
331
+ }
332
+ }
333
+ }
334
+ material.dispose();
335
+ }
336
+ static DisposeObject(object) {
337
+ if (!object) {
338
+ return;
339
+ }
340
+ if (object.children.length) {
341
+ object.children.forEach(child => this.DisposeObject(child));
342
+ }
343
+ ObjectUtils.DisposeNode(object);
344
+ }
345
+ static GetParentCustomObject(element) {
346
+ if (!element.parent) {
347
+ return null;
348
+ }
349
+ if (element.parent instanceof Object3D) {
350
+ return element.parent;
351
+ }
352
+ else {
353
+ return ObjectUtils.GetParentCustomObject(element.parent);
354
+ }
355
+ }
356
+ static DisposeNode(node) {
357
+ node.traverse((obj) => {
358
+ if (obj instanceof Mesh) {
359
+ if (obj.geometry) {
360
+ obj.geometry.dispose();
361
+ }
362
+ ObjectUtils.DisposeMaterial(obj.material);
363
+ }
364
+ if (typeof obj.dispose === 'function') {
365
+ // @ts-ignore
366
+ obj.dispose();
367
+ }
368
+ });
369
+ }
370
+ static _remapFloat(v, min0, max0, min1, max1) {
371
+ return min1 + (v - min0) / (max0 - min0) * (max1 - min1);
372
+ }
373
+ }
374
+
375
+ class ThreedUtils {
376
+ clips = [];
377
+ _objectCache = new Map();
378
+ clearCache() {
379
+ const objs = Array.from(this._objectCache.values());
380
+ objs.forEach(o => ObjectUtils.DisposeObject(o));
381
+ this._objectCache.clear();
382
+ }
383
+ download3DSource(fileName, setVisibleFalse = true) {
384
+ return new Promise(async (resolve, reject) => {
385
+ fileName = fileName.replace('.unity3d', '');
386
+ let lookupFileName = fileName + (fileName.indexOf('.glb') < 0 ? '.glb' : '');
387
+ if (this._objectCache.has(lookupFileName)) {
388
+ resolve(this._objectCache.get(lookupFileName));
389
+ return;
390
+ }
391
+ this.loadGlbSource(lookupFileName, setVisibleFalse)
392
+ .then((obj) => {
393
+ this._objectCache.set(lookupFileName, obj);
394
+ resolve(obj);
395
+ })
396
+ .catch(() => {
397
+ reject(`GLB source not found! (${lookupFileName})`);
398
+ });
399
+ });
400
+ }
401
+ readFileAsText(file) {
402
+ const fileReader = new FileReader();
403
+ return new Promise((resolve, reject) => {
404
+ fileReader.onload = (e) => resolve(e.target.result);
405
+ fileReader.onerror = error => reject(error);
406
+ fileReader.readAsText(file);
407
+ });
408
+ }
409
+ async loadJsonModel(filePath) {
410
+ if (this._objectCache.has(filePath)) {
411
+ return Promise.resolve(this._objectCache.get(filePath));
412
+ }
413
+ const json = await this._readJsonFile(filePath);
414
+ const loader = new THREE.ObjectLoader();
415
+ loader.crossOrigin = 'Anonymous';
416
+ return new Promise((resolve, reject) => {
417
+ try {
418
+ const obj = loader.parse(json);
419
+ this._objectCache.set(filePath, obj);
420
+ resolve(obj);
421
+ }
422
+ catch (e) {
423
+ reject(null);
424
+ }
425
+ });
426
+ }
427
+ async loadJsonObjectModel(filePath) {
428
+ if (this._objectCache.has(filePath)) {
429
+ return Promise.resolve(this._objectCache.get(filePath));
430
+ }
431
+ const loader = new THREE.ObjectLoader();
432
+ loader.setCrossOrigin('Anonymous');
433
+ loader.setResourcePath('');
434
+ return new Promise((resolve, reject) => {
435
+ loader.load(filePath, (object) => {
436
+ const obj = (object.type === 'Scene' && object.children[0].type === 'Object3D') ? object.children[0] : object;
437
+ this._objectCache.set(filePath, obj);
438
+ resolve(obj);
439
+ }, xhr => null, error => reject(error));
440
+ });
441
+ }
442
+ async loadGlbModel(file, cleanUp = true, dracoPath) {
443
+ if (this._objectCache.has(file)) {
444
+ return Promise.resolve(this._objectCache.get(file));
445
+ }
446
+ if (!window.hasOwnProperty('loadGLTF')) {
447
+ return new Promise((resolve, reject) => {
448
+ const loader = new GLTFLoader();
449
+ loader.setDRACOLoader(new DRACOLoader());
450
+ loader.dracoLoader.setDecoderPath(dracoPath);
451
+ loader.dracoLoader.setDecoderConfig({ type: 'js' });
452
+ loader.load(file, async (object) => {
453
+ this.clips = object.animations || [];
454
+ const obj = this.cleanedUpObjectsWeb(object.scene, false);
455
+ this._objectCache.set(file, obj);
456
+ resolve(obj);
457
+ }, xhr => this._handleUpdateProgressBar(xhr.loaded / xhr.total * 100), error => {
458
+ console.log('Error! ', error);
459
+ reject(error);
460
+ });
461
+ });
462
+ }
463
+ else {
464
+ const obj = await window.loadGLTF(file);
465
+ if (obj) {
466
+ this._cleanedUpObjects(obj, cleanUp);
467
+ this._objectCache.set(file, obj);
468
+ return obj;
469
+ }
470
+ else {
471
+ return null;
472
+ }
473
+ }
474
+ }
475
+ async loadGlbSource(file, setVisibleFalse = true, dracoPath) {
476
+ const obj = await this.loadGlbModel(file, true, dracoPath);
477
+ if (obj) {
478
+ obj.children = obj.children.filter(c => !(c instanceof THREE.Camera || (c.constructor.name && c.constructor.name.toLowerCase().indexOf('camera') > -1)));
479
+ obj.children.sort((a, b) => a['name'] < b['name'] ? 1 : -1);
480
+ obj.children.forEach((c) => {
481
+ c.traverse((t) => {
482
+ t.visible = !t.name.toLowerCase().startsWith('c_');
483
+ });
484
+ if (setVisibleFalse) {
485
+ c.visible = false;
486
+ }
487
+ });
488
+ if (obj.children.length === 1 && (obj.children[0] instanceof THREE.Group || obj.children[0].constructor.name === 'Group')) { // flatten children
489
+ const children = obj.children[0].children.slice();
490
+ const name = obj.children[0].name;
491
+ obj.children.length = 0;
492
+ const parent = new THREE.Object3D();
493
+ parent.name = name;
494
+ parent.children = children;
495
+ obj.add(parent);
496
+ }
497
+ }
498
+ return obj;
499
+ }
500
+ async textureFromUrl(url) {
501
+ // url = this._includeBaseUrl(url);
502
+ return new Promise((resolve, reject) => {
503
+ const textureLoader = new THREE.TextureLoader();
504
+ // textureLoader.setBaseU(this._settingsService.settings.assetPath);
505
+ textureLoader.load(url, (texture) => {
506
+ texture.wrapT = THREE.RepeatWrapping;
507
+ texture.wrapS = THREE.RepeatWrapping;
508
+ texture.repeat.set(1, 1);
509
+ texture.needsUpdate = true;
510
+ return resolve(texture);
511
+ }, null, reject);
512
+ });
513
+ }
514
+ cleanedUpObjectsWeb(object, cleanUp = true) {
515
+ let obj = new Object3D();
516
+ // console.log(this.clips);
517
+ object.children.forEach((c) => {
518
+ if (!(c instanceof Camera)) {
519
+ if (cleanUp && c instanceof Group || (c.children.length === 1 && c.children[0] instanceof Group)) {
520
+ this._addGroupChildrenToObject(c, obj);
521
+ }
522
+ else if ((cleanUp && !(c instanceof Mesh)) || (!(c instanceof Mesh) && !c.name && c.children.length > 0)) {
523
+ c.children.forEach((child) => {
524
+ child.translateY(c.position.y);
525
+ });
526
+ c.translateY(-c.position.y);
527
+ if (obj.children.length > 0) {
528
+ obj.add(c.clone());
529
+ }
530
+ else {
531
+ obj = c.clone();
532
+ }
533
+ }
534
+ else {
535
+ // if (c.position.y > 0 || c.position.y < 0) {
536
+ // c.children.forEach((child) => {
537
+ // child.translateY(c.position.y);
538
+ // });
539
+ // c.translateY(-c.position.y);
540
+ // }
541
+ obj.add(c.clone());
542
+ }
543
+ }
544
+ });
545
+ const boundingBox = new Box3().setFromObject(obj);
546
+ const bbCenterPivot = new Vector3();
547
+ boundingBox.getCenter(bbCenterPivot);
548
+ const delta = new Vector3().add(bbCenterPivot).negate();
549
+ obj.children.forEach((cc) => {
550
+ cc.position.add(delta);
551
+ });
552
+ return obj;
553
+ }
554
+ _cleanedUpObjects(object, cleanUp = true) {
555
+ let obj = new THREE.Object3D();
556
+ object.children.forEach((c) => {
557
+ if (!(c instanceof THREE.Camera || (c.constructor.name && c.constructor.name.toLowerCase().indexOf('camera') > -1))) {
558
+ if (cleanUp && (c instanceof THREE.Group || c.constructor.name === 'Group') || (c.children.length === 1 && (c.children[0] instanceof THREE.Group || c.children[0].constructor.name === 'Group'))) {
559
+ this._addGroupChildrenToObject(c, obj);
560
+ }
561
+ else if ((cleanUp && !(c instanceof THREE.Mesh || c.constructor.name === 'Mesh')) || (!(c instanceof THREE.Mesh || c.constructor.name === 'Mesh') && !c.name && c.children.length > 0)) {
562
+ c.children.forEach((child) => {
563
+ child.translateY(c.position.y);
564
+ });
565
+ c.translateY(-c.position.y);
566
+ if (obj.children.length > 0) {
567
+ obj.add(c.clone());
568
+ }
569
+ else {
570
+ obj = c.clone();
571
+ }
572
+ }
573
+ else {
574
+ if (c.position.y > 0 || c.position.y < 0) {
575
+ c.children.forEach((child) => {
576
+ child.translateY(c.position.y);
577
+ });
578
+ c.translateY(-c.position.y);
579
+ }
580
+ obj.add(c.clone());
581
+ }
582
+ }
583
+ });
584
+ const boundingBox = new THREE.Box3().setFromObject(obj);
585
+ const bbCenterPivot = new THREE.Vector3();
586
+ boundingBox.getCenter(bbCenterPivot);
587
+ const delta = new THREE.Vector3().add(bbCenterPivot).negate();
588
+ obj.children.forEach((cc) => {
589
+ cc.position.add(delta);
590
+ });
591
+ return obj;
592
+ }
593
+ _addGroupChildrenToObject(group, obj, cleanUp = true) {
594
+ group.children.forEach((gc) => {
595
+ if (cleanUp && (gc instanceof THREE.Group || gc.constructor.name === 'Group')) {
596
+ this._addGroupChildrenToObject(gc, obj);
597
+ }
598
+ else {
599
+ // gc.translateX(-group.position.x);
600
+ gc.translateY(group.position.y);
601
+ // gc.translateZ(-group.position.z);
602
+ obj.add(gc.clone());
603
+ }
604
+ });
605
+ }
606
+ _iterateChildren(children, callback) {
607
+ for (let i = 0; i < children.length; i++) {
608
+ const child = children[i];
609
+ if (child instanceof THREE.Mesh || child.constructor.name === 'Mesh') {
610
+ callback(child);
611
+ }
612
+ else {
613
+ this._iterateChildren(child.children, callback);
614
+ }
615
+ }
616
+ }
617
+ async _readJsonFile(filePath) {
618
+ try {
619
+ const response = await fetch(filePath);
620
+ if (!response.ok) {
621
+ return '';
622
+ }
623
+ else {
624
+ let empty = false;
625
+ await response.clone().text().then((value) => {
626
+ if (value === '') {
627
+ empty = true;
628
+ }
629
+ });
630
+ return empty ? {} : response.json();
631
+ }
632
+ }
633
+ catch (e) {
634
+ return '';
635
+ }
636
+ }
637
+ _handleUpdateProgressBar(loaded) {
638
+ // this._progressService.progress = loaded;
639
+ }
640
+ }
641
+
642
+ // @dynamic
643
+ class ImageUtils {
644
+ static textures = new Map();
645
+ static CreateTextureFromBase64(id, base64, material, defaultFlip = false) {
646
+ return new Promise((resolve) => {
647
+ if (ImageUtils.textures.has(id)) {
648
+ resolve(ImageUtils.textures.get(id));
649
+ }
650
+ else {
651
+ if (!base64) {
652
+ resolve(new Texture());
653
+ }
654
+ else {
655
+ const loader = new THREE.TextureLoader();
656
+ loader.load(base64, (texture) => {
657
+ texture.anisotropy = 16;
658
+ texture.wrapS = THREE.RepeatWrapping;
659
+ texture.wrapT = THREE.RepeatWrapping;
660
+ texture.repeat = new THREE.Vector2(material ? material.repeatX : 1, material ? material.repeatY : 1);
661
+ texture.flipY = defaultFlip; // sabina stoel gaat hierdoor kapot
662
+ texture.colorSpace = LinearSRGBColorSpace;
663
+ texture.needsUpdate = true;
664
+ ImageUtils.textures.set(id, texture);
665
+ resolve(texture);
666
+ });
667
+ }
668
+ }
669
+ });
670
+ }
671
+ static getFixedImageFilepathUrl(imageFilepathUrl) {
672
+ return imageFilepathUrl.replace('/content/', '/content43/');
673
+ }
674
+ static getDocBodyWithMimeTypeDefinition(fileName, documentBody) {
675
+ if (documentBody === null) {
676
+ return documentBody;
677
+ }
678
+ const fileExtension = fileName.substr(fileName.lastIndexOf('.') + 1);
679
+ switch (fileExtension.toLowerCase()) {
680
+ case 'jpg':
681
+ case 'jpeg':
682
+ return 'data:image/jpg;base64,' + documentBody;
683
+ case 'png':
684
+ return 'data:image/png;base64,' + documentBody;
685
+ case 'gif':
686
+ return 'data:image/gif;base64,' + documentBody;
687
+ case 'bmp':
688
+ return 'data:image/bmp;base64,' + documentBody;
689
+ default:
690
+ return 'data:image/png;base64,' + documentBody;
691
+ }
692
+ }
693
+ static ClearCache() {
694
+ ImageUtils.textures.forEach(value => {
695
+ value.dispose();
696
+ });
697
+ ImageUtils.textures.clear();
698
+ }
699
+ }
700
+
701
+ class Material {
702
+ name;
703
+ schema;
704
+ specular = new THREE.Color(0.3, 0.3, 0.3);
705
+ shininess = 0;
706
+ // public roughness = 1;
707
+ // public metalness = 0.5;
708
+ normal;
709
+ texture;
710
+ ao;
711
+ metalness;
712
+ roughness;
713
+ repeatX = 4;
714
+ repeatY = 4;
715
+ loaded = false;
716
+ metalnessValue;
717
+ roughnessValue;
718
+ displacement;
719
+ displacementValue;
720
+ sheen;
721
+ sheenColor;
722
+ sheenColorMap;
723
+ sheenRoughness;
724
+ sheenRoughnessMap;
725
+ emissive;
726
+ emissiveValue;
727
+ emissiveIntensityValue;
728
+ envMap;
729
+ envMapIntensity;
730
+ opacity;
731
+ transparent;
732
+ reflectivity;
733
+ side;
734
+ ior;
735
+ transmission;
736
+ thickness;
737
+ specularIntensity;
738
+ transmissionMap;
739
+ clearcoatMap;
740
+ clearcoatRoughnessMap;
741
+ }
742
+
743
+ class FurnitureMaterial extends Material {
744
+ textureFilename;
745
+ normalFilename;
746
+ aoFilename;
747
+ metalnessFilename;
748
+ roughnessFilename;
749
+ displacementFilename;
750
+ envMapFilename;
751
+ emissiveFileName;
752
+ sheenColorMapFileName;
753
+ sheenRoughnessMapFileName;
754
+ transmissionMapFileName;
755
+ }
756
+ class AssetUtils {
757
+ static async CreateMaterialFromAsset(asset) {
758
+ const material = new Material();
759
+ if (!asset) {
760
+ return material;
761
+ }
762
+ if (asset.settings) {
763
+ AssetUtils.setSettingsOfMaterial(material, asset.settings);
764
+ }
765
+ if (asset.texture) {
766
+ material.texture = await ImageUtils.CreateTextureFromBase64(asset.id + '_texture', asset.texture, material);
767
+ material.texture.colorSpace = SRGBColorSpace;
768
+ material.textureFilename = asset.textureFilename;
769
+ }
770
+ if (asset.normal) {
771
+ material.normal = await ImageUtils.CreateTextureFromBase64(asset.id + '_normal', asset.normal, material);
772
+ material.normalFilename = asset.normalFilename;
773
+ }
774
+ if (asset.orm) {
775
+ material.ao = await ImageUtils.CreateTextureFromBase64(asset.id + '_orm', asset.orm, material);
776
+ material.roughness = material.ao;
777
+ material.metalness = material.ao;
778
+ material.aoFilename = asset.ormFilename;
779
+ material.roughnessFilename = asset.ormFilename;
780
+ material.metalnessFilename = asset.ormFilename;
781
+ }
782
+ else {
783
+ if (asset.ao) {
784
+ material.ao = await ImageUtils.CreateTextureFromBase64(asset.id + '_ao', asset.ao, material);
785
+ material.aoFilename = asset.aoFilename;
786
+ }
787
+ if (asset.metalness) {
788
+ material.metalness = await ImageUtils.CreateTextureFromBase64(asset.id + '_metalness', asset.metalness, material);
789
+ material.metalnessFilename = asset.metalnessFilename;
790
+ }
791
+ if (asset.roughness) {
792
+ material.roughness = await ImageUtils.CreateTextureFromBase64(asset.id + '_roughness', asset.roughness, material);
793
+ material.roughnessFilename = asset.roughnessFilename;
794
+ }
795
+ }
796
+ if (asset.displacement) {
797
+ material.displacement = await ImageUtils.CreateTextureFromBase64(asset.id + '_displacement', asset.displacement, material);
798
+ material.displacementFilename = asset.displacementFilename;
799
+ }
800
+ if (asset.envMap) {
801
+ material.envMap = await ImageUtils.CreateTextureFromBase64(asset.id + '_env', asset.envMap, material, true);
802
+ material.envMapFilename = asset.envMapFilename;
803
+ }
804
+ if (asset.sheenColorMap) {
805
+ material.sheenColorMap = await ImageUtils.CreateTextureFromBase64(asset.id + '_sheen_color', asset.sheenColorMap, material);
806
+ material.sheenColorMapFileName = asset.sheenColorMapFilename;
807
+ }
808
+ if (asset.sheenRoughnessMap) {
809
+ material.sheenRoughnessMap = await ImageUtils.CreateTextureFromBase64(asset.id + '_sheen_roughness', asset.sheenRoughnessMap, material);
810
+ material.sheenRoughnessMapFileName = asset.sheenRoughnessMapFilename;
811
+ }
812
+ if (asset.transmissionMap) {
813
+ material.transmissionMap = await ImageUtils.CreateTextureFromBase64(asset.id + '_transmission', asset.transmissionMap, material);
814
+ material.transmissionMapFileName = asset.transmissionMapFilename;
815
+ }
816
+ if (asset.emissive) {
817
+ material.emissive = await ImageUtils.CreateTextureFromBase64(asset.id + '_emissive', asset.emissive, material);
818
+ material.emissiveFileName = asset.emissiveFilename;
819
+ }
820
+ material.sheen = asset.sheen;
821
+ material.emissiveIntensityValue = asset.emissiveIntensity;
822
+ material.emissiveValue = asset.emissiveValue;
823
+ material.side = asset.side;
824
+ material.sheenColor = asset.sheenColor;
825
+ material.sheenRoughness = asset.sheenRoughness;
826
+ material.opacity = asset.opacity;
827
+ material.transparent = asset.transparent;
828
+ material.envMapIntensity = asset.envMapIntensity;
829
+ material.reflectivity = asset.reflectivity;
830
+ material.displacementValue = asset.displacementValue;
831
+ material.transmission = asset.transmission;
832
+ material.ior = asset.ior;
833
+ material.thickness = asset.thickness;
834
+ material.specularIntensity = asset.specularIntensity;
835
+ return material;
836
+ }
837
+ static setSettingsOfMaterial(material, settings) {
838
+ const shininess = parseFloat(settings['power']);
839
+ if (!isNaN(shininess)) {
840
+ material.shininess = shininess;
841
+ // material.roughness = 1 - (shininess / 100);
842
+ }
843
+ const level = parseFloat(settings['level']);
844
+ if (!isNaN(level)) {
845
+ material.specular = new THREE.Color(level, level, level);
846
+ }
847
+ const repeatObjectUsed = settings.hasOwnProperty('repeat');
848
+ const repeatx = repeatObjectUsed ? parseFloat(settings['repeat']['repeatx']) : parseFloat(settings['repeatx']);
849
+ if (!isNaN(repeatx)) {
850
+ material.repeatX = repeatx;
851
+ }
852
+ const repeaty = repeatObjectUsed ? parseFloat(settings['repeat']['repeaty']) : parseFloat(settings['repeaty']);
853
+ if (!isNaN(repeaty)) {
854
+ material.repeatY = repeaty;
855
+ }
856
+ const metalness = parseFloat(settings['metalness']);
857
+ if (!isNaN(metalness)) {
858
+ material.metalnessValue = Math.min(1, metalness);
859
+ }
860
+ const roughness = parseFloat(settings['roughness']);
861
+ if (!isNaN(roughness)) {
862
+ material.roughnessValue = Math.min(1, roughness);
863
+ }
864
+ }
865
+ }
866
+
867
+ class VariationSettingsSettings {
868
+ repeatx;
869
+ repeaty;
870
+ roughness;
871
+ metalness;
872
+ }
873
+ class VariationSettings {
874
+ id;
875
+ settings = new VariationSettingsSettings();
876
+ texture;
877
+ textureFilename;
878
+ normal;
879
+ normalFilename;
880
+ ao;
881
+ aoFilename;
882
+ metalness;
883
+ metalnessFilename;
884
+ roughness;
885
+ roughnessFilename;
886
+ orm;
887
+ ormFilename;
888
+ displacement;
889
+ displacementValue;
890
+ displacementFilename;
891
+ sheen;
892
+ sheenColor;
893
+ sheenColorMap;
894
+ sheenColorMapFilename;
895
+ sheenRoughness;
896
+ sheenRoughnessMap;
897
+ sheenRoughnessMapFilename;
898
+ transmissionMap;
899
+ transmissionMapFilename;
900
+ emissive;
901
+ emissiveFilename;
902
+ emissiveIntensity;
903
+ emissiveValue;
904
+ opacity;
905
+ transparent;
906
+ envMap;
907
+ envMapFilename;
908
+ envMapIntensity;
909
+ reflectivity;
910
+ side;
911
+ transmission;
912
+ ior;
913
+ thickness;
914
+ specularIntensity;
915
+ loading = false;
916
+ loaded = new Subject();
917
+ material;
918
+ copyFrom(settings) {
919
+ this.settings = settings.settings;
920
+ this.texture = settings.texture;
921
+ this.textureFilename = settings.textureFilename;
922
+ this.normal = settings.normal;
923
+ this.normalFilename = settings.normalFilename;
924
+ this.ao = settings.ao;
925
+ this.aoFilename = settings.aoFilename;
926
+ this.metalness = settings.metalness;
927
+ this.metalnessFilename = settings.metalnessFilename;
928
+ this.roughness = settings.roughness;
929
+ this.roughnessFilename = settings.roughnessFilename;
930
+ this.orm = settings.orm;
931
+ this.ormFilename = settings.ormFilename;
932
+ this.displacement = settings.displacement;
933
+ this.displacementValue = settings.displacementValue;
934
+ this.displacementFilename = settings.displacementFilename;
935
+ this.sheen = settings.sheen;
936
+ this.sheenColor = settings.sheenColor;
937
+ this.sheenColorMap = settings.sheenColorMap;
938
+ this.sheenColorMapFilename = settings.sheenColorMapFilename;
939
+ this.transmissionMap = settings.transmissionMap;
940
+ this.transmissionMapFilename = settings.transmissionMapFilename;
941
+ this.sheenRoughness = settings.sheenRoughness;
942
+ this.sheenRoughnessMap = settings.sheenRoughnessMap;
943
+ this.sheenRoughnessMapFilename = settings.sheenRoughnessMapFilename;
944
+ this.emissive = settings.emissive;
945
+ this.emissiveFilename = settings.emissiveFilename;
946
+ this.emissiveIntensity = settings.emissiveIntensity;
947
+ this.emissiveValue = settings.emissiveValue;
948
+ this.opacity = settings.opacity;
949
+ this.transparent = settings.transparent;
950
+ this.envMap = settings.envMap;
951
+ this.envMapFilename = settings.envMapFilename;
952
+ this.envMapIntensity = settings.envMapIntensity;
953
+ this.reflectivity = settings.reflectivity;
954
+ this.side = settings.side;
955
+ this.transmission = settings.transmission;
956
+ this.ior = settings.ior;
957
+ this.thickness = settings.thickness;
958
+ this.specularIntensity = settings.specularIntensity;
959
+ this.material = settings.material;
960
+ }
961
+ }
962
+
963
+ // Static utility functions holder related to files.
964
+ // @dynamic
965
+ class FileUtils {
966
+ static _cachedTextureUploads = new Map();
967
+ static _filesDontExist = [];
968
+ static _filesExist = [];
969
+ static async DoesFileExist(url) {
970
+ try {
971
+ if (this._filesDontExist.indexOf(url) > -1) { // no need to check again
972
+ return false;
973
+ }
974
+ if (this._filesExist.indexOf(url) > -1) { // no need to check again
975
+ return true;
976
+ }
977
+ const response = await fetch(url, { method: 'HEAD' });
978
+ if (response.ok && response.status === 200) {
979
+ this._filesExist.push(url);
980
+ return true;
981
+ }
982
+ this._filesDontExist.push(url);
983
+ return false;
984
+ }
985
+ catch (error) {
986
+ this._filesDontExist.push(url);
987
+ return false;
988
+ }
989
+ }
990
+ // Returns the given dataUri string stripped of the mimetype part.
991
+ static StripMimeStringFromDataUri(dataUri) {
992
+ if (!dataUri) {
993
+ return '';
994
+ }
995
+ const mimeString = this.GetMimeStringFromDataUri(dataUri);
996
+ return dataUri.substr(mimeString.length + 1);
997
+ }
998
+ static GetMimeStringFromDataUri(dataUri) {
999
+ if (!dataUri) {
1000
+ return '';
1001
+ }
1002
+ return dataUri.split(',')[0];
1003
+ }
1004
+ static async FileExists(url, assetPath) {
1005
+ if (!assetPath.endsWith('/')) {
1006
+ assetPath = assetPath + '/';
1007
+ }
1008
+ const result = await axios({
1009
+ method: 'get',
1010
+ url: `${assetPath}getFiles.php?assetUrl=${url}`,
1011
+ responseType: "json"
1012
+ });
1013
+ return !!result.data;
1014
+ }
1015
+ /**
1016
+ * There is some discrepancy regarding locations of h3d objects
1017
+ * Therefor we need to (temporarily?!) fix the content url
1018
+ * Also we need to change some additionals
1019
+ */
1020
+ static FixUrl(assetUrl) {
1021
+ if (!assetUrl) {
1022
+ return '';
1023
+ }
1024
+ let fixedUrl = assetUrl;
1025
+ if (!fixedUrl.endsWith('glb') && !fixedUrl.endsWith('obj') && !fixedUrl.endsWith('glb.gz') && !fixedUrl.endsWith('obj.gz')) {
1026
+ fixedUrl = FileUtils.FixUnityUrl(fixedUrl);
1027
+ }
1028
+ fixedUrl = fixedUrl.replace('/content/', '/content43/');
1029
+ return fixedUrl;
1030
+ }
1031
+ static FixGlbUrl(assetUrl) {
1032
+ if (!assetUrl) {
1033
+ return '';
1034
+ }
1035
+ let fixedUrl = assetUrl;
1036
+ if (assetUrl.indexOf('_webplayer') === -1) {
1037
+ fixedUrl = assetUrl.concat('_webplayer');
1038
+ }
1039
+ fixedUrl = fixedUrl.replace('/content/', '/content43/');
1040
+ if (!fixedUrl.endsWith('.glb')) {
1041
+ fixedUrl = fixedUrl.concat('.glb');
1042
+ }
1043
+ return fixedUrl;
1044
+ }
1045
+ // Return the mimetype of the given filename.
1046
+ static MimeTypeFromFilename(fileName) {
1047
+ const regEx = /(?:\.([^.]+))?$/; // regex to find extension
1048
+ const extension = regEx.exec(fileName)[1];
1049
+ switch (extension) {
1050
+ case 'jpg':
1051
+ case 'jpeg':
1052
+ return 'image/jpeg';
1053
+ case 'png':
1054
+ return 'image/png';
1055
+ case 'bmp':
1056
+ return 'image/bmp';
1057
+ }
1058
+ }
1059
+ /**
1060
+ * Downloads the given file to the users default download location.
1061
+ * @param fileName With or without extension
1062
+ * @param content File content as a string
1063
+ * @param [mimeType = 'text/plain']
1064
+ */
1065
+ static DownloadFile(fileName, content, mimeType = 'text/plain') {
1066
+ const element = document.createElement('a');
1067
+ element.setAttribute('href', 'data:' + mimeType + ';charset=utf-8,' + encodeURIComponent(content));
1068
+ element.setAttribute('download', fileName);
1069
+ element.style.display = 'none';
1070
+ document.body.appendChild(element);
1071
+ element.click();
1072
+ document.body.removeChild(element);
1073
+ }
1074
+ // Returns the contents of given file as a text string.
1075
+ static ReadAsText(file) {
1076
+ return new Promise((resolve) => {
1077
+ const reader = new FileReader();
1078
+ reader.readAsText(file);
1079
+ reader.onloadend = (event) => {
1080
+ if (event.target.readyState === 2) {
1081
+ resolve(event.target.result);
1082
+ }
1083
+ };
1084
+ });
1085
+ }
1086
+ static GetExtensionFromDataUri(dataUri) {
1087
+ return dataUri.replace(/(data:image\/)(.*?)(;base64.*)/gi, '$2');
1088
+ }
1089
+ static CreateDownloadFileNameFromBase64(dataUri) {
1090
+ const ext = this.GetExtensionFromDataUri(dataUri);
1091
+ return 'preview.' + ext;
1092
+ }
1093
+ static DownloadFromDataUri(dataUri, fileName) {
1094
+ const link = document.createElement('a');
1095
+ link.download = fileName;
1096
+ link.href = dataUri;
1097
+ document.body.appendChild(link);
1098
+ link.click();
1099
+ document.body.removeChild(link);
1100
+ }
1101
+ // todo remove once Connector deals with mimetypes and this method isn't used anymore
1102
+ static DocumentBodyToDataUri(documentBody, mimeType) {
1103
+ if (!!documentBody) {
1104
+ return 'data:' + mimeType + ';base64,' + documentBody;
1105
+ }
1106
+ else {
1107
+ return '';
1108
+ }
1109
+ }
1110
+ static FixUnityUrl(url) {
1111
+ if (url.indexOf('_webplayer') === -1) {
1112
+ url = url.concat('_webplayer.ione3d');
1113
+ }
1114
+ else {
1115
+ url = url.replace('_webplayer', '_webplayer.ione3d');
1116
+ }
1117
+ if (!url.endsWith('.gz')) {
1118
+ url = url.concat('.gz');
1119
+ }
1120
+ return url.replace('.unity3d', '');
1121
+ }
1122
+ }
1123
+
1124
+ class DebugUtils {
1125
+ static log(message) {
1126
+ console.log(message);
1127
+ }
1128
+ }
1129
+
1130
+ // @dynamic
1131
+ class VariationUtils {
1132
+ static MaterialCache = new Map();
1133
+ static ClearCache() {
1134
+ this.MaterialCache.clear();
1135
+ }
1136
+ static async LoadVariation(url, fileName, includeVariationFolder = true) {
1137
+ if (!fileName) {
1138
+ return null;
1139
+ }
1140
+ if (!fileName.includes('ione3d')) {
1141
+ fileName = fileName.concat('.ione3d');
1142
+ }
1143
+ const id = fileName.substr(0, fileName.lastIndexOf('.ione3d'));
1144
+ const variationUrl = `${url + (includeVariationFolder ? '/variation' : '')}/${fileName}`;
1145
+ if (this.MaterialCache.has(id)) {
1146
+ return this.MaterialCache.get(id);
1147
+ }
1148
+ if (!window.hasOwnProperty('downloadVariation')) {
1149
+ try {
1150
+ const fileExists = await FileUtils.DoesFileExist(variationUrl);
1151
+ if (!fileExists) {
1152
+ DebugUtils.log(`Failed to find variation file: ${variationUrl}`);
1153
+ return null;
1154
+ }
1155
+ else {
1156
+ const response = await fetch(variationUrl);
1157
+ if (!response.ok) {
1158
+ DebugUtils.log(`Failed to fetch variation file: ${variationUrl} - ${response.status}`);
1159
+ return null;
1160
+ }
1161
+ const compressed = await response.blob();
1162
+ return await VariationUtils.GetVariationSettingsFromFile(id, compressed);
1163
+ }
1164
+ }
1165
+ catch (err) {
1166
+ const mute = err;
1167
+ return null;
1168
+ }
1169
+ }
1170
+ else {
1171
+ try {
1172
+ const file = await window.downloadVariation(variationUrl);
1173
+ const settings = await VariationUtils.GetVariationSettingsFromFile(id, file);
1174
+ this.MaterialCache.set(id, settings);
1175
+ return settings;
1176
+ }
1177
+ catch (err) {
1178
+ console.error('Error loading variation:', err);
1179
+ throw err;
1180
+ }
1181
+ }
1182
+ }
1183
+ static async LoadVariationByUrl(materialCode, url) {
1184
+ if (!url) {
1185
+ return null;
1186
+ }
1187
+ if (!url.includes('ione3d')) {
1188
+ url = url.concat('.ione3d');
1189
+ }
1190
+ if (this.MaterialCache.has(materialCode)) {
1191
+ return this.MaterialCache.get(materialCode);
1192
+ }
1193
+ if (!window.hasOwnProperty('downloadVariation')) {
1194
+ // Normal download
1195
+ try {
1196
+ const fileExists = await FileUtils.DoesFileExist(url);
1197
+ if (!fileExists) {
1198
+ DebugUtils.log(`Failed to find variation file: ${url}`);
1199
+ return null;
1200
+ }
1201
+ else {
1202
+ const response = await fetch(url);
1203
+ if (!response.ok) {
1204
+ DebugUtils.log(`Failed to fetch variation file: ${url} - ${response.status}`);
1205
+ return null;
1206
+ }
1207
+ const compressed = await response.blob();
1208
+ return await VariationUtils.GetVariationSettingsFromFile(materialCode, compressed);
1209
+ }
1210
+ }
1211
+ catch (err) {
1212
+ console.error('Error loading variation:', err);
1213
+ throw err;
1214
+ }
1215
+ // console.error('downloadVariation not defined in window global');
1216
+ // throw new Error('downloadVariation not defined in window global');
1217
+ }
1218
+ else {
1219
+ // Headless
1220
+ try {
1221
+ const file = await window.downloadVariation(url);
1222
+ const settings = await VariationUtils.GetVariationSettingsFromFile(materialCode, file);
1223
+ this.MaterialCache.set(materialCode, settings);
1224
+ return settings;
1225
+ }
1226
+ catch (err) {
1227
+ console.error('Error loading variation:', err);
1228
+ throw err;
1229
+ }
1230
+ }
1231
+ }
1232
+ static GetUrl(assetPath, url, fileName, includeVariationFolder = true) {
1233
+ if (fileName.indexOf('ione3d') < 0) {
1234
+ fileName = fileName.concat('.ione3d');
1235
+ }
1236
+ if (assetPath && !assetPath.endsWith('/')) {
1237
+ assetPath += '/';
1238
+ }
1239
+ return `${assetPath}${url + (includeVariationFolder ? '/variation' : '')}/${fileName}`;
1240
+ }
1241
+ static GetFloorAssetUrl(assetPath, url, fileName, articleNumber) {
1242
+ if (fileName.indexOf('ione3d') < 0) {
1243
+ fileName = fileName.concat('.ione3d');
1244
+ }
1245
+ if (assetPath && !assetPath.endsWith('/')) {
1246
+ assetPath += '/';
1247
+ }
1248
+ return `${assetPath}${url + '/' + articleNumber}/${fileName}`;
1249
+ }
1250
+ // public static GetMaterialFromName(variationFolder: string, material: any): Promise<MeshStandardMaterial> {
1251
+ // return new Promise((resolve: Function) => {
1252
+ // if (!material) {
1253
+ // resolve(null);
1254
+ // }
1255
+ // if (this.MaterialCache.has(material.name)) {
1256
+ // resolve(this.MaterialCache.get(material.name).clone());
1257
+ // } else {
1258
+ // VariationUtils.LoadVariation(variationFolder, material.name, false).then((settings: VariationSettings) => {
1259
+ // if (settings) {
1260
+ // AssetUtils.CreateMaterialFromAsset(settings).then((materialObj: Material) => {
1261
+ // const materialParams: MeshStandardMaterialParameters = {};
1262
+ // materialParams.name = material.name;
1263
+ // materialParams.map = materialObj.texture;
1264
+ // materialParams.normalMap = materialObj.normal;
1265
+ // materialParams.aoMap = materialObj.ao;
1266
+ // materialParams.roughnessMap = materialObj.roughness;
1267
+ // materialParams.metalnessMap = materialObj.metalness;
1268
+ // materialParams.roughness = 1;
1269
+ // materialParams.metalness = 1;
1270
+ // const pbrMat: MeshStandardMaterial = new MeshStandardMaterial(materialParams);
1271
+ // this.MaterialCache.set(material.name, pbrMat);
1272
+ // resolve(pbrMat.clone());
1273
+ // });
1274
+ // } else {
1275
+ // resolve(material);
1276
+ // }
1277
+ // });
1278
+ // }
1279
+ // });
1280
+ // }
1281
+ static async GetVariationSettingsFromFile(id, compressedFile) {
1282
+ // TODO CHECK with Patrick
1283
+ if (!compressedFile) {
1284
+ return null;
1285
+ }
1286
+ // const zipContent = await VariationUtils.GetZipContent(compressedFile);
1287
+ // const variationSettings: VariationSettings = new VariationSettings();
1288
+ // let index;
1289
+ // variationSettings.id = id;
1290
+ //
1291
+ // if (zipContent) {
1292
+ // if (zipContent.files['material.glb']) {
1293
+ // const material: string = await zipContent.files['material.glb'].async('arraybuffer');
1294
+ // variationSettings.material = await VariationUtils.LoadMaterialFromGlb(material);
1295
+ // } else {
1296
+ // if (zipContent.files['index.json']) {
1297
+ // const indexFile: string = await zipContent.files['index.json'].async('string');
1298
+ // index = JSON.parse(indexFile);
1299
+ // }
1300
+ // if (index) {
1301
+ // await VariationUtils.CreateSettingsBasedOnIndex(index, variationSettings, zipContent);
1302
+ // } else {
1303
+ // await VariationUtils.CreateSettingsBasedOnFileName(variationSettings, zipContent);
1304
+ // }
1305
+ // }
1306
+ // }
1307
+ // return variationSettings;
1308
+ const zipContent = await VariationUtils.GetZipContent(compressedFile);
1309
+ const variationSettings = new VariationSettings();
1310
+ let index;
1311
+ variationSettings.id = id;
1312
+ if (zipContent.files.hasOwnProperty('index.json')) {
1313
+ const indexFile = await zipContent.files['index.json'].async('string');
1314
+ index = JSON.parse(indexFile);
1315
+ }
1316
+ if (index) {
1317
+ await VariationUtils.CreateSettingsBasedOnIndex(index, variationSettings, zipContent);
1318
+ }
1319
+ else {
1320
+ await VariationUtils.CreateSettingsBasedOnFileName(variationSettings, zipContent);
1321
+ }
1322
+ return variationSettings;
1323
+ }
1324
+ static async CreateSettingsBasedOnIndex(index, variationSettings, zipContent) {
1325
+ if (index.normalFile) {
1326
+ variationSettings.normal = await VariationUtils.LoadFileContentFromZip(zipContent.files[index.normalFile], index.normalFile);
1327
+ variationSettings.normalFilename = index.normalFile;
1328
+ }
1329
+ if (index.diffuseFile) {
1330
+ variationSettings.texture = await VariationUtils.LoadFileContentFromZip(zipContent.files[index.diffuseFile], index.diffuseFile);
1331
+ variationSettings.textureFilename = index.diffuseFile;
1332
+ }
1333
+ if (index.ormFile) {
1334
+ variationSettings.orm = await VariationUtils.LoadFileContentFromZip(zipContent.files[index.ormFile], index.ormFile);
1335
+ variationSettings.ormFilename = index.ormFile;
1336
+ }
1337
+ else {
1338
+ if (index.aoFile) {
1339
+ variationSettings.ao = await VariationUtils.LoadFileContentFromZip(zipContent.files[index.aoFile], index.aoFile);
1340
+ variationSettings.aoFilename = index.aoFile;
1341
+ }
1342
+ if (index.metalnessFile) {
1343
+ variationSettings.metalness = await VariationUtils.LoadFileContentFromZip(zipContent.files[index.metalnessFile], index.metalnessFile);
1344
+ variationSettings.metalnessFilename = index.metalnessFile;
1345
+ }
1346
+ if (index.roughnessFile) {
1347
+ variationSettings.roughness = await VariationUtils.LoadFileContentFromZip(zipContent.files[index.roughnessFile], index.roughnessFile);
1348
+ variationSettings.roughnessFilename = index.roughnessFile;
1349
+ }
1350
+ }
1351
+ if (index.displacementFile) {
1352
+ variationSettings.displacement = await VariationUtils.LoadFileContentFromZip(zipContent.files[index.displacementFile], index.displacementFile);
1353
+ variationSettings.displacementFilename = index.displacementFile;
1354
+ }
1355
+ if (index.emissiveFile) {
1356
+ variationSettings.emissive = await VariationUtils.LoadFileContentFromZip(zipContent.files[index.emissiveFile], index.emissiveFile);
1357
+ variationSettings.emissiveFilename = index.emissiveFile;
1358
+ }
1359
+ if (index.envMapFile) {
1360
+ variationSettings.envMap = await VariationUtils.LoadFileContentFromZip(zipContent.files[index.envMapFile], index.envMapFile);
1361
+ variationSettings.envMapFilename = index.envMapFile;
1362
+ }
1363
+ if (index.sheenColorMapFile) {
1364
+ variationSettings.sheenColorMap = await VariationUtils.LoadFileContentFromZip(zipContent.files[index.sheenColorMapFile], index.sheenColorMapFile);
1365
+ variationSettings.sheenColorMapFilename = index.sheenColorMapFile;
1366
+ }
1367
+ if (index.sheenRoughnessMapFile) {
1368
+ variationSettings.sheenRoughnessMap = await VariationUtils.LoadFileContentFromZip(zipContent.files[index.sheenRoughnessMapFile], index.sheenRoughnessMapFile);
1369
+ variationSettings.sheenRoughnessMapFilename = index.sheenColorMapFile;
1370
+ }
1371
+ if (index.transmissionMapFile) {
1372
+ variationSettings.transmissionMap = await VariationUtils.LoadFileContentFromZip(zipContent.files[index.transmissionMapFile], index.transmissionMapFile);
1373
+ variationSettings.transmissionMapFilename = index.transmissionMapFile;
1374
+ }
1375
+ if (index.repeat) {
1376
+ variationSettings.settings.repeatx = index.repeat.repeatx;
1377
+ variationSettings.settings.repeaty = index.repeat.repeaty;
1378
+ }
1379
+ variationSettings.settings.metalness = index.metalness;
1380
+ variationSettings.settings.roughness = index.roughness;
1381
+ variationSettings.sheen = index.sheen;
1382
+ if (index.sheenColor) {
1383
+ variationSettings.sheenColor = new Color(index.sheenColor.r, index.sheenColor.g, index.sheenColor.b);
1384
+ }
1385
+ variationSettings.sheenRoughness = index.sheenRoughness;
1386
+ variationSettings.envMapIntensity = index.envMapIntensity;
1387
+ variationSettings.transparent = index.transparent;
1388
+ variationSettings.opacity = index.opacity;
1389
+ if (index.emissiveValue) {
1390
+ variationSettings.emissiveValue = new Color(index.emissiveValue.r, index.emissiveValue.g, index.emissiveValue.b);
1391
+ }
1392
+ variationSettings.emissiveIntensity = index.emissiveIntensity;
1393
+ variationSettings.reflectivity = index.reflectivity;
1394
+ variationSettings.displacementValue = index.displacementValue;
1395
+ variationSettings.side = index.side;
1396
+ variationSettings.transmission = index.transmission;
1397
+ variationSettings.ior = index.ior;
1398
+ variationSettings.thickness = index.thickness;
1399
+ variationSettings.specularIntensity = index.specularIntensity;
1400
+ }
1401
+ static async CreateSettingsBasedOnFileName(variationSettings, zipContent) {
1402
+ const allLoaded = [];
1403
+ for (const fileName in zipContent.files) {
1404
+ if (zipContent.files.hasOwnProperty(fileName)) {
1405
+ const file = await zipContent.files[fileName];
1406
+ if (file.name.toLowerCase().indexOf('normal') > -1 && this.FileIsImage(file.name)) {
1407
+ allLoaded.push(zipContent.files[fileName].async('base64').then((normalFile) => {
1408
+ variationSettings.normal = `data:image/${this.GetBase64FileType(file.name)};base64,${normalFile}`;
1409
+ variationSettings.normalFilename = fileName;
1410
+ }));
1411
+ }
1412
+ else if (file.name.toLowerCase().indexOf('ao') > -1 && this.FileIsImage(file.name)) {
1413
+ allLoaded.push(zipContent.files[fileName].async('base64').then((aoFile) => {
1414
+ variationSettings.ao = `data:image/${this.GetBase64FileType(file.name)};base64,${aoFile}`;
1415
+ variationSettings.aoFilename = fileName;
1416
+ }));
1417
+ }
1418
+ else if (file.name.toLowerCase().indexOf('metalness') > -1 && this.FileIsImage(file.name)) {
1419
+ allLoaded.push(zipContent.files[fileName].async('base64').then((metalnessFile) => {
1420
+ variationSettings.metalness = `data:image/${this.GetBase64FileType(file.name)};base64,${metalnessFile}`;
1421
+ variationSettings.metalnessFilename = fileName;
1422
+ }));
1423
+ }
1424
+ else if (file.name.toLowerCase().indexOf('roughness') > -1 && this.FileIsImage(file.name)) {
1425
+ allLoaded.push(zipContent.files[fileName].async('base64').then((roughnessFile) => {
1426
+ variationSettings.roughness = `data:image/${this.GetBase64FileType(file.name)};base64,${roughnessFile}`;
1427
+ variationSettings.roughnessFilename = fileName;
1428
+ }));
1429
+ }
1430
+ else if (file.name.toLowerCase().indexOf('diffuse') > -1 && this.FileIsImage(file.name)) {
1431
+ allLoaded.push(zipContent.files[fileName].async('base64').then((diffuseFile) => {
1432
+ variationSettings.texture = `data:image/${this.GetBase64FileType(file.name)};base64,${diffuseFile}`;
1433
+ variationSettings.textureFilename = fileName;
1434
+ }));
1435
+ }
1436
+ else if (file.name.toLowerCase().indexOf('displacement') > -1 && this.FileIsImage(file.name)) {
1437
+ allLoaded.push(zipContent.files[fileName].async('base64').then((displacementFile) => {
1438
+ variationSettings.displacement = `data:image/${this.GetBase64FileType(file.name)};base64,${displacementFile}`;
1439
+ variationSettings.displacementFilename = fileName;
1440
+ }));
1441
+ }
1442
+ else if (file.name.toLowerCase().indexOf('emissive') > -1 && this.FileIsImage(file.name)) {
1443
+ allLoaded.push(zipContent.files[fileName].async('base64').then((emissiveFile) => {
1444
+ variationSettings.emissive = `data:image/${this.GetBase64FileType(file.name)};base64,${emissiveFile}`;
1445
+ variationSettings.emissiveFilename = fileName;
1446
+ }));
1447
+ }
1448
+ else if (file.name.toLowerCase().indexOf('envMap') > -1 && this.FileIsImage(file.name)) {
1449
+ allLoaded.push(zipContent.files[fileName].async('base64').then((envMapFile) => {
1450
+ variationSettings.envMap = `data:image/${this.GetBase64FileType(file.name)};base64,${envMapFile}`;
1451
+ variationSettings.envMapFilename = fileName;
1452
+ }));
1453
+ }
1454
+ else if (file.name.toLowerCase().indexOf('sheenColorMap') > -1 && this.FileIsImage(file.name)) {
1455
+ allLoaded.push(zipContent.files[fileName].async('base64').then((sheenColorMapFile) => {
1456
+ variationSettings.sheenColorMap = `data:image/${this.GetBase64FileType(file.name)};base64,${sheenColorMapFile}`;
1457
+ variationSettings.sheenColorMapFilename = fileName;
1458
+ }));
1459
+ }
1460
+ else if (file.name.toLowerCase().indexOf('sheenRoughnessMap') > -1 && this.FileIsImage(file.name)) {
1461
+ allLoaded.push(zipContent.files[fileName].async('base64').then((sheenRoughnessMapFile) => {
1462
+ variationSettings.sheenRoughnessMap = `data:image/${this.GetBase64FileType(file.name)};base64,${sheenRoughnessMapFile}`;
1463
+ variationSettings.sheenRoughnessMapFilename = fileName;
1464
+ }));
1465
+ }
1466
+ else if (file.name.toLowerCase().indexOf('transmissionMap') > -1 && this.FileIsImage(file.name)) {
1467
+ allLoaded.push(zipContent.files[fileName].async('base64').then((transmissionMapFile) => {
1468
+ variationSettings.transmissionMap = `data:image/${this.GetBase64FileType(file.name)};base64,${transmissionMapFile}`;
1469
+ variationSettings.transmissionMapFilename = fileName;
1470
+ }));
1471
+ }
1472
+ else if (file.name.indexOf('.jp') > -1) {
1473
+ allLoaded.push(zipContent.files[fileName].async('base64').then((textureFile) => {
1474
+ variationSettings.texture = 'data:image/jpeg;base64,' + textureFile;
1475
+ variationSettings.textureFilename = fileName;
1476
+ }));
1477
+ }
1478
+ else if (file.name.indexOf('.json') > -1) {
1479
+ allLoaded.push(zipContent.files[fileName].async('string').then((settingsFile) => {
1480
+ const settingsFileObj = JSON.parse(settingsFile);
1481
+ for (const key in settingsFileObj) {
1482
+ if (settingsFileObj.hasOwnProperty(key)) {
1483
+ variationSettings.settings[key] = settingsFileObj[key];
1484
+ }
1485
+ }
1486
+ }));
1487
+ }
1488
+ }
1489
+ }
1490
+ await Promise.all(allLoaded);
1491
+ }
1492
+ static LoadMaterialFromJson(material) {
1493
+ const loader = new MaterialLoader();
1494
+ if (material.textures) {
1495
+ const textures = {};
1496
+ for (let i = 0; i < material.textures.length; i++) {
1497
+ textures[material.textures[i].uuid] = material.textures[i];
1498
+ }
1499
+ loader.setTextures(textures);
1500
+ }
1501
+ return loader.parse(material);
1502
+ }
1503
+ static LoadMaterialFromGlb(material) {
1504
+ return new Promise(async (resolve, reject) => {
1505
+ if (!window.hasOwnProperty('loadGLTF')) {
1506
+ console.error('loadGLTF not defined in window global');
1507
+ return reject();
1508
+ }
1509
+ const obj = await window.loadGLTF(material, false);
1510
+ if (obj) {
1511
+ return resolve(obj.children[0].material);
1512
+ }
1513
+ });
1514
+ }
1515
+ static LoadFileContentFromZip(zip, fileName, base64 = true) {
1516
+ return new Promise((resolve) => {
1517
+ if (zip && fileName) {
1518
+ zip.async(base64 ? 'base64' : 'string').then((fileContent) => {
1519
+ if (base64) {
1520
+ resolve(`data:image/${this.GetBase64FileType(fileName)};base64,${fileContent}`);
1521
+ }
1522
+ else {
1523
+ resolve(fileContent);
1524
+ }
1525
+ });
1526
+ }
1527
+ else {
1528
+ resolve(undefined);
1529
+ }
1530
+ });
1531
+ }
1532
+ static FileIsImage(fileName) {
1533
+ const ext = fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length).toLowerCase();
1534
+ switch (ext) {
1535
+ case 'jpg':
1536
+ case 'jpeg':
1537
+ case 'png':
1538
+ case 'bmp':
1539
+ return true;
1540
+ default:
1541
+ return false;
1542
+ }
1543
+ }
1544
+ static GetBase64FileType(fileName) {
1545
+ const ext = fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length).toLowerCase();
1546
+ switch (ext) {
1547
+ case 'jpg':
1548
+ case 'jpeg':
1549
+ return 'jpeg';
1550
+ case 'png':
1551
+ return 'png';
1552
+ case 'bmp':
1553
+ return 'bmp';
1554
+ default:
1555
+ return 'jpeg';
1556
+ }
1557
+ }
1558
+ // Get the content
1559
+ static async GetZipContent(file) {
1560
+ try {
1561
+ const jszip = new JSZip();
1562
+ return await jszip.loadAsync(file);
1563
+ }
1564
+ catch (e) {
1565
+ return null;
1566
+ }
1567
+ }
1568
+ }
1569
+
1570
+ class Variation {
1571
+ decoId;
1572
+ brandId;
1573
+ gameObjectName;
1574
+ node;
1575
+ optionText;
1576
+ supplierArticleNr;
1577
+ materialId;
1578
+ material;
1579
+ }
1580
+
1581
+ class VariationCacheHelper {
1582
+ _variations = [];
1583
+ assetPath = '';
1584
+ threeDAssetPath = '';
1585
+ needsMaterials = true;
1586
+ debug;
1587
+ destroy() {
1588
+ this._variations.length = 0;
1589
+ }
1590
+ preloadVariations(schema, decoNodes, articleAssetPath = '') {
1591
+ if (!articleAssetPath.endsWith('/')) {
1592
+ articleAssetPath += '/';
1593
+ }
1594
+ decoNodes.forEach((decoNode) => {
1595
+ let fileName;
1596
+ let fileUrl;
1597
+ let includeVariationFolder = true;
1598
+ if (decoNode.materialUrl) {
1599
+ const fileNameFromUrl = decoNode.materialUrl ? decoNode.materialUrl.substr(decoNode.materialUrl.lastIndexOf('/') + 1, decoNode.materialUrl.length) : null;
1600
+ fileName = fileNameFromUrl ? fileNameFromUrl.substr(0, fileNameFromUrl.lastIndexOf('.')) : null; // chop extension
1601
+ fileUrl = decoNode.materialUrl ? decoNode.materialUrl.substr(0, decoNode.materialUrl.lastIndexOf('/')) : null;
1602
+ includeVariationFolder = false;
1603
+ }
1604
+ else if (decoNode.gameObjectName) {
1605
+ fileName = decoNode.gameObjectName;
1606
+ fileUrl = (articleAssetPath ? articleAssetPath : this.threeDAssetPath) + schema;
1607
+ }
1608
+ if (fileUrl && fileName && this.needsMaterials) {
1609
+ if (!this._get(fileName)) {
1610
+ const settings = this._createSettingsForFileName(fileName);
1611
+ VariationUtils.LoadVariation(fileUrl, fileName, includeVariationFolder).then((variationSettings) => {
1612
+ settings.loading = false;
1613
+ settings.loaded.next(variationSettings);
1614
+ if (variationSettings) {
1615
+ this._set(variationSettings);
1616
+ }
1617
+ });
1618
+ }
1619
+ }
1620
+ });
1621
+ }
1622
+ async preloadVariationsAsync(schema, decoNodes, articleAssetPath = '') {
1623
+ const promises = [];
1624
+ decoNodes.forEach((decoNode, index, array) => {
1625
+ let fileName;
1626
+ let fileUrl;
1627
+ let includeVariationFolder = true;
1628
+ if (decoNode.materialUrl) {
1629
+ const fileNameFromUrl = decoNode.materialUrl ? decoNode.materialUrl.substr(decoNode.materialUrl.lastIndexOf('/') + 1, decoNode.materialUrl.length) : null;
1630
+ fileName = fileNameFromUrl ? fileNameFromUrl.substr(0, fileNameFromUrl.lastIndexOf('.')) : null; // chop extension
1631
+ fileUrl = decoNode.materialUrl ? decoNode.materialUrl.substr(0, decoNode.materialUrl.lastIndexOf('/')) : null;
1632
+ includeVariationFolder = false;
1633
+ }
1634
+ else if (decoNode instanceof DecoNode && decoNode.gameObjectName) {
1635
+ fileName = decoNode.gameObjectName;
1636
+ fileUrl = (articleAssetPath ? articleAssetPath : this.threeDAssetPath) + schema;
1637
+ }
1638
+ else if ((decoNode instanceof Answer || decoNode instanceof SelectorStructure) && decoNode.gameObject) {
1639
+ fileName = decoNode.gameObject;
1640
+ fileUrl = (articleAssetPath ? articleAssetPath : this.threeDAssetPath) + schema;
1641
+ }
1642
+ if (fileName && !this._get(fileName) && this.needsMaterials) {
1643
+ const settings = this._createSettingsForFileName(fileName);
1644
+ promises.push(new Promise(resolve => {
1645
+ VariationUtils.LoadVariation(fileUrl, fileName, includeVariationFolder).then((variationSettings) => {
1646
+ settings.loading = false;
1647
+ settings.loaded.next(variationSettings);
1648
+ if (variationSettings) {
1649
+ this._set(variationSettings);
1650
+ resolve();
1651
+ }
1652
+ });
1653
+ }));
1654
+ }
1655
+ });
1656
+ await Promise.all(promises);
1657
+ }
1658
+ log(message) {
1659
+ if (this.debug) {
1660
+ this.debug(message);
1661
+ console.log(new Date(), message);
1662
+ }
1663
+ }
1664
+ async loadCachedVariation(fileName) {
1665
+ if (!fileName) {
1666
+ return null;
1667
+ }
1668
+ if (this._get(fileName)) {
1669
+ const settings = this._get(fileName);
1670
+ if (settings.loading) {
1671
+ return await this._waitForVariationToLoad(settings);
1672
+ }
1673
+ return settings;
1674
+ }
1675
+ }
1676
+ _get(id) {
1677
+ const len = this._variations.length;
1678
+ for (let i = 0; i < len; i++) {
1679
+ if (this._variations[i].id && this._variations[i].id === id) {
1680
+ return this._variations[i];
1681
+ }
1682
+ }
1683
+ }
1684
+ _set(variations) {
1685
+ const idx = this._variations.findIndex(v => v.id === variations.id);
1686
+ if (idx > -1) {
1687
+ this._variations[idx] = variations;
1688
+ }
1689
+ else {
1690
+ this._variations.push(variations);
1691
+ }
1692
+ }
1693
+ _waitForVariationToLoad(settings) {
1694
+ return new Promise((resolve) => {
1695
+ settings.loaded.subscribe((value) => {
1696
+ resolve(value);
1697
+ });
1698
+ });
1699
+ }
1700
+ _createSettingsForFileName(fileName) {
1701
+ const settings = new VariationSettings();
1702
+ settings.id = fileName;
1703
+ settings.loading = true;
1704
+ this._variations.push(settings);
1705
+ return settings;
1706
+ }
1707
+ }
1708
+
1709
+ class VariationHelper extends VariationCacheHelper {
1710
+ loadedAsset;
1711
+ _lastKnownVariations = new Map();
1712
+ clearCache() {
1713
+ const variationMap = Array.from(this._lastKnownVariations.values());
1714
+ variationMap.forEach((variations) => {
1715
+ variations.forEach((variation) => {
1716
+ try {
1717
+ if (variation.material) {
1718
+ if (variation.material.texture) {
1719
+ variation.material.texture.dispose();
1720
+ }
1721
+ if (variation.material.normal) {
1722
+ variation.material.normal.dispose();
1723
+ }
1724
+ if (variation.material.metalness) {
1725
+ variation.material.metalness.dispose();
1726
+ }
1727
+ if (variation.material.roughness) {
1728
+ variation.material.roughness.dispose();
1729
+ }
1730
+ if (variation.material.ao) {
1731
+ variation.material.ao.dispose();
1732
+ }
1733
+ }
1734
+ }
1735
+ catch (e) {
1736
+ DebugUtils.log('Failed to dispose material: ' + e);
1737
+ }
1738
+ });
1739
+ });
1740
+ }
1741
+ async loadPart(assetPath, parts, obj, usePbr = false) {
1742
+ try {
1743
+ const len = parts.length;
1744
+ for (let i = 0; i < len; i++) {
1745
+ if (parts[i].decoNode && parts[i].decoNode.gameObjectName && parts[i].decoNode.type === DecoNodeType.Part) {
1746
+ let partSettings;
1747
+ partSettings = await this.loadCachedVariation(parts[i].decoNode.materialUrl ? parts[i].decoNode.materialCode : (parts[i].decoNode.gameObjectName || parts[i].decoId));
1748
+ if (partSettings) {
1749
+ const partVariation = new Variation();
1750
+ partVariation.decoId = parseFloat(parts[i].decoNode.id);
1751
+ partVariation.brandId = parts[i].decoNode.brandId;
1752
+ partVariation.gameObjectName = parts[i].decoNode.gameObjectName;
1753
+ partVariation.material = await AssetUtils.CreateMaterialFromAsset(partSettings);
1754
+ this.loadedAsset = VariationUtils.GetUrl(assetPath, parts[i].schema, parts[i].decoNode.gameObjectName || parts[i].decoId);
1755
+ // parts[i].variation = partVariation;
1756
+ this._applyMaterialPart(partVariation, parts[i].node, obj, usePbr);
1757
+ }
1758
+ }
1759
+ }
1760
+ }
1761
+ catch (e) {
1762
+ DebugUtils.log('Failed to load part: ' + e);
1763
+ // console.error(e);
1764
+ }
1765
+ }
1766
+ async loadVariation(assetPath, parts, obj, usePbr = false) {
1767
+ return new Promise(async (resolve, reject) => {
1768
+ try {
1769
+ const len = parts.length;
1770
+ for (let i = 0; i < len; i++) {
1771
+ const variations = parts[i].variations;
1772
+ if (!variations || variations.length === 0) {
1773
+ if (this._getLastKnownVariations(`${parts[i].nodeId}_${parts[i].node}`)) {
1774
+ parts[i].variations = this._getLastKnownVariations(`${parts[i].nodeId}_${parts[i].node}`);
1775
+ this._applyVariations(parts[i], obj, usePbr);
1776
+ continue;
1777
+ }
1778
+ else {
1779
+ continue;
1780
+ }
1781
+ }
1782
+ const lastKnownVariations = [];
1783
+ const variationPromise = [];
1784
+ for (let j = 0; j < variations.length; j++) {
1785
+ variationPromise.push(this.loadCachedVariation(variations[j].materialUrl ? variations[j].materialCode : (variations[j].gameObjectName || parts[i].decoId)).then(async (variationSettings) => {
1786
+ const newVariation = this._createVariationFromNode(variations[j]);
1787
+ newVariation.material = await AssetUtils.CreateMaterialFromAsset(variationSettings);
1788
+ this.loadedAsset = variations[j].materialUrl ? variations[j].materialUrl : VariationUtils.GetUrl(assetPath, parts[i].schema, variations[j].gameObjectName || parts[i].decoId);
1789
+ lastKnownVariations.push(newVariation);
1790
+ }));
1791
+ }
1792
+ await Promise.all(variationPromise);
1793
+ this._setLastKnownVariations(`${parts[i].nodeId}_${parts[i].node}`, lastKnownVariations);
1794
+ parts[i].variations = lastKnownVariations;
1795
+ this._applyVariations(parts[i], obj, usePbr);
1796
+ }
1797
+ resolve();
1798
+ }
1799
+ catch (e) {
1800
+ DebugUtils.log('Failed to load variation: ' + e);
1801
+ // console.error(e);
1802
+ reject();
1803
+ }
1804
+ });
1805
+ }
1806
+ async loadVariationFromLocalFile(id, file, obj) {
1807
+ try {
1808
+ const variationSettings = await VariationUtils.GetVariationSettingsFromFile(id, file);
1809
+ const material = await AssetUtils.CreateMaterialFromAsset(variationSettings);
1810
+ this._applyMaterial(obj, material);
1811
+ }
1812
+ catch (e) {
1813
+ console.error(e);
1814
+ }
1815
+ }
1816
+ async loadVariationForMaterial(gameObject) {
1817
+ const partSettings = await this.loadCachedVariation(gameObject);
1818
+ if (partSettings) {
1819
+ const partVariation = new Variation();
1820
+ partVariation.gameObjectName = gameObject;
1821
+ partVariation.material = await AssetUtils.CreateMaterialFromAsset(partSettings);
1822
+ return this._setMeshMaterialFromVariation(gameObject, partVariation.material, true);
1823
+ }
1824
+ }
1825
+ async loadVariationByAssetUrl(assetUrl) {
1826
+ const fileName = assetUrl.substring(assetUrl.lastIndexOf('/') + 1);
1827
+ const id = 'default_' + fileName.substring(0, fileName.lastIndexOf('.'));
1828
+ const variationSettings = await VariationUtils.LoadVariationByUrl(id, assetUrl);
1829
+ if (variationSettings) {
1830
+ const materialFromAsset = await AssetUtils.CreateMaterialFromAsset(variationSettings);
1831
+ if (materialFromAsset) {
1832
+ return this._setMeshMaterialFromVariation(id, materialFromAsset, true);
1833
+ }
1834
+ }
1835
+ }
1836
+ async createVariationForMaterialPreview(compressedZip) {
1837
+ const id = String(Math.floor(Math.random() * 10) + 1);
1838
+ const variationSettings = await VariationUtils.GetVariationSettingsFromFile(id, compressedZip);
1839
+ if (variationSettings) {
1840
+ const materialFromAsset = await AssetUtils.CreateMaterialFromAsset(variationSettings);
1841
+ if (materialFromAsset) {
1842
+ return this._setMeshMaterialFromVariation(id, materialFromAsset, true);
1843
+ }
1844
+ }
1845
+ }
1846
+ _applyMaterialPart(partMaterial, nodeId, obj, usePbr = false) {
1847
+ const children = this._getChildrenFilterByProp(obj, 'selection', 'node', nodeId);
1848
+ const len = children.length;
1849
+ for (let i = 0; i < len; i++) {
1850
+ const child = children[i];
1851
+ if (child !== null && child !== undefined) {
1852
+ child.traverse((mesh) => {
1853
+ if (mesh instanceof Mesh && partMaterial) {
1854
+ if (Array.isArray(mesh.material)) { // multimaterial support
1855
+ for (let j = 0, jlen = mesh.material.length; j < jlen; j++) {
1856
+ if (mesh.material[j].name.toLowerCase().indexOf('fixed_frame') !== -1) {
1857
+ mesh.material[j] = this._setMeshMaterialFromVariation(mesh.material[j].name, partMaterial, usePbr);
1858
+ }
1859
+ }
1860
+ }
1861
+ else {
1862
+ if (mesh.material.name.toLowerCase().indexOf('fixed_frame') !== -1) {
1863
+ mesh.material = this._setMeshMaterialFromVariation(mesh.material.name, partMaterial, usePbr);
1864
+ }
1865
+ }
1866
+ }
1867
+ });
1868
+ }
1869
+ }
1870
+ }
1871
+ _applyVariations(part, obj, usePbr = false) {
1872
+ const children = this._getChildrenFilterByProp(obj, 'selection', 'node', part.node);
1873
+ const len = children.length;
1874
+ for (let i = 0; i < len; i++) {
1875
+ const child = children[i];
1876
+ if (child !== null && child !== undefined) {
1877
+ child.traverse((mesh) => {
1878
+ if (mesh instanceof Mesh && part.variations && part.variations.length > 0 && mesh.name.toLowerCase().indexOf('c_')) {
1879
+ //if ((mesh instanceof Mesh || mesh.constructor.name === 'Mesh') && part.variations && part.variations.length > 0 && mesh.name.toLowerCase().indexOf('c_')) {
1880
+ for (let j = 0; j < part.variations.length; j++) {
1881
+ const variation = part.variations[j];
1882
+ if (variation.material) {
1883
+ const materialToSearch = !!variation.materialId ? variation.materialId.toLowerCase() : 'default';
1884
+ if (Array.isArray(mesh.material)) { // multimaterial support
1885
+ for (let m = 0, mlen = mesh.material.length; m < mlen; m++) {
1886
+ if ((variation.materialId && mesh.material[m].name.toLowerCase() === materialToSearch) ||
1887
+ (!variation.materialId && mesh.material[m].name.toLowerCase().indexOf(materialToSearch) !== -1)) {
1888
+ mesh.material[m] = this._setMeshMaterialFromVariation(mesh.material[m].name, variation.material, usePbr);
1889
+ }
1890
+ }
1891
+ }
1892
+ else {
1893
+ if ((variation.materialId && mesh.material.name.toLowerCase() === materialToSearch) ||
1894
+ (!variation.materialId && mesh.material.name.toLowerCase().indexOf(materialToSearch) !== -1)) {
1895
+ mesh.material = this._setMeshMaterialFromVariation(mesh.material.name, variation.material, usePbr);
1896
+ }
1897
+ }
1898
+ }
1899
+ }
1900
+ }
1901
+ });
1902
+ }
1903
+ }
1904
+ }
1905
+ _applyMaterial(obj, material) {
1906
+ obj.traverse((mesh) => {
1907
+ if (mesh instanceof Mesh) {
1908
+ if (Array.isArray(mesh.material)) { // multimaterial support
1909
+ for (let m = 0, mlen = mesh.material.length; m < mlen; m++) {
1910
+ if (mesh.material[m].name.toLowerCase().indexOf('default') !== -1) {
1911
+ ObjectUtils.DisposeMaterial(mesh.material[m]);
1912
+ mesh.material[m] = this._setMeshMaterialFromVariation(mesh.material[m].name, material, true);
1913
+ }
1914
+ }
1915
+ }
1916
+ else {
1917
+ if (mesh.material.name.toLowerCase().indexOf('default') !== -1) {
1918
+ ObjectUtils.DisposeMaterial(mesh.material);
1919
+ mesh.material = this._setMeshMaterialFromVariation(mesh.material.name, material, true);
1920
+ }
1921
+ }
1922
+ }
1923
+ });
1924
+ }
1925
+ _setMeshMaterialFromVariation(name, material, usePbr = false) {
1926
+ if (material.texture) {
1927
+ material.texture.needsUpdate = true;
1928
+ material.texture.mapping = EquirectangularReflectionMapping;
1929
+ }
1930
+ if (!usePbr) {
1931
+ const newMaterial = new MeshPhongMaterial({
1932
+ name: name,
1933
+ shininess: material.shininess,
1934
+ specular: material.specular,
1935
+ map: material.texture,
1936
+ normalMap: material.normal
1937
+ });
1938
+ newMaterial.color.setRGB(0.8, 0.8, 0.8);
1939
+ newMaterial.needsUpdate = true;
1940
+ return newMaterial;
1941
+ }
1942
+ if (material.sheen || material.sheenColor || material.reflectivity || material.transmission) { // TODO: Proper check for all unique physical material properties
1943
+ const params = {
1944
+ // side: material.side ? material.side : FrontSide,
1945
+ name: name,
1946
+ roughness: material.roughnessValue !== undefined && material.roughnessValue !== null ? material.roughnessValue : 1,
1947
+ metalness: material.metalnessValue !== undefined && material.metalnessValue !== null ? material.metalnessValue : 0,
1948
+ envMapIntensity: material.envMapIntensity ? material.envMapIntensity : 0.01,
1949
+ clearcoatMap: material.clearcoatMap ? material.clearcoatMap : null,
1950
+ clearcoatRoughnessMap: material.clearcoatRoughnessMap ? material.clearcoatRoughnessMap : null,
1951
+ attenuationDistance: Infinity
1952
+ // wireframe: true,
1953
+ // opacity: 0.3,
1954
+ // transparent: true,
1955
+ };
1956
+ if (material.displacementValue) {
1957
+ params.displacementScale = material.displacementValue;
1958
+ }
1959
+ if (material.opacity) {
1960
+ params.opacity = material.opacity;
1961
+ }
1962
+ if (material.transparent) {
1963
+ params.transparent = material.transparent;
1964
+ }
1965
+ if (material.emissiveValue) {
1966
+ params.emissive = material.emissiveValue;
1967
+ }
1968
+ if (material.emissiveIntensityValue) {
1969
+ params.emissiveIntensity = material.emissiveIntensityValue;
1970
+ }
1971
+ if (material.texture) {
1972
+ params.map = material.texture;
1973
+ params.map.needsUpdate = true;
1974
+ }
1975
+ if (material.ao) {
1976
+ params.aoMap = material.ao;
1977
+ params.aoMap.needsUpdate = true;
1978
+ }
1979
+ if (material.roughness) {
1980
+ params.roughnessMap = material.roughness;
1981
+ params.roughnessMap.needsUpdate = true;
1982
+ // material.roughness.magFilter = NearestFilter;
1983
+ }
1984
+ if (material.metalness) {
1985
+ params.metalnessMap = material.metalness;
1986
+ params.metalnessMap.needsUpdate = true;
1987
+ }
1988
+ if (material.normal) {
1989
+ params.normalMap = material.normal;
1990
+ params.normalMap.needsUpdate = true;
1991
+ }
1992
+ if (material.displacement) {
1993
+ params.displacementMap = material.displacement;
1994
+ params.displacementMap.needsUpdate = true;
1995
+ }
1996
+ if (material.emissive) {
1997
+ params.emissiveMap = material.emissive;
1998
+ params.emissiveMap.needsUpdate = true;
1999
+ }
2000
+ if (material.envMap) {
2001
+ if (material.envMapIntensity != null || 0) {
2002
+ params.envMap = material.envMap;
2003
+ params.envMap.needsUpdate = true;
2004
+ material.envMap.mapping = EquirectangularReflectionMapping;
2005
+ material.envMap.minFilter = NearestFilter;
2006
+ material.envMap.magFilter = NearestFilter;
2007
+ }
2008
+ }
2009
+ if (material.reflectivity) {
2010
+ params.reflectivity = material.reflectivity;
2011
+ }
2012
+ if (material.side) {
2013
+ params.side = material.side;
2014
+ }
2015
+ else {
2016
+ params.side = DoubleSide;
2017
+ }
2018
+ const newMaterial = new MeshPhysicalMaterial(params);
2019
+ newMaterial.color.setRGB(1, 1, 1);
2020
+ if (material.sheen) {
2021
+ newMaterial.sheen = material.sheen;
2022
+ }
2023
+ if (material.sheenColor) {
2024
+ newMaterial.sheenColor = material.sheenColor;
2025
+ }
2026
+ else {
2027
+ // Add fallback for blender render
2028
+ newMaterial.sheenColor = new Color(1, 1, 1);
2029
+ }
2030
+ if (material.sheenRoughness) {
2031
+ newMaterial.sheenRoughness = material.sheenRoughness;
2032
+ }
2033
+ if (material.sheenColorMap) {
2034
+ newMaterial.sheenColorMap = material.sheenColorMap;
2035
+ newMaterial.sheenColorMap.needsUpdate = true;
2036
+ }
2037
+ if (material.sheenRoughnessMap) {
2038
+ newMaterial.sheenRoughnessMap = material.sheenRoughnessMap;
2039
+ newMaterial.sheenRoughnessMap.needsUpdate = true;
2040
+ }
2041
+ if (material.transmission) {
2042
+ newMaterial.transmission = material.transmission;
2043
+ }
2044
+ if (material.ior !== undefined && material.ior !== null) {
2045
+ newMaterial.ior = material.ior;
2046
+ }
2047
+ if (material.thickness !== undefined && material.thickness !== null) {
2048
+ newMaterial.thickness = material.thickness;
2049
+ }
2050
+ if (material.specularIntensity !== undefined && material.specularIntensity !== null) {
2051
+ newMaterial.specularIntensity = material.specularIntensity;
2052
+ }
2053
+ if (material.transmissionMap) {
2054
+ newMaterial.transmissionMap = material.transmissionMap;
2055
+ newMaterial.transmissionMap.needsUpdate = true;
2056
+ }
2057
+ newMaterial.needsUpdate = true;
2058
+ return newMaterial;
2059
+ }
2060
+ else {
2061
+ const params = {
2062
+ // side: material.side ? material.side : FrontSide,
2063
+ name: name,
2064
+ roughness: material.roughnessValue !== undefined && material.roughnessValue !== null ? material.roughnessValue : 1,
2065
+ metalness: material.metalnessValue !== undefined && material.metalnessValue !== null ? material.metalnessValue : 0,
2066
+ envMapIntensity: material.envMapIntensity ? material.envMapIntensity : 0.01
2067
+ // wireframe: true,
2068
+ // opacity: 0.3,
2069
+ // transparent: true,
2070
+ };
2071
+ if (material.displacementValue) {
2072
+ params.displacementScale = material.displacementValue;
2073
+ }
2074
+ if (material.opacity) {
2075
+ params.opacity = material.opacity;
2076
+ }
2077
+ if (material.transparent) {
2078
+ params.transparent = material.transparent;
2079
+ }
2080
+ if (material.emissiveValue) {
2081
+ params.emissive = material.emissiveValue;
2082
+ }
2083
+ if (material.emissiveIntensityValue) {
2084
+ params.emissiveIntensity = material.emissiveIntensityValue;
2085
+ }
2086
+ if (material.texture) {
2087
+ params.map = material.texture;
2088
+ params.map.needsUpdate = true;
2089
+ }
2090
+ if (material.ao) {
2091
+ params.aoMap = material.ao;
2092
+ params.aoMap.needsUpdate = true;
2093
+ }
2094
+ if (material.roughness) {
2095
+ params.roughnessMap = material.roughness;
2096
+ params.roughnessMap.needsUpdate = true;
2097
+ // material.roughness.magFilter = NearestFilter;
2098
+ }
2099
+ if (material.metalness) {
2100
+ params.metalnessMap = material.metalness;
2101
+ params.metalnessMap.needsUpdate = true;
2102
+ }
2103
+ if (material.normal) {
2104
+ params.normalMap = material.normal;
2105
+ params.normalMap.needsUpdate = true;
2106
+ }
2107
+ if (material.displacement) {
2108
+ params.displacementMap = material.displacement;
2109
+ params.displacementMap.needsUpdate = true;
2110
+ }
2111
+ if (material.emissive) {
2112
+ params.emissiveMap = material.emissive;
2113
+ params.emissiveMap.needsUpdate = true;
2114
+ }
2115
+ if (material.envMap) {
2116
+ if (material.envMapIntensity != null || 0) {
2117
+ params.envMap = material.envMap;
2118
+ params.envMap.needsUpdate = true;
2119
+ material.envMap.mapping = EquirectangularReflectionMapping;
2120
+ material.envMap.minFilter = NearestFilter;
2121
+ material.envMap.magFilter = NearestFilter;
2122
+ }
2123
+ }
2124
+ if (material.side) {
2125
+ params.side = material.side;
2126
+ }
2127
+ else {
2128
+ params.side = DoubleSide;
2129
+ }
2130
+ const newMaterial = new MeshStandardMaterial(params);
2131
+ newMaterial.color.setRGB(1, 1, 1);
2132
+ newMaterial.needsUpdate = true;
2133
+ return newMaterial;
2134
+ }
2135
+ }
2136
+ _setLastKnownVariations(id, variation) {
2137
+ this._lastKnownVariations.set(id, variation);
2138
+ }
2139
+ _getLastKnownVariations(id) {
2140
+ if (this._lastKnownVariations.has(id)) {
2141
+ return this._lastKnownVariations.get(id);
2142
+ }
2143
+ return null;
2144
+ }
2145
+ _getChildrenFilterByProp(obj, userDataObjectProp, prop, value) {
2146
+ return obj.children.filter((child) => {
2147
+ return child.userData &&
2148
+ child.userData.hasOwnProperty(userDataObjectProp) &&
2149
+ child.userData[userDataObjectProp].hasOwnProperty(prop) &&
2150
+ child.userData[userDataObjectProp][prop] === value;
2151
+ });
2152
+ }
2153
+ _createVariationFromNode(node) {
2154
+ const newVariation = new Variation();
2155
+ newVariation.decoId = node instanceof Variation ? node.decoId : parseFloat(node.id);
2156
+ newVariation.node = node instanceof Variation ? node.node : node.nodeId;
2157
+ newVariation.brandId = node.brandId;
2158
+ newVariation.optionText = node.optionText;
2159
+ newVariation.gameObjectName = node.gameObjectName;
2160
+ newVariation.supplierArticleNr = node.supplierArticleNr;
2161
+ newVariation.materialId = node.materialId;
2162
+ return newVariation;
2163
+ }
2164
+ }
2165
+
2166
+ class Scene3DUtil {
2167
+ static updatePivot(obj) {
2168
+ const boundingBox = new Box3().setFromObject(obj);
2169
+ const bbCenterPivot = new Vector3();
2170
+ boundingBox.getCenter(bbCenterPivot);
2171
+ const delta = new Vector3().sub(bbCenterPivot).setY(Math.abs(Math.min(boundingBox.min.y, 0)));
2172
+ obj.children.forEach((child) => {
2173
+ child.position.add(delta);
2174
+ });
2175
+ obj.updateWorldMatrix(false, true);
2176
+ }
2177
+ static TrySelectorConnection(scene, parent, part1, part2, isFirstElement, createAddableFn) {
2178
+ let addable;
2179
+ if (createAddableFn) {
2180
+ addable = part2;
2181
+ part2 = createAddableFn(addable);
2182
+ }
2183
+ if (part1 && part2) {
2184
+ let con1;
2185
+ let con2;
2186
+ // if part2 is an addable and decoConnection is present, then no need to iterate through connectors
2187
+ if (addable && addable instanceof Selection && addable.decoConnection && addable.hdecoPositioning === HdecoPositioning.Variable) {
2188
+ con1 = part1.children.find(c => c.name === addable.decoConnection);
2189
+ if (!con1) {
2190
+ return false;
2191
+ }
2192
+ for (let ii = 0; ii < part2.children.length; ii++) {
2193
+ con2 = part2.children[ii];
2194
+ // if these connectors can connect connect them
2195
+ if (!con1.userData.connected && !con2.userData.connected && this.CanSelectorConnect(part1.children, con1, con2, isFirstElement)) {
2196
+ // if (!con1['connected'] && !con2['connected'] && this.CanSelectorConnect(part1.children, con1, con2, isFirstElement)) {
2197
+ // if addable, continue this loop with a new addable
2198
+ if (addable) {
2199
+ const newPart2 = createAddableFn(addable, part2, part1);
2200
+ // reference of part2 changed, so refresh con2
2201
+ this.SelectorConnect(scene, parent, con1, newPart2.children[ii]);
2202
+ }
2203
+ else {
2204
+ return this.SelectorConnect(scene, parent, con1, con2);
2205
+ }
2206
+ }
2207
+ }
2208
+ }
2209
+ else {
2210
+ for (let i = 0; i < part1.children.length; i++) {
2211
+ for (let ii = 0; ii < part2.children.length; ii++) {
2212
+ con1 = part1.children[i];
2213
+ con2 = part2.children[ii];
2214
+ let con1Connected = false;
2215
+ if (con1 && con1.userData) {
2216
+ con1Connected = con1.userData.connected;
2217
+ }
2218
+ let con2Connected = false;
2219
+ if (con2 && con2.userData) {
2220
+ con2Connected = con2.userData.connected;
2221
+ }
2222
+ // if these connectors can connect connect them
2223
+ if (!con1Connected && !con2Connected && this.CanSelectorConnect(part1.children, con1, con2, isFirstElement)) {
2224
+ // if addable, continue this loop with a new addable
2225
+ if (addable) {
2226
+ const newPart2 = createAddableFn(addable, part2, part1);
2227
+ // reference of part2 changed, so refresh con2
2228
+ this.SelectorConnect(scene, parent, con1, newPart2.children[ii]);
2229
+ }
2230
+ else {
2231
+ return this.SelectorConnect(scene, parent, con1, con2);
2232
+ }
2233
+ }
2234
+ }
2235
+ }
2236
+ }
2237
+ }
2238
+ return false;
2239
+ }
2240
+ // public static TrySelectorConnection(scene: Scene, parent: Object3D,
2241
+ // part1: any, part2: any, createAddableFn?: Function): boolean {
2242
+ // let addable: any;
2243
+ // if (createAddableFn) {
2244
+ // addable = part2;
2245
+ // part2 = createAddableFn(addable);
2246
+ // }
2247
+ // for (let i = 0; i < part1.children.length; i++) {
2248
+ // for (let ii = 0; ii < part2.children.length; ii++) {
2249
+ // const con1: Object3D = part1.children[i];
2250
+ // const con2: Object3D = part2.children[ii];
2251
+ // // if these connectors can connect connect them
2252
+ // if (!con1['connected'] && !con2['connected'] && this.CanSelectorConnect(con1, con2)) {
2253
+ // // if addable, continue this loop with a new addable
2254
+ // if (addable) {
2255
+ // const newPart2: Object3D = createAddableFn(addable, part2, part1);
2256
+ // // reference of part2 changed, so refresh con2
2257
+ // this.SelectorConnect(scene, parent, con1, newPart2.children[ii]);
2258
+ // } else {
2259
+ // return this.SelectorConnect(scene, parent, con1, con2);
2260
+ // }
2261
+ // }
2262
+ // }
2263
+ // }
2264
+ // return false;
2265
+ // }
2266
+ static Convert3DPointToScreenPoint(point, camera, width, height) {
2267
+ if (!point) {
2268
+ return new Vector2();
2269
+ }
2270
+ const vector = point.clone().project(camera);
2271
+ vector.x = (vector.x + 1) / 2 * width;
2272
+ vector.y = -(vector.y - 1) / 2 * height;
2273
+ return new Vector2(vector.x, vector.y);
2274
+ }
2275
+ static CanSelectorConnect(consPart1, con1, con2, startWithMale) {
2276
+ const con1Name = (con1 instanceof Object3D || con1 instanceof Mesh) ? con1.name : con1.connector;
2277
+ const con2Name = (con2 instanceof Object3D || con2 instanceof Mesh) ? con2.name : con2.connector;
2278
+ const sameParent = (con1 instanceof Object3D || con1 instanceof Mesh) &&
2279
+ (con2 instanceof Object3D || con2 instanceof Mesh) ? con1.parent === con2.parent : false;
2280
+ if (!con1Name || !con2Name) {
2281
+ return false;
2282
+ }
2283
+ const parts1 = con1Name.toUpperCase().split('_'), parts2 = con2Name.toUpperCase().split('_');
2284
+ if (parts1.length >= 3 && parts2.length >= 3 && !sameParent) {
2285
+ const connectable = parts1[0] === 'C' && parts2[0] === 'C' &&
2286
+ parts1[1] === parts2[1] && parts1[2] !== parts2[2];
2287
+ return connectable;
2288
+ }
2289
+ return false;
2290
+ }
2291
+ // public static CanSelectorConnect(con1: Object3D | any, con2: Object3D | any): boolean {
2292
+ // const con1Name: string = con1 instanceof Object3D ? con1.name : con1.connector;
2293
+ // const con2Name: string = con2 instanceof Object3D ? con2.name : con2.connector;
2294
+ // const sameParent: boolean = con1 instanceof Object3D && con2 instanceof Object3D ? con1.parent === con2.parent : false;
2295
+ // const parts1: string[] = con1Name.toUpperCase().split('_'),
2296
+ // parts2: string[] = con2Name.toUpperCase().split('_');
2297
+ // if (parts1.length >= 3 && parts2.length >= 3 && !sameParent) {
2298
+ // const connectable: boolean = parts1[0] === 'C' && parts2[0] === 'C' &&
2299
+ // parts1[1] === parts2[1] && parts1[2] !== parts2[2];
2300
+ // return connectable;
2301
+ // }
2302
+ // return false;
2303
+ // }
2304
+ static SelectorConnect(scene, parent, con1, con2) {
2305
+ if (!scene || !parent) {
2306
+ return false;
2307
+ }
2308
+ con1['connectedTo'] = con2 && con2.parent ? con2.parent.name : '';
2309
+ con2['connectedTo'] = con1 && con1.parent ? con1.parent.name : '';
2310
+ con1.userData.connectedTo = con2 && con2.parent ? con2.parent.name : '';
2311
+ con1.userData.connected = false;
2312
+ con2.userData.connectedTo = con1 && con1.parent ? con1.parent.name : '';
2313
+ con2.userData.connected = false;
2314
+ const motherRotation = new Euler(0, 0, 0);
2315
+ const motherPosition = new Vector3(0, 0, 0);
2316
+ parent.updateMatrixWorld();
2317
+ motherRotation.copy(parent.rotation);
2318
+ parent.getWorldPosition(motherPosition);
2319
+ parent.rotation.set(0, 0, 0);
2320
+ parent.position.set(0, 0, 0);
2321
+ scene.updateMatrixWorld(true);
2322
+ const con1Quat = new Quaternion();
2323
+ if (con1) {
2324
+ con1.getWorldQuaternion(con1Quat);
2325
+ }
2326
+ const con2Quat = new Quaternion();
2327
+ if (con2) {
2328
+ con2.getWorldQuaternion(con2Quat);
2329
+ }
2330
+ const con1QuatParent = new Quaternion();
2331
+ if (con1 && con1.parent) {
2332
+ con1.parent.getWorldQuaternion(con1QuatParent);
2333
+ }
2334
+ const con2QuatParent = new Quaternion();
2335
+ if (con2 && con2.parent) {
2336
+ con2.parent.getWorldQuaternion(con2QuatParent);
2337
+ }
2338
+ const rotation = new Quaternion().multiplyQuaternions(con1Quat.invert(), con2Quat).multiply(new Quaternion().multiplyQuaternions(con1QuatParent, con1QuatParent));
2339
+ if (con2 && con2.parent) {
2340
+ con2.parent.quaternion.copy(rotation);
2341
+ }
2342
+ // Update because the matrix has been tempered with
2343
+ scene.updateMatrixWorld(true);
2344
+ // Move the connectors towards eachother
2345
+ const con1Pos = new Vector3();
2346
+ if (con1) {
2347
+ con1.getWorldPosition(con1Pos);
2348
+ }
2349
+ const con2Pos = new Vector3();
2350
+ if (con2) {
2351
+ con2.getWorldPosition(con2Pos);
2352
+ }
2353
+ const move = con1Pos.sub(con2Pos);
2354
+ if (con2 && con2.parent) {
2355
+ con2.parent.position.x += move.x;
2356
+ con2.parent.position.y += move.y;
2357
+ con2.parent.position.z += move.z;
2358
+ }
2359
+ // reset parent's objects rotation and position
2360
+ parent.rotation.copy(motherRotation);
2361
+ parent.position.copy(motherPosition);
2362
+ scene.updateMatrixWorld(true);
2363
+ // Set the connected flag
2364
+ if (con1) {
2365
+ // con1['connected'] = true;
2366
+ con1.userData.connected = true;
2367
+ }
2368
+ if (con2) {
2369
+ // con2['connected'] = true;
2370
+ con2.userData.connected = true;
2371
+ }
2372
+ return true;
2373
+ }
2374
+ }
2375
+
2376
+ class SelectionUtils {
2377
+ static BuildFromAdjustables(scene, item, adjustables, placedAdjustables, sourceObj, activeConnector, baseElementNodeId) {
2378
+ const result = [];
2379
+ if (adjustables && adjustables.length > 0) {
2380
+ let connected = false;
2381
+ for (let i = 0, len = adjustables.length; i < len; i++) {
2382
+ const adjustable = SelectionUtils.CreateAdjustable(item, adjustables[i], placedAdjustables, sourceObj);
2383
+ if (adjustable) {
2384
+ for (let ii = placedAdjustables.length - 1; ii >= 0; ii--) {
2385
+ if (SelectionUtils.IsMoveableObject(adjustable)) { // vrije verplaatsing mogelijk, connecten aan opgeslagen connector
2386
+ const selection = adjustable.userData.selection;
2387
+ SelectionUtils.AddElementOnConnector(item, adjustable, selection.decoConnection ? selection.decoConnection : activeConnector, baseElementNodeId);
2388
+ adjustable.visible = true;
2389
+ connected = true;
2390
+ break;
2391
+ }
2392
+ else {
2393
+ if (Scene3DUtil.TrySelectorConnection(scene, item, placedAdjustables[ii], adjustable, i === 1)) {
2394
+ adjustable.visible = true;
2395
+ connected = true;
2396
+ break;
2397
+ }
2398
+ }
2399
+ connected = false;
2400
+ }
2401
+ }
2402
+ if (!connected && placedAdjustables.length > 1) {
2403
+ DebugUtils.log(`No connection found for: ${adjustable ? adjustable.name : adjustables[i].decoNode.gameObjectName}`);
2404
+ result.push(`No connection found for: ${adjustable ? adjustable.name : adjustables[i].decoNode.gameObjectName}`);
2405
+ }
2406
+ }
2407
+ }
2408
+ return result;
2409
+ }
2410
+ static LinkSelectionsAndDecos(selections, decos, variations) {
2411
+ const len = selections.length;
2412
+ for (let i = 0; i < len; i++) {
2413
+ // const id: string = this.selections[i].artNodeIdDeco;
2414
+ let deco = decos ? decos.find(d => d.nodeId === selections[i].node) : null;
2415
+ if (!deco) {
2416
+ deco = new DecoNode();
2417
+ }
2418
+ if (deco.type !== DecoNodeType.Variation) {
2419
+ const variation = variations ? variations.find(v => v.parentId === selections[i].artNodeIdDeco) : null;
2420
+ if (variation) {
2421
+ deco.variation = variation;
2422
+ }
2423
+ }
2424
+ selections[i].decoNode = deco;
2425
+ }
2426
+ }
2427
+ static PrepareTheSelections(selections, adjustables, addables) {
2428
+ const selectionOfArticle = selections.find(s => s.nodeType === NodeType.Article);
2429
+ const adjustableSelections = selections.filter(s => s.addAdjust === '2');
2430
+ const isArticleWithMaterial = adjustableSelections.length === 0 &&
2431
+ selectionOfArticle.gameObject &&
2432
+ selectionOfArticle.gameObject.length > 0;
2433
+ // get generic variations
2434
+ const globalVariations = selections
2435
+ .filter(s => (s.generic || adjustableSelections.length === 1) && s.decoNode.variation && (!s.elementType || s.elementType === 'variation'))
2436
+ .map((value, index, array) => {
2437
+ // find selection of variation
2438
+ const sel = selections.find(s => s.artNodeIdDeco === value.decoNode.variation.parentId);
2439
+ if (sel) {
2440
+ value.decoNode.variation.supplierArticleNr = sel.supplierArticleNr;
2441
+ }
2442
+ value.decoNode.variation.materialId = SelectionUtils.GetMaterialIdFromParent(selections, value.node);
2443
+ return value.decoNode.variation;
2444
+ });
2445
+ let globalVariationsRewrite = true;
2446
+ const len = selections.length;
2447
+ let lastAdded;
2448
+ let lastAdjustable;
2449
+ let newPart = false;
2450
+ let previousPresLevel = -1;
2451
+ let positioning;
2452
+ let optionText;
2453
+ for (let i = 0; i < len; i++) {
2454
+ const selection = selections[i];
2455
+ if (selection.presentationLevel === 1) {
2456
+ positioning = selection.hdecoPositioning;
2457
+ optionText = selection.question;
2458
+ }
2459
+ if (selection.presentationLevel > previousPresLevel) {
2460
+ selection.hdecoPositioning = selection.hdecoPositioning || positioning;
2461
+ }
2462
+ if (selection.decoNode) {
2463
+ if (selection.decoNode.type === DecoNodeType.Variation) {
2464
+ const decoNode = selection.decoNode;
2465
+ decoNode.optionText = optionText;
2466
+ // find selection of variation and set materialId in case of multimaterial
2467
+ if (!decoNode.materialId) {
2468
+ decoNode.materialId = SelectionUtils.GetMaterialIdFromParent(selections, decoNode.nodeId);
2469
+ }
2470
+ if (lastAdded) {
2471
+ if (newPart) {
2472
+ if (decoNode.materialId) {
2473
+ lastAdded.variations.push(decoNode);
2474
+ }
2475
+ else {
2476
+ lastAdded.variations = [decoNode];
2477
+ }
2478
+ }
2479
+ else {
2480
+ lastAdded.variations.push(decoNode);
2481
+ }
2482
+ newPart = false;
2483
+ }
2484
+ else {
2485
+ globalVariations.push(decoNode);
2486
+ }
2487
+ // else {
2488
+ // if (globalVariationsRewrite) {
2489
+ // globalVariations.length = 0;
2490
+ // globalVariationsRewrite = false;
2491
+ // }
2492
+ // globalVariations.push(decoNode);
2493
+ // }
2494
+ // const variation: FurniturePartVariation = new FurniturePartVariation();
2495
+ // variation.BrandId = selection.decoNode.brandId;
2496
+ // variation.Description = selection.decoNode.gameObjectName;
2497
+ // variation.MaterialId = (selection.decoNode as any).materialId;
2498
+ // variation.SupplierArticleNr = (selection.decoNode as any).supplierArticleNr;
2499
+ // usedVariations.push(variation);
2500
+ }
2501
+ else if ((selection.decoNode.type === DecoNodeType.Part &&
2502
+ typeof selection.decoNode.gameObjectName === 'string' &&
2503
+ selection.decoNode.gameObjectName.length) || (isArticleWithMaterial)) {
2504
+ if (selection.decoNode.kind === DecoNodeKind.Adjustable || (isArticleWithMaterial)) {
2505
+ selection.variations = globalVariations.slice();
2506
+ adjustables.push(selection);
2507
+ if (!lastAdded || (lastAdded.nodeId !== selection.nodeId || lastAdded.node !== selection.node)) {
2508
+ newPart = true;
2509
+ }
2510
+ lastAdded = selection;
2511
+ lastAdjustable = selection;
2512
+ }
2513
+ else if (selection.decoNode.kind === DecoNodeKind.Addable) {
2514
+ selection.variations = lastAdjustable ? lastAdjustable.variations : globalVariations.slice();
2515
+ if (lastAdjustable) {
2516
+ lastAdjustable.addables.push(selection);
2517
+ }
2518
+ addables.push(selection);
2519
+ if (!lastAdded || (lastAdded.nodeId !== selection.nodeId || lastAdded.node !== selection.node)) {
2520
+ newPart = true;
2521
+ }
2522
+ lastAdded = selection;
2523
+ }
2524
+ }
2525
+ }
2526
+ previousPresLevel = selection.presentationLevel;
2527
+ }
2528
+ // this._upsertFurniture(this.activeInstance,
2529
+ // {Variations: usedVariations});
2530
+ }
2531
+ /**
2532
+ * Returns the materialId of the parent up the tree until presentionlevel 1 is reached
2533
+ * @param id
2534
+ * @private
2535
+ */
2536
+ static GetMaterialIdFromParent(selections, id) {
2537
+ if (!selections) {
2538
+ return undefined;
2539
+ }
2540
+ const idx = selections.findIndex(s => s.node === id);
2541
+ let currentLevel = 999;
2542
+ if (idx > -1) {
2543
+ for (let i = idx; i > 0; i--) {
2544
+ const selection = selections[i];
2545
+ if (selection.presentationLevel >= currentLevel) {
2546
+ break;
2547
+ }
2548
+ currentLevel = selection.presentationLevel;
2549
+ if (selection.hdecoGameObject) {
2550
+ return selection.hdecoGameObject;
2551
+ }
2552
+ }
2553
+ }
2554
+ return undefined;
2555
+ }
2556
+ static CreateAdjustable(item, adj, placedAdjustables, sourceObj) {
2557
+ let obj = sourceObj ? sourceObj.getObjectByName(adj.decoNode.gameObjectName || adj.gameObject) : null;
2558
+ if (!obj) {
2559
+ // in case of glb files, the importer removes dots from names
2560
+ const name = adj.decoNode.gameObjectName.replace(/[.]/g, '');
2561
+ obj = sourceObj ? sourceObj.getObjectByName(name) : null;
2562
+ if (!obj) {
2563
+ DebugUtils.log(`No object with name ${adj.decoNode.gameObjectName} found!`);
2564
+ return null;
2565
+ }
2566
+ }
2567
+ let adjustable = obj.clone();
2568
+ adjustable.name = SelectionUtils.CreateUniqueName(item, obj);
2569
+ adjustable.userData.selection = adj;
2570
+ adjustable.position.set(0, 0, 0);
2571
+ adjustable.traverse(c => {
2572
+ c.visible = !c.name.toLowerCase().startsWith('c_');
2573
+ });
2574
+ item.add(adjustable);
2575
+ adjustable = item.getObjectByName(adjustable.name);
2576
+ placedAdjustables.push(adjustable);
2577
+ return adjustable;
2578
+ }
2579
+ static CreateAddable(sourceObj, item, placedAddables, add, part2, part1) {
2580
+ const obj = sourceObj.getObjectByName(add.decoNode.gameObjectName);
2581
+ if (!obj) {
2582
+ return null;
2583
+ }
2584
+ // const selection: Selection = this._getSelectionFromId(adj.artNodeIdDeco);
2585
+ const addable = obj.clone();
2586
+ addable.name = SelectionUtils.CreateUniqueName(item, obj);
2587
+ addable.userData.selection = add;
2588
+ addable.position.set(0, 0, 0);
2589
+ addable.visible = true;
2590
+ if (part1 && part2) {
2591
+ return SelectionUtils.PlaceAddable(item, placedAddables, addable);
2592
+ }
2593
+ return addable;
2594
+ }
2595
+ static PlaceAddable(item, placedAddables, add) {
2596
+ item.add(add);
2597
+ const addable = item.getObjectByName(add.name);
2598
+ placedAddables.push(addable);
2599
+ return addable;
2600
+ }
2601
+ static PlaceAddables(scene, sourceObj, item, placedAdjustables, placedAddables) {
2602
+ const len = placedAdjustables.length;
2603
+ for (let i = 0; i < len; i++) {
2604
+ if (placedAdjustables[i].userData.hasOwnProperty('selection') && placedAdjustables[i].userData.selection) {
2605
+ const adjustableSelection = placedAdjustables[i].userData.selection;
2606
+ if (adjustableSelection) {
2607
+ const lena = adjustableSelection.addables.length;
2608
+ let conAddable;
2609
+ for (let ia = 0; ia < lena; ia++) {
2610
+ Scene3DUtil.TrySelectorConnection(scene, item, placedAdjustables[i], adjustableSelection.addables[ia], false, (add, part2, part1) => SelectionUtils.CreateAddable(sourceObj, item, placedAddables, add, part2, part1));
2611
+ if (conAddable) {
2612
+ Scene3DUtil.TrySelectorConnection(scene, item, conAddable, adjustableSelection.addables[ia], false, (add, part2, part1) => SelectionUtils.CreateAddable(sourceObj, item, placedAddables, add, part2, part1));
2613
+ }
2614
+ conAddable = placedAddables[placedAddables.length - 1];
2615
+ }
2616
+ }
2617
+ }
2618
+ }
2619
+ }
2620
+ static CreateUniqueName(item, obj) {
2621
+ let iteration = 0;
2622
+ let name = `${item.name}_${obj.name}`;
2623
+ let existingObj = item.getObjectByName(name);
2624
+ while (existingObj) {
2625
+ iteration++;
2626
+ name = `${item.name}_${obj.name}` + (iteration !== 0 ? ('_' + iteration) : '');
2627
+ existingObj = item.getObjectByName(name);
2628
+ }
2629
+ return name;
2630
+ }
2631
+ static IsMoveableObject(obj) {
2632
+ return obj.userData && obj.userData.selection && obj.userData.selection.hdecoPositioning === "V";
2633
+ }
2634
+ static AddElementOnConnector(currentItem, element, connector, connectedNodeId) {
2635
+ // TODO this should do something, but not sure what
2636
+ // const connectorFromElement = currentItem.connectorFromElement(element.userData.selection);
2637
+ // const idFromBaseElement: number = connectedNodeId ? connectedNodeId : connectorFromElement.connectedNodeId;
2638
+ // const baseElement: Object3D[] = currentItem.getObjectsByUserDataProp('selection', 'nodeId', idFromBaseElement);
2639
+ //
2640
+ // const item: Item = ObjectUtils.GetParentItem(element);
2641
+ // if (item) {
2642
+ // const conn: string =
2643
+ // await this.elementService.placeElementOnConnector(
2644
+ // item.metadata.itemId,
2645
+ // element,
2646
+ // baseElement.length > 0 ? baseElement[0] : null,
2647
+ // false,
2648
+ // connector,
2649
+ // false
2650
+ // );
2651
+ // this.saveElementConnector(element, conn);
2652
+ // }
2653
+ }
2654
+ }
2655
+
2656
+ class Builder {
2657
+ modelLoaded = new BehaviorSubject(null);
2658
+ set debug(value) {
2659
+ if (value) {
2660
+ this._debug = value;
2661
+ if (this._variationHelper) {
2662
+ this._variationHelper.debug = this._debug;
2663
+ }
2664
+ }
2665
+ }
2666
+ get debug() {
2667
+ return this._debug;
2668
+ }
2669
+ _scene;
2670
+ _instanceId;
2671
+ _sku;
2672
+ _goodId;
2673
+ _assetPath;
2674
+ _selections = [];
2675
+ _answers = [];
2676
+ _decos = [];
2677
+ _placedAdjustables = [];
2678
+ _placedAddables = [];
2679
+ _adjustables = [];
2680
+ _addables = [];
2681
+ _debug;
2682
+ _imageCache = new Map();
2683
+ _boFactory;
2684
+ _source;
2685
+ _threedUtils;
2686
+ _variationHelper;
2687
+ _articleCache = new Map();
2688
+ constructor() {
2689
+ }
2690
+ init(scene) {
2691
+ this._boFactory = new BusinessObjectFactory();
2692
+ this._threedUtils = new ThreedUtils();
2693
+ this._variationHelper = new VariationHelper();
2694
+ this._variationHelper.debug = this._debug;
2695
+ if (!scene) {
2696
+ throw 'No scene object provided!';
2697
+ }
2698
+ // this._imageCacheService = new ImageCacheService();
2699
+ this._scene = scene;
2700
+ }
2701
+ // public async buildModel(sku?: string, instanceId?: string, goodId?: number): Promise<THREE.Object3D> {
2702
+ // if (!this._scene) {
2703
+ // return;
2704
+ // }
2705
+ // try {
2706
+ // await this._setInstanceId(sku, instanceId, goodId);
2707
+ // await this._prepareConfiguration(this._sku, this._goodId);
2708
+ // if (this._source) {
2709
+ // await this._configuratorService.setInstanceToConfigure(this._instanceId);
2710
+ // const promises: Promise<any>[] = [];
2711
+ // this._reset();
2712
+ // promises.push(this.getQuestionAndAnswers());
2713
+ // promises.push(this._getSelections(this._instanceId));
2714
+ // promises.push(this._getDecos());
2715
+ // await Promise.all(promises);
2716
+ // if (this._answers.length > 0) {
2717
+ // return;
2718
+ // }
2719
+ // this._linkSelectionsAndDecos();
2720
+ // this._prepareTheSelections();
2721
+ // this._preloadMaterials();
2722
+ // const build = await this._build();
2723
+ // return build;
2724
+ // } else {
2725
+ // throw 'GLB source not found!';
2726
+ // }
2727
+ // } catch (e) {
2728
+ // throw e;
2729
+ // }
2730
+ // }
2731
+ async buildModelFromData(selections, decos, assetUrl, cdnUrl, schema) {
2732
+ return new Promise(async (resolve, reject) => {
2733
+ if (!this._scene) {
2734
+ reject('no scene provided!');
2735
+ }
2736
+ try {
2737
+ this._log('start build', true);
2738
+ await this._downloadAsset(assetUrl, cdnUrl, schema, selections && selections.length > 1 && decos && decos.length > 0);
2739
+ try {
2740
+ if (this._source) {
2741
+ try {
2742
+ if (selections && selections.length > 1 && decos && decos.length > 0) {
2743
+ this._reset();
2744
+ this._assetPath = assetUrl;
2745
+ this._selections = selections;
2746
+ this._decos = decos;
2747
+ this._linkSelectionsAndDecos();
2748
+ this._prepareTheSelections();
2749
+ this._preloadMaterials(schema, cdnUrl);
2750
+ const build = await this._build();
2751
+ resolve(build);
2752
+ }
2753
+ else {
2754
+ resolve(this._source); // just return the source for simplicity
2755
+ }
2756
+ }
2757
+ catch (e) {
2758
+ reject(e.message);
2759
+ }
2760
+ {
2761
+ }
2762
+ }
2763
+ else {
2764
+ if (assetUrl) {
2765
+ reject(`Error downloading source: ${assetUrl}`);
2766
+ }
2767
+ else {
2768
+ reject('No 3D source provided!');
2769
+ }
2770
+ }
2771
+ }
2772
+ finally {
2773
+ this._cleanUp();
2774
+ this._log('finish build', true);
2775
+ }
2776
+ }
2777
+ catch (e) {
2778
+ reject(e);
2779
+ }
2780
+ });
2781
+ }
2782
+ destroy() {
2783
+ this._threedUtils.clearCache();
2784
+ this._variationHelper.clearCache();
2785
+ }
2786
+ _cleanUp() {
2787
+ this._sku = undefined;
2788
+ this._goodId = undefined;
2789
+ this._instanceId = undefined;
2790
+ this._variationHelper.clearCache();
2791
+ }
2792
+ _handleResponseData(includeMimetype, thumb, responseData) {
2793
+ if (responseData && responseData.resultObject) {
2794
+ if (responseData.resultObject.filePath !== null && responseData.resultObject.filePath !== "") {
2795
+ return ImageUtils.getFixedImageFilepathUrl(responseData.resultObject.filePath);
2796
+ }
2797
+ else {
2798
+ if (includeMimetype) {
2799
+ return ImageUtils.getDocBodyWithMimeTypeDefinition(responseData.resultObject.fileName, thumb ? responseData.resultObject.thumbnailBody : responseData.resultObject.documentBody);
2800
+ }
2801
+ else {
2802
+ return thumb ? responseData.resultObject.thumbnailBody : responseData.resultObject.documentBody;
2803
+ }
2804
+ }
2805
+ }
2806
+ else {
2807
+ return '';
2808
+ }
2809
+ }
2810
+ _preloadMaterials(schema, assetPath) {
2811
+ if (this._decos && this._decos.length) {
2812
+ const materials = [...new Set(this._decos
2813
+ .filter(d => (d.gameObjectName || d.materialUrl) && d.type === DecoNodeType.Variation)
2814
+ .map(d => d))];
2815
+ this._variationHelper.preloadVariations(schema, materials, assetPath);
2816
+ }
2817
+ }
2818
+ async _downloadAsset(assetUrl, cdnUrl, schema, setVisibleFalse = true) {
2819
+ this._source = await this._threedUtils.download3DSource(assetUrl, setVisibleFalse)
2820
+ .catch((error) => {
2821
+ throw error;
2822
+ });
2823
+ if (cdnUrl) {
2824
+ this._variationHelper.assetPath =
2825
+ cdnUrl +
2826
+ (cdnUrl.endsWith('/') ? '' : '/') +
2827
+ (schema ? schema : 'UP_DBA');
2828
+ }
2829
+ }
2830
+ async _build() {
2831
+ const adjustables = this._getAdjustables();
2832
+ if (adjustables && adjustables.length === 0 && this._selections && this._selections.length > 0) { // check if article has a gameObject
2833
+ if (this._selections[0].nodeType === NodeType.Article && this._selections[0].gameObject) {
2834
+ adjustables.push(this._selections[0]);
2835
+ }
2836
+ }
2837
+ const obj = new THREE.Object3D();
2838
+ obj.visible = false; // no need to render visualy
2839
+ const adjustableResult = SelectionUtils.BuildFromAdjustables(this._scene, obj, adjustables, this._placedAdjustables, this._source);
2840
+ if (adjustableResult.length === 0) {
2841
+ this._log('place addables');
2842
+ SelectionUtils.PlaceAddables(this._scene, this._source, obj, this._placedAdjustables, this._placedAddables);
2843
+ this._log('update pivot');
2844
+ this._updatePivot(obj);
2845
+ this._log('load variations');
2846
+ await this._loadVariations(this._assetPath, obj);
2847
+ // this._variationHelper.clearCache();
2848
+ this._log('remove from scene');
2849
+ this._scene.remove(obj);
2850
+ ObjectUtils.DisposeObject(this._source);
2851
+ // this._cleanUp();
2852
+ this._log('ready!');
2853
+ this.modelLoaded.next(obj);
2854
+ return obj;
2855
+ }
2856
+ else {
2857
+ throw 'No adjustables found!';
2858
+ // this._building = false;
2859
+ // if (this.selections.length < 2) {
2860
+ // this._errorService.addError(this.activeInstance, {message: 'NO_SELECTIONS_OR_DECOS', description: 'NO_SELECTIONS_OR_DECOS_INFO'});
2861
+ // this.buildFinished.next({instanceId: this.activeInstance, resultType: ResultType.NoSelections});
2862
+ // } else if (this.decos.size === 0) {
2863
+ // this._errorService.addError(this.activeInstance, {message: 'NO_SELECTIONS_OR_DECOS', description: 'NO_SELECTIONS_OR_DECOS_INFO'});
2864
+ // this.buildFinished.next({instanceId: this.activeInstance, resultType: ResultType.NoDecoNodes});
2865
+ // } else {
2866
+ // this._errorService.addError(this.activeInstance, {message: 'NO_ADJUSTABLES', description: 'NO_ADJUSTABLES_INFO'});
2867
+ // this.buildFinished.next({instanceId: this.activeInstance, resultType: ResultType.NoAdjustables});
2868
+ // }
2869
+ }
2870
+ }
2871
+ _prepareTheSelections() {
2872
+ if (!this._selections || !this._selections.length) {
2873
+ return;
2874
+ }
2875
+ SelectionUtils.PrepareTheSelections(this._selections, this._adjustables, this._addables);
2876
+ }
2877
+ _linkSelectionsAndDecos() {
2878
+ if (!this._selections || !this._selections.length) {
2879
+ return;
2880
+ }
2881
+ SelectionUtils.LinkSelectionsAndDecos(this._selections, this._decos, this._getVariations());
2882
+ }
2883
+ _getVariations() {
2884
+ if (this._decos && this._decos.length) {
2885
+ return this._decos.filter((deco) => {
2886
+ return deco.type === DecoNodeType.Variation;
2887
+ });
2888
+ }
2889
+ return [];
2890
+ }
2891
+ async _loadVariations(assetPath, obj) {
2892
+ this._log('load variation for parts');
2893
+ await this._variationHelper.loadPart(assetPath, this._adjustables, obj, true);
2894
+ this._log('load variation for adjustables');
2895
+ await this._variationHelper.loadVariation(assetPath, this._adjustables, obj, true);
2896
+ this._log('load variation for addables');
2897
+ await this._variationHelper.loadVariation(assetPath, this._addables, obj, true);
2898
+ this._log('load variation clear cache');
2899
+ this._variationHelper.clearCache();
2900
+ this._log('load variations done');
2901
+ }
2902
+ _getAdjustables() {
2903
+ if (!this._selections || !this._selections.length) {
2904
+ return [];
2905
+ }
2906
+ return this._selections.filter(s => {
2907
+ const node = s.decoNode;
2908
+ return node && node.gameObjectName && node.type === DecoNodeType.Part && node.kind === DecoNodeKind.Adjustable;
2909
+ });
2910
+ }
2911
+ _updatePivot(obj) {
2912
+ const boundingBox = new THREE.Box3().setFromObject(obj);
2913
+ const bbCenterPivot = new THREE.Vector3();
2914
+ boundingBox.getCenter(bbCenterPivot);
2915
+ const delta = new THREE.Vector3().sub(bbCenterPivot).setY(Math.abs(Math.min(boundingBox.min.y, 0)));
2916
+ obj.children.forEach((child) => {
2917
+ child.position.add(delta);
2918
+ });
2919
+ obj.updateWorldMatrix(false, true);
2920
+ }
2921
+ _reset() {
2922
+ this._placedAddables.length = 0;
2923
+ this._placedAdjustables.length = 0;
2924
+ this._adjustables.length = 0;
2925
+ this._addables.length = 0;
2926
+ // this._prepareTheSelections();
2927
+ }
2928
+ _log(message, force = false) {
2929
+ if (this.debug) {
2930
+ this.debug(message);
2931
+ console.log(new Date(), message);
2932
+ }
2933
+ }
2934
+ }
2935
+
2936
+ /**
2937
+ * Generated bundle index. Do not edit.
2938
+ */
2939
+
2940
+ export { Builder, DebugUtils, ImageUtils, Material, ObjectUtils, Scene3DUtil, SelectionUtils, ThreedUtils, VariationCacheHelper, VariationHelper };
2941
+ //# sourceMappingURL=colijnit-configuratorcore.mjs.map