@configura/babylon-view 1.3.0-alpha.2 → 1.3.0-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/animation/coordinator/CoordinatorDropAndSpin.js +1 -1
  2. package/dist/animation/coordinator/CoordinatorPulseBounce.js +1 -1
  3. package/dist/animation/coordinator/CoordinatorPulseHighlight.js +1 -1
  4. package/dist/animation/coordinator/CoordinatorPulseInflate.js +1 -1
  5. package/dist/camera/CfgArcRotateCameraPointersInput.d.ts +16 -0
  6. package/dist/camera/CfgArcRotateCameraPointersInput.js +17 -15
  7. package/dist/geometry/CfgGeometry.d.ts +22 -5
  8. package/dist/geometry/CfgGeometry.js +131 -102
  9. package/dist/geometry/CfgMesh.d.ts +4 -1
  10. package/dist/geometry/CfgMesh.js +32 -2
  11. package/dist/geometry/stretch/CfgMorphTarget.d.ts +16 -0
  12. package/dist/geometry/stretch/CfgMorphTarget.js +65 -0
  13. package/dist/geometry/stretch/CfgStretchData.d.ts +115 -0
  14. package/dist/geometry/stretch/CfgStretchData.js +340 -0
  15. package/dist/geometry/stretch/CfgStretchMorphGeometry.d.ts +17 -0
  16. package/dist/geometry/stretch/CfgStretchMorphGeometry.js +95 -0
  17. package/dist/material/CfgMaterial.d.ts +19 -4
  18. package/dist/material/CfgMaterial.js +38 -30
  19. package/dist/material/material.js +3 -3
  20. package/dist/material/texture.js +10 -8
  21. package/dist/nodes/CfgDeferredMeshNode.d.ts +8 -1
  22. package/dist/nodes/CfgDeferredMeshNode.js +48 -18
  23. package/dist/nodes/CfgProductNode.d.ts +68 -3
  24. package/dist/nodes/CfgProductNode.js +130 -38
  25. package/dist/nodes/CfgSymNode.d.ts +14 -6
  26. package/dist/nodes/CfgSymNode.js +50 -17
  27. package/dist/nodes/CfgSymRootNode.d.ts +18 -6
  28. package/dist/nodes/CfgSymRootNode.js +62 -17
  29. package/dist/nodes/CfgTransformNode.d.ts +4 -0
  30. package/dist/nodes/CfgTransformNode.js +4 -2
  31. package/dist/utilities/CfgBoundingBox.d.ts +5 -0
  32. package/dist/utilities/CfgBoundingBox.js +18 -1
  33. package/dist/utilities/anchor/anchor.d.ts +52 -0
  34. package/dist/utilities/anchor/anchor.js +136 -0
  35. package/dist/utilities/anchor/anchorMap.d.ts +21 -0
  36. package/dist/utilities/anchor/anchorMap.js +111 -0
  37. package/dist/utilities/utilities3D.d.ts +44 -0
  38. package/dist/utilities/utilities3D.js +97 -19
  39. package/dist/utilities/utilitiesSymRootIdentifier.d.ts +3 -1
  40. package/dist/utilities/utilitiesSymRootIdentifier.js +10 -4
  41. package/dist/view/BaseView.d.ts +9 -1
  42. package/dist/view/BaseView.js +16 -10
  43. package/dist/view/RenderEnv.d.ts +6 -1
  44. package/dist/view/SingleProductDefaultCameraView.js +1 -1
  45. package/dist/view/SingleProductView.d.ts +8 -1
  46. package/dist/view/SingleProductView.js +1 -0
  47. package/package.json +5 -5
@@ -36,7 +36,7 @@ export class CoordinatorDropAndSpin extends CoordinatorNodeQueues {
36
36
  }
37
37
  static getCoordinatorCreator(config) {
38
38
  return (view, phaseObservable) => {
39
- return new CoordinatorDropAndSpin(view, phaseObservable, config || {});
39
+ return new this(view, phaseObservable, config || {});
40
40
  };
41
41
  }
42
42
  destroy() {
@@ -15,7 +15,7 @@ const defaultConfig = Object.assign(Object.assign({}, baseDefaultConfig), { heig
15
15
  export class CoordinatorPulseBounce extends CoordinatorPulse {
16
16
  static getCoordinatorCreator(config) {
17
17
  return (view, phaseObservable) => {
18
- return new CoordinatorPulseBounce(view, phaseObservable, config || {});
18
+ return new this(view, phaseObservable, config || {});
19
19
  };
20
20
  }
21
21
  constructor(view, phaseObservable, config) {
@@ -14,7 +14,7 @@ const defaultConfig = Object.assign(Object.assign({}, baseDefaultConfig), animat
14
14
  export class CoordinatorPulseHighlight extends CoordinatorPulse {
15
15
  static getCoordinatorCreator(config) {
16
16
  return (view, phaseObservable) => {
17
- return new CoordinatorPulseHighlight(view, phaseObservable, config || {});
17
+ return new this(view, phaseObservable, config || {});
18
18
  };
19
19
  }
20
20
  constructor(view, phaseObservable, config) {
@@ -14,7 +14,7 @@ const defaultConfig = Object.assign(Object.assign({}, baseDefaultConfig), { size
14
14
  export class CoordinatorPulseInflate extends CoordinatorPulse {
15
15
  static getCoordinatorCreator(config) {
16
16
  return (view, phaseObservable) => {
17
- return new CoordinatorPulseInflate(view, phaseObservable, config || {});
17
+ return new this(view, phaseObservable, config || {});
18
18
  };
19
19
  }
20
20
  constructor(view, phaseObservable, config) {
@@ -1,5 +1,21 @@
1
1
  import { ArcRotateCameraPointersInput } from "@babylonjs/core/Cameras/Inputs/arcRotateCameraPointersInput.js";
2
2
  import "@babylonjs/core/Engines/Extensions/engine.webVR.js";
3
+ /**
4
+ * This class exists to workaround a severe touch input issue that exists on at least iOS 13.4 in
5
+ * Babylon release 4.1.0 and possibly later.
6
+ *
7
+ * The main workaround is marked with "--- START OF WORKAROUND ---" in the source below.
8
+ *
9
+ * Most of the code below is copied from the Babylon source class BaseCameraPointersInput.ts,
10
+ * which ArcRotateCameraPointersInput extends. Some additional private support code from
11
+ * BaseCameraPointersInput is also included and suffixed with "CfgPatch".
12
+ *
13
+ * @warning: DO NOT do any other changes to this file! They WILL be lost when this workaround is no
14
+ * longer needed.
15
+ *
16
+ * @todo Every time the babylon module is updated to a new release, test if the workaround is still
17
+ * needed. If not, simply remove this file and update CfgOrbitalCamera.ts to not use it.
18
+ */
3
19
  export declare class CfgArcRotateCameraPointersInput extends ArcRotateCameraPointersInput {
4
20
  attachControl(noPreventDefault?: boolean): void;
5
21
  detachControl(ignored?: any): void;
@@ -1,21 +1,23 @@
1
- /// This class exists to workaround a severe touch input issue that exists on at least iOS 13.4 in
2
- /// Babylon release 4.1.0 and possibly later.
3
- ///
4
- /// The main workaround is marked with "--- START OF WORKAROUND ---" in the source below.
5
- ///
6
- /// Most of the code below is copied from the Babylon source class BaseCameraPointersInput.ts,
7
- /// which ArcRotateCameraPointersInput extends. Some additional private support code from
8
- /// BaseCameraPointersInput is also included and suffixed with "CfgPatch".
9
- ///
10
- /// WARNING: DO NOT do any other changes to this file! They WILL be lost when this workaround is no
11
- /// longer needed.
12
- ///
13
- /// TODO: Every time the babylon module is updated to a new release, test if the workaround is still
14
- /// needed. If not, simply remove this file and update CfgOrbitalCamera.ts to not use it.
15
1
  import { ArcRotateCameraPointersInput } from "@babylonjs/core/Cameras/Inputs/arcRotateCameraPointersInput.js";
16
2
  import "@babylonjs/core/Engines/Extensions/engine.webVR.js"; // isInVRExclusivePointerMode
17
- import { PointerEventTypes } from "@babylonjs/core/Events/pointerEvents.js";
3
+ import { PointerEventTypes, } from "@babylonjs/core/Events/pointerEvents.js";
18
4
  import { Tools } from "@babylonjs/core/Misc/tools.js";
5
+ /**
6
+ * This class exists to workaround a severe touch input issue that exists on at least iOS 13.4 in
7
+ * Babylon release 4.1.0 and possibly later.
8
+ *
9
+ * The main workaround is marked with "--- START OF WORKAROUND ---" in the source below.
10
+ *
11
+ * Most of the code below is copied from the Babylon source class BaseCameraPointersInput.ts,
12
+ * which ArcRotateCameraPointersInput extends. Some additional private support code from
13
+ * BaseCameraPointersInput is also included and suffixed with "CfgPatch".
14
+ *
15
+ * @warning: DO NOT do any other changes to this file! They WILL be lost when this workaround is no
16
+ * longer needed.
17
+ *
18
+ * @todo Every time the babylon module is updated to a new release, test if the workaround is still
19
+ * needed. If not, simply remove this file and update CfgOrbitalCamera.ts to not use it.
20
+ */
19
21
  export class CfgArcRotateCameraPointersInput extends ArcRotateCameraPointersInput {
20
22
  constructor() {
21
23
  super(...arguments);
@@ -1,13 +1,30 @@
1
1
  import { Geometry } from "@babylonjs/core/Meshes/geometry.js";
2
- import { Mesh } from "@babylonjs/core/Meshes/mesh.js";
3
2
  import { Scene } from "@babylonjs/core/scene.js";
4
3
  import { ATriMeshF } from "@configura/web-core/dist/cm/core3D/ATriMeshF.js";
5
- import { LogObservable, LogProducer } from "@configura/web-utilities";
4
+ import { UVMapper } from "@configura/web-core/dist/cm/core3D/uvmapper/UVMapper";
5
+ import { Logger, LogObservable, LogProducer } from "@configura/web-utilities";
6
+ import { CfgStretchData } from "./stretch/CfgStretchData.js";
7
+ import { CfgMorphTwiceStretchedGeometry } from "./stretch/CfgStretchMorphGeometry.js";
8
+ export declare function makeIndexMap(indices: Uint32Array | number[]): {
9
+ indexMap: Map<number, number>;
10
+ packedIndices: Uint32Array;
11
+ };
6
12
  export declare class CfgGeometry extends Geometry implements LogProducer {
7
- private _aTriMesh;
13
+ private readonly _ind;
14
+ private readonly _pos;
15
+ private readonly _uvs;
16
+ private readonly _nor;
17
+ private readonly _tan;
18
+ private readonly _col;
19
+ readonly morphGeometries: CfgMorphTwiceStretchedGeometry[];
8
20
  logger: LogObservable;
9
- constructor(id: string, mesh: ATriMeshF, scene: Scene, indices?: Uint32Array | number[]);
21
+ static fromATriMeshF(logger: Logger, id: string, mesh: ATriMeshF, scene: Scene, stretchDatas: CfgStretchData[], uvMapper: UVMapper | undefined): CfgGeometry;
22
+ private constructor();
23
+ shallowClone(): CfgGeometry;
24
+ /**
25
+ * Returns a new CfgGeometry containing a subset of this CfgGeometry based on the given
26
+ * indices.
27
+ */
10
28
  cloneWithSubset(indices: Uint32Array | number[]): CfgGeometry;
11
- releaseForMesh(mesh: Mesh, shouldDispose?: boolean | undefined): void;
12
29
  }
13
30
  //# sourceMappingURL=CfgGeometry.d.ts.map
@@ -1,117 +1,146 @@
1
1
  import { Geometry } from "@babylonjs/core/Meshes/geometry.js";
2
2
  import { VertexData } from "@babylonjs/core/Meshes/mesh.vertexData.js";
3
3
  import { LogObservable } from "@configura/web-utilities";
4
+ import { CfgMorphTwiceStretchedGeometry } from "./stretch/CfgStretchMorphGeometry.js";
5
+ export function makeIndexMap(indices) {
6
+ // Loop through all old index values, creating an index map between each unique old
7
+ // index value and their corresponding new index into the shorter arrays.
8
+ const indicesLength = indices.length;
9
+ const packedIndices = new Uint32Array(indicesLength);
10
+ const indexMap = new Map();
11
+ for (let i = 0; i < indicesLength; i++) {
12
+ const index = indices[i];
13
+ let newIndex = indexMap.get(index);
14
+ if (newIndex === undefined) {
15
+ newIndex = indexMap.size;
16
+ indexMap.set(index, newIndex);
17
+ }
18
+ packedIndices[i] = newIndex;
19
+ }
20
+ return { indexMap, packedIndices };
21
+ }
4
22
  export class CfgGeometry extends Geometry {
5
- constructor(id, mesh, scene, indices) {
23
+ constructor(id, scene, _ind, _pos, _uvs, _nor, _tan, _col, morphGeometries) {
6
24
  super(id, scene);
25
+ this._ind = _ind;
26
+ this._pos = _pos;
27
+ this._uvs = _uvs;
28
+ this._nor = _nor;
29
+ this._tan = _tan;
30
+ this._col = _col;
31
+ this.morphGeometries = morphGeometries;
7
32
  this.logger = new LogObservable();
8
- // Hold on to the ATriMeshF if we need to split the geometry later. It should be faster to
9
- // do it from the original data than to extract it again from the Geometry object and the
10
- // ATriMeshF seems to be kept around anyway in it's SymMesh. This is however something
11
- // that we should try to optimize further.
12
- this._aTriMesh = mesh;
13
- const hasUVs = mesh.uvs.length > 0;
14
- const hasNor = mesh.normals.length > 0;
15
- const hasCol = mesh.colors.length > 0;
16
- const hasTan = mesh.tangents.length > 0;
17
- let ind;
18
- let pos;
33
+ const vertexData = new VertexData();
34
+ vertexData.indices = _ind;
35
+ vertexData.positions = _pos;
36
+ vertexData.uvs = _uvs;
37
+ vertexData.normals = _nor;
38
+ vertexData.tangents = _tan;
39
+ vertexData.colors = _col;
40
+ this.setAllVerticesData(vertexData, false);
41
+ }
42
+ static fromATriMeshF(logger, id, mesh, scene, stretchDatas, uvMapper) {
43
+ const positions = mesh.vertices;
44
+ const normals = 0 < mesh.normals.length ? mesh.normals : undefined;
45
+ const meshUvs = mesh.uvs;
19
46
  let uvs;
20
- let nor;
21
- let tan;
22
- let col;
23
- if (indices === undefined) {
24
- ind = mesh.indices;
25
- pos = mesh.vertices;
26
- uvs = hasUVs ? mesh.uvs : null;
27
- nor = hasNor ? mesh.normals : null;
28
- tan = hasTan ? mesh.tangents : null;
29
- col = hasCol ? mesh.colors : null;
47
+ if (meshUvs.length > 0) {
48
+ // Always use pre-baked UVs from CET if they exist.
49
+ // See comment on UVMapper.createUVCoordinates for more details.
50
+ uvs = meshUvs;
30
51
  }
31
- else {
32
- const num = indices.length;
33
- // Babylon will use all vertex positions in the Geometry (not only the ones referenced
34
- // by the indices) to determine the bounding box. As such, we need to go through the
35
- // the new subset of indices and only keep the references values in each attribute.
36
- // Loop through all old index values, creating an index map between each unique old
37
- // index value and their corresponding new index into the shorter arrays.
38
- const newIndices = new Uint32Array(num);
39
- const indexMap = new Map();
40
- for (let i = 0; i < num; i++) {
41
- const index = indices[i];
42
- let newIndex = indexMap.get(index);
43
- if (newIndex === undefined) {
44
- newIndex = indexMap.size;
45
- indexMap.set(index, newIndex);
46
- }
47
- newIndices[i] = newIndex;
48
- }
49
- ind = newIndices;
50
- // The length of the indexMap tells us how many unique index value we found.
51
- const count = indexMap.size;
52
- console.log(`Creating CfgGeometry subset (${num / 3}/${mesh.indices.length / 3} triangles, ${count} unique indices)`);
53
- pos = new Float32Array(count * 3);
54
- uvs = hasUVs ? new Float32Array(count * 2) : null;
55
- nor = hasNor ? new Float32Array(count * 3) : null;
56
- tan = hasTan ? new Float32Array(count * 4) : null;
57
- col = hasCol ? new Float32Array(count * 4) : null;
58
- // Use the index map to move only the referenced used values from all the attributes
59
- // over to new smaller arrays.
60
- //
61
- // A note about the code: The code below is "unrolled" by design since it can be a lot
62
- // of indexes to process. A more compact version with an inner loop of 0..3 with some
63
- // additional if checks turned out to be up to 30 times slower in Chrome (1.5ms vs
64
- // 40ms) for the "repack" call when loading and splitting the forklift model.
65
- for (const [index, newIndex] of indexMap) {
66
- pos[newIndex * 3] = mesh.vertices[index * 3];
67
- pos[newIndex * 3 + 1] = mesh.vertices[index * 3 + 1];
68
- pos[newIndex * 3 + 2] = mesh.vertices[index * 3 + 2];
69
- if (uvs) {
70
- uvs[newIndex * 2] = mesh.uvs[index * 2];
71
- uvs[newIndex * 2 + 1] = mesh.uvs[index * 2 + 1];
72
- }
73
- if (nor) {
74
- nor[newIndex * 3] = mesh.normals[index * 3];
75
- nor[newIndex * 3 + 1] = mesh.normals[index * 3 + 1];
76
- nor[newIndex * 3 + 2] = mesh.normals[index * 3 + 2];
77
- }
78
- if (tan) {
79
- tan[newIndex * 4] = mesh.tangents[index * 4];
80
- tan[newIndex * 4 + 1] = mesh.tangents[index * 4 + 1];
81
- tan[newIndex * 4 + 2] = mesh.tangents[index * 4 + 2];
82
- tan[newIndex * 4 + 3] = mesh.tangents[index * 4 + 3];
83
- }
84
- if (col) {
85
- col[newIndex * 4] = mesh.colors[index * 4];
86
- col[newIndex * 4 + 1] = mesh.colors[index * 4 + 1];
87
- col[newIndex * 4 + 2] = mesh.colors[index * 4 + 2];
88
- col[newIndex * 4 + 3] = mesh.colors[index * 4 + 3];
89
- }
52
+ else if (uvMapper !== undefined && normals !== undefined) {
53
+ const min = [
54
+ Number.POSITIVE_INFINITY,
55
+ Number.POSITIVE_INFINITY,
56
+ Number.POSITIVE_INFINITY,
57
+ ];
58
+ const max = [
59
+ Number.NEGATIVE_INFINITY,
60
+ Number.NEGATIVE_INFINITY,
61
+ Number.NEGATIVE_INFINITY,
62
+ ];
63
+ // Calculate the bounds for each axis based on vertex positions
64
+ let p = 0;
65
+ let axis = 0;
66
+ for (let i = 0; i < positions.length; i++) {
67
+ p = positions[i];
68
+ axis = i % 3; // x, y, z
69
+ min[axis] = Math.min(min[axis], p);
70
+ max[axis] = Math.max(max[axis], p);
90
71
  }
72
+ const size = max.map((m, axis) => m - min[axis]);
73
+ uvs = uvMapper.createUVCoordinates(logger, positions, {
74
+ min,
75
+ max,
76
+ size,
77
+ }, normals);
91
78
  }
92
- const vertexData = new VertexData();
93
- vertexData.indices = ind;
94
- vertexData.positions = pos;
95
- vertexData.uvs = uvs;
96
- vertexData.normals = nor;
97
- vertexData.tangents = tan;
98
- vertexData.colors = col;
99
- this.setAllVerticesData(vertexData, false);
79
+ else if (uvMapper !== undefined) {
80
+ logger.warn("Mesh uses UV-mapper without mesh normals which is not supported");
81
+ }
82
+ return new this(id, scene, mesh.indices, positions, uvs !== undefined && uvs.length > 0 ? uvs : null, normals || null, mesh.tangents.length > 0 ? mesh.tangents : null, mesh.colors.length > 0 ? mesh.colors : null, stretchDatas.map((stretchData) => CfgMorphTwiceStretchedGeometry.make(logger, uvMapper, stretchData, positions, normals, uvs)));
100
83
  }
101
- /// Returns a new CfgGeometry containing a subset of this CfgGeometry based on the given indices
102
- cloneWithSubset(indices) {
103
- return new CfgGeometry(this.id + ` (subset ${indices.length})`, this._aTriMesh, this.getScene(), indices);
84
+ shallowClone() {
85
+ return new CfgGeometry(this.id, this.getScene(), this._ind, this._pos, this._uvs, this._nor, this._tan, this._col, this.morphGeometries);
104
86
  }
105
- /* This is a hack to prevent this Geometry subclass to get automatically disposed
106
- * when the last associated mesh is disposed, for example when changing options back and
107
- * forth.
108
- *
109
- * IIRC we are using a geometry cache which is probably holding on firmly to this instance
110
- * and reusing it for the next time we need the same mesh. If that cache is ever cleared,
111
- * it should also dispose the geometry inside it.
112
- *
87
+ /**
88
+ * Returns a new CfgGeometry containing a subset of this CfgGeometry based on the given
89
+ * indices.
113
90
  */
114
- releaseForMesh(mesh, shouldDispose) {
115
- super.releaseForMesh(mesh, false);
91
+ cloneWithSubset(indices) {
92
+ const thisInd = this._ind;
93
+ const thisPos = this._pos;
94
+ const thisUvs = this._uvs;
95
+ const thisNor = this._nor;
96
+ const thisCol = this._col;
97
+ const thisTan = this._tan;
98
+ const indicesLength = indices.length;
99
+ // Babylon will use all vertex positions in the Geometry (not only the ones referenced
100
+ // by the indices) to determine the bounding box. As such, we need to go through the
101
+ // the new subset of indices and only keep the references values in each attribute.
102
+ const { indexMap, packedIndices: ind } = makeIndexMap(indices);
103
+ // The length of the indexMap tells us how many unique index value we found.
104
+ const count = indexMap.size;
105
+ console.log(`Creating CfgGeometry subset (${indicesLength / 3}/${thisInd.length / 3} triangles, ${count} unique indices)`);
106
+ const pos = new Float32Array(count * 3);
107
+ const uvs = thisUvs && new Float32Array(count * 2);
108
+ const nor = thisNor && new Float32Array(count * 3);
109
+ const tan = thisTan && new Float32Array(count * 4);
110
+ const col = thisCol && new Float32Array(count * 4);
111
+ // Use the index map to move only the referenced used values from all the attributes
112
+ // over to new smaller arrays.
113
+ //
114
+ // A note about the code: The code below is "unrolled" by design since it can be a lot
115
+ // of indexes to process. A more compact version with an inner loop of 0..3 with some
116
+ // additional if checks turned out to be up to 30 times slower in Chrome (1.5ms vs
117
+ // 40ms) for the "repack" call when loading and splitting the forklift model.
118
+ for (const [oldIndex, newIndex] of indexMap) {
119
+ pos[newIndex * 3] = thisPos[oldIndex * 3];
120
+ pos[newIndex * 3 + 1] = thisPos[oldIndex * 3 + 1];
121
+ pos[newIndex * 3 + 2] = thisPos[oldIndex * 3 + 2];
122
+ if (uvs !== null && thisUvs !== null) {
123
+ uvs[newIndex * 2] = thisUvs[oldIndex * 2];
124
+ uvs[newIndex * 2 + 1] = thisUvs[oldIndex * 2 + 1];
125
+ }
126
+ if (nor !== null && thisNor !== null) {
127
+ nor[newIndex * 3] = thisNor[oldIndex * 3];
128
+ nor[newIndex * 3 + 1] = thisNor[oldIndex * 3 + 1];
129
+ nor[newIndex * 3 + 2] = thisNor[oldIndex * 3 + 2];
130
+ }
131
+ if (tan !== null && thisTan !== null) {
132
+ tan[newIndex * 4] = thisTan[oldIndex * 4];
133
+ tan[newIndex * 4 + 1] = thisTan[oldIndex * 4 + 1];
134
+ tan[newIndex * 4 + 2] = thisTan[oldIndex * 4 + 2];
135
+ tan[newIndex * 4 + 3] = thisTan[oldIndex * 4 + 3];
136
+ }
137
+ if (col !== null && thisCol !== null) {
138
+ col[newIndex * 4] = thisCol[oldIndex * 4];
139
+ col[newIndex * 4 + 1] = thisCol[oldIndex * 4 + 1];
140
+ col[newIndex * 4 + 2] = thisCol[oldIndex * 4 + 2];
141
+ col[newIndex * 4 + 3] = thisCol[oldIndex * 4 + 3];
142
+ }
143
+ }
144
+ return new CfgGeometry(this.id + ` (subset ${indices.length})`, this.getScene(), ind, pos, uvs, nor, tan, col, this.morphGeometries.map((morphTarget) => morphTarget.cloneWithSubset(indices)));
116
145
  }
117
146
  }
@@ -1,8 +1,11 @@
1
1
  import { PBRMaterial } from "@babylonjs/core/Materials/PBR/pbrMaterial.js";
2
2
  import { Mesh } from "@babylonjs/core/Meshes/mesh.js";
3
+ import { CfgProductNode } from "../nodes/CfgProductNode.js";
3
4
  import { RenderEnv } from "../view/RenderEnv.js";
4
5
  import { CfgGeometry } from "./CfgGeometry.js";
5
6
  export declare class CfgMesh extends Mesh {
6
- constructor(name: string, renderEnvironment: RenderEnv, geometry: CfgGeometry, material: PBRMaterial);
7
+ private readonly _morphTargets;
8
+ constructor(name: string, renderEnvironment: RenderEnv, cfgProductNodeParent: CfgProductNode, geometry: CfgGeometry, material: PBRMaterial);
9
+ refreshStretch(): void;
7
10
  }
8
11
  //# sourceMappingURL=CfgMesh.d.ts.map
@@ -1,8 +1,38 @@
1
1
  import { Mesh } from "@babylonjs/core/Meshes/mesh.js";
2
+ import { MorphTargetManager } from "@babylonjs/core/Morph/morphTargetManager.js";
3
+ import { CfgMorphTarget } from "./stretch/CfgMorphTarget.js";
2
4
  export class CfgMesh extends Mesh {
3
- constructor(name, renderEnvironment, geometry, material) {
5
+ constructor(name, renderEnvironment, cfgProductNodeParent, geometry, material) {
4
6
  super(name, renderEnvironment.scene);
5
- geometry.applyToMesh(this);
7
+ this._morphTargets = [];
8
+ // Cloning is a hack to handle two things: the Geometry subclass getting automatically
9
+ // disposed when the last associated mesh is disposed, for example when changing options
10
+ // back and forth. This way every CfgMesh has it's own instance so we do not need to worry
11
+ // about it being disposed.
12
+ // The other issue being handled is the a short circuit in Babylon between CfgMeshes when
13
+ // using stretch. Meshes using the same CfgGeometry are affected when one of them is unloaded.
14
+ // IIRC we are using a geometry cache which is probably holding on firmly to this instance
15
+ // and reusing it for the next time we need the same mesh. If that cache is ever cleared,
16
+ // it should also dispose the geometry inside it.
17
+ const cloneGeometry = geometry.shallowClone();
18
+ cloneGeometry.applyToMesh(this);
6
19
  this.material = material;
20
+ const morphGeometries = cloneGeometry.morphGeometries;
21
+ if (morphGeometries.length !== 0) {
22
+ const manager = new MorphTargetManager();
23
+ manager.enableNormalMorphing = false;
24
+ manager.enableTangentMorphing = false;
25
+ this.morphTargetManager = manager;
26
+ for (const morphGeometry of morphGeometries) {
27
+ const morphTarget = new CfgMorphTarget(morphGeometry, cfgProductNodeParent);
28
+ this._morphTargets.push(morphTarget);
29
+ manager.addTarget(morphTarget);
30
+ }
31
+ }
32
+ }
33
+ refreshStretch() {
34
+ for (const morphTarget of this._morphTargets) {
35
+ morphTarget.refreshStretch();
36
+ }
7
37
  }
8
38
  }
@@ -0,0 +1,16 @@
1
+ import { MorphTarget } from "@babylonjs/core/Morph/morphTarget.js";
2
+ import { CfgProductNode } from "../../nodes/CfgProductNode.js";
3
+ import { CfgStretchData } from "./CfgStretchData.js";
4
+ import { CfgMorphTwiceStretchedGeometry } from "./CfgStretchMorphGeometry.js";
5
+ /**
6
+ * Contains geometry which when added to the original geometry will move the vertices to where
7
+ * they should be when the stretchReference length doubles.
8
+ */
9
+ export declare class CfgMorphTarget extends MorphTarget {
10
+ private _morphGeometry;
11
+ readonly cfgProductNodeParent: CfgProductNode;
12
+ constructor(_morphGeometry: CfgMorphTwiceStretchedGeometry, cfgProductNodeParent: CfgProductNode);
13
+ get stretchData(): CfgStretchData;
14
+ refreshStretch(): void;
15
+ }
16
+ //# sourceMappingURL=CfgMorphTarget.d.ts.map
@@ -0,0 +1,65 @@
1
+ import { MorphTarget } from "@babylonjs/core/Morph/morphTarget.js";
2
+ /**
3
+ * Contains geometry which when added to the original geometry will move the vertices to where
4
+ * they should be when the stretchReference length doubles.
5
+ */
6
+ export class CfgMorphTarget extends MorphTarget {
7
+ constructor(_morphGeometry, cfgProductNodeParent) {
8
+ super(_morphGeometry.id);
9
+ this._morphGeometry = _morphGeometry;
10
+ this.cfgProductNodeParent = cfgProductNodeParent;
11
+ const { pos, uvs } = _morphGeometry;
12
+ this.setPositions(pos);
13
+ if (uvs !== undefined) {
14
+ this.setUVs(uvs);
15
+ }
16
+ this.refreshStretch();
17
+ }
18
+ get stretchData() {
19
+ return this._morphGeometry.stretchData;
20
+ }
21
+ refreshStretch() {
22
+ const stretchReferenceLengthsByMeasureParamCode = this.cfgProductNodeParent.product.configuration._internal
23
+ .stretchReferenceLengthsByMeasureParamCode;
24
+ const referenceLength = stretchReferenceLengthsByMeasureParamCode.get(this.stretchData.measureParam);
25
+ const influence = referenceLength === undefined
26
+ ? 0
27
+ : this.stretchData.calculateInfluenceGivenTwiceMorphTarget(referenceLength.current);
28
+ if (this.influence === influence) {
29
+ return;
30
+ }
31
+ /* WORKAROUND for render failure
32
+ *
33
+ * Setting an influence to 0 (zero) will actually remove the MorphTarget from the list of
34
+ * "active" MorphTargets in the MorphTargetManager in Babylon.js.
35
+ *
36
+ * This is probably an performance optimization since it means less calculations has to be
37
+ * done the shaders. Another reason could be a workaround for a technical limitation in
38
+ * WebGL1 that limits the maximum number of MorphTargets in Babylon.js:
39
+ * https://doc.babylonjs.com/divingDeeper/mesh/morphTargets#limitations
40
+ *
41
+ * For some reason this causes problems for us, at least with the combination of Windows 10
42
+ * + Chromium + Babylon 4.2, in where disabling a previous active MorphTarget (i.e. setting
43
+ * influence to 0) will cause the first frame in BaseView.BrowserTick to fail to render,
44
+ * ending up an empty frame with the following error in the console:
45
+ *
46
+ * WebGL: INVALID_OPERATION: drawElements: no buffer is bound to enabled attribute
47
+ * ThinEngine.drawElementsType @ thinEngine.ts:1995
48
+ * Mesh._draw @ mesh.ts:1542
49
+ * Mesh._processRendering @ mesh.ts:1740
50
+ * Mesh.render @ mesh.ts:1930
51
+ * [...]
52
+ *
53
+ * The theory we have is that the change of active MorphTargets needs some time or other
54
+ * event to properly settle in, perhaps due to it changing the shaders. Digging in the
55
+ * Babylon.js source code has not reveled anything useful so far.
56
+ *
57
+ * As a workaround, set the influence to Number.MIN_VALUE (smallest non-zero value) instead
58
+ * of 0. This keeps it the actual influence as zero in practice, but keeps the MorphTarget
59
+ * active and no longer triggers the render failure.
60
+ *
61
+ * TODO: Revisit this when we switch to a new major release of Babylon.js, 5.0 is next.
62
+ */
63
+ this.influence = influence === 0 ? Number.MIN_VALUE : influence;
64
+ }
65
+ }
@@ -0,0 +1,115 @@
1
+ import { Matrix, Vector3 } from "@babylonjs/core/Maths/math.vector.js";
2
+ import { LengthValue, StretchMap } from "@configura/web-api";
3
+ import { SymNode } from "@configura/web-core/dist/cm/format/cmsym/SymNode.js";
4
+ import { DetailLevel } from "@configura/web-core/dist/cm/geometry/DetailMask.js";
5
+ import { Logger } from "@configura/web-utilities";
6
+ /**
7
+ * How far along from sp to ep is a point on a plane crossing the sp to ep line?
8
+ * 1 means at ep, 0 means at sp.
9
+ */
10
+ export declare function calculateAlongness(p: Vector3, sp: Vector3, spToEpLength: number, normalizedNormal: Vector3): number;
11
+ /**
12
+ * Where does the point p end up given the CfgStretchDatas and the StretchMap.
13
+ * This is the same equations that Babylon uses for stretching, only Babylon does it in the
14
+ * shaders during rendering and is thus GPU accelerated.
15
+ * This version is much slower, but that is okay as long as this function is sparingly used.
16
+ */
17
+ export declare const toStretchedPoint: (p: Vector3, stretchDatas: CfgStretchData[], stretchReferenceLengthsByMeasureParamCode: StretchMap) => Vector3;
18
+ /**
19
+ * The end of a section is the beginning of the next.
20
+ * Start/end are redundant, but both are kept for ease of access.
21
+ * The first section will have startAlongness 0. This does not mean that things can't be outside.
22
+ * The start section should be thought of as endless in the sp-direction.
23
+ * The last section will have endAlongness 0. This does not mean that things can't be outside.
24
+ * The end section should be thought of as endless in the ep-direction.
25
+ */
26
+ export declare type StretchSection = {
27
+ last: boolean;
28
+ sizeChange: number;
29
+ move: boolean;
30
+ startAlongness: number;
31
+ endAlongness: number;
32
+ atTwiceTranslation: number;
33
+ atTwiceScale: number;
34
+ };
35
+ /**
36
+ * Please note that stretch is not bound to be contained between sp and ep.
37
+ *
38
+ * The thinking in the equations in CfgStretchData is that we want to support MorphTargets in an as
39
+ * efficient manner as possible. As MorphTargets work by morphing between pre-compiled meshes we
40
+ * calculate what happens if you stretch to twice the referenceLength, to make MorphTargets for
41
+ * twice the referenceLength, and then calculate how these MorphTargets should be applied to
42
+ * achieve to requested referenceLength.
43
+ *
44
+ * This adaption to MorphTargets might make the code for stretching points for other purposes look
45
+ * (be) a bit complicated. However, as a mesh often contains millions of vertices that needs to be
46
+ * moved and calculating for bounding boxes, and anchor points and such moves very few vertices,
47
+ * this is okay.
48
+ *
49
+ * Using "Twice" as the precomputed target is arbitrarily, we just chose it because it makes
50
+ * equations simple.
51
+ */
52
+ export declare class CfgStretchData {
53
+ readonly detailLevel: DetailLevel;
54
+ readonly measureParam: string;
55
+ readonly originalReferenceLength: number | undefined;
56
+ readonly sp: Vector3;
57
+ readonly ep: Vector3;
58
+ readonly spToEpLength: number;
59
+ readonly originalSpToEpLength: number;
60
+ readonly normal: Vector3;
61
+ readonly normalNormalized: Vector3;
62
+ readonly sections: StretchSection[];
63
+ static make(logger: Logger, symNode: SymNode, detailLevel: DetailLevel): CfgStretchData;
64
+ private static processSections;
65
+ /**
66
+ * It is assumed that the symNode passed really is stretch (symNodeIsStretch has been run)
67
+ * so any data missing for stretch will be an Exception.
68
+ *
69
+ * @param detailLevel
70
+ * @param measureParam Name of the parameter controlling the stretch
71
+ * @param originalReferenceLength Server-side calculated original referenceLength. We do not
72
+ * actually use this for anything, but it can be handy for debug.
73
+ * @param sp Start Point, the start anchor of the stretch line.
74
+ * @param ep End Point, the end anchor of the stretch line.
75
+ * @param spToEpLength Distance from sp to ep.
76
+ * @param originalSpToEpLength spToEpLength before transforms.
77
+ * @param normal The normal is from sp to ep and is the normal of every plane that divides this
78
+ * stretch.
79
+ * @param normalNormalized Normalized version of normal
80
+ * @param sections The slices the model is sliced into
81
+ */
82
+ private constructor();
83
+ private _hash;
84
+ get hash(): number;
85
+ /** How far along between sp and ep is p? */
86
+ getAlongness: (p: Vector3) => number;
87
+ /**
88
+ * If the Matrix is identity the original CfgStretchData is returned.
89
+ * If not a new CfgStretchData will be created.
90
+ * sp, ep, spToEpLength, normal, normalNormalized, scale will be cloned and transformed
91
+ * The rest will still reference the original objects
92
+ */
93
+ applyMatrix(matrix: Matrix): CfgStretchData;
94
+ /**
95
+ * Calculates the translation of a point which moves it from its original position to where it
96
+ * would be if we stretched to twice the reference length. Please note: this gives the
97
+ * translation, not the new location. The new location will be p.add(stretchPointToTwice(p))
98
+ */
99
+ stretchPointToTwice(p: Vector3): Vector3;
100
+ /**
101
+ * Given a reference length calculates the influence that should be applied to a MorphTarget to
102
+ * achieve the requested length, given that these MorphTargets are constructed so that they
103
+ * will double the length when the influence 1 is applied.
104
+ *
105
+ * Please note that the referenceLength is the wanted length between sp and ep, that is, the
106
+ * stretch line. This line does not have to be span the entire model. Also, there can be
107
+ * multiple stretch lines that are not orthogonal to this, and the will also affect the actual
108
+ * size. Finally there can be transforms applied in the tree. So, the referenceLength will only
109
+ * be the actual length of the model in specific cases. However, we believe that models with
110
+ * stretch are often constructed in such a way that in real use cases the referenceLength is
111
+ * the actual real length.
112
+ */
113
+ calculateInfluenceGivenTwiceMorphTarget(referenceLength: LengthValue): number;
114
+ }
115
+ //# sourceMappingURL=CfgStretchData.d.ts.map