@colijnit/configurator 1.0.19 → 1.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.idea/Configurator.iml +12 -0
- package/.idea/codeStyles/Project.xml +21 -0
- package/.idea/codeStyles/codeStyleConfig.xml +5 -0
- package/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/.idea/misc.xml +9 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/angular.json +193 -0
- package/configurator.iml +11 -0
- package/dist/configurator/index.html +14 -0
- package/dist/configurator/main-es2015.js +3129 -0
- package/dist/configurator/main-es2015.js.map +1 -0
- package/dist/configurator/main-es5.js +5482 -0
- package/dist/configurator/main-es5.js.map +1 -0
- package/dist/configurator/polyfills-es2015.js +4520 -0
- package/dist/configurator/polyfills-es2015.js.map +1 -0
- package/dist/configurator/polyfills-es5.js +18375 -0
- package/dist/configurator/polyfills-es5.js.map +1 -0
- package/dist/configurator/runtime-es2015.js +155 -0
- package/dist/configurator/runtime-es2015.js.map +1 -0
- package/dist/configurator/runtime-es5.js +155 -0
- package/dist/configurator/runtime-es5.js.map +1 -0
- package/dist/configurator/styles-es2015.js +450 -0
- package/dist/configurator/styles-es2015.js.map +1 -0
- package/dist/configurator/styles-es5.js +432 -0
- package/dist/configurator/styles-es5.js.map +1 -0
- package/dist/configurator/vendor-es2015.js +155551 -0
- package/dist/configurator/vendor-es2015.js.map +1 -0
- package/dist/configurator/vendor-es5.js +183588 -0
- package/dist/configurator/vendor-es5.js.map +1 -0
- package/ng-package.json +9 -0
- package/package.json +47 -14
- package/src/app/app.component.ts +222 -0
- package/src/app/app.module.ts +34 -0
- package/src/app/builder.ts +480 -0
- package/src/app/components/answers/answer/answer.component.ts +61 -0
- package/src/app/components/answers/answers.component.ts +41 -0
- package/src/app/components/answers/answers.module.ts +26 -0
- package/src/app/components/selections/selections.component.ts +131 -0
- package/src/app/components/selections/selections.module.ts +20 -0
- package/src/app/components/shared/loader/loader.component.scss +33 -0
- package/src/app/components/shared/loader/loader.component.ts +20 -0
- package/src/app/components/shared/shared.module.ts +16 -0
- package/src/app/directives/visibility-observer-master.directive.ts +71 -0
- package/src/app/directives/visibility-observer.directive.ts +74 -0
- package/src/app/services/configurator.service.ts +86 -0
- package/src/app/services/image-cache.service.ts +56 -0
- package/src/app/services/locator.service.ts +6 -0
- package/src/environments/environment.prod.ts +3 -0
- package/src/environments/environment.ts +8 -0
- package/src/helper/variation-helper.ts +220 -0
- package/src/index.html +14 -0
- package/src/main.ts +11 -0
- package/src/model/material.ts +22 -0
- package/src/model/variation-settings.ts +14 -0
- package/src/model/variation.ts +11 -0
- package/src/polyfills.ts +73 -0
- package/{public_api.d.ts → src/public_api.ts} +6 -6
- package/src/style/shared.scss +173 -0
- package/src/style/styles.scss +45 -0
- package/src/tsconfig.app.json +16 -0
- package/src/tsconfig.spec.json +19 -0
- package/src/utils/asset.utils.ts +88 -0
- package/src/utils/file.utils.ts +156 -0
- package/src/utils/file.utils.unit.test.ts +8 -0
- package/src/utils/image.utils.ts +54 -0
- package/src/utils/object.utils.ts +52 -0
- package/src/utils/scene-utils.ts +119 -0
- package/src/utils/threed.utils.ts +219 -0
- package/src/utils/variation-utils.ts +216 -0
- package/tsconfig.json +23 -0
- package/tslint.json +132 -0
- package/app/builder.d.ts +0 -53
- package/app/components/answers/answer/answer.component.d.ts +0 -11
- package/app/components/answers/answers.component.d.ts +0 -8
- package/app/components/answers/answers.module.d.ts +0 -2
- package/app/components/selections/selections.component.d.ts +0 -22
- package/app/components/selections/selections.module.d.ts +0 -2
- package/app/components/shared/loader/loader.component.d.ts +0 -2
- package/app/components/shared/shared.module.d.ts +0 -2
- package/app/directives/visibility-observer-master.directive.d.ts +0 -9
- package/app/directives/visibility-observer.directive.d.ts +0 -13
- package/app/services/configurator.service.d.ts +0 -22
- package/app/services/image-cache.service.d.ts +0 -10
- package/app/services/locator.service.d.ts +0 -4
- package/bundles/colijnit-configurator.umd.js +0 -2745
- package/bundles/colijnit-configurator.umd.js.map +0 -1
- package/bundles/colijnit-configurator.umd.min.js +0 -17
- package/bundles/colijnit-configurator.umd.min.js.map +0 -1
- package/colijnit-configurator.d.ts +0 -10
- package/colijnit-configurator.metadata.json +0 -1
- package/esm2015/app/builder.js +0 -477
- package/esm2015/app/components/answers/answer/answer.component.js +0 -69
- package/esm2015/app/components/answers/answers.component.js +0 -43
- package/esm2015/app/components/answers/answers.module.js +0 -29
- package/esm2015/app/components/selections/selections.component.js +0 -134
- package/esm2015/app/components/selections/selections.module.js +0 -23
- package/esm2015/app/components/shared/loader/loader.component.js +0 -24
- package/esm2015/app/components/shared/shared.module.js +0 -21
- package/esm2015/app/directives/visibility-observer-master.directive.js +0 -51
- package/esm2015/app/directives/visibility-observer.directive.js +0 -57
- package/esm2015/app/services/configurator.service.js +0 -94
- package/esm2015/app/services/image-cache.service.js +0 -66
- package/esm2015/app/services/locator.service.js +0 -10
- package/esm2015/colijnit-configurator.js +0 -11
- package/esm2015/helper/variation-helper.js +0 -216
- package/esm2015/model/material.js +0 -11
- package/esm2015/model/variation-settings.js +0 -6
- package/esm2015/model/variation.js +0 -3
- package/esm2015/public_api.js +0 -7
- package/esm2015/utils/asset.utils.js +0 -74
- package/esm2015/utils/file.utils.js +0 -139
- package/esm2015/utils/image.utils.js +0 -52
- package/esm2015/utils/object.utils.js +0 -49
- package/esm2015/utils/scene-utils.js +0 -94
- package/esm2015/utils/threed.utils.js +0 -222
- package/esm2015/utils/variation-utils.js +0 -224
- package/esm5/app/builder.js +0 -591
- package/esm5/app/components/answers/answer/answer.component.js +0 -64
- package/esm5/app/components/answers/answers.component.js +0 -27
- package/esm5/app/components/answers/answers.module.js +0 -32
- package/esm5/app/components/selections/selections.component.js +0 -104
- package/esm5/app/components/selections/selections.module.js +0 -26
- package/esm5/app/components/shared/loader/loader.component.js +0 -16
- package/esm5/app/components/shared/shared.module.js +0 -24
- package/esm5/app/directives/visibility-observer-master.directive.js +0 -64
- package/esm5/app/directives/visibility-observer.directive.js +0 -59
- package/esm5/app/services/configurator.service.js +0 -160
- package/esm5/app/services/image-cache.service.js +0 -69
- package/esm5/app/services/locator.service.js +0 -13
- package/esm5/colijnit-configurator.js +0 -11
- package/esm5/helper/variation-helper.js +0 -268
- package/esm5/model/material.js +0 -13
- package/esm5/model/variation-settings.js +0 -8
- package/esm5/model/variation.js +0 -7
- package/esm5/public_api.js +0 -7
- package/esm5/utils/asset.utils.js +0 -106
- package/esm5/utils/file.utils.js +0 -151
- package/esm5/utils/image.utils.js +0 -56
- package/esm5/utils/object.utils.js +0 -56
- package/esm5/utils/scene-utils.js +0 -98
- package/esm5/utils/threed.utils.js +0 -279
- package/esm5/utils/variation-utils.js +0 -327
- package/fesm2015/colijnit-configurator.js +0 -2109
- package/fesm2015/colijnit-configurator.js.map +0 -1
- package/fesm5/colijnit-configurator.js +0 -2527
- package/fesm5/colijnit-configurator.js.map +0 -1
- package/helper/variation-helper.d.ts +0 -14
- package/model/material.d.ts +0 -17
- package/model/variation-settings.d.ts +0 -14
- package/model/variation.d.ts +0 -10
- package/utils/asset.utils.d.ts +0 -13
- package/utils/file.utils.d.ts +0 -27
- package/utils/image.utils.d.ts +0 -8
- package/utils/object.utils.d.ts +0 -7
- package/utils/scene-utils.d.ts +0 -7
- package/utils/threed.utils.d.ts +0 -16
- package/utils/variation-utils.d.ts +0 -12
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
|
|
3
|
+
export class SceneUtils {
|
|
4
|
+
|
|
5
|
+
public static TrySelectorConnection(scene: THREE.Scene, parent: THREE.Object3D,
|
|
6
|
+
part1: any, part2: any, createAddableFn?: Function): boolean {
|
|
7
|
+
let addable: any;
|
|
8
|
+
if (createAddableFn) {
|
|
9
|
+
addable = part2;
|
|
10
|
+
part2 = createAddableFn(addable);
|
|
11
|
+
}
|
|
12
|
+
for (let i = 0; i < part1.children.length; i++) {
|
|
13
|
+
for (let ii = 0; ii < part2.children.length; ii++) {
|
|
14
|
+
const con1: THREE.Object3D = part1.children[i];
|
|
15
|
+
const con2: THREE.Object3D = part2.children[ii];
|
|
16
|
+
// if these connectors can connect connect them
|
|
17
|
+
if (!con1['connected'] && !con2['connected'] && this.CanSelectorConnect(con1, con2)) {
|
|
18
|
+
// if addable, continue this loop with a new addable
|
|
19
|
+
if (addable) {
|
|
20
|
+
const newPart2: THREE.Object3D = createAddableFn(addable, part2, part1);
|
|
21
|
+
// reference of part2 changed, so refresh con2
|
|
22
|
+
this.SelectorConnect(scene, parent, con1, newPart2.children[ii]);
|
|
23
|
+
} else {
|
|
24
|
+
return this.SelectorConnect(scene, parent, con1, con2);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public static Convert3DPointToScreenPoint(point: THREE.Vector3, camera: THREE.Camera, width: number, height: number): THREE.Vector2 {
|
|
33
|
+
if (!point) {
|
|
34
|
+
return new THREE.Vector2();
|
|
35
|
+
}
|
|
36
|
+
const vector: THREE.Vector3 = point.clone().project(camera);
|
|
37
|
+
vector.x = (vector.x + 1) / 2 * width;
|
|
38
|
+
vector.y = -(vector.y - 1) / 2 * height;
|
|
39
|
+
return new THREE.Vector2(vector.x, vector.y);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public static CanSelectorConnect(con1: THREE.Object3D | any, con2: THREE.Object3D | any): boolean {
|
|
43
|
+
const con1Name: string = con1 instanceof THREE.Object3D ? con1.name : con1.connector;
|
|
44
|
+
const con2Name: string = con2 instanceof THREE.Object3D ? con2.name : con2.connector;
|
|
45
|
+
const sameParent: boolean = con1 instanceof THREE.Object3D && con2 instanceof THREE.Object3D ? con1.parent === con2.parent : false;
|
|
46
|
+
const parts1: string[] = con1Name.toUpperCase().split('_'),
|
|
47
|
+
parts2: string[] = con2Name.toUpperCase().split('_');
|
|
48
|
+
if (parts1.length >= 3 && parts2.length >= 3 && !sameParent) {
|
|
49
|
+
const connectable: boolean = parts1[0] === 'C' && parts2[0] === 'C' &&
|
|
50
|
+
parts1[1] === parts2[1] && parts1[2] !== parts2[2];
|
|
51
|
+
return connectable;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private static SelectorConnect(scene: THREE.Scene, parent: THREE.Object3D, con1: THREE.Object3D, con2: THREE.Object3D): boolean {
|
|
57
|
+
if (!scene || !parent) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
con1['connectedTo'] = con2.parent.name;
|
|
61
|
+
con2['connectedTo'] = con1.parent.name;
|
|
62
|
+
|
|
63
|
+
const motherRotation: THREE.Euler = new THREE.Euler(0, 0, 0);
|
|
64
|
+
const motherPosition: THREE.Vector3 = new THREE.Vector3(0, 0, 0);
|
|
65
|
+
|
|
66
|
+
scene.updateMatrixWorld(true);
|
|
67
|
+
motherRotation.copy(parent.rotation);
|
|
68
|
+
parent.getWorldPosition(motherPosition);
|
|
69
|
+
|
|
70
|
+
parent.rotation.set(0, 0, 0);
|
|
71
|
+
parent.position.set(0, 0, 0);
|
|
72
|
+
|
|
73
|
+
const con1Quat: THREE.Quaternion = new THREE.Quaternion();
|
|
74
|
+
con1.getWorldQuaternion(con1Quat);
|
|
75
|
+
const con2Quat: THREE.Quaternion = new THREE.Quaternion();
|
|
76
|
+
con2.getWorldQuaternion(con2Quat);
|
|
77
|
+
const con1QuatParent: THREE.Quaternion = new THREE.Quaternion();
|
|
78
|
+
con1.parent.getWorldQuaternion(con1QuatParent);
|
|
79
|
+
const con2QuatParent: THREE.Quaternion = new THREE.Quaternion();
|
|
80
|
+
con2.parent.getWorldQuaternion(con2QuatParent);
|
|
81
|
+
|
|
82
|
+
const rotation: THREE.Quaternion = new THREE.Quaternion().multiplyQuaternions(
|
|
83
|
+
con1Quat.invert(),
|
|
84
|
+
con2Quat
|
|
85
|
+
).multiply(
|
|
86
|
+
new THREE.Quaternion().multiplyQuaternions(
|
|
87
|
+
con1QuatParent,
|
|
88
|
+
con1QuatParent
|
|
89
|
+
)
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
con2.parent.quaternion.copy(rotation);
|
|
93
|
+
// Update because the matrix has been tempered with
|
|
94
|
+
scene.updateMatrixWorld(true);
|
|
95
|
+
|
|
96
|
+
// Move the connectors towards eachother
|
|
97
|
+
const con1Pos: THREE.Vector3 = new THREE.Vector3();
|
|
98
|
+
con1.getWorldPosition(con1Pos);
|
|
99
|
+
const con2Pos: THREE.Vector3 = new THREE.Vector3();
|
|
100
|
+
con2.getWorldPosition(con2Pos);
|
|
101
|
+
|
|
102
|
+
const move: THREE.Vector3 = con1Pos.sub(con2Pos);
|
|
103
|
+
con2.parent.position.x += move.x;
|
|
104
|
+
con2.parent.position.y += move.y;
|
|
105
|
+
con2.parent.position.z += move.z;
|
|
106
|
+
|
|
107
|
+
// reset parent's objects rotation and position
|
|
108
|
+
parent.rotation.copy(motherRotation);
|
|
109
|
+
parent.position.copy(motherPosition);
|
|
110
|
+
|
|
111
|
+
scene.updateMatrixWorld(true);
|
|
112
|
+
|
|
113
|
+
// Set the connected flag
|
|
114
|
+
con1['connected'] = true;
|
|
115
|
+
con2['connected'] = true;
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import * as THREE from "three";
|
|
2
|
+
import {ObjectUtils} from "./object.utils";
|
|
3
|
+
import {FileUtils} from "./file.utils";
|
|
4
|
+
|
|
5
|
+
export class ThreedUtils {
|
|
6
|
+
|
|
7
|
+
private _objectCache: Map<string, THREE.Object3D> = new Map();
|
|
8
|
+
|
|
9
|
+
public clearCache(): void {
|
|
10
|
+
const objs: THREE.Object3D[] = Array.from(this._objectCache.values());
|
|
11
|
+
objs.forEach(o => ObjectUtils.DisposeObject(o));
|
|
12
|
+
this._objectCache.clear();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public download3DSource(fileName: string): Promise<THREE.Object3D> {
|
|
16
|
+
return new Promise(async (resolve: Function, reject: Function) => {
|
|
17
|
+
fileName = fileName.replace('.unity3d', '');
|
|
18
|
+
let lookupFileName: string = fileName + (fileName.indexOf('.glb') < 0 ? '.glb' : '');
|
|
19
|
+
if (this._objectCache.has(lookupFileName)) {
|
|
20
|
+
resolve(this._objectCache.get(lookupFileName));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (await FileUtils.FileExists(lookupFileName, 'https://cdn1.colijn-it.nl/content43/')) {
|
|
24
|
+
this.loadGlbSource(lookupFileName).then((obj: THREE.Object3D) => {
|
|
25
|
+
this._objectCache.set(lookupFileName, obj);
|
|
26
|
+
resolve(obj);
|
|
27
|
+
});
|
|
28
|
+
} else {
|
|
29
|
+
reject(`GLB source not found! (${lookupFileName})`);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public readFileAsText(file: File): Promise<string> {
|
|
35
|
+
const fileReader = new FileReader();
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
fileReader.onload = (e: any) => resolve(e.target.result);
|
|
38
|
+
fileReader.onerror = error => reject(error);
|
|
39
|
+
fileReader.readAsText(file);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public async loadJsonModel(filePath: string): Promise<any> {
|
|
44
|
+
if (this._objectCache.has(filePath)) {
|
|
45
|
+
return Promise.resolve(this._objectCache.get(filePath));
|
|
46
|
+
}
|
|
47
|
+
const json: string = await this._readJsonFile(filePath);
|
|
48
|
+
const loader = new THREE.ObjectLoader();
|
|
49
|
+
loader.crossOrigin = 'Anonymous';
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
try {
|
|
52
|
+
const obj = loader.parse(json);
|
|
53
|
+
this._objectCache.set(filePath, obj);
|
|
54
|
+
resolve(obj);
|
|
55
|
+
} catch (e) {
|
|
56
|
+
reject(null);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public async loadJsonObjectModel(filePath: string): Promise<any> {
|
|
62
|
+
if (this._objectCache.has(filePath)) {
|
|
63
|
+
return Promise.resolve(this._objectCache.get(filePath));
|
|
64
|
+
}
|
|
65
|
+
const loader = new THREE.ObjectLoader();
|
|
66
|
+
loader.setCrossOrigin('Anonymous');
|
|
67
|
+
loader.setResourcePath('');
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
loader.load(filePath,
|
|
70
|
+
(object: any) => {
|
|
71
|
+
const obj: THREE.Object3D = (object.type === 'Scene' && object.children[0].type === 'Object3D') ? object.children[0] : object;
|
|
72
|
+
this._objectCache.set(filePath, obj);
|
|
73
|
+
resolve(obj as THREE.Object3D);
|
|
74
|
+
},
|
|
75
|
+
xhr => null,
|
|
76
|
+
error => reject(error)
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public async loadGlbModel(file: string, cleanUp: boolean = true): Promise<THREE.Object3D> {
|
|
82
|
+
if (this._objectCache.has(file)) {
|
|
83
|
+
return Promise.resolve(this._objectCache.get(file));
|
|
84
|
+
}
|
|
85
|
+
if (!window.hasOwnProperty('loadGLTF')) {
|
|
86
|
+
console.error('loadGLTF not defined in window global');
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
// const loader = new GLTFLoader();
|
|
90
|
+
const obj: THREE.Object3D = await (window as any).loadGLTF(file);
|
|
91
|
+
if (obj) {
|
|
92
|
+
this._cleanedUpObjects(obj, cleanUp);
|
|
93
|
+
this._objectCache.set(file, obj);
|
|
94
|
+
return obj;
|
|
95
|
+
} else {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public async loadGlbSource(file: string): Promise<THREE.Object3D> {
|
|
101
|
+
const obj = await this.loadGlbModel(file, false);
|
|
102
|
+
if (obj) {
|
|
103
|
+
obj.children = obj.children.filter(c => !(c instanceof THREE.Camera));
|
|
104
|
+
obj.children.sort((a, b) => a['name'] < b['name'] ? 1 : -1);
|
|
105
|
+
obj.children.forEach((c: THREE.Object3D) => {
|
|
106
|
+
c.traverse((t: THREE.Object3D) => {
|
|
107
|
+
t.visible = !t.name.toLowerCase().startsWith('c_');
|
|
108
|
+
});
|
|
109
|
+
c.visible = false;
|
|
110
|
+
});
|
|
111
|
+
if (obj.children.length === 1 && obj.children[0] instanceof THREE.Group) { // flatten children
|
|
112
|
+
const children: any = obj.children[0].children.slice();
|
|
113
|
+
obj.children.length = 0;
|
|
114
|
+
obj.children = children;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return obj;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public async textureFromUrl(url: string): Promise<any> {
|
|
121
|
+
// url = this._includeBaseUrl(url);
|
|
122
|
+
return new Promise((resolve, reject) => {
|
|
123
|
+
const textureLoader = new THREE.TextureLoader();
|
|
124
|
+
// textureLoader.setBaseU(this._settingsService.settings.assetPath);
|
|
125
|
+
textureLoader.load(url,
|
|
126
|
+
(texture: THREE.Texture) => {
|
|
127
|
+
texture.wrapT = THREE.RepeatWrapping;
|
|
128
|
+
texture.wrapS = THREE.RepeatWrapping;
|
|
129
|
+
texture.repeat.set(1, 1);
|
|
130
|
+
texture.needsUpdate = true;
|
|
131
|
+
return resolve(texture);
|
|
132
|
+
},
|
|
133
|
+
null,
|
|
134
|
+
reject
|
|
135
|
+
);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private _cleanedUpObjects(object: THREE.Object3D, cleanUp: boolean = true): THREE.Object3D {
|
|
140
|
+
let obj: THREE.Object3D = new THREE.Object3D();
|
|
141
|
+
object.children.forEach((c: THREE.Object3D) => {
|
|
142
|
+
if (!(c instanceof THREE.Camera)) {
|
|
143
|
+
if (cleanUp && c instanceof THREE.Group || (c.children.length === 1 && c.children[0] instanceof THREE.Group)) {
|
|
144
|
+
this._addGroupChildrenToObject(c, obj);
|
|
145
|
+
} else if ((cleanUp && !(c instanceof THREE.Mesh)) || (!(c instanceof THREE.Mesh) && !c.name && c.children.length > 0)) {
|
|
146
|
+
c.children.forEach((child) => {
|
|
147
|
+
child.translateY(c.position.y);
|
|
148
|
+
});
|
|
149
|
+
c.translateY(-c.position.y);
|
|
150
|
+
if (obj.children.length > 0) {
|
|
151
|
+
obj.add(c.clone());
|
|
152
|
+
} else {
|
|
153
|
+
obj = c.clone();
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
// if (c.position.y > 0 || c.position.y < 0) {
|
|
157
|
+
// c.children.forEach((child) => {
|
|
158
|
+
// child.translateY(c.position.y);
|
|
159
|
+
// });
|
|
160
|
+
// c.translateY(-c.position.y);
|
|
161
|
+
// }
|
|
162
|
+
obj.add(c.clone());
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
const boundingBox = new THREE.Box3().setFromObject(obj);
|
|
167
|
+
const bbCenterPivot = new THREE.Vector3();
|
|
168
|
+
boundingBox.getCenter(bbCenterPivot);
|
|
169
|
+
const delta: THREE.Vector3 = new THREE.Vector3().add(bbCenterPivot).negate();
|
|
170
|
+
obj.children.forEach((cc) => {
|
|
171
|
+
cc.position.add(delta);
|
|
172
|
+
});
|
|
173
|
+
return obj;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private _addGroupChildrenToObject(group: THREE.Object3D, obj: THREE.Object3D, cleanUp: boolean = true): void {
|
|
177
|
+
group.children.forEach((gc: THREE.Object3D) => {
|
|
178
|
+
if (cleanUp && gc instanceof THREE.Group) {
|
|
179
|
+
this._addGroupChildrenToObject(gc, obj);
|
|
180
|
+
} else {
|
|
181
|
+
// gc.translateX(-group.position.x);
|
|
182
|
+
gc.translateY(group.position.y);
|
|
183
|
+
// gc.translateZ(-group.position.z);
|
|
184
|
+
obj.add(gc.clone());
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private _iterateChildren(children: THREE.Object3D[], callback: (child: THREE.Mesh) => {}): void {
|
|
190
|
+
for (let i = 0; i < children.length; i++) {
|
|
191
|
+
const child: THREE.Object3D = children[i];
|
|
192
|
+
if (child instanceof THREE.Mesh) {
|
|
193
|
+
callback(child);
|
|
194
|
+
} else {
|
|
195
|
+
this._iterateChildren(child.children, callback);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private async _readJsonFile(filePath: string): Promise<any> {
|
|
201
|
+
try {
|
|
202
|
+
const response = await fetch(filePath);
|
|
203
|
+
if (!response.ok) {
|
|
204
|
+
return '';
|
|
205
|
+
} else {
|
|
206
|
+
let empty = false;
|
|
207
|
+
await response.clone().text().then((value) => {
|
|
208
|
+
if (value === '') {
|
|
209
|
+
empty = true;
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
return empty ? {} : response.json();
|
|
213
|
+
}
|
|
214
|
+
} catch (e) {
|
|
215
|
+
return '';
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import * as JSZip from 'jszip';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import {FileUtils} from "./file.utils";
|
|
4
|
+
import {VariationSettings} from "../model/variation-settings";
|
|
5
|
+
import axios from "axios";
|
|
6
|
+
|
|
7
|
+
// @dynamic
|
|
8
|
+
export class VariationUtils {
|
|
9
|
+
private static MaterialCache: Map<string, THREE.MeshStandardMaterial> = new Map();
|
|
10
|
+
|
|
11
|
+
public static async LoadVariation(assetPath: string, fileName: string): Promise<VariationSettings> {
|
|
12
|
+
if (!fileName) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
if (fileName.indexOf('ione3d') < 0) {
|
|
16
|
+
fileName = fileName.concat('.ione3d');
|
|
17
|
+
}
|
|
18
|
+
const id: string = fileName.substr(0, fileName.lastIndexOf('.ione3d'));
|
|
19
|
+
try {
|
|
20
|
+
if (!assetPath.endsWith('/')) {
|
|
21
|
+
assetPath += '/';
|
|
22
|
+
}
|
|
23
|
+
if (!window.hasOwnProperty('downloadVariation')) {
|
|
24
|
+
console.error('downloadVariation not defined in window global');
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const file: any = await (window as any).downloadVariation(`${assetPath.replace('https://cdn1.colijn-it.nl/', '')}variation/${fileName}`);
|
|
28
|
+
return await VariationUtils.GetVariationSettingsFromFile(id, file);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
const mute = err;
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// public static GetMaterialFromName(variationFolder: string, material: any): Promise<MeshStandardMaterial> {
|
|
36
|
+
// return new Promise((resolve: Function) => {
|
|
37
|
+
// if (!material) {
|
|
38
|
+
// resolve(null);
|
|
39
|
+
// }
|
|
40
|
+
// if (this.MaterialCache.has(material.name)) {
|
|
41
|
+
// resolve(this.MaterialCache.get(material.name).clone());
|
|
42
|
+
// } else {
|
|
43
|
+
// VariationUtils.LoadVariation(variationFolder, material.name, false).then((settings: VariationSettings) => {
|
|
44
|
+
// if (settings) {
|
|
45
|
+
// AssetUtils.CreateMaterialFromAsset(settings).then((materialObj: Material) => {
|
|
46
|
+
// const materialParams: MeshStandardMaterialParameters = {};
|
|
47
|
+
// materialParams.name = material.name;
|
|
48
|
+
// materialParams.map = materialObj.texture;
|
|
49
|
+
// materialParams.normalMap = materialObj.normal;
|
|
50
|
+
// materialParams.aoMap = materialObj.ao;
|
|
51
|
+
// materialParams.roughnessMap = materialObj.roughness;
|
|
52
|
+
// materialParams.metalnessMap = materialObj.metalness;
|
|
53
|
+
// materialParams.roughness = 1;
|
|
54
|
+
// materialParams.metalness = 1;
|
|
55
|
+
// const pbrMat: MeshStandardMaterial = new MeshStandardMaterial(materialParams);
|
|
56
|
+
// this.MaterialCache.set(material.name, pbrMat);
|
|
57
|
+
// resolve(pbrMat.clone());
|
|
58
|
+
// });
|
|
59
|
+
// } else {
|
|
60
|
+
// resolve(material);
|
|
61
|
+
// }
|
|
62
|
+
// });
|
|
63
|
+
// }
|
|
64
|
+
// });
|
|
65
|
+
// }
|
|
66
|
+
|
|
67
|
+
private static async GetVariationSettingsFromFile(id: string, compressedFile: any): Promise<VariationSettings> {
|
|
68
|
+
if (!compressedFile) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
const zipContent = await VariationUtils.GetZipContent(compressedFile);
|
|
72
|
+
const variationSettings: VariationSettings = new VariationSettings();
|
|
73
|
+
let index;
|
|
74
|
+
variationSettings.id = id;
|
|
75
|
+
|
|
76
|
+
if (zipContent.files['index.json']) {
|
|
77
|
+
const indexFile: string = await zipContent.files['index.json'].async('string');
|
|
78
|
+
index = JSON.parse(indexFile);
|
|
79
|
+
}
|
|
80
|
+
if (index) {
|
|
81
|
+
await VariationUtils.CreateSettingsBasedOnIndex(index, variationSettings, zipContent);
|
|
82
|
+
} else {
|
|
83
|
+
await VariationUtils.CreateSettingsBasedOnFileName(variationSettings, zipContent);
|
|
84
|
+
}
|
|
85
|
+
return variationSettings;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private static async CreateSettingsBasedOnIndex(index: any, variationSettings: VariationSettings, zipContent: any): Promise<void> {
|
|
89
|
+
if (index.normalFile) {
|
|
90
|
+
variationSettings.normal = await VariationUtils.LoadFileContentFromZip(zipContent.files[index.normalFile], index.normalFile);
|
|
91
|
+
variationSettings.normalFilename = index.normalFile;
|
|
92
|
+
}
|
|
93
|
+
if (index.aoFile) {
|
|
94
|
+
variationSettings.ao = await VariationUtils.LoadFileContentFromZip(zipContent.files[index.aoFile], index.aoFile);
|
|
95
|
+
variationSettings.aoFilename = index.aoFile;
|
|
96
|
+
}
|
|
97
|
+
if (index.metalnessFile) {
|
|
98
|
+
variationSettings.metalness = await VariationUtils.LoadFileContentFromZip(zipContent.files[index.metalnessFile], index.metalnessFile);
|
|
99
|
+
variationSettings.metalnessFilename = index.metalnessFile;
|
|
100
|
+
}
|
|
101
|
+
if (index.roughnessFile) {
|
|
102
|
+
variationSettings.roughness = await VariationUtils.LoadFileContentFromZip(zipContent.files[index.roughnessFile], index.roughnessFile);
|
|
103
|
+
variationSettings.roughnessFilename = index.roughnessFile;
|
|
104
|
+
}
|
|
105
|
+
if (index.diffuseFile) {
|
|
106
|
+
variationSettings.texture = await VariationUtils.LoadFileContentFromZip(zipContent.files[index.diffuseFile], index.diffuseFile);
|
|
107
|
+
variationSettings.textureFilename = index.diffuseFile;
|
|
108
|
+
}
|
|
109
|
+
variationSettings.settings =
|
|
110
|
+
await VariationUtils.LoadFileContentFromZip(zipContent.files['repeat.json'], 'repeat.json', false) || {};
|
|
111
|
+
Object.assign(variationSettings.settings, index.repeat);
|
|
112
|
+
variationSettings.settings.metalness = index.metalness;
|
|
113
|
+
variationSettings.settings.roughness = index.roughness;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private static async CreateSettingsBasedOnFileName(variationSettings: VariationSettings, zipContent: any): Promise<void> {
|
|
117
|
+
const allLoaded: Promise<VariationSettings>[] = [];
|
|
118
|
+
for (const fileName in zipContent.files) {
|
|
119
|
+
if (zipContent.files[fileName]) {
|
|
120
|
+
const file: any = await zipContent.files[fileName];
|
|
121
|
+
if (file.name.toLowerCase().indexOf('normal') > -1 && this.FileIsImage(file.name)) {
|
|
122
|
+
allLoaded.push(zipContent.files[fileName].async('base64').then((normalFile: any) => {
|
|
123
|
+
variationSettings.normal = `data:image/${this.GetBase64FileType(file.name)};base64,${normalFile}`;
|
|
124
|
+
variationSettings.normalFilename = fileName;
|
|
125
|
+
}));
|
|
126
|
+
} else if (file.name.toLowerCase().indexOf('ao') > -1 && this.FileIsImage(file.name)) {
|
|
127
|
+
allLoaded.push(zipContent.files[fileName].async('base64').then((aoFile: any) => {
|
|
128
|
+
variationSettings.ao = `data:image/${this.GetBase64FileType(file.name)};base64,${aoFile}`;
|
|
129
|
+
variationSettings.aoFilename = fileName;
|
|
130
|
+
}));
|
|
131
|
+
} else if (file.name.toLowerCase().indexOf('metalness') > -1 && this.FileIsImage(file.name)) {
|
|
132
|
+
allLoaded.push(zipContent.files[fileName].async('base64').then((metalnessFile: any) => {
|
|
133
|
+
variationSettings.metalness = `data:image/${this.GetBase64FileType(file.name)};base64,${metalnessFile}`;
|
|
134
|
+
variationSettings.metalnessFilename = fileName;
|
|
135
|
+
}));
|
|
136
|
+
} else if (file.name.toLowerCase().indexOf('roughness') > -1 && this.FileIsImage(file.name)) {
|
|
137
|
+
allLoaded.push(zipContent.files[fileName].async('base64').then((roughnessFile: any) => {
|
|
138
|
+
variationSettings.roughness = `data:image/${this.GetBase64FileType(file.name)};base64,${roughnessFile}`;
|
|
139
|
+
variationSettings.roughnessFilename = fileName;
|
|
140
|
+
}));
|
|
141
|
+
} else if (file.name.toLowerCase().indexOf('diffuse') > -1 && this.FileIsImage(file.name)) {
|
|
142
|
+
allLoaded.push(zipContent.files[fileName].async('base64').then((diffuseFile: any) => {
|
|
143
|
+
variationSettings.texture = `data:image/${this.GetBase64FileType(file.name)};base64,${diffuseFile}`;
|
|
144
|
+
variationSettings.textureFilename = fileName;
|
|
145
|
+
}));
|
|
146
|
+
} else if (file.name.indexOf('.jp') > -1) {
|
|
147
|
+
allLoaded.push(zipContent.files[fileName].async('base64').then((textureFile: any) => {
|
|
148
|
+
variationSettings.texture = 'data:image/jpeg;base64,' + textureFile;
|
|
149
|
+
variationSettings.textureFilename = fileName;
|
|
150
|
+
}));
|
|
151
|
+
} else if (file.name.indexOf('.json') > -1) {
|
|
152
|
+
allLoaded.push(zipContent.files[fileName].async('string').then((settingsFile: any) => {
|
|
153
|
+
const settingsFileObj = JSON.parse(settingsFile);
|
|
154
|
+
for (const key in settingsFileObj) {
|
|
155
|
+
if (settingsFileObj[key]) {
|
|
156
|
+
variationSettings.settings[key] = settingsFileObj[key];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
await Promise.all(allLoaded);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private static LoadFileContentFromZip(zip: any, fileName: string, base64: boolean = true): Promise<string> {
|
|
167
|
+
return new Promise((resolve: Function) => {
|
|
168
|
+
if (zip && fileName) {
|
|
169
|
+
zip.async(base64 ? 'base64' : 'string').then((fileContent: any) => {
|
|
170
|
+
if (base64) {
|
|
171
|
+
resolve(`data:image/${this.GetBase64FileType(fileName)};base64,${fileContent}`);
|
|
172
|
+
} else {
|
|
173
|
+
resolve(fileContent);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
} else {
|
|
177
|
+
resolve(undefined);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private static FileIsImage(fileName: string): boolean {
|
|
183
|
+
const ext: string = fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length).toLowerCase();
|
|
184
|
+
switch (ext) {
|
|
185
|
+
case 'jpg':
|
|
186
|
+
case 'jpeg':
|
|
187
|
+
case 'png':
|
|
188
|
+
case 'bmp':
|
|
189
|
+
return true;
|
|
190
|
+
default:
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private static GetBase64FileType(fileName: string): string {
|
|
196
|
+
const ext: string = fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length).toLowerCase();
|
|
197
|
+
switch (ext) {
|
|
198
|
+
case 'jpg':
|
|
199
|
+
case 'jpeg':
|
|
200
|
+
return 'jpeg';
|
|
201
|
+
case 'png':
|
|
202
|
+
return 'png';
|
|
203
|
+
case 'bmp':
|
|
204
|
+
return 'bmp';
|
|
205
|
+
default:
|
|
206
|
+
return 'jpeg';
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Get the content
|
|
211
|
+
private static async GetZipContent(file): Promise<any> {
|
|
212
|
+
const jszip = new JSZip();
|
|
213
|
+
const content = await jszip.loadAsync(file);
|
|
214
|
+
return content;
|
|
215
|
+
}
|
|
216
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compileOnSave": false,
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"baseUrl": ".",
|
|
5
|
+
"outDir": "./dist/out-tsc",
|
|
6
|
+
"sourceMap": false,
|
|
7
|
+
"declaration": false,
|
|
8
|
+
"module": "commonjs",
|
|
9
|
+
"moduleResolution": "node",
|
|
10
|
+
"emitDecoratorMetadata": true,
|
|
11
|
+
"experimentalDecorators": true,
|
|
12
|
+
"importHelpers": true,
|
|
13
|
+
"target": "es6",
|
|
14
|
+
"typeRoots": [
|
|
15
|
+
"node_modules/@types"
|
|
16
|
+
],
|
|
17
|
+
"allowJs": true,
|
|
18
|
+
"lib": [
|
|
19
|
+
"es2017",
|
|
20
|
+
"dom"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
}
|