@configura/babylon-view 1.2.1 → 1.3.0-alpha.3

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 (115) 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 +10 -10
  42. package/dist/camera/CfgArcRotateCameraPointersInput.js +262 -262
  43. package/dist/camera/CfgOrbitalCamera.d.ts +68 -64
  44. package/dist/camera/CfgOrbitalCamera.js +250 -233
  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 +12 -12
  52. package/dist/geometry/CfgGeometry.js +117 -117
  53. package/dist/geometry/CfgMesh.d.ts +7 -7
  54. package/dist/geometry/CfgMesh.js +8 -8
  55. package/dist/geometry/geoSplitter.d.ts +8 -8
  56. package/dist/geometry/geoSplitter.js +192 -192
  57. package/dist/index.d.ts +13 -13
  58. package/dist/index.js +13 -13
  59. package/dist/light/CfgDirectionalLight.d.ts +8 -8
  60. package/dist/light/CfgDirectionalLight.js +18 -18
  61. package/dist/light/CfgHemisphericLight.d.ts +7 -7
  62. package/dist/light/CfgHemisphericLight.js +17 -17
  63. package/dist/light/CfgPointLight.d.ts +8 -8
  64. package/dist/light/CfgPointLight.js +18 -18
  65. package/dist/light/DefaultLightRig.d.ts +19 -19
  66. package/dist/light/DefaultLightRig.js +77 -77
  67. package/dist/light/LightRigCreator.d.ts +9 -9
  68. package/dist/light/LightRigCreator.js +3 -3
  69. package/dist/material/CfgMaterial.d.ts +53 -53
  70. package/dist/material/CfgMaterial.js +454 -454
  71. package/dist/material/DummyMaterialCreator.d.ts +4 -4
  72. package/dist/material/DummyMaterialCreator.js +15 -15
  73. package/dist/material/material.d.ts +18 -18
  74. package/dist/material/material.js +128 -128
  75. package/dist/material/texture.d.ts +14 -14
  76. package/dist/material/texture.js +304 -304
  77. package/dist/nodes/CfgContentRootNode.d.ts +19 -19
  78. package/dist/nodes/CfgContentRootNode.js +75 -75
  79. package/dist/nodes/CfgDeferredMeshNode.d.ts +48 -48
  80. package/dist/nodes/CfgDeferredMeshNode.js +347 -347
  81. package/dist/nodes/CfgProductNode.d.ts +61 -61
  82. package/dist/nodes/CfgProductNode.js +486 -486
  83. package/dist/nodes/CfgSymNode.d.ts +42 -42
  84. package/dist/nodes/CfgSymNode.js +216 -216
  85. package/dist/nodes/CfgSymRootNode.d.ts +33 -33
  86. package/dist/nodes/CfgSymRootNode.js +175 -175
  87. package/dist/nodes/CfgTransformNode.d.ts +29 -29
  88. package/dist/nodes/CfgTransformNode.js +81 -81
  89. package/dist/scene/SceneCreator.d.ts +6 -6
  90. package/dist/scene/SceneCreator.js +22 -22
  91. package/dist/utilities/CfgBoundingBox.d.ts +16 -16
  92. package/dist/utilities/CfgBoundingBox.js +64 -64
  93. package/dist/utilities/utilities3D.d.ts +26 -26
  94. package/dist/utilities/utilities3D.js +187 -187
  95. package/dist/utilities/utilitiesColor.d.ts +18 -18
  96. package/dist/utilities/utilitiesColor.js +48 -48
  97. package/dist/utilities/utilitiesImage.d.ts +6 -6
  98. package/dist/utilities/utilitiesImage.js +107 -107
  99. package/dist/utilities/utilitiesSymRootIdentifier.d.ts +5 -5
  100. package/dist/utilities/utilitiesSymRootIdentifier.js +20 -20
  101. package/dist/view/BaseView.d.ts +70 -70
  102. package/dist/view/BaseView.js +291 -291
  103. package/dist/view/BaseViewConfiguration.d.ts +32 -32
  104. package/dist/view/BaseViewConfiguration.js +8 -8
  105. package/dist/view/RenderEnv.d.ts +38 -38
  106. package/dist/view/RenderEnv.js +7 -7
  107. package/dist/view/SingleProductDefaultCameraView.d.ts +33 -33
  108. package/dist/view/SingleProductDefaultCameraView.js +141 -140
  109. package/dist/view/SingleProductDefaultCameraViewConfiguration.d.ts +46 -44
  110. package/dist/view/SingleProductDefaultCameraViewConfiguration.js +11 -11
  111. package/dist/view/SingleProductView.d.ts +42 -42
  112. package/dist/view/SingleProductView.js +205 -205
  113. package/dist/view/SingleProductViewConfiguration.d.ts +32 -32
  114. package/dist/view/SingleProductViewConfiguration.js +19 -19
  115. package/package.json +5 -5
@@ -1,304 +1,304 @@
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
- /// Derives a normal map from a supplied height map. Both are used in CET for bump-maps but Babylon
120
- /// only supports normal maps.
121
- ///
122
- /// The derived normal map and the effect from `scale` is carefully crafted to be as similar to how
123
- /// the RedSDK 3D renderer in CET operates and match it's final output. Do not change this code to
124
- /// make it visually "better", the goal is instead to be as similar as possible.
125
- function deriveNormalMapFromHeightMap(image, amount, logger) {
126
- const w = image.width;
127
- const h = image.height;
128
- const canvas = document.createElement("canvas");
129
- canvas.width = w;
130
- canvas.height = h;
131
- const context = canvas.getContext("2d");
132
- if (context !== null) {
133
- const tick = performance.now();
134
- context.drawImage(image, 0, 0);
135
- const imgData = context.getImageData(0, 0, image.width, image.height);
136
- const data = imgData.data;
137
- // The scale conversion and default value comes from the CET source code.
138
- const scale = amount ? 15 * amount : 0.1;
139
- /// Number of components, canvas returns RGBA
140
- const c = 4;
141
- // Go through the image and calculate a gray scale version, storing it in the A channel.
142
- // Since this is legacy support code, we might as well support non-gray scale height maps,
143
- // which we have seen in the wild. This is also how RedSDK in CET works according to docs.
144
- for (let i = 0; i < w * h * c; i += c) {
145
- data[i + 3] = (data[i] + data[i + 1] + data[i + 2]) / 3;
146
- }
147
- // Derive the height map to create a normal map, store the X and Y components in the R and
148
- // G channels respectively.
149
- //
150
- // We are assuming that the map will be repeated when displayed since the Material Lab does
151
- // not expose texture repeats in it's UI, and the default mode is wrapped. This means that
152
- // edge sampling should wrap around as well.
153
- for (let i = 0; i < w * h; i++) {
154
- // The sample kernel:
155
- // [x][r]
156
- // [b]
157
- //
158
- // 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.
159
- const offset = i * c + 3; // A channel
160
- const x = data[offset];
161
- let r, b;
162
- if (i % w === w - 1) {
163
- // End of the row, sample from the beginning of the same row
164
- r = data[offset - (w - 1) * c];
165
- }
166
- else {
167
- r = data[offset + c];
168
- }
169
- if (i >= (h - 1) * w) {
170
- // Last row, sample from the same column in the first row
171
- b = data[offset - (h - 1) * w * c];
172
- }
173
- else {
174
- b = data[offset + w * c];
175
- }
176
- // y-axis is inverted, OpenGL vs DirectX
177
- const normal = new Vector3((x - r) * scale, (b - x) * scale, 255).normalize();
178
- data[offset - 3] = Math.max(0, Math.min(255, normal.x * 128 + 127)); // R, x-axis
179
- data[offset - 2] = Math.max(0, Math.min(255, normal.y * 128 + 127)); // G, y-axis
180
- data[offset - 1] = Math.max(0, Math.min(255, normal.z * 128 + 127)); // B, z-axis
181
- }
182
- // Set the A channel to 255 to avoid any strange edge case and let it compress better.
183
- for (let offset = 3; offset < w * h * c; offset += c) {
184
- data[offset] = 255; // A
185
- }
186
- context.putImageData(imgData, 0, 0);
187
- const convertedImage = new Image();
188
- convertedImage.src = canvas.toDataURL("image/png");
189
- logger.info(`Converting height based bump map`, `of size ${w}x${h} took ${Math.round(performance.now() - tick)}ms. For optimal performance, update all materials to use normal maps rather than height maps.`);
190
- return convertedImage;
191
- }
192
- return image;
193
- }
194
- function isGMaterial3DTexture(v) {
195
- return v instanceof GMaterial3DTexture;
196
- }
197
- function getToImageLoadFunc(logger, renderEnvironment) {
198
- return function (gm) {
199
- let im;
200
- if (gm === undefined || gm.textureUrl === undefined) {
201
- im = Promise.resolve(undefined);
202
- }
203
- else {
204
- im = loadCachedImage(logger, gm.textureUrl.value, renderEnvironment);
205
- }
206
- return { gm, im };
207
- };
208
- }
209
- function getApplyImageConversionsFunc(logger, renderEnvironment) {
210
- return function (gmv) {
211
- const { gm } = gmv;
212
- if (gm instanceof Bump3D && !gm.prepared) {
213
- /* A non-"prepared" bump texture means that the bump map is actually an old school
214
- * height map rather than a more modern (and faster) normal map. Babylon.js does not
215
- * support height maps so we will need to convert it manually.
216
- * https://forum.babylonjs.com/t/old-school-bump-map-height-map-not-supported/13447
217
- *
218
- * Normal map vs Height map:
219
- * https://docs.unity3d.com/2019.3/Documentation/Manual/StandardShaderMaterialParameterNormalMap.html
220
- */
221
- const textureUrl = gm.textureUrl;
222
- if (textureUrl !== undefined) {
223
- const mimeType = getImageMimeType(getFileExtension(textureUrl.value));
224
- if (mimeType === "image/png" || mimeType === "image/jpeg") {
225
- // Wrap the pure image load promise with an normal map derive promise
226
- gmv.im = renderEnvironment.derivedNormalMapCache.get(
227
- // The derivation depends on gm.amount, include it in the key. See WRD-664.
228
- textureUrl.value + "_scale_" + gm.amount, () => __awaiter(this, void 0, void 0, function* () {
229
- let image = yield gmv.im;
230
- if (image !== undefined) {
231
- image = deriveNormalMapFromHeightMap(image, gm.amount, logger);
232
- }
233
- return image;
234
- }));
235
- }
236
- }
237
- }
238
- return gmv;
239
- };
240
- }
241
- function getToTextureLoadFunc(renderEnvironment) {
242
- return function (gmv) {
243
- const { gm, im } = gmv;
244
- const tx = (() => __awaiter(this, void 0, void 0, function* () {
245
- const image = yield im;
246
- if (image === undefined) {
247
- return undefined;
248
- }
249
- return loadTextureFromURL(image.src, renderEnvironment);
250
- }))();
251
- return { gm, tx };
252
- };
253
- }
254
- function getApplyTexturePropertiesFromGMaterialFunc(logger) {
255
- return function (gmv) {
256
- return __awaiter(this, void 0, void 0, function* () {
257
- const gm = gmv.gm;
258
- const tx = yield gmv.tx;
259
- if (tx !== undefined) {
260
- tx.wrapU = symWrapToBabylonWrap(logger, gm.wrapU);
261
- tx.wrapV = symWrapToBabylonWrap(logger, gm.wrapV);
262
- tx.uScale = gm.uScale;
263
- tx.vScale = gm.vScale;
264
- tx.uOffset = gm.uOffset;
265
- tx.vOffset = gm.vOffset;
266
- tx.uRotationCenter = 0;
267
- tx.vRotationCenter = 0;
268
- tx.wAng = gm.rot;
269
- tx.updateSamplingMode(symFilterToBabylonFilter(gm.minFilter, gm.magFilter, gm.mipFilter));
270
- }
271
- return { gm, tx };
272
- });
273
- };
274
- }
275
- export function getTextures(logger, renderEnvironment, gm) {
276
- var _a, _b, _c, _d, _e, _f, _g;
277
- return __awaiter(this, void 0, void 0, function* () {
278
- let textures = [];
279
- if (gm instanceof GMaterialClassic) {
280
- // Ambient is not used in CET.
281
- // Emissive no longer exist in CET.
282
- // Specular is rare (and we can currently only handle its general value,
283
- // not its texture since the MeshStandardMaterial does not accept such a thing.)
284
- textures = [gm.bump, gm.diffuse, gm.specular, gm.transparency];
285
- }
286
- if (gm instanceof GMaterialPBR) {
287
- textures = [
288
- (_a = gm.base) === null || _a === void 0 ? void 0 : _a.texture,
289
- (_b = gm.emissive) === null || _b === void 0 ? void 0 : _b.texture,
290
- (_c = gm.metallic) === null || _c === void 0 ? void 0 : _c.texture,
291
- (_d = gm.normal) === null || _d === void 0 ? void 0 : _d.texture,
292
- (_e = gm.occlusion) === null || _e === void 0 ? void 0 : _e.texture,
293
- (_f = gm.opacity) === null || _f === void 0 ? void 0 : _f.texture,
294
- (_g = gm.roughness) === null || _g === void 0 ? void 0 : _g.texture,
295
- ];
296
- }
297
- return yield Promise.all(textures
298
- .filter(isGMaterial3DTexture)
299
- .map(getToImageLoadFunc(logger, renderEnvironment))
300
- .map(getApplyImageConversionsFunc(logger, renderEnvironment))
301
- .map(getToTextureLoadFunc(renderEnvironment))
302
- .map(getApplyTexturePropertiesFromGMaterialFunc(logger)));
303
- });
304
- }
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
+ /// Derives a normal map from a supplied height map. Both are used in CET for bump-maps but Babylon
120
+ /// only supports normal maps.
121
+ ///
122
+ /// The derived normal map and the effect from `scale` is carefully crafted to be as similar to how
123
+ /// the RedSDK 3D renderer in CET operates and match it's final output. Do not change this code to
124
+ /// make it visually "better", the goal is instead to be as similar as possible.
125
+ function deriveNormalMapFromHeightMap(image, amount, logger) {
126
+ const w = image.width;
127
+ const h = image.height;
128
+ const canvas = document.createElement("canvas");
129
+ canvas.width = w;
130
+ canvas.height = h;
131
+ const context = canvas.getContext("2d");
132
+ if (context !== null) {
133
+ const tick = performance.now();
134
+ context.drawImage(image, 0, 0);
135
+ const imgData = context.getImageData(0, 0, image.width, image.height);
136
+ const data = imgData.data;
137
+ // The scale conversion and default value comes from the CET source code.
138
+ const scale = amount ? 15 * amount : 0.1;
139
+ /// Number of components, canvas returns RGBA
140
+ const c = 4;
141
+ // Go through the image and calculate a gray scale version, storing it in the A channel.
142
+ // Since this is legacy support code, we might as well support non-gray scale height maps,
143
+ // which we have seen in the wild. This is also how RedSDK in CET works according to docs.
144
+ for (let i = 0; i < w * h * c; i += c) {
145
+ data[i + 3] = (data[i] + data[i + 1] + data[i + 2]) / 3;
146
+ }
147
+ // Derive the height map to create a normal map, store the X and Y components in the R and
148
+ // G channels respectively.
149
+ //
150
+ // We are assuming that the map will be repeated when displayed since the Material Lab does
151
+ // not expose texture repeats in it's UI, and the default mode is wrapped. This means that
152
+ // edge sampling should wrap around as well.
153
+ for (let i = 0; i < w * h; i++) {
154
+ // The sample kernel:
155
+ // [x][r]
156
+ // [b]
157
+ //
158
+ // 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.
159
+ const offset = i * c + 3; // A channel
160
+ const x = data[offset];
161
+ let r, b;
162
+ if (i % w === w - 1) {
163
+ // End of the row, sample from the beginning of the same row
164
+ r = data[offset - (w - 1) * c];
165
+ }
166
+ else {
167
+ r = data[offset + c];
168
+ }
169
+ if (i >= (h - 1) * w) {
170
+ // Last row, sample from the same column in the first row
171
+ b = data[offset - (h - 1) * w * c];
172
+ }
173
+ else {
174
+ b = data[offset + w * c];
175
+ }
176
+ // y-axis is inverted, OpenGL vs DirectX
177
+ const normal = new Vector3((x - r) * scale, (b - x) * scale, 255).normalize();
178
+ data[offset - 3] = Math.max(0, Math.min(255, normal.x * 128 + 127)); // R, x-axis
179
+ data[offset - 2] = Math.max(0, Math.min(255, normal.y * 128 + 127)); // G, y-axis
180
+ data[offset - 1] = Math.max(0, Math.min(255, normal.z * 128 + 127)); // B, z-axis
181
+ }
182
+ // Set the A channel to 255 to avoid any strange edge case and let it compress better.
183
+ for (let offset = 3; offset < w * h * c; offset += c) {
184
+ data[offset] = 255; // A
185
+ }
186
+ context.putImageData(imgData, 0, 0);
187
+ const convertedImage = new Image();
188
+ convertedImage.src = canvas.toDataURL("image/png");
189
+ logger.info(`Converting height based bump map`, `of size ${w}x${h} took ${Math.round(performance.now() - tick)}ms. For optimal performance, update all materials to use normal maps rather than height maps.`);
190
+ return convertedImage;
191
+ }
192
+ return image;
193
+ }
194
+ function isGMaterial3DTexture(v) {
195
+ return v instanceof GMaterial3DTexture;
196
+ }
197
+ function getToImageLoadFunc(logger, renderEnvironment) {
198
+ return function (gm) {
199
+ let im;
200
+ if (gm === undefined || gm.textureUrl === undefined) {
201
+ im = Promise.resolve(undefined);
202
+ }
203
+ else {
204
+ im = loadCachedImage(logger, gm.textureUrl.value, renderEnvironment);
205
+ }
206
+ return { gm, im };
207
+ };
208
+ }
209
+ function getApplyImageConversionsFunc(logger, renderEnvironment) {
210
+ return function (gmv) {
211
+ const { gm } = gmv;
212
+ if (gm instanceof Bump3D && !gm.prepared) {
213
+ /* A non-"prepared" bump texture means that the bump map is actually an old school
214
+ * height map rather than a more modern (and faster) normal map. Babylon.js does not
215
+ * support height maps so we will need to convert it manually.
216
+ * https://forum.babylonjs.com/t/old-school-bump-map-height-map-not-supported/13447
217
+ *
218
+ * Normal map vs Height map:
219
+ * https://docs.unity3d.com/2019.3/Documentation/Manual/StandardShaderMaterialParameterNormalMap.html
220
+ */
221
+ const textureUrl = gm.textureUrl;
222
+ if (textureUrl !== undefined) {
223
+ const mimeType = getImageMimeType(getFileExtension(textureUrl.value));
224
+ if (mimeType === "image/png" || mimeType === "image/jpeg") {
225
+ // Wrap the pure image load promise with an normal map derive promise
226
+ gmv.im = renderEnvironment.derivedNormalMapCache.get(
227
+ // The derivation depends on gm.amount, include it in the key. See WRD-664.
228
+ textureUrl.value + "_scale_" + gm.amount, () => __awaiter(this, void 0, void 0, function* () {
229
+ let image = yield gmv.im;
230
+ if (image !== undefined) {
231
+ image = deriveNormalMapFromHeightMap(image, gm.amount, logger);
232
+ }
233
+ return image;
234
+ }));
235
+ }
236
+ }
237
+ }
238
+ return gmv;
239
+ };
240
+ }
241
+ function getToTextureLoadFunc(renderEnvironment) {
242
+ return function (gmv) {
243
+ const { gm, im } = gmv;
244
+ const tx = (() => __awaiter(this, void 0, void 0, function* () {
245
+ const image = yield im;
246
+ if (image === undefined) {
247
+ return undefined;
248
+ }
249
+ return loadTextureFromURL(image.src, renderEnvironment);
250
+ }))();
251
+ return { gm, tx };
252
+ };
253
+ }
254
+ function getApplyTexturePropertiesFromGMaterialFunc(logger) {
255
+ return function (gmv) {
256
+ return __awaiter(this, void 0, void 0, function* () {
257
+ const gm = gmv.gm;
258
+ const tx = yield gmv.tx;
259
+ if (tx !== undefined) {
260
+ tx.wrapU = symWrapToBabylonWrap(logger, gm.wrapU);
261
+ tx.wrapV = symWrapToBabylonWrap(logger, gm.wrapV);
262
+ tx.uScale = gm.uScale;
263
+ tx.vScale = gm.vScale;
264
+ tx.uOffset = gm.uOffset;
265
+ tx.vOffset = gm.vOffset;
266
+ tx.uRotationCenter = 0;
267
+ tx.vRotationCenter = 0;
268
+ tx.wAng = gm.rot;
269
+ tx.updateSamplingMode(symFilterToBabylonFilter(gm.minFilter, gm.magFilter, gm.mipFilter));
270
+ }
271
+ return { gm, tx };
272
+ });
273
+ };
274
+ }
275
+ export function getTextures(logger, renderEnvironment, gm) {
276
+ var _a, _b, _c, _d, _e, _f, _g;
277
+ return __awaiter(this, void 0, void 0, function* () {
278
+ let textures = [];
279
+ if (gm instanceof GMaterialClassic) {
280
+ // Ambient is not used in CET.
281
+ // Emissive no longer exist in CET.
282
+ // Specular is rare (and we can currently only handle its general value,
283
+ // not its texture since the MeshStandardMaterial does not accept such a thing.)
284
+ textures = [gm.bump, gm.diffuse, gm.specular, gm.transparency];
285
+ }
286
+ if (gm instanceof GMaterialPBR) {
287
+ textures = [
288
+ (_a = gm.base) === null || _a === void 0 ? void 0 : _a.texture,
289
+ (_b = gm.emissive) === null || _b === void 0 ? void 0 : _b.texture,
290
+ (_c = gm.metallic) === null || _c === void 0 ? void 0 : _c.texture,
291
+ (_d = gm.normal) === null || _d === void 0 ? void 0 : _d.texture,
292
+ (_e = gm.occlusion) === null || _e === void 0 ? void 0 : _e.texture,
293
+ (_f = gm.opacity) === null || _f === void 0 ? void 0 : _f.texture,
294
+ (_g = gm.roughness) === null || _g === void 0 ? void 0 : _g.texture,
295
+ ];
296
+ }
297
+ return yield Promise.all(textures
298
+ .filter(isGMaterial3DTexture)
299
+ .map(getToImageLoadFunc(logger, renderEnvironment))
300
+ .map(getApplyImageConversionsFunc(logger, renderEnvironment))
301
+ .map(getToTextureLoadFunc(renderEnvironment))
302
+ .map(getApplyTexturePropertiesFromGMaterialFunc(logger)));
303
+ });
304
+ }
@@ -1,20 +1,20 @@
1
- import { Matrix } from "@babylonjs/core/Maths/math.vector.js";
2
- import { RenderEnv } from "../view/RenderEnv.js";
3
- import { CfgTransformNode } from "./CfgTransformNode.js";
4
- export declare class CfgContentRootNode extends CfgTransformNode {
5
- constructor(renderEnvironment: RenderEnv);
6
- get cfgClassName(): string;
7
- get originalMatrix(): Matrix;
8
- protected addInspectorProperties(): void;
9
- private get _inspectorLogGeometryCache();
10
- private set _inspectorLogGeometryCache(value);
11
- private get _inspectorLogMaterialCache();
12
- private set _inspectorLogMaterialCache(value);
13
- private get _inspectorLogSymNodeCache();
14
- private set _inspectorLogSymNodeCache(value);
15
- private get _inspectorLogTextureImageCache();
16
- private set _inspectorLogTextureImageCache(value);
17
- private get _inspectorLogDerivedNormalMapCache();
18
- private set _inspectorLogDerivedNormalMapCache(value);
19
- }
1
+ import { Matrix } from "@babylonjs/core/Maths/math.vector.js";
2
+ import { RenderEnv } from "../view/RenderEnv.js";
3
+ import { CfgTransformNode } from "./CfgTransformNode.js";
4
+ export declare class CfgContentRootNode extends CfgTransformNode {
5
+ constructor(renderEnvironment: RenderEnv);
6
+ get cfgClassName(): string;
7
+ get originalMatrix(): Matrix;
8
+ protected addInspectorProperties(): void;
9
+ private get _inspectorLogGeometryCache();
10
+ private set _inspectorLogGeometryCache(value);
11
+ private get _inspectorLogMaterialCache();
12
+ private set _inspectorLogMaterialCache(value);
13
+ private get _inspectorLogSymNodeCache();
14
+ private set _inspectorLogSymNodeCache(value);
15
+ private get _inspectorLogTextureImageCache();
16
+ private set _inspectorLogTextureImageCache(value);
17
+ private get _inspectorLogDerivedNormalMapCache();
18
+ private set _inspectorLogDerivedNormalMapCache(value);
19
+ }
20
20
  //# sourceMappingURL=CfgContentRootNode.d.ts.map