@configura/babylon-view 1.4.0-alpha.4 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +1 -1
  3. package/dist/animation/AnimatableObject.d.ts +8 -8
  4. package/dist/animation/AnimatableObject.js +3 -3
  5. package/dist/animation/animator/Animator.d.ts +33 -33
  6. package/dist/animation/animator/Animator.js +58 -58
  7. package/dist/animation/animator/AnimatorEasing.d.ts +16 -16
  8. package/dist/animation/animator/AnimatorEasing.js +31 -31
  9. package/dist/animation/animator/AnimatorEasingMatrix.d.ts +14 -14
  10. package/dist/animation/animator/AnimatorEasingMatrix.js +16 -16
  11. package/dist/animation/animator/AnimatorHighlight.d.ts +16 -16
  12. package/dist/animation/animator/AnimatorHighlight.js +32 -32
  13. package/dist/animation/animator/AnimatorPointToPoint.d.ts +8 -8
  14. package/dist/animation/animator/AnimatorPointToPoint.js +14 -14
  15. package/dist/animation/animator/AnimatorQueue.d.ts +13 -13
  16. package/dist/animation/animator/AnimatorQueue.js +57 -57
  17. package/dist/animation/animator/AnimatorScale.d.ts +8 -8
  18. package/dist/animation/animator/AnimatorScale.js +13 -13
  19. package/dist/animation/animator/AnimatorSpin.d.ts +10 -10
  20. package/dist/animation/animator/AnimatorSpin.js +13 -13
  21. package/dist/animation/animator/EasingFunctions.d.ts +35 -35
  22. package/dist/animation/animator/EasingFunctions.js +137 -137
  23. package/dist/animation/coordinator/Coordinator.d.ts +28 -28
  24. package/dist/animation/coordinator/Coordinator.js +47 -47
  25. package/dist/animation/coordinator/CoordinatorDropAndSpin.d.ts +22 -22
  26. package/dist/animation/coordinator/CoordinatorDropAndSpin.js +133 -133
  27. package/dist/animation/coordinator/CoordinatorIdentity.d.ts +11 -11
  28. package/dist/animation/coordinator/CoordinatorIdentity.js +12 -12
  29. package/dist/animation/coordinator/CoordinatorNodeQueues.d.ts +18 -18
  30. package/dist/animation/coordinator/CoordinatorNodeQueues.js +50 -50
  31. package/dist/animation/coordinator/CoordinatorPulse.d.ts +21 -21
  32. package/dist/animation/coordinator/CoordinatorPulse.js +47 -47
  33. package/dist/animation/coordinator/CoordinatorPulseBounce.d.ts +14 -14
  34. package/dist/animation/coordinator/CoordinatorPulseBounce.js +40 -40
  35. package/dist/animation/coordinator/CoordinatorPulseHighlight.d.ts +13 -13
  36. package/dist/animation/coordinator/CoordinatorPulseHighlight.js +34 -34
  37. package/dist/animation/coordinator/CoordinatorPulseInflate.d.ts +14 -14
  38. package/dist/animation/coordinator/CoordinatorPulseInflate.js +30 -30
  39. package/dist/camera/CameraCreator.d.ts +5 -5
  40. package/dist/camera/CameraCreator.js +4 -4
  41. package/dist/camera/CfgArcRotateCameraPointersInput.d.ts +26 -26
  42. package/dist/camera/CfgArcRotateCameraPointersInput.js +264 -264
  43. package/dist/camera/CfgOrbitalCamera.d.ts +68 -68
  44. package/dist/camera/CfgOrbitalCamera.js +250 -250
  45. package/dist/camera/CfgOrbitalCameraControlProps.d.ts +6 -6
  46. package/dist/camera/CfgOrbitalCameraControlProps.js +3 -3
  47. package/dist/camera/GradingApplier.d.ts +3 -3
  48. package/dist/camera/GradingApplier.js +48 -48
  49. package/dist/engine/EngineCreator.d.ts +3 -3
  50. package/dist/engine/EngineCreator.js +10 -10
  51. package/dist/geometry/CfgGeometry.d.ts +29 -29
  52. package/dist/geometry/CfgGeometry.js +146 -146
  53. package/dist/geometry/CfgMesh.d.ts +10 -10
  54. package/dist/geometry/CfgMesh.js +38 -38
  55. package/dist/geometry/geoSplitter.d.ts +8 -8
  56. package/dist/geometry/geoSplitter.js +192 -192
  57. package/dist/geometry/stretch/CfgMorphTarget.d.ts +15 -15
  58. package/dist/geometry/stretch/CfgMorphTarget.js +65 -65
  59. package/dist/geometry/stretch/CfgStretchData.d.ts +116 -114
  60. package/dist/geometry/stretch/CfgStretchData.js +346 -340
  61. package/dist/geometry/stretch/CfgStretchMorphGeometry.d.ts +16 -16
  62. package/dist/geometry/stretch/CfgStretchMorphGeometry.js +95 -95
  63. package/dist/index.d.ts +13 -13
  64. package/dist/index.js +13 -13
  65. package/dist/light/CfgDirectionalLight.d.ts +8 -8
  66. package/dist/light/CfgDirectionalLight.js +18 -18
  67. package/dist/light/CfgHemisphericLight.d.ts +7 -7
  68. package/dist/light/CfgHemisphericLight.js +17 -17
  69. package/dist/light/CfgPointLight.d.ts +8 -8
  70. package/dist/light/CfgPointLight.js +18 -18
  71. package/dist/light/DefaultLightRig.d.ts +19 -19
  72. package/dist/light/DefaultLightRig.js +77 -77
  73. package/dist/light/LightRigCreator.d.ts +9 -9
  74. package/dist/light/LightRigCreator.js +3 -3
  75. package/dist/material/CfgMaterial.d.ts +68 -68
  76. package/dist/material/CfgMaterial.js +482 -482
  77. package/dist/material/DummyMaterialCreator.d.ts +4 -4
  78. package/dist/material/DummyMaterialCreator.js +15 -15
  79. package/dist/material/material.d.ts +18 -18
  80. package/dist/material/material.js +128 -128
  81. package/dist/material/texture.d.ts +14 -14
  82. package/dist/material/texture.js +306 -306
  83. package/dist/nodes/CfgContentRootNode.d.ts +19 -19
  84. package/dist/nodes/CfgContentRootNode.js +75 -75
  85. package/dist/nodes/CfgDeferredMeshNode.d.ts +55 -55
  86. package/dist/nodes/CfgDeferredMeshNode.js +377 -377
  87. package/dist/nodes/CfgProductNode.d.ts +126 -126
  88. package/dist/nodes/CfgProductNode.js +578 -578
  89. package/dist/nodes/CfgSymNode.d.ts +50 -50
  90. package/dist/nodes/CfgSymNode.js +249 -249
  91. package/dist/nodes/CfgSymRootNode.d.ts +45 -45
  92. package/dist/nodes/CfgSymRootNode.js +220 -220
  93. package/dist/nodes/CfgTransformNode.d.ts +33 -33
  94. package/dist/nodes/CfgTransformNode.js +83 -83
  95. package/dist/scene/SceneCreator.d.ts +6 -6
  96. package/dist/scene/SceneCreator.js +22 -22
  97. package/dist/utilities/CfgBoundingBox.d.ts +21 -21
  98. package/dist/utilities/CfgBoundingBox.js +81 -81
  99. package/dist/utilities/anchor/anchor.d.ts +55 -51
  100. package/dist/utilities/anchor/anchor.js +140 -136
  101. package/dist/utilities/anchor/anchorMap.d.ts +20 -20
  102. package/dist/utilities/anchor/anchorMap.js +111 -111
  103. package/dist/utilities/utilities3D.d.ts +70 -70
  104. package/dist/utilities/utilities3D.js +265 -265
  105. package/dist/utilities/utilitiesColor.d.ts +18 -18
  106. package/dist/utilities/utilitiesColor.js +48 -48
  107. package/dist/utilities/utilitiesImage.d.ts +6 -6
  108. package/dist/utilities/utilitiesImage.js +107 -107
  109. package/dist/utilities/utilitiesSymRootIdentifier.d.ts +7 -7
  110. package/dist/utilities/utilitiesSymRootIdentifier.js +26 -26
  111. package/dist/view/BaseView.d.ts +78 -78
  112. package/dist/view/BaseView.js +297 -297
  113. package/dist/view/BaseViewConfiguration.d.ts +32 -32
  114. package/dist/view/BaseViewConfiguration.js +10 -8
  115. package/dist/view/RenderEnv.d.ts +43 -43
  116. package/dist/view/RenderEnv.js +7 -7
  117. package/dist/view/SingleProductDefaultCameraView.d.ts +33 -33
  118. package/dist/view/SingleProductDefaultCameraView.js +141 -141
  119. package/dist/view/SingleProductDefaultCameraViewConfiguration.d.ts +46 -46
  120. package/dist/view/SingleProductDefaultCameraViewConfiguration.js +11 -11
  121. package/dist/view/SingleProductView.d.ts +42 -42
  122. package/dist/view/SingleProductView.js +206 -206
  123. package/dist/view/SingleProductViewConfiguration.d.ts +32 -32
  124. package/dist/view/SingleProductViewConfiguration.js +19 -19
  125. package/package.json +5 -5
@@ -1,306 +1,306 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import { Texture } from "@babylonjs/core/Materials/Textures/texture.js";
11
- import { Vector3 } from "@babylonjs/core/Maths/math.vector.js";
12
- import { GMaterial3DTexture, } from "@configura/web-core/dist/cm/core3D/GMaterial3D.js";
13
- import { Bump3D, GMaterialClassic } from "@configura/web-core/dist/cm/core3D/GMaterialClassic.js";
14
- import { GMaterialPBR } from "@configura/web-core/dist/cm/core3D/GMaterialPBR.js";
15
- import { CMMIPS_MIMETYPE, extractCmMips, getFileExtension, loadImage, LogObservable, } from "@configura/web-utilities";
16
- function getImageMimeType(fileExtension) {
17
- switch (fileExtension.toLowerCase()) {
18
- case "jpg":
19
- case "jpeg":
20
- return "image/jpeg";
21
- case "png":
22
- return "image/png";
23
- case "bmp":
24
- return "image/bmp";
25
- case "cmmips":
26
- return CMMIPS_MIMETYPE;
27
- default:
28
- throw Error("unrecognized file extension: " + fileExtension);
29
- }
30
- }
31
- function symWrapToBabylonWrap(logger, wrap) {
32
- switch (wrap) {
33
- case 3 /* clampToEdge */:
34
- return Texture.CLAMP_ADDRESSMODE;
35
- case 0 /* repeat */:
36
- return Texture.WRAP_ADDRESSMODE;
37
- case 1 /* mirroredRepeat */:
38
- logger.warn("Mirrored repeat wrapping not fully tested.");
39
- return Texture.MIRROR_ADDRESSMODE;
40
- case 2 /* clamp */:
41
- // From the CmSym specification this sounds like a "clamp to border" with a black
42
- // or transparent border color, not supported by WebGL or WebGPU.
43
- throw Error("Clamp wrapping not supported");
44
- case 4 /* clampToBorder */:
45
- // Not supported by WebGL or WebGPU
46
- throw Error("ClampToBorder wrapping not supported");
47
- default:
48
- throw Error("wrapping not implemented");
49
- }
50
- }
51
- const TEXTURE_FILTER_LOOKUP = [
52
- Texture.NEAREST_NEAREST,
53
- Texture.LINEAR_NEAREST,
54
- Texture.NEAREST_LINEAR,
55
- Texture.LINEAR_LINEAR,
56
- Texture.NEAREST_NEAREST_MIPNEAREST,
57
- Texture.LINEAR_NEAREST_MIPNEAREST,
58
- Texture.NEAREST_LINEAR_MIPNEAREST,
59
- Texture.LINEAR_LINEAR_MIPNEAREST,
60
- Texture.NEAREST_NEAREST_MIPLINEAR,
61
- Texture.LINEAR_NEAREST_MIPLINEAR,
62
- Texture.NEAREST_LINEAR_MIPLINEAR,
63
- Texture.LINEAR_LINEAR_MIPLINEAR,
64
- ];
65
- function symFilterToBabylonFilter(mag, min, mip) {
66
- let lookup = mip === undefined ? 0 : mip === 1 /* nearest */ ? 4 : 8;
67
- lookup += min === 1 /* nearest */ ? 0 : 2;
68
- lookup += mag === 1 /* nearest */ ? 0 : 1;
69
- return TEXTURE_FILTER_LOOKUP[lookup];
70
- }
71
- function loadCachedImage(logger, resourceUrl, renderEnvironment) {
72
- return __awaiter(this, void 0, void 0, function* () {
73
- const textureImageWithMeta = yield renderEnvironment.textureImageCache.get(resourceUrl, () => __awaiter(this, void 0, void 0, function* () {
74
- const result = {
75
- image: undefined,
76
- logger: new LogObservable(),
77
- };
78
- let mimeType = getImageMimeType(getFileExtension(resourceUrl));
79
- const reader = renderEnvironment.dexManager.readers.get(resourceUrl);
80
- if (reader === undefined) {
81
- result.logger.warn("No reader for", resourceUrl);
82
- return result;
83
- }
84
- let bytes = reader.bytes();
85
- if (mimeType === CMMIPS_MIMETYPE) {
86
- const mip = extractCmMips(bytes, result.logger); // Max quality
87
- // const mip = extractCmMips(bytes, 2048); // High quality
88
- // const mip = extractCmMips(bytes, 256); // Low quality
89
- if ((mip === null || mip === void 0 ? void 0 : mip.data) === undefined) {
90
- result.logger.warn("Could not extract image from CmMips", resourceUrl);
91
- return result;
92
- }
93
- mimeType = getImageMimeType(mip.suffix);
94
- bytes = mip.data;
95
- }
96
- const blob = new Blob([bytes], { type: mimeType });
97
- const blobImageUrl = window.URL.createObjectURL(blob);
98
- try {
99
- result.image = yield loadImage(blobImageUrl);
100
- return result;
101
- }
102
- catch (e) {
103
- result.logger.warnFromCaught(e);
104
- return result;
105
- }
106
- }));
107
- textureImageWithMeta.logger.accumulated.forEach((p) => logger.addPrebaked(p));
108
- return textureImageWithMeta.image;
109
- });
110
- }
111
- export function loadTextureFromURL(url, renderEnvironment) {
112
- const task = renderEnvironment.assetsManager.addTextureTask("(loadTextureFromURL)", url);
113
- return new Promise((resolve, reject) => {
114
- task.runTask(renderEnvironment.scene, () => {
115
- resolve(task.texture);
116
- }, reject);
117
- });
118
- }
119
- /**
120
- * Derives a normal map from a supplied height map (aka bump-map). Classic materials in CET can use
121
- * both height maps and normal maps but Babylon.js only supports normal maps.
122
- *
123
- * The derived normal map and the effect from `scale` is carefully crafted to be as similar to how
124
- * the RedSDK 3D renderer in CET operates and match it's final output. Do not change this code to
125
- * make it visually "better", the goal is instead to be as similar as possible.
126
- */
127
- function deriveNormalMapFromHeightMap(image, amount, logger) {
128
- const w = image.width;
129
- const h = image.height;
130
- const canvas = document.createElement("canvas");
131
- canvas.width = w;
132
- canvas.height = h;
133
- const context = canvas.getContext("2d");
134
- if (context !== null) {
135
- const tick = performance.now();
136
- context.drawImage(image, 0, 0);
137
- const imgData = context.getImageData(0, 0, image.width, image.height);
138
- const data = imgData.data;
139
- // The scale conversion and default value comes from the CET source code.
140
- const scale = amount ? 15 * amount : 0.1;
141
- // Number of components, canvas returns RGBA
142
- const c = 4;
143
- // Go through the image and calculate a gray scale version, storing it in the A channel.
144
- // Since this is legacy support code, we might as well support non-gray scale height maps,
145
- // which we have seen in the wild. This is also how RedSDK in CET works according to docs.
146
- for (let i = 0; i < w * h * c; i += c) {
147
- data[i + 3] = (data[i] + data[i + 1] + data[i + 2]) / 3;
148
- }
149
- // Derive the height map to create a normal map, store the X and Y components in the R and
150
- // G channels respectively.
151
- //
152
- // We are assuming that the map will be repeated when displayed since the Material Lab does
153
- // not expose texture repeats in it's UI, and the default mode is wrapped. This means that
154
- // edge sampling should wrap around as well.
155
- for (let i = 0; i < w * h; i++) {
156
- // The sample kernel:
157
- // [x][r]
158
- // [b]
159
- //
160
- // Produces results shifted half a pixel diagonally and is very local, but is about the // same as the kernel used in CET's render engine and also OpenGLs derivation extension.
161
- const offset = i * c + 3; // A channel
162
- const x = data[offset];
163
- let r, b;
164
- if (i % w === w - 1) {
165
- // End of the row, sample from the beginning of the same row
166
- r = data[offset - (w - 1) * c];
167
- }
168
- else {
169
- r = data[offset + c];
170
- }
171
- if (i >= (h - 1) * w) {
172
- // Last row, sample from the same column in the first row
173
- b = data[offset - (h - 1) * w * c];
174
- }
175
- else {
176
- b = data[offset + w * c];
177
- }
178
- // y-axis is inverted, OpenGL vs DirectX
179
- const normal = new Vector3((x - r) * scale, (b - x) * scale, 255).normalize();
180
- data[offset - 3] = Math.max(0, Math.min(255, normal.x * 128 + 127)); // R, x-axis
181
- data[offset - 2] = Math.max(0, Math.min(255, normal.y * 128 + 127)); // G, y-axis
182
- data[offset - 1] = Math.max(0, Math.min(255, normal.z * 128 + 127)); // B, z-axis
183
- }
184
- // Set the A channel to 255 to avoid any strange edge case and let it compress better.
185
- for (let offset = 3; offset < w * h * c; offset += c) {
186
- data[offset] = 255; // A
187
- }
188
- context.putImageData(imgData, 0, 0);
189
- const convertedImage = new Image();
190
- convertedImage.src = canvas.toDataURL("image/png");
191
- logger.info(`Converting height based bump map`, `of size ${w}x${h} took ${Math.round(performance.now() - tick)}ms. For optimal performance, update all classic materials to use normal maps rather than height maps or switch to PBR materials.`);
192
- return convertedImage;
193
- }
194
- return image;
195
- }
196
- function isGMaterial3DTexture(v) {
197
- return v instanceof GMaterial3DTexture;
198
- }
199
- function getToImageLoadFunc(logger, renderEnvironment) {
200
- return function (gm) {
201
- let im;
202
- if (gm === undefined || gm.textureUrl === undefined) {
203
- im = Promise.resolve(undefined);
204
- }
205
- else {
206
- im = loadCachedImage(logger, gm.textureUrl.value, renderEnvironment);
207
- }
208
- return { gm, im };
209
- };
210
- }
211
- function getApplyImageConversionsFunc(logger, renderEnvironment) {
212
- return function (gmv) {
213
- const { gm } = gmv;
214
- if (gm instanceof Bump3D && !gm.prepared) {
215
- /* A non-"prepared" bump texture means that the bump map is actually an old school
216
- * height map rather than a more modern (and faster) normal map. Babylon.js does not
217
- * support height maps so we will need to convert it manually.
218
- * https://forum.babylonjs.com/t/old-school-bump-map-height-map-not-supported/13447
219
- *
220
- * Normal map vs Height map:
221
- * https://docs.unity3d.com/2019.3/Documentation/Manual/StandardShaderMaterialParameterNormalMap.html
222
- */
223
- const textureUrl = gm.textureUrl;
224
- if (textureUrl !== undefined) {
225
- const mimeType = getImageMimeType(getFileExtension(textureUrl.value));
226
- if (mimeType === "image/png" || mimeType === "image/jpeg") {
227
- // Wrap the pure image load promise with an normal map derive promise
228
- gmv.im = renderEnvironment.derivedNormalMapCache.get(
229
- // The derivation depends on gm.amount, include it in the key. See WRD-664.
230
- textureUrl.value + "_scale_" + gm.amount, () => __awaiter(this, void 0, void 0, function* () {
231
- let image = yield gmv.im;
232
- if (image !== undefined) {
233
- image = deriveNormalMapFromHeightMap(image, gm.amount, logger);
234
- }
235
- return image;
236
- }));
237
- }
238
- }
239
- }
240
- return gmv;
241
- };
242
- }
243
- function getToTextureLoadFunc(renderEnvironment) {
244
- return function (gmv) {
245
- const { gm, im } = gmv;
246
- const tx = (() => __awaiter(this, void 0, void 0, function* () {
247
- const image = yield im;
248
- if (image === undefined) {
249
- return undefined;
250
- }
251
- return loadTextureFromURL(image.src, renderEnvironment);
252
- }))();
253
- return { gm, tx };
254
- };
255
- }
256
- function getApplyTexturePropertiesFromGMaterialFunc(logger) {
257
- return function (gmv) {
258
- return __awaiter(this, void 0, void 0, function* () {
259
- const gm = gmv.gm;
260
- const tx = yield gmv.tx;
261
- if (tx !== undefined) {
262
- tx.wrapU = symWrapToBabylonWrap(logger, gm.wrapU);
263
- tx.wrapV = symWrapToBabylonWrap(logger, gm.wrapV);
264
- tx.uScale = gm.uScale;
265
- tx.vScale = gm.vScale;
266
- tx.uOffset = gm.uOffset;
267
- tx.vOffset = gm.vOffset;
268
- tx.uRotationCenter = 0;
269
- tx.vRotationCenter = 0;
270
- tx.wAng = gm.rot;
271
- tx.updateSamplingMode(symFilterToBabylonFilter(gm.minFilter, gm.magFilter, gm.mipFilter));
272
- }
273
- return { gm, tx };
274
- });
275
- };
276
- }
277
- export function getTextures(logger, renderEnvironment, gm) {
278
- var _a, _b, _c, _d, _e, _f, _g;
279
- return __awaiter(this, void 0, void 0, function* () {
280
- let textures = [];
281
- if (gm instanceof GMaterialClassic) {
282
- // Ambient is not used in CET.
283
- // Emissive no longer exist in CET.
284
- // Specular is rare (and we can currently only handle its general value,
285
- // not its texture since the MeshStandardMaterial does not accept such a thing.)
286
- textures = [gm.bump, gm.diffuse, gm.specular, gm.transparency];
287
- }
288
- if (gm instanceof GMaterialPBR) {
289
- textures = [
290
- (_a = gm.base) === null || _a === void 0 ? void 0 : _a.texture,
291
- (_b = gm.emissive) === null || _b === void 0 ? void 0 : _b.texture,
292
- (_c = gm.metallic) === null || _c === void 0 ? void 0 : _c.texture,
293
- (_d = gm.normal) === null || _d === void 0 ? void 0 : _d.texture,
294
- (_e = gm.occlusion) === null || _e === void 0 ? void 0 : _e.texture,
295
- (_f = gm.opacity) === null || _f === void 0 ? void 0 : _f.texture,
296
- (_g = gm.roughness) === null || _g === void 0 ? void 0 : _g.texture,
297
- ];
298
- }
299
- return yield Promise.all(textures
300
- .filter(isGMaterial3DTexture)
301
- .map(getToImageLoadFunc(logger, renderEnvironment))
302
- .map(getApplyImageConversionsFunc(logger, renderEnvironment))
303
- .map(getToTextureLoadFunc(renderEnvironment))
304
- .map(getApplyTexturePropertiesFromGMaterialFunc(logger)));
305
- });
306
- }
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { Texture } from "@babylonjs/core/Materials/Textures/texture.js";
11
+ import { Vector3 } from "@babylonjs/core/Maths/math.vector.js";
12
+ import { GMaterial3DTexture, } from "@configura/web-core/dist/cm/core3D/GMaterial3D.js";
13
+ import { Bump3D, GMaterialClassic } from "@configura/web-core/dist/cm/core3D/GMaterialClassic.js";
14
+ import { GMaterialPBR } from "@configura/web-core/dist/cm/core3D/GMaterialPBR.js";
15
+ import { CMMIPS_MIMETYPE, extractCmMips, getFileExtension, loadImage, LogObservable, } from "@configura/web-utilities";
16
+ function getImageMimeType(fileExtension) {
17
+ switch (fileExtension.toLowerCase()) {
18
+ case "jpg":
19
+ case "jpeg":
20
+ return "image/jpeg";
21
+ case "png":
22
+ return "image/png";
23
+ case "bmp":
24
+ return "image/bmp";
25
+ case "cmmips":
26
+ return CMMIPS_MIMETYPE;
27
+ default:
28
+ throw Error("unrecognized file extension: " + fileExtension);
29
+ }
30
+ }
31
+ function symWrapToBabylonWrap(logger, wrap) {
32
+ switch (wrap) {
33
+ case 3 /* clampToEdge */:
34
+ return Texture.CLAMP_ADDRESSMODE;
35
+ case 0 /* repeat */:
36
+ return Texture.WRAP_ADDRESSMODE;
37
+ case 1 /* mirroredRepeat */:
38
+ logger.warn("Mirrored repeat wrapping not fully tested.");
39
+ return Texture.MIRROR_ADDRESSMODE;
40
+ case 2 /* clamp */:
41
+ // From the CmSym specification this sounds like a "clamp to border" with a black
42
+ // or transparent border color, not supported by WebGL or WebGPU.
43
+ throw Error("Clamp wrapping not supported");
44
+ case 4 /* clampToBorder */:
45
+ // Not supported by WebGL or WebGPU
46
+ throw Error("ClampToBorder wrapping not supported");
47
+ default:
48
+ throw Error("wrapping not implemented");
49
+ }
50
+ }
51
+ const TEXTURE_FILTER_LOOKUP = [
52
+ Texture.NEAREST_NEAREST,
53
+ Texture.LINEAR_NEAREST,
54
+ Texture.NEAREST_LINEAR,
55
+ Texture.LINEAR_LINEAR,
56
+ Texture.NEAREST_NEAREST_MIPNEAREST,
57
+ Texture.LINEAR_NEAREST_MIPNEAREST,
58
+ Texture.NEAREST_LINEAR_MIPNEAREST,
59
+ Texture.LINEAR_LINEAR_MIPNEAREST,
60
+ Texture.NEAREST_NEAREST_MIPLINEAR,
61
+ Texture.LINEAR_NEAREST_MIPLINEAR,
62
+ Texture.NEAREST_LINEAR_MIPLINEAR,
63
+ Texture.LINEAR_LINEAR_MIPLINEAR,
64
+ ];
65
+ function symFilterToBabylonFilter(mag, min, mip) {
66
+ let lookup = mip === undefined ? 0 : mip === 1 /* nearest */ ? 4 : 8;
67
+ lookup += min === 1 /* nearest */ ? 0 : 2;
68
+ lookup += mag === 1 /* nearest */ ? 0 : 1;
69
+ return TEXTURE_FILTER_LOOKUP[lookup];
70
+ }
71
+ function loadCachedImage(logger, resourceUrl, renderEnvironment) {
72
+ return __awaiter(this, void 0, void 0, function* () {
73
+ const textureImageWithMeta = yield renderEnvironment.textureImageCache.get(resourceUrl, () => __awaiter(this, void 0, void 0, function* () {
74
+ const result = {
75
+ image: undefined,
76
+ logger: new LogObservable(),
77
+ };
78
+ let mimeType = getImageMimeType(getFileExtension(resourceUrl));
79
+ const reader = renderEnvironment.dexManager.readers.get(resourceUrl);
80
+ if (reader === undefined) {
81
+ result.logger.warn("No reader for", resourceUrl);
82
+ return result;
83
+ }
84
+ let bytes = reader.bytes();
85
+ if (mimeType === CMMIPS_MIMETYPE) {
86
+ const mip = extractCmMips(bytes, result.logger); // Max quality
87
+ // const mip = extractCmMips(bytes, 2048); // High quality
88
+ // const mip = extractCmMips(bytes, 256); // Low quality
89
+ if ((mip === null || mip === void 0 ? void 0 : mip.data) === undefined) {
90
+ result.logger.warn("Could not extract image from CmMips", resourceUrl);
91
+ return result;
92
+ }
93
+ mimeType = getImageMimeType(mip.suffix);
94
+ bytes = mip.data;
95
+ }
96
+ const blob = new Blob([bytes], { type: mimeType });
97
+ const blobImageUrl = window.URL.createObjectURL(blob);
98
+ try {
99
+ result.image = yield loadImage(blobImageUrl);
100
+ return result;
101
+ }
102
+ catch (e) {
103
+ result.logger.warnFromCaught(e);
104
+ return result;
105
+ }
106
+ }));
107
+ textureImageWithMeta.logger.accumulated.forEach((p) => logger.addPrebaked(p));
108
+ return textureImageWithMeta.image;
109
+ });
110
+ }
111
+ export function loadTextureFromURL(url, renderEnvironment) {
112
+ const task = renderEnvironment.assetsManager.addTextureTask("(loadTextureFromURL)", url);
113
+ return new Promise((resolve, reject) => {
114
+ task.runTask(renderEnvironment.scene, () => {
115
+ resolve(task.texture);
116
+ }, reject);
117
+ });
118
+ }
119
+ /**
120
+ * Derives a normal map from a supplied height map (aka bump-map). Classic materials in CET can use
121
+ * both height maps and normal maps but Babylon.js only supports normal maps.
122
+ *
123
+ * The derived normal map and the effect from `scale` is carefully crafted to be as similar to how
124
+ * the RedSDK 3D renderer in CET operates and match it's final output. Do not change this code to
125
+ * make it visually "better", the goal is instead to be as similar as possible.
126
+ */
127
+ function deriveNormalMapFromHeightMap(image, amount, logger) {
128
+ const w = image.width;
129
+ const h = image.height;
130
+ const canvas = document.createElement("canvas");
131
+ canvas.width = w;
132
+ canvas.height = h;
133
+ const context = canvas.getContext("2d");
134
+ if (context !== null) {
135
+ const tick = performance.now();
136
+ context.drawImage(image, 0, 0);
137
+ const imgData = context.getImageData(0, 0, image.width, image.height);
138
+ const data = imgData.data;
139
+ // The scale conversion and default value comes from the CET source code.
140
+ const scale = amount ? 15 * amount : 0.1;
141
+ // Number of components, canvas returns RGBA
142
+ const c = 4;
143
+ // Go through the image and calculate a gray scale version, storing it in the A channel.
144
+ // Since this is legacy support code, we might as well support non-gray scale height maps,
145
+ // which we have seen in the wild. This is also how RedSDK in CET works according to docs.
146
+ for (let i = 0; i < w * h * c; i += c) {
147
+ data[i + 3] = (data[i] + data[i + 1] + data[i + 2]) / 3;
148
+ }
149
+ // Derive the height map to create a normal map, store the X and Y components in the R and
150
+ // G channels respectively.
151
+ //
152
+ // We are assuming that the map will be repeated when displayed since the Material Lab does
153
+ // not expose texture repeats in it's UI, and the default mode is wrapped. This means that
154
+ // edge sampling should wrap around as well.
155
+ for (let i = 0; i < w * h; i++) {
156
+ // The sample kernel:
157
+ // [x][r]
158
+ // [b]
159
+ //
160
+ // Produces results shifted half a pixel diagonally and is very local, but is about the // same as the kernel used in CET's render engine and also OpenGLs derivation extension.
161
+ const offset = i * c + 3; // A channel
162
+ const x = data[offset];
163
+ let r, b;
164
+ if (i % w === w - 1) {
165
+ // End of the row, sample from the beginning of the same row
166
+ r = data[offset - (w - 1) * c];
167
+ }
168
+ else {
169
+ r = data[offset + c];
170
+ }
171
+ if (i >= (h - 1) * w) {
172
+ // Last row, sample from the same column in the first row
173
+ b = data[offset - (h - 1) * w * c];
174
+ }
175
+ else {
176
+ b = data[offset + w * c];
177
+ }
178
+ // y-axis is inverted, OpenGL vs DirectX
179
+ const normal = new Vector3((x - r) * scale, (b - x) * scale, 255).normalize();
180
+ data[offset - 3] = Math.max(0, Math.min(255, normal.x * 128 + 127)); // R, x-axis
181
+ data[offset - 2] = Math.max(0, Math.min(255, normal.y * 128 + 127)); // G, y-axis
182
+ data[offset - 1] = Math.max(0, Math.min(255, normal.z * 128 + 127)); // B, z-axis
183
+ }
184
+ // Set the A channel to 255 to avoid any strange edge case and let it compress better.
185
+ for (let offset = 3; offset < w * h * c; offset += c) {
186
+ data[offset] = 255; // A
187
+ }
188
+ context.putImageData(imgData, 0, 0);
189
+ const convertedImage = new Image();
190
+ convertedImage.src = canvas.toDataURL("image/png");
191
+ logger.info(`Converting height based bump map`, `of size ${w}x${h} took ${Math.round(performance.now() - tick)}ms. For optimal performance, update all classic materials to use normal maps rather than height maps or switch to PBR materials.`);
192
+ return convertedImage;
193
+ }
194
+ return image;
195
+ }
196
+ function isGMaterial3DTexture(v) {
197
+ return v instanceof GMaterial3DTexture;
198
+ }
199
+ function getToImageLoadFunc(logger, renderEnvironment) {
200
+ return function (gm) {
201
+ let im;
202
+ if (gm === undefined || gm.textureUrl === undefined) {
203
+ im = Promise.resolve(undefined);
204
+ }
205
+ else {
206
+ im = loadCachedImage(logger, gm.textureUrl.value, renderEnvironment);
207
+ }
208
+ return { gm, im };
209
+ };
210
+ }
211
+ function getApplyImageConversionsFunc(logger, renderEnvironment) {
212
+ return function (gmv) {
213
+ const { gm } = gmv;
214
+ if (gm instanceof Bump3D && !gm.prepared) {
215
+ /* A non-"prepared" bump texture means that the bump map is actually an old school
216
+ * height map rather than a more modern (and faster) normal map. Babylon.js does not
217
+ * support height maps so we will need to convert it manually.
218
+ * https://forum.babylonjs.com/t/old-school-bump-map-height-map-not-supported/13447
219
+ *
220
+ * Normal map vs Height map:
221
+ * https://docs.unity3d.com/2019.3/Documentation/Manual/StandardShaderMaterialParameterNormalMap.html
222
+ */
223
+ const textureUrl = gm.textureUrl;
224
+ if (textureUrl !== undefined) {
225
+ const mimeType = getImageMimeType(getFileExtension(textureUrl.value));
226
+ if (mimeType === "image/png" || mimeType === "image/jpeg") {
227
+ // Wrap the pure image load promise with an normal map derive promise
228
+ gmv.im = renderEnvironment.derivedNormalMapCache.get(
229
+ // The derivation depends on gm.amount, include it in the key. See WRD-664.
230
+ textureUrl.value + "_scale_" + gm.amount, () => __awaiter(this, void 0, void 0, function* () {
231
+ let image = yield gmv.im;
232
+ if (image !== undefined) {
233
+ image = deriveNormalMapFromHeightMap(image, gm.amount, logger);
234
+ }
235
+ return image;
236
+ }));
237
+ }
238
+ }
239
+ }
240
+ return gmv;
241
+ };
242
+ }
243
+ function getToTextureLoadFunc(renderEnvironment) {
244
+ return function (gmv) {
245
+ const { gm, im } = gmv;
246
+ const tx = (() => __awaiter(this, void 0, void 0, function* () {
247
+ const image = yield im;
248
+ if (image === undefined) {
249
+ return undefined;
250
+ }
251
+ return loadTextureFromURL(image.src, renderEnvironment);
252
+ }))();
253
+ return { gm, tx };
254
+ };
255
+ }
256
+ function getApplyTexturePropertiesFromGMaterialFunc(logger) {
257
+ return function (gmv) {
258
+ return __awaiter(this, void 0, void 0, function* () {
259
+ const gm = gmv.gm;
260
+ const tx = yield gmv.tx;
261
+ if (tx !== undefined) {
262
+ tx.wrapU = symWrapToBabylonWrap(logger, gm.wrapU);
263
+ tx.wrapV = symWrapToBabylonWrap(logger, gm.wrapV);
264
+ tx.uScale = gm.uScale;
265
+ tx.vScale = gm.vScale;
266
+ tx.uOffset = gm.uOffset;
267
+ tx.vOffset = gm.vOffset;
268
+ tx.uRotationCenter = 0;
269
+ tx.vRotationCenter = 0;
270
+ tx.wAng = gm.rot;
271
+ tx.updateSamplingMode(symFilterToBabylonFilter(gm.minFilter, gm.magFilter, gm.mipFilter));
272
+ }
273
+ return { gm, tx };
274
+ });
275
+ };
276
+ }
277
+ export function getTextures(logger, renderEnvironment, gm) {
278
+ var _a, _b, _c, _d, _e, _f, _g;
279
+ return __awaiter(this, void 0, void 0, function* () {
280
+ let textures = [];
281
+ if (gm instanceof GMaterialClassic) {
282
+ // Ambient is not used in CET.
283
+ // Emissive no longer exist in CET.
284
+ // Specular is rare (and we can currently only handle its general value,
285
+ // not its texture since the MeshStandardMaterial does not accept such a thing.)
286
+ textures = [gm.bump, gm.diffuse, gm.specular, gm.transparency];
287
+ }
288
+ if (gm instanceof GMaterialPBR) {
289
+ textures = [
290
+ (_a = gm.base) === null || _a === void 0 ? void 0 : _a.texture,
291
+ (_b = gm.emissive) === null || _b === void 0 ? void 0 : _b.texture,
292
+ (_c = gm.metallic) === null || _c === void 0 ? void 0 : _c.texture,
293
+ (_d = gm.normal) === null || _d === void 0 ? void 0 : _d.texture,
294
+ (_e = gm.occlusion) === null || _e === void 0 ? void 0 : _e.texture,
295
+ (_f = gm.opacity) === null || _f === void 0 ? void 0 : _f.texture,
296
+ (_g = gm.roughness) === null || _g === void 0 ? void 0 : _g.texture,
297
+ ];
298
+ }
299
+ return yield Promise.all(textures
300
+ .filter(isGMaterial3DTexture)
301
+ .map(getToImageLoadFunc(logger, renderEnvironment))
302
+ .map(getApplyImageConversionsFunc(logger, renderEnvironment))
303
+ .map(getToTextureLoadFunc(renderEnvironment))
304
+ .map(getApplyTexturePropertiesFromGMaterialFunc(logger)));
305
+ });
306
+ }