@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
@@ -0,0 +1,340 @@
1
+ import { Vector3 } from "@babylonjs/core/Maths/math.vector.js";
2
+ import { SymGfxMode } from "@configura/web-core/dist/cm/format/cmsym/components/SymComponent.js";
3
+ import { convertLength, hashCode } from "@configura/web-utilities";
4
+ import { MESH_UNIT } from "../../nodes/CfgSymRootNode.js";
5
+ import { cmPointToVector3 } from "../../utilities/utilities3D.js";
6
+ function calculateNormals(sp, ep) {
7
+ const normal = ep.subtract(sp);
8
+ const spToEpLength = normal.length();
9
+ const normalizedNormal = normal.normalizeToNew();
10
+ return {
11
+ spToEpLength,
12
+ normal,
13
+ normalizedNormal,
14
+ };
15
+ }
16
+ /**
17
+ * How far along from sp to ep is a point on a plane crossing the sp to ep line?
18
+ * 1 means at ep, 0 means at sp.
19
+ */
20
+ export function calculateAlongness(p, sp, spToEpLength, normalizedNormal) {
21
+ const towardTp = p.subtract(sp);
22
+ return ((normalizedNormal.x * towardTp.x +
23
+ normalizedNormal.y * towardTp.y +
24
+ normalizedNormal.z * towardTp.z) /
25
+ spToEpLength);
26
+ }
27
+ /**
28
+ * Where does the point p end up given the CfgStretchDatas and the StretchMap.
29
+ * This is the same equations that Babylon uses for stretching, only Babylon does it in the
30
+ * shaders during rendering and is thus GPU accelerated.
31
+ * This version is much slower, but that is okay as long as this function is sparingly used.
32
+ */
33
+ export const toStretchedPoint = (p, stretchDatas, stretchReferenceLengthsByMeasureParamCode) => stretchDatas.reduce((acc, stretchData) => {
34
+ const referenceLength = stretchReferenceLengthsByMeasureParamCode.get(stretchData.measureParam);
35
+ if (referenceLength === undefined) {
36
+ return acc;
37
+ }
38
+ return acc.add(stretchData
39
+ .stretchPointToTwice(p)
40
+ .scale(stretchData.calculateInfluenceGivenTwiceMorphTarget(referenceLength.current)));
41
+ }, p);
42
+ function spOrEpToPoint(node) {
43
+ const trans = node.symTransform(true);
44
+ if (trans.scale.x !== 1 || trans.scale.y !== 1 || trans.scale.z !== 1) {
45
+ throw new Error("We do not expect scale on sp or ep");
46
+ }
47
+ if (trans.rot !== undefined) {
48
+ throw new Error("We do not expect rotation on sp or ep");
49
+ }
50
+ if (trans.pivot !== undefined) {
51
+ throw new Error("We do not expect pivot on sp or ep");
52
+ }
53
+ const pos = trans.pos;
54
+ if (pos === undefined) {
55
+ return new Vector3();
56
+ }
57
+ return cmPointToVector3(pos);
58
+ }
59
+ /**
60
+ * Please note that stretch is not bound to be contained between sp and ep.
61
+ *
62
+ * The thinking in the equations in CfgStretchData is that we want to support MorphTargets in an as
63
+ * efficient manner as possible. As MorphTargets work by morphing between pre-compiled meshes we
64
+ * calculate what happens if you stretch to twice the referenceLength, to make MorphTargets for
65
+ * twice the referenceLength, and then calculate how these MorphTargets should be applied to
66
+ * achieve to requested referenceLength.
67
+ *
68
+ * This adaption to MorphTargets might make the code for stretching points for other purposes look
69
+ * (be) a bit complicated. However, as a mesh often contains millions of vertices that needs to be
70
+ * moved and calculating for bounding boxes, and anchor points and such moves very few vertices,
71
+ * this is okay.
72
+ *
73
+ * Using "Twice" as the precomputed target is arbitrarily, we just chose it because it makes
74
+ * equations simple.
75
+ */
76
+ export class CfgStretchData {
77
+ /**
78
+ * It is assumed that the symNode passed really is stretch (symNodeIsStretch has been run)
79
+ * so any data missing for stretch will be an Exception.
80
+ *
81
+ * @param detailLevel
82
+ * @param measureParam Name of the parameter controlling the stretch
83
+ * @param originalReferenceLength Server-side calculated original referenceLength. We do not
84
+ * actually use this for anything, but it can be handy for debug.
85
+ * @param sp Start Point, the start anchor of the stretch line.
86
+ * @param ep End Point, the end anchor of the stretch line.
87
+ * @param spToEpLength Distance from sp to ep.
88
+ * @param originalSpToEpLength spToEpLength before transforms.
89
+ * @param normal The normal is from sp to ep and is the normal of every plane that divides this
90
+ * stretch.
91
+ * @param normalNormalized Normalized version of normal
92
+ * @param sections The slices the model is sliced into
93
+ */
94
+ constructor(detailLevel, measureParam, originalReferenceLength, sp, ep, spToEpLength, originalSpToEpLength, normal, normalNormalized, sections) {
95
+ this.detailLevel = detailLevel;
96
+ this.measureParam = measureParam;
97
+ this.originalReferenceLength = originalReferenceLength;
98
+ this.sp = sp;
99
+ this.ep = ep;
100
+ this.spToEpLength = spToEpLength;
101
+ this.originalSpToEpLength = originalSpToEpLength;
102
+ this.normal = normal;
103
+ this.normalNormalized = normalNormalized;
104
+ this.sections = sections;
105
+ /** How far along between sp and ep is p? */
106
+ this.getAlongness = (p) => calculateAlongness(p, this.sp, this.spToEpLength, this.normalNormalized);
107
+ }
108
+ static make(logger, symNode, detailLevel) {
109
+ const symMeasure = symNode.getMeasure();
110
+ if (symMeasure === undefined) {
111
+ throw new Error("No symMeasure found");
112
+ }
113
+ const { measureParam, sections, originalLength } = symMeasure;
114
+ if (measureParam === undefined) {
115
+ throw new Error("No measureParam");
116
+ }
117
+ const children = symNode.children(logger, true, true);
118
+ let sp;
119
+ let ep;
120
+ const detailLevelToDividerNameToPos = new Map();
121
+ let detailLevelNodeName3D;
122
+ let detailLevelNodeName2D;
123
+ for (const [name, node] of children) {
124
+ const symReps = node.symReps(true);
125
+ const details = symReps._details;
126
+ // We relax the requirements on detail-level a bit when it comes to sp and ep.
127
+ // If we can not find sp and/or ep that is the right detail-level we take the
128
+ // first one we found.
129
+ if (sp === undefined && name === "sp") {
130
+ sp = spOrEpToPoint(node);
131
+ }
132
+ else if (ep === undefined && name === "ep") {
133
+ ep = spOrEpToPoint(node);
134
+ }
135
+ // We only handle children which either has no detail-level or has an acceptable
136
+ // detail-level
137
+ if (details !== undefined) {
138
+ const detailMask = details.values().next().value;
139
+ if (!detailMask.includes(detailLevel)) {
140
+ continue;
141
+ }
142
+ }
143
+ if (name.startsWith("divider")) {
144
+ const nodeChildren = node.children(logger, true, true);
145
+ for (const [subLevelNodeName, subNode] of nodeChildren) {
146
+ const trans = subNode.symTransform(true);
147
+ if (trans === undefined) {
148
+ continue;
149
+ }
150
+ const pos = trans.pos;
151
+ if (pos === undefined) {
152
+ continue;
153
+ }
154
+ let dividerNameToPos = detailLevelToDividerNameToPos.get(subLevelNodeName);
155
+ if (dividerNameToPos === undefined) {
156
+ dividerNameToPos = new Map();
157
+ detailLevelToDividerNameToPos.set(subLevelNodeName, dividerNameToPos);
158
+ }
159
+ dividerNameToPos.set(name, cmPointToVector3(pos));
160
+ }
161
+ }
162
+ else if (name.startsWith("3D")) {
163
+ detailLevelNodeName3D = name;
164
+ }
165
+ else if (name.startsWith("2D")) {
166
+ detailLevelNodeName2D = name;
167
+ }
168
+ else if (name === "sp") {
169
+ sp = spOrEpToPoint(node);
170
+ }
171
+ else if (name === "ep") {
172
+ ep = spOrEpToPoint(node);
173
+ }
174
+ }
175
+ if (sp === undefined) {
176
+ throw new Error("Start point missing");
177
+ }
178
+ if (ep === undefined) {
179
+ throw new Error("End point missing");
180
+ }
181
+ if (sp.equals(ep)) {
182
+ throw new Error("Start point and End point are equal");
183
+ }
184
+ const { spToEpLength, normal, normalizedNormal } = calculateNormals(sp, ep);
185
+ if (detailLevelToDividerNameToPos.size === 0) {
186
+ throw new Error("Dividers missing");
187
+ }
188
+ if (detailLevelNodeName3D === undefined && detailLevelNodeName2D === undefined) {
189
+ throw new Error("Details level node name missing");
190
+ }
191
+ let detailLevelGfxMode = SymGfxMode.undefined;
192
+ let dividerNameToPos;
193
+ if (detailLevelNodeName3D !== undefined) {
194
+ dividerNameToPos = detailLevelToDividerNameToPos.get(detailLevelNodeName3D);
195
+ if (dividerNameToPos !== undefined) {
196
+ detailLevelGfxMode = SymGfxMode.x3D;
197
+ }
198
+ }
199
+ if (dividerNameToPos === undefined && detailLevelNodeName2D !== undefined) {
200
+ dividerNameToPos = detailLevelToDividerNameToPos.get(detailLevelNodeName2D);
201
+ if (dividerNameToPos !== undefined) {
202
+ detailLevelGfxMode = SymGfxMode.x2D;
203
+ }
204
+ }
205
+ if (dividerNameToPos === undefined) {
206
+ throw new Error("Dividers for detail level missing");
207
+ }
208
+ const processedSections = CfgStretchData.processSections(sections, detailLevel, detailLevelGfxMode, dividerNameToPos, sp, spToEpLength, normalizedNormal);
209
+ return new this(detailLevel, measureParam, originalLength, sp, ep, spToEpLength, spToEpLength, normal, normalizedNormal, processedSections);
210
+ }
211
+ static processSections(sections, detailLevel, detailLevelGfxMode, dividerNameToPos, sp, spToEpLength, normalizedNormal) {
212
+ // The morph target shall morph to making the morphed thing
213
+ // twice as long as before morph, that is, 100% increase.
214
+ // The sizeChange parameters are assumed to aggregate to 1.
215
+ // The alongness goes from 0 at sp to 1 at ep. So summing these
216
+ // should make the postStretchEndAlongness of the last one be 2.
217
+ let accumulatedSizeChange = 0;
218
+ let startAlongness = 0;
219
+ let atTwiceStartAlongness = 0;
220
+ return sections.map((section, sectionIndex) => {
221
+ const { move, sizeChange, gfxModeToDetailLevelToMultiSelector } = section;
222
+ const sectionsLength = sections.length;
223
+ if (sectionsLength < 1) {
224
+ throw new Error("At lease one section needed for stretch");
225
+ }
226
+ const first = sectionIndex === 0;
227
+ const last = sectionIndex === sectionsLength - 1;
228
+ const detailLevelToMultiSelector = gfxModeToDetailLevelToMultiSelector.get(detailLevelGfxMode);
229
+ if (detailLevelToMultiSelector === undefined) {
230
+ throw new Error("No x3D or x2D for section");
231
+ }
232
+ const multiSelector = detailLevelToMultiSelector.get(detailLevel);
233
+ if (multiSelector === undefined) {
234
+ throw new Error("No multiSelector for detail level");
235
+ }
236
+ const selectors = multiSelector.selectors;
237
+ const selectorsLength = selectors.length;
238
+ if ((first || last ? 1 : 2) !== selectorsLength) {
239
+ throw new Error("Divider plane count expected to be 1 for first or last and 2 for others");
240
+ }
241
+ let endAlongness;
242
+ if (last) {
243
+ endAlongness = 1;
244
+ }
245
+ else {
246
+ const dividerId = selectors[selectorsLength - 1].dividerId;
247
+ const pos = dividerNameToPos.get(dividerId);
248
+ if (pos === undefined) {
249
+ throw new Error("No position found for symDividerSelector");
250
+ }
251
+ endAlongness = calculateAlongness(pos, sp, spToEpLength, normalizedNormal);
252
+ }
253
+ accumulatedSizeChange += sizeChange;
254
+ const atTwiceEndAlongness = endAlongness + accumulatedSizeChange;
255
+ const result = {
256
+ last,
257
+ move,
258
+ sizeChange,
259
+ startAlongness,
260
+ endAlongness,
261
+ atTwiceTranslation: move && 0 < sectionIndex
262
+ ? atTwiceEndAlongness - endAlongness
263
+ : atTwiceStartAlongness - startAlongness,
264
+ atTwiceScale: move
265
+ ? 0
266
+ : (atTwiceEndAlongness - atTwiceStartAlongness) /
267
+ (endAlongness - startAlongness) -
268
+ 1,
269
+ };
270
+ startAlongness = endAlongness;
271
+ atTwiceStartAlongness = atTwiceEndAlongness;
272
+ return result;
273
+ });
274
+ }
275
+ get hash() {
276
+ if (this._hash === undefined) {
277
+ this._hash = hashCode(`${this.detailLevel}-${this.measureParam}-${this.sp.asArray()}-${this.ep.asArray()}-${this.originalSpToEpLength}-${this.sections.map((section) => `${section.startAlongness}-${section.endAlongness}-${section.move}-${section.sizeChange}`)}`);
278
+ }
279
+ return this._hash;
280
+ }
281
+ /**
282
+ * If the Matrix is identity the original CfgStretchData is returned.
283
+ * If not a new CfgStretchData will be created.
284
+ * sp, ep, spToEpLength, normal, normalNormalized, scale will be cloned and transformed
285
+ * The rest will still reference the original objects
286
+ */
287
+ applyMatrix(matrix) {
288
+ if (matrix.isIdentity()) {
289
+ return this;
290
+ }
291
+ const sp = Vector3.TransformCoordinates(this.sp, matrix);
292
+ const ep = Vector3.TransformCoordinates(this.ep, matrix);
293
+ const { spToEpLength, normal, normalizedNormal } = calculateNormals(sp, ep);
294
+ return new CfgStretchData(this.detailLevel, this.measureParam, this.originalReferenceLength, sp, ep, spToEpLength, this.spToEpLength, normal, normalizedNormal, this.sections);
295
+ }
296
+ /**
297
+ * Calculates the translation of a point which moves it from its original position to where it
298
+ * would be if we stretched to twice the reference length. Please note: this gives the
299
+ * translation, not the new location. The new location will be p.add(stretchPointToTwice(p))
300
+ */
301
+ stretchPointToTwice(p) {
302
+ const alongness = this.getAlongness(p);
303
+ const { sections, normal } = this;
304
+ for (const section of sections) {
305
+ const endAlongness = section.endAlongness;
306
+ if (section.last || alongness <= endAlongness) {
307
+ let translation = section.atTwiceTranslation;
308
+ if (!section.move) {
309
+ translation += (alongness - section.startAlongness) * section.atTwiceScale;
310
+ }
311
+ return normal.scale(translation);
312
+ }
313
+ }
314
+ throw new Error("Should not be possible");
315
+ }
316
+ /**
317
+ * Given a reference length calculates the influence that should be applied to a MorphTarget to
318
+ * achieve the requested length, given that these MorphTargets are constructed so that they
319
+ * will double the length when the influence 1 is applied.
320
+ *
321
+ * Please note that the referenceLength is the wanted length between sp and ep, that is, the
322
+ * stretch line. This line does not have to be span the entire model. Also, there can be
323
+ * multiple stretch lines that are not orthogonal to this, and the will also affect the actual
324
+ * size. Finally there can be transforms applied in the tree. So, the referenceLength will only
325
+ * be the actual length of the model in specific cases. However, we believe that models with
326
+ * stretch are often constructed in such a way that in real use cases the referenceLength is
327
+ * the actual real length.
328
+ */
329
+ calculateInfluenceGivenTwiceMorphTarget(referenceLength) {
330
+ const { originalSpToEpLength } = this;
331
+ if (referenceLength.length === 0) {
332
+ // CET considers 0 as a no-value and will return the stretch to the default value.
333
+ // If this changes, remove this workaround.
334
+ return 0;
335
+ }
336
+ return ((convertLength(referenceLength.length, referenceLength.unit, MESH_UNIT) -
337
+ originalSpToEpLength) /
338
+ originalSpToEpLength);
339
+ }
340
+ }
@@ -0,0 +1,17 @@
1
+ import { UVMapper } from "@configura/web-core/dist/cm/core3D/uvmapper/UVMapper";
2
+ import { Logger } from "@configura/web-utilities";
3
+ import { CfgStretchData } from "./CfgStretchData.js";
4
+ /**
5
+ * Contains positions which are where stretching to twice the referenceLength would place them.
6
+ * This is raw data for CfgMorphTarget.
7
+ */
8
+ export declare class CfgMorphTwiceStretchedGeometry {
9
+ readonly id: string;
10
+ readonly stretchData: CfgStretchData;
11
+ readonly pos: Float32Array;
12
+ readonly uvs: Float32Array | undefined;
13
+ static make(logger: Logger, uvMapper: UVMapper | undefined, stretchData: CfgStretchData, pos: Float32Array, normals: Float32Array | undefined, uvs: Float32Array | undefined): CfgMorphTwiceStretchedGeometry;
14
+ private constructor();
15
+ cloneWithSubset(indices: Uint32Array | number[]): CfgMorphTwiceStretchedGeometry;
16
+ }
17
+ //# sourceMappingURL=CfgStretchMorphGeometry.d.ts.map
@@ -0,0 +1,95 @@
1
+ import { Vector3 } from "@babylonjs/core";
2
+ import { makeIndexMap } from "../CfgGeometry.js";
3
+ /**
4
+ * CET has a built in error in how bounds are calculated. I will add 1mm to each side of the
5
+ * bounding box. This gives pronounced errors on small objects, less so on larger objects. To be
6
+ * consistent with CET we also add 1 mm. Remove this when this is corrected in CET.
7
+ */
8
+ const CET_COMPENSATION = 0.001;
9
+ /**
10
+ * Contains positions which are where stretching to twice the referenceLength would place them.
11
+ * This is raw data for CfgMorphTarget.
12
+ */
13
+ export class CfgMorphTwiceStretchedGeometry {
14
+ constructor(id, stretchData, pos, uvs) {
15
+ this.id = id;
16
+ this.stretchData = stretchData;
17
+ this.pos = pos;
18
+ this.uvs = uvs;
19
+ }
20
+ static make(logger, uvMapper, stretchData, pos, normals, uvs) {
21
+ const posLength = pos.length;
22
+ const stretchedPos = new Float32Array(posLength);
23
+ const min = [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY];
24
+ const max = [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY];
25
+ const stretchedMin = [
26
+ Number.POSITIVE_INFINITY,
27
+ Number.POSITIVE_INFINITY,
28
+ Number.POSITIVE_INFINITY,
29
+ ];
30
+ const stretchedMax = [
31
+ Number.NEGATIVE_INFINITY,
32
+ Number.NEGATIVE_INFINITY,
33
+ Number.NEGATIVE_INFINITY,
34
+ ];
35
+ const c = new Vector3();
36
+ let p = 0;
37
+ for (let i = 0; i < posLength; i += 3) {
38
+ for (let axis = 0; axis < 3; axis++) {
39
+ p = pos[i + axis];
40
+ min[axis] = Math.min(min[axis], p);
41
+ max[axis] = Math.max(max[axis], p);
42
+ }
43
+ c.fromArray(pos, i);
44
+ c.addInPlace(stretchData.stretchPointToTwice(c));
45
+ c.toArray(stretchedPos, i);
46
+ for (let axis = 0; axis < 3; axis++) {
47
+ p = stretchedPos[i + axis];
48
+ stretchedMin[axis] = Math.min(stretchedMin[axis], p);
49
+ stretchedMax[axis] = Math.max(stretchedMax[axis], p);
50
+ }
51
+ }
52
+ for (let axis = 0; axis < 3; axis++) {
53
+ min[axis] = min[axis] - CET_COMPENSATION;
54
+ max[axis] = max[axis] + CET_COMPENSATION;
55
+ stretchedMin[axis] = stretchedMin[axis] - CET_COMPENSATION;
56
+ stretchedMax[axis] = stretchedMax[axis] + CET_COMPENSATION;
57
+ }
58
+ const size = max.map((m, axis) => m - min[axis]);
59
+ const stretchedSize = stretchedMax.map((m, axis) => m - stretchedMin[axis]);
60
+ let stretchedUvs = uvs;
61
+ if (uvMapper !== undefined && normals !== undefined && uvs !== undefined) {
62
+ stretchedUvs = uvMapper.createUVCoordinatesForStretched(logger, stretchedPos, {
63
+ min: stretchedMin,
64
+ max: stretchedMax,
65
+ size: stretchedSize,
66
+ }, normals, pos, {
67
+ min,
68
+ max,
69
+ size,
70
+ }, uvs);
71
+ }
72
+ return new this(stretchData.measureParam, stretchData, stretchedPos, stretchedUvs);
73
+ }
74
+ cloneWithSubset(indices) {
75
+ const { indexMap } = makeIndexMap(indices);
76
+ const count = indexMap.size;
77
+ const thisPos = this.pos;
78
+ const newPos = new Float32Array(count * 3);
79
+ for (const [oldIndex, newIndex] of indexMap) {
80
+ newPos[newIndex * 3] = thisPos[oldIndex * 3];
81
+ newPos[newIndex * 3 + 1] = thisPos[oldIndex * 3 + 1];
82
+ newPos[newIndex * 3 + 2] = thisPos[oldIndex * 3 + 2];
83
+ }
84
+ const thisUvs = this.uvs;
85
+ let newUvs = undefined;
86
+ if (thisUvs !== undefined) {
87
+ newUvs = new Float32Array(count * 2);
88
+ for (const [oldIndex, newIndex] of indexMap) {
89
+ newUvs[newIndex * 2] = thisUvs[oldIndex * 2];
90
+ newUvs[newIndex * 2 + 1] = thisUvs[oldIndex * 2 + 1];
91
+ }
92
+ }
93
+ return new CfgMorphTwiceStretchedGeometry(this.id + ` (subset ${indices.length})`, this.stretchData, newPos, newUvs);
94
+ }
95
+ }
@@ -7,21 +7,32 @@ import { LogObservable, LogProducer } from "@configura/web-utilities";
7
7
  import { RenderEnv } from "../view/RenderEnv.js";
8
8
  import { MaterialMetaData } from "./material.js";
9
9
  import { GMAndTexture } from "./texture.js";
10
+ /**
11
+ * A wrapper around Babylon.js PBRMaterial class.
12
+ *
13
+ * Also contains logic to create light weight "variants" of the main PBRMaterial to take into
14
+ * account that CmSym allows the meshes to have properties that affects the material currently
15
+ * applied to the mesh, such as double doubled sided or flipping the textures along the y-axis.
16
+ *
17
+ * The variants are created on demand if the request to getPBRMaterial specifies properties that
18
+ * does not match the main PBRMaterial. The variants are then cached.
19
+ */
10
20
  export declare class CfgMaterial implements LogProducer {
11
21
  private _material;
12
22
  private _variants;
13
23
  isTransparent: boolean;
14
24
  logger: LogObservable;
15
25
  constructor(material: PBRMaterial, maxSimultaneousLights: number);
26
+ /** This material is supposed to be rendered as double sided by default. */
16
27
  isDoubleSided(): boolean;
17
- static fromTexture(renderEnvironment: RenderEnv, texture: Texture, sourcePath: string[]): CfgMaterial;
18
- static compileFromGm(renderEnvironment: RenderEnv, meta: MaterialMetaData, gMaterial: GMaterial3D, textures: GMAndTexture[]): CfgMaterial;
19
- static compileFromGmPBR(renderEnvironment: RenderEnv, meta: MaterialMetaData, gMaterial: GMaterialPBR, textures: GMAndTexture[], name: string): {
28
+ static makeFromTexture(renderEnvironment: RenderEnv, texture: Texture, sourcePath: string[]): CfgMaterial;
29
+ static makeFromGm(renderEnvironment: RenderEnv, meta: MaterialMetaData, gMaterial: GMaterial3D, textures: GMAndTexture[]): CfgMaterial;
30
+ static makeFromGmPBR(renderEnvironment: RenderEnv, meta: MaterialMetaData, gMaterial: GMaterialPBR, textures: GMAndTexture[], name: string): {
20
31
  material: PBRMaterial;
21
32
  transparent: boolean;
22
33
  doubleSided: boolean;
23
34
  };
24
- static compileFromGmClassic(renderEnvironment: RenderEnv, meta: MaterialMetaData, gMaterial: GMaterialClassic, textures: GMAndTexture[], name: string): {
35
+ static makeFromGmClassic(renderEnvironment: RenderEnv, meta: MaterialMetaData, gMaterial: GMaterialClassic, textures: GMAndTexture[], name: string): {
25
36
  material: PBRMaterial;
26
37
  transparent: boolean;
27
38
  doubleSided: boolean;
@@ -50,5 +61,9 @@ export declare class CfgMaterial implements LogProducer {
50
61
  */
51
62
  getPBRMaterial(doubleSided?: boolean, backThenFront?: boolean, flipTextures?: boolean): PBRMaterial;
52
63
  }
64
+ /**
65
+ * The exact changes this method makes depends on the material's separateCullingPass setting, so
66
+ * make sure make any changes to that property before calling this method.
67
+ */
53
68
  export declare function makeMaterialDoubleSided(material: PBRMaterial, doubleSided: boolean): void;
54
69
  //# sourceMappingURL=CfgMaterial.d.ts.map
@@ -16,14 +16,16 @@ function findTexture(textures, texture) {
16
16
  const DBL = 1;
17
17
  const BTF = 2;
18
18
  const FLP = 4;
19
- /// A wrapper around Babylon.js PBRMaterial class.
20
- ///
21
- /// Also contains logic to create light weight "variants" of the main PBRMaterial to take into
22
- /// account that CmSym allows the meshes to have properties that affects the material currently
23
- /// applied to the mesh, such as double doubled sided or flipping the textures along the y-axis.
24
- ///
25
- /// The variants are created on demand if the request to getPBRMaterial specifies properties that
26
- /// does not match the main PBRMaterial. The variants are then cached.
19
+ /**
20
+ * A wrapper around Babylon.js PBRMaterial class.
21
+ *
22
+ * Also contains logic to create light weight "variants" of the main PBRMaterial to take into
23
+ * account that CmSym allows the meshes to have properties that affects the material currently
24
+ * applied to the mesh, such as double doubled sided or flipping the textures along the y-axis.
25
+ *
26
+ * The variants are created on demand if the request to getPBRMaterial specifies properties that
27
+ * does not match the main PBRMaterial. The variants are then cached.
28
+ */
27
29
  export class CfgMaterial {
28
30
  constructor(material, maxSimultaneousLights) {
29
31
  this.isTransparent = false;
@@ -41,13 +43,13 @@ export class CfgMaterial {
41
43
  this._variants = new Array(8);
42
44
  this._variants[this.indexFromMaterial(material)] = material;
43
45
  }
44
- /// This material is supposed to be rendered as double sided by default
46
+ /** This material is supposed to be rendered as double sided by default. */
45
47
  isDoubleSided() {
46
48
  return !this._material.backFaceCulling;
47
49
  }
48
- static fromTexture(renderEnvironment, texture, sourcePath) {
50
+ static makeFromTexture(renderEnvironment, texture, sourcePath) {
49
51
  var _a, _b;
50
- sourcePath.push("fromTexture");
52
+ sourcePath.push("makeFromTexture");
51
53
  let name = "(Img)";
52
54
  const fileName = (_b = (_a = texture.name) === null || _a === void 0 ? void 0 : _a.split("\\").pop()) === null || _b === void 0 ? void 0 : _b.split("/").pop();
53
55
  if (fileName) {
@@ -58,9 +60,9 @@ export class CfgMaterial {
58
60
  material.roughness = 1;
59
61
  material.metallic = 0;
60
62
  // TODO Babylon: What happens if the texture has an alpha map? Compare to Three.js and CET
61
- return new CfgMaterial(material, renderEnvironment.lightRig.lightCount);
63
+ return new this(material, renderEnvironment.lightRig.lightCount);
62
64
  }
63
- static compileFromGm(renderEnvironment, meta, gMaterial, textures) {
65
+ static makeFromGm(renderEnvironment, meta, gMaterial, textures) {
64
66
  var _a, _b;
65
67
  // Use the materials given name or fallback to the materialKey, if any.
66
68
  const miscName = (_a = gMaterial.misc) === null || _a === void 0 ? void 0 : _a.get("name");
@@ -68,16 +70,16 @@ export class CfgMaterial {
68
70
  ? miscName
69
71
  : (_b = gMaterial.materialKey) !== null && _b !== void 0 ? _b : "";
70
72
  const { material, transparent, doubleSided } = gMaterial instanceof GMaterialPBR
71
- ? this.compileFromGmPBR(renderEnvironment, meta, gMaterial, textures, name)
72
- : this.compileFromGmClassic(renderEnvironment, meta, gMaterial, textures, name);
73
+ ? this.makeFromGmPBR(renderEnvironment, meta, gMaterial, textures, name)
74
+ : this.makeFromGmClassic(renderEnvironment, meta, gMaterial, textures, name);
73
75
  makeMaterialDoubleSided(material, doubleSided);
74
- const cfgMaterial = new CfgMaterial(material, renderEnvironment.lightRig.lightCount);
76
+ const cfgMaterial = new this(material, renderEnvironment.lightRig.lightCount);
75
77
  cfgMaterial.isTransparent = transparent;
76
78
  return cfgMaterial;
77
79
  }
78
- static compileFromGmPBR(renderEnvironment, meta, gMaterial, textures, name) {
80
+ static makeFromGmPBR(renderEnvironment, meta, gMaterial, textures, name) {
79
81
  var _a;
80
- meta.sourcePath.push("compileFromGmPBR");
82
+ meta.sourcePath.push("makeFromGmPBR");
81
83
  name = "(PBR) " + name;
82
84
  const { doubleSided, base, emissive, metallic, normal, occlusion, opacity, refraction, roughness, } = gMaterial;
83
85
  const material = new PBRMaterial(name, renderEnvironment.scene);
@@ -225,8 +227,8 @@ export class CfgMaterial {
225
227
  }
226
228
  return { material, transparent, doubleSided };
227
229
  }
228
- static compileFromGmClassic(renderEnvironment, meta, gMaterial, textures, name) {
229
- meta.sourcePath.push("compileFromGmClassic");
230
+ static makeFromGmClassic(renderEnvironment, meta, gMaterial, textures, name) {
231
+ meta.sourcePath.push("makeFromGmClassic");
230
232
  name = "(GM) " + name;
231
233
  const { doubleSided, redCustomProperties: redEngineCustomProperties, reflection, bump, diffuse, specular, transparency, } = gMaterial;
232
234
  const bumpTexture = findTexture(textures, bump);
@@ -397,8 +399,10 @@ export class CfgMaterial {
397
399
  return clone;
398
400
  }
399
401
  }
400
- /// The exact changes this method makes depends on the material's separateCullingPass setting, so
401
- /// make sure make any changes to that property before calling this method.
402
+ /**
403
+ * The exact changes this method makes depends on the material's separateCullingPass setting, so
404
+ * make sure make any changes to that property before calling this method.
405
+ */
402
406
  export function makeMaterialDoubleSided(material, doubleSided) {
403
407
  if (doubleSided) {
404
408
  // SeparateCullingPass breaks twoSidedLightning by not flipping the normals when
@@ -414,11 +418,13 @@ export function makeMaterialDoubleSided(material, doubleSided) {
414
418
  material.forceNormalForward = false; // Don't change the normals to front facing
415
419
  }
416
420
  }
417
- /// Clones all the textures used in the material and flips them along the y-axis by inverting the
418
- /// texture's vScale, vOffset and wRotation.
419
- ///
420
- /// @note: The flipped state is not stored in the material it self, so calling this method twice on
421
- /// the same material will undo the flip.
421
+ /**
422
+ * Clones all the textures used in the material and flips them along the y-axis by inverting the
423
+ * texture's vScale, vOffset and wRotation.
424
+ *
425
+ * @note: The flipped state is not stored in the material it self, so calling this method twice on
426
+ * the same material will undo the flip.
427
+ */
422
428
  function flipMaterialTextures(material) {
423
429
  material.albedoTexture = cloneAndFlipTexture(material.albedoTexture);
424
430
  material.bumpTexture = cloneAndFlipTexture(material.bumpTexture);
@@ -438,9 +444,11 @@ function flipMaterialTextures(material) {
438
444
  material.reflectivityTexture = cloneAndFlipTexture(material.reflectivityTexture);
439
445
  material.microSurfaceTexture = cloneAndFlipTexture(material.microSurfaceTexture);
440
446
  }
441
- /// Returns undefined if texture is undefined.
442
- /// Returns original texture if it isn't of type "Texture".
443
- /// Otherwise, Clones and flips the texture along the y-axis by inverting vScale, vOffset and wAng.
447
+ /**
448
+ * Returns undefined if texture is undefined.
449
+ * Returns original texture if it isn't of type "Texture".
450
+ * Otherwise, Clones and flips the texture along the y-axis by inverting vScale, vOffset and wAng.
451
+ */
444
452
  function cloneAndFlipTexture(texture) {
445
453
  if (texture instanceof Texture) {
446
454
  texture = texture.clone();
@@ -58,7 +58,7 @@ function mtrlSourceUrlToCachedCfgMaterial(meta, renderEnvironment, mtrl) {
58
58
  if (getFileExtension(url) !== "gm") {
59
59
  const texture = yield loadTextureFromURL(url, renderEnvironment);
60
60
  innerMeta.sourcePath.push("image");
61
- result.material = CfgMaterial.fromTexture(renderEnvironment, texture, innerMeta.sourcePath);
61
+ result.material = CfgMaterial.makeFromTexture(renderEnvironment, texture, innerMeta.sourcePath);
62
62
  return result;
63
63
  }
64
64
  const multiGMaterial = yield loadMaterialFromUrl(innerMeta.logger, url, renderEnvironment.dexManager);
@@ -82,7 +82,7 @@ function bufferToCfgMaterial(renderEnvironment, meta, mtrl) {
82
82
  }
83
83
  meta.sourcePath.push("image");
84
84
  const texture = yield loadTextureFromURL(`data:image/${fileExtension};base64,${btoa(String.fromCharCode(...new Uint8Array(buffer)))}`, renderEnvironment);
85
- return CfgMaterial.fromTexture(renderEnvironment, texture, meta.sourcePath);
85
+ return CfgMaterial.makeFromTexture(renderEnvironment, texture, meta.sourcePath);
86
86
  }
87
87
  const multiGMaterial = makeMaterialFromBuffer(meta.logger, mtrl.buffer, renderEnvironment.dexManager);
88
88
  meta.sourcePath.push("gm");
@@ -100,7 +100,7 @@ export function gMaterialToCfgMaterial(meta, renderEnvironment, gMaterial) {
100
100
  innerMeta.sourcePath.push("cache_gMaterial");
101
101
  innerMeta.gMaterial = gMaterial;
102
102
  const textures = yield getTextures(innerMeta.logger, renderEnvironment, gMaterial);
103
- const material = CfgMaterial.compileFromGm(renderEnvironment, innerMeta, gMaterial, textures);
103
+ const material = CfgMaterial.makeFromGm(renderEnvironment, innerMeta, gMaterial, textures);
104
104
  return {
105
105
  material: material,
106
106
  meta: innerMeta,