@configura/babylon-view 2.1.0-alpha.1 → 2.1.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 (134) hide show
  1. package/.eslintrc.json +5 -5
  2. package/LICENSE +201 -201
  3. package/README.md +1 -1
  4. package/dist/animation/AnimatableObject.d.ts +8 -8
  5. package/dist/animation/AnimatableObject.js +3 -3
  6. package/dist/animation/animator/Animator.d.ts +33 -33
  7. package/dist/animation/animator/Animator.js +58 -58
  8. package/dist/animation/animator/AnimatorEasing.d.ts +16 -16
  9. package/dist/animation/animator/AnimatorEasing.js +31 -31
  10. package/dist/animation/animator/AnimatorEasingMatrix.d.ts +14 -14
  11. package/dist/animation/animator/AnimatorEasingMatrix.js +16 -16
  12. package/dist/animation/animator/AnimatorHighlight.d.ts +16 -16
  13. package/dist/animation/animator/AnimatorHighlight.js +32 -32
  14. package/dist/animation/animator/AnimatorPointToPoint.d.ts +8 -8
  15. package/dist/animation/animator/AnimatorPointToPoint.js +14 -14
  16. package/dist/animation/animator/AnimatorQueue.d.ts +13 -13
  17. package/dist/animation/animator/AnimatorQueue.js +57 -57
  18. package/dist/animation/animator/AnimatorScale.d.ts +8 -8
  19. package/dist/animation/animator/AnimatorScale.js +13 -13
  20. package/dist/animation/animator/AnimatorSpin.d.ts +10 -10
  21. package/dist/animation/animator/AnimatorSpin.js +13 -13
  22. package/dist/animation/animator/EasingFunctions.d.ts +35 -35
  23. package/dist/animation/animator/EasingFunctions.js +137 -137
  24. package/dist/animation/coordinator/Coordinator.d.ts +28 -28
  25. package/dist/animation/coordinator/Coordinator.js +53 -53
  26. package/dist/animation/coordinator/CoordinatorDropAndSpin.d.ts +22 -22
  27. package/dist/animation/coordinator/CoordinatorDropAndSpin.js +138 -138
  28. package/dist/animation/coordinator/CoordinatorIdentity.d.ts +11 -11
  29. package/dist/animation/coordinator/CoordinatorIdentity.js +14 -14
  30. package/dist/animation/coordinator/CoordinatorNodeQueues.d.ts +18 -18
  31. package/dist/animation/coordinator/CoordinatorNodeQueues.js +50 -50
  32. package/dist/animation/coordinator/CoordinatorPulse.d.ts +21 -21
  33. package/dist/animation/coordinator/CoordinatorPulse.js +47 -47
  34. package/dist/animation/coordinator/CoordinatorPulseBounce.d.ts +14 -14
  35. package/dist/animation/coordinator/CoordinatorPulseBounce.js +35 -35
  36. package/dist/animation/coordinator/CoordinatorPulseHighlight.d.ts +13 -13
  37. package/dist/animation/coordinator/CoordinatorPulseHighlight.js +29 -29
  38. package/dist/animation/coordinator/CoordinatorPulseInflate.d.ts +14 -14
  39. package/dist/animation/coordinator/CoordinatorPulseInflate.js +23 -23
  40. package/dist/camera/CameraCreator.d.ts +5 -5
  41. package/dist/camera/CameraCreator.js +4 -4
  42. package/dist/camera/CfgArcRotateCameraPointersInput.d.ts +26 -26
  43. package/dist/camera/CfgArcRotateCameraPointersInput.js +266 -266
  44. package/dist/camera/CfgOrbitalCamera.d.ts +76 -76
  45. package/dist/camera/CfgOrbitalCamera.js +281 -281
  46. package/dist/camera/CfgOrbitalCameraControlProps.d.ts +14 -14
  47. package/dist/camera/CfgOrbitalCameraControlProps.js +7 -7
  48. package/dist/camera/GradingApplier.d.ts +3 -3
  49. package/dist/camera/GradingApplier.js +48 -48
  50. package/dist/engine/EngineCreator.d.ts +3 -3
  51. package/dist/engine/EngineCreator.js +10 -10
  52. package/dist/geometry/CfgGeometry.d.ts +29 -29
  53. package/dist/geometry/CfgGeometry.js +146 -146
  54. package/dist/geometry/CfgMesh.d.ts +10 -10
  55. package/dist/geometry/CfgMesh.js +38 -38
  56. package/dist/geometry/geoSplitter.d.ts +8 -8
  57. package/dist/geometry/geoSplitter.js +192 -192
  58. package/dist/geometry/stretch/CfgMorphTarget.d.ts +15 -15
  59. package/dist/geometry/stretch/CfgMorphTarget.js +65 -65
  60. package/dist/geometry/stretch/CfgStretchData.d.ts +116 -116
  61. package/dist/geometry/stretch/CfgStretchData.js +350 -350
  62. package/dist/geometry/stretch/CfgStretchMorphGeometry.d.ts +16 -16
  63. package/dist/geometry/stretch/CfgStretchMorphGeometry.js +95 -95
  64. package/dist/index.d.ts +16 -16
  65. package/dist/index.js +16 -16
  66. package/dist/io/CfgHistoryToCameraConfConnector.d.ts +31 -31
  67. package/dist/io/CfgHistoryToCameraConfConnector.js +90 -90
  68. package/dist/io/CfgIOCameraConfConnector.d.ts +35 -35
  69. package/dist/io/CfgIOCameraConfConnector.js +81 -81
  70. package/dist/io/CfgObservableStateToCameraConfConnector.d.ts +10 -10
  71. package/dist/io/CfgObservableStateToCameraConfConnector.js +11 -11
  72. package/dist/io/CfgWindowMessageToCameraConfConnector.d.ts +10 -10
  73. package/dist/io/CfgWindowMessageToCameraConfConnector.js +11 -11
  74. package/dist/light/CfgDirectionalLight.d.ts +8 -8
  75. package/dist/light/CfgDirectionalLight.js +18 -18
  76. package/dist/light/CfgHemisphericLight.d.ts +7 -7
  77. package/dist/light/CfgHemisphericLight.js +17 -17
  78. package/dist/light/CfgPointLight.d.ts +8 -8
  79. package/dist/light/CfgPointLight.js +18 -18
  80. package/dist/light/DefaultLightRig.d.ts +19 -19
  81. package/dist/light/DefaultLightRig.js +77 -77
  82. package/dist/light/LightRigCreator.d.ts +9 -9
  83. package/dist/light/LightRigCreator.js +3 -3
  84. package/dist/material/CfgMaterial.d.ts +68 -68
  85. package/dist/material/CfgMaterial.js +482 -482
  86. package/dist/material/DummyMaterialCreator.d.ts +4 -4
  87. package/dist/material/DummyMaterialCreator.js +15 -15
  88. package/dist/material/material.d.ts +18 -18
  89. package/dist/material/material.js +128 -128
  90. package/dist/material/texture.d.ts +14 -14
  91. package/dist/material/texture.js +306 -306
  92. package/dist/nodes/CfgContentRootNode.d.ts +19 -19
  93. package/dist/nodes/CfgContentRootNode.js +75 -75
  94. package/dist/nodes/CfgDeferredMeshNode.d.ts +55 -55
  95. package/dist/nodes/CfgDeferredMeshNode.js +378 -378
  96. package/dist/nodes/CfgProductNode.d.ts +127 -127
  97. package/dist/nodes/CfgProductNode.js +598 -598
  98. package/dist/nodes/CfgSymNode.d.ts +50 -50
  99. package/dist/nodes/CfgSymNode.js +249 -249
  100. package/dist/nodes/CfgSymRootNode.d.ts +45 -45
  101. package/dist/nodes/CfgSymRootNode.js +229 -229
  102. package/dist/nodes/CfgTransformNode.d.ts +33 -33
  103. package/dist/nodes/CfgTransformNode.js +83 -83
  104. package/dist/scene/SceneCreator.d.ts +6 -6
  105. package/dist/scene/SceneCreator.js +22 -22
  106. package/dist/utilities/CfgBoundingBox.d.ts +21 -21
  107. package/dist/utilities/CfgBoundingBox.js +81 -81
  108. package/dist/utilities/anchor/anchor.d.ts +50 -50
  109. package/dist/utilities/anchor/anchor.js +133 -133
  110. package/dist/utilities/anchor/anchorMap.d.ts +20 -20
  111. package/dist/utilities/anchor/anchorMap.js +111 -111
  112. package/dist/utilities/utilities3D.d.ts +70 -70
  113. package/dist/utilities/utilities3D.js +265 -265
  114. package/dist/utilities/utilitiesColor.d.ts +18 -18
  115. package/dist/utilities/utilitiesColor.js +50 -50
  116. package/dist/utilities/utilitiesImage.d.ts +6 -6
  117. package/dist/utilities/utilitiesImage.js +107 -107
  118. package/dist/utilities/utilitiesSymRootIdentifier.d.ts +7 -7
  119. package/dist/utilities/utilitiesSymRootIdentifier.js +26 -26
  120. package/dist/view/BaseView.d.ts +78 -78
  121. package/dist/view/BaseView.js +303 -303
  122. package/dist/view/BaseViewConfiguration.d.ts +32 -32
  123. package/dist/view/BaseViewConfiguration.js +10 -10
  124. package/dist/view/RenderEnv.d.ts +43 -43
  125. package/dist/view/RenderEnv.js +7 -7
  126. package/dist/view/SingleProductDefaultCameraView.d.ts +38 -38
  127. package/dist/view/SingleProductDefaultCameraView.js +149 -149
  128. package/dist/view/SingleProductDefaultCameraViewConfiguration.d.ts +44 -44
  129. package/dist/view/SingleProductDefaultCameraViewConfiguration.js +11 -11
  130. package/dist/view/SingleProductView.d.ts +44 -44
  131. package/dist/view/SingleProductView.js +212 -212
  132. package/dist/view/SingleProductViewConfiguration.d.ts +32 -32
  133. package/dist/view/SingleProductViewConfiguration.js +19 -19
  134. package/package.json +5 -5
@@ -1,482 +1,482 @@
1
- import { PBRMaterial } from "@babylonjs/core/Materials/PBR/pbrMaterial.js";
2
- import { Texture } from "@babylonjs/core/Materials/Textures/texture.js";
3
- import { Color3 } from "@babylonjs/core/Maths/math.color.js";
4
- import { Color } from "@configura/web-core/dist/cm/basic/Color.js";
5
- import { GMaterialPBR } from "@configura/web-core/dist/cm/core3D/GMaterialPBR.js";
6
- import { LogObservable } from "@configura/web-utilities";
7
- import { hsl2rgb, rgb2hsl, toColor3 } from "../utilities/utilitiesColor.js";
8
- function findTexture(textures, texture) {
9
- var _a;
10
- if (texture === undefined) {
11
- return undefined;
12
- }
13
- return (_a = textures.find((gmv) => gmv.gm.type === texture.type)) === null || _a === void 0 ? void 0 : _a.tx;
14
- }
15
- // Constants (power of two) used to create the index for the variant
16
- const DBL = 1;
17
- const BTF = 2;
18
- const FLP = 4;
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
- */
29
- export class CfgMaterial {
30
- constructor(material, maxSimultaneousLights) {
31
- this.isTransparent = false;
32
- this.logger = new LogObservable();
33
- // Side orientation needs to be set, or the materials will be inside-out
34
- material.sideOrientation = PBRMaterial.CounterClockWiseSideOrientation;
35
- material.maxSimultaneousLights = maxSimultaneousLights;
36
- material.allowShaderHotSwapping = true;
37
- // Depending on which environment map is used, you might want to disable the radiance over
38
- // alpha setting since a very strong light can cause too much of the effect in for example
39
- // glass surfaces causing glitch-like effects. You can read more about it here:
40
- // https://doc.babylonjs.com/how_to/physically_based_rendering_master
41
- material.useRadianceOverAlpha = false;
42
- this._material = material;
43
- this._variants = new Array(8);
44
- this._variants[this.indexFromMaterial(material)] = material;
45
- }
46
- /** This material is supposed to be rendered as double sided by default. */
47
- isDoubleSided() {
48
- return !this._material.backFaceCulling;
49
- }
50
- static makeFromTexture(renderEnvironment, texture, sourcePath) {
51
- var _a, _b;
52
- sourcePath.push("makeFromTexture");
53
- let name = "(Img)";
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();
55
- if (fileName) {
56
- name += " " + fileName;
57
- }
58
- const material = new PBRMaterial(name, renderEnvironment.scene);
59
- material.albedoTexture = texture;
60
- material.roughness = 1;
61
- material.metallic = 0;
62
- // TODO Babylon: What happens if the texture has an alpha map? Compare to Three.js and CET
63
- return new this(material, renderEnvironment.lightRig.lightCount);
64
- }
65
- static makeFromGm(renderEnvironment, meta, gMaterial, textures) {
66
- var _a, _b;
67
- // Use the materials given name or fallback to the materialKey, if any.
68
- const miscName = (_a = gMaterial.misc) === null || _a === void 0 ? void 0 : _a.get("name");
69
- const name = typeof miscName == "string" && miscName.length > 0
70
- ? miscName
71
- : (_b = gMaterial.materialKey) !== null && _b !== void 0 ? _b : "";
72
- const { material, transparent, doubleSided } = gMaterial instanceof GMaterialPBR
73
- ? this.makeFromGmPBR(renderEnvironment, meta, gMaterial, textures, name)
74
- : this.makeFromGmClassic(renderEnvironment, meta, gMaterial, textures, name);
75
- makeMaterialDoubleSided(material, doubleSided);
76
- const cfgMaterial = new this(material, renderEnvironment.lightRig.lightCount);
77
- cfgMaterial.isTransparent = transparent;
78
- return cfgMaterial;
79
- }
80
- static makeFromGmPBR(renderEnvironment, meta, gMaterial, textures, name) {
81
- var _a;
82
- meta.sourcePath.push("makeFromGmPBR");
83
- name = "(PBR) " + name;
84
- const { doubleSided, base, emissive, metallic, normal, occlusion, opacity, refraction, roughness, } = gMaterial;
85
- const material = new PBRMaterial(name, renderEnvironment.scene);
86
- // Specular AA can help a lot with for example edges of very shiny materials, like the
87
- // base of our demo chair when the base overlaps / is overlapped by other geometry.
88
- material.enableSpecularAntiAliasing = true;
89
- material.albedoColor = Color3.White();
90
- if (base !== undefined) {
91
- // TODO: Double check if the color space used in CmSym is linear or gamma
92
- material.albedoColor = toColor3(base.factor).toLinearSpace();
93
- const texture = findTexture(textures, base.texture);
94
- if (texture !== undefined) {
95
- material.albedoTexture = texture;
96
- }
97
- }
98
- if (emissive !== undefined) {
99
- material.emissiveColor = toColor3((base === null || base === void 0 ? void 0 : base.factor) || Color.BLACK).toLinearSpace();
100
- const texture = findTexture(textures, emissive.texture);
101
- if (texture !== undefined) {
102
- material.emissiveTexture = texture;
103
- }
104
- }
105
- // Classic defaults to 0.1, but materials should either be 1 (metallic) or 0 (dielectric).
106
- // PBR has been defined to use 0.0 by default.
107
- material.metallic = 0;
108
- if (metallic !== undefined) {
109
- material.metallic = metallic.factor;
110
- const channel = metallic.channel;
111
- if (channel !== undefined) {
112
- const texture = findTexture(textures, metallic.texture);
113
- if (texture !== undefined) {
114
- material.metallicTexture = texture;
115
- material.useMetallnessFromMetallicTextureBlue = channel === "b"; // false is "r"
116
- }
117
- }
118
- }
119
- if (normal !== undefined) {
120
- const texture = findTexture(textures, normal.texture);
121
- if (texture !== undefined) {
122
- texture.level = normal.scale;
123
- material.bumpTexture = texture;
124
- }
125
- }
126
- /**
127
- * This flag indicates that a material might contain semi or fully transparent pixels and
128
- * that alpha blending / testing has been enabled.
129
- *
130
- * For a PBR material to be considered transparent it must fulfill the following:
131
- * 1) An opacity section is defined.
132
- * 2) The opacity section must specify a non-opaque blending mode.
133
- * 3A) The "factor" must be < 1,
134
- * 3B) OR an opacity texture is supplied,
135
- * 3C) OR "useAlphaFromBase" is set and an albedo (base) texture exists.
136
- */
137
- let transparent = false;
138
- if ((opacity === null || opacity === void 0 ? void 0 : opacity.mode) === "translucent" ||
139
- (opacity === null || opacity === void 0 ? void 0 : opacity.mode) === "mask" ||
140
- (opacity === null || opacity === void 0 ? void 0 : opacity.mode) === "translucentAndMask") {
141
- material.alpha = opacity.factor;
142
- transparent = material.alpha < 1;
143
- if (opacity.useAlphaFromBase === true) {
144
- if (material.albedoTexture !== null) {
145
- material.albedoTexture.hasAlpha = true; // Or alpha blending won't be enabled
146
- material.useAlphaFromAlbedoTexture = true;
147
- transparent = true;
148
- }
149
- }
150
- else {
151
- const texture = findTexture(textures, opacity.texture);
152
- if (texture !== undefined) {
153
- material.opacityTexture = texture;
154
- material.opacityTexture.getAlphaFromRGB = opacity.channels === "r";
155
- transparent = true;
156
- }
157
- }
158
- if (transparent) {
159
- // Enabling separateCullingPass will first render any back facing triangles, then
160
- // any front facing. This type of "back then front" rendering helps with some common
161
- // rendering issues for meshes with transparent materials.
162
- material.separateCullingPass = true;
163
- if (opacity.mode === "mask") {
164
- // Pure mask mode, only do alpha testing and no blending
165
- material.transparencyMode = PBRMaterial.PBRMATERIAL_ALPHATEST;
166
- // Masked pixels are either visible or not visible and should be rendered with
167
- // the depth buffer enabled. This solves some alpha blending issues.
168
- material.forceDepthWrite = true;
169
- }
170
- else {
171
- // "translucent" and "translucentAndMask" are both rendered as "alpha test and
172
- // blend". The proper transparency mode for "translucent" is really "alpha
173
- // blend" but it's not much use to render very, very transparent pixels to the
174
- // frame buffer. Rejecting them early can speed up performance by reducing
175
- // overdraw and can also help with some alpha blending issues.
176
- material.transparencyMode = PBRMaterial.PBRMATERIAL_ALPHATESTANDBLEND;
177
- }
178
- // Since we use alpha testing in all three modes above, make sure it is turned on.
179
- // WARNING: Babylon.js (at least 4.1.0) will not do pure alpha testing without this.
180
- material.forceAlphaTest = true;
181
- material.alphaCutOff =
182
- opacity.mode === "translucent" ? 0.01 : opacity.maskThreshold;
183
- }
184
- }
185
- if (occlusion !== undefined) {
186
- material.ambientTextureStrength = occlusion.strength;
187
- const channel = occlusion.channel;
188
- const texture = occlusion.texture;
189
- if (channel !== undefined && texture !== undefined) {
190
- // Use useAmbientOcclusionFromMetallicTextureRed if using a linked ORM texture
191
- material.useAmbientInGrayScale = channel === "r"; // false means alpha ("a")
192
- const texture = findTexture(textures, occlusion.texture);
193
- if (texture !== undefined) {
194
- material.ambientTexture = texture;
195
- }
196
- }
197
- }
198
- if (refraction !== undefined) {
199
- // TODO: Double check that this is defined the same as CET and not the inverse.
200
- // For example, use a sphere with ior >= 1.5 and check to see if it turns the
201
- // background up-side-down.
202
- material.subSurface.refractionIntensity = 1.0;
203
- if (refraction.ior !== undefined) {
204
- material.subSurface.indexOfRefraction = refraction.ior;
205
- }
206
- // Hack, see KNOWN ISSUE below.
207
- material.subSurface.isRefractionEnabled = transparent;
208
- // By default the refraction intensity is 1.0 which means that the refraction fully
209
- // fully takes over the color of the material, which is not what we want. Setting it to
210
- // 0.5 for now which looks like a good compromise to bring base color back and at the
211
- // same time show the refraction.
212
- material.subSurface.refractionIntensity = 0.5;
213
- /* KNOWN ISSUE
214
- *
215
- * Enabling refraction on the white test materials will make them very dark. You can
216
- * adjust that by enabling subSurface.linkRefractionWithTransparency, but then the
217
- * material gets quite dark when it is very transparent. Using a lower
218
- * refractionIntensity above works around that somewhat.
219
- *
220
- * It is not yet known what the "correct" way is to do this, and unfortunately glTF 2.0
221
- * does not support refraction (without an extension) so we don't have a good reference
222
- * to lean on.
223
- *
224
- * More work is needed here comparing Babylon, RedSDK (CET) and perhaps Blender.
225
- * Reading up more on the maths behind refraction and PBR could also be good.
226
- *
227
- * Note: Predicating subSurface.isRefractionEnabled on say transparent (which we do
228
- * above) is NOT correct since the IOR setting affects non-transparent materials in
229
- * RedSDK. And in any case, transparency is really per-pixel, not per material.
230
- */
231
- }
232
- // Classic uses a default roughness value of 0.9. PBR is defined to use 0.5
233
- // NOTE: The roughness code below needs to run AFTER metallic due to dependencies.
234
- material.roughness = 0.5;
235
- if (roughness !== undefined) {
236
- material.roughness = roughness.factor;
237
- const channel = roughness.channel;
238
- const texture = findTexture(textures, roughness.texture);
239
- if (channel !== undefined && texture !== undefined) {
240
- material.useRoughnessFromMetallicTextureGreen = channel === "g";
241
- material.useRoughnessFromMetallicTextureAlpha = channel === "a";
242
- // KNOWN ISSUE
243
- // Babylon.js (as well as glTF) only supports a single texture for both metallness
244
- // and roughness. See comments in GMaterialPBR.ts Give priority to metallic texture.
245
- material.metallicTexture = (_a = material.metallicTexture) !== null && _a !== void 0 ? _a : texture;
246
- }
247
- }
248
- return { material, transparent, doubleSided };
249
- }
250
- static makeFromGmClassic(renderEnvironment, meta, gMaterial, textures, name) {
251
- meta.sourcePath.push("makeFromGmClassic");
252
- name = "(GM) " + name;
253
- const { doubleSided, redCustomProperties: redEngineCustomProperties, reflection, bump, diffuse, specular, transparency, } = gMaterial;
254
- const bumpTexture = findTexture(textures, bump);
255
- const diffuseTexture = findTexture(textures, diffuse);
256
- const transparencyTexture = findTexture(textures, transparency);
257
- const material = new PBRMaterial(name, renderEnvironment.scene);
258
- // Specular AA can help a lot with for example edges of very shiny materials, like the
259
- // base of our demo chair when the base overlaps / is overlapped by other geometry.
260
- material.enableSpecularAntiAliasing = true;
261
- if (bump !== undefined && bumpTexture !== undefined) {
262
- material.bumpTexture = bumpTexture;
263
- // Bump the bump strength slightly to visually better match the render results in CET.
264
- bumpTexture.level = 1.2;
265
- }
266
- if (diffuse !== undefined) {
267
- if (diffuseTexture !== undefined) {
268
- diffuseTexture.hasAlpha = false;
269
- material.albedoTexture = diffuseTexture;
270
- }
271
- else {
272
- // From CmSym spec: "If an image url is specified it overrides the color."
273
- material.albedoColor = toColor3(diffuse.c || Color.BLACK).toLinearSpace();
274
- }
275
- }
276
- let transparent = false;
277
- if (transparency !== undefined) {
278
- if (transparency.opacity) {
279
- if (transparency.opacity < 1) {
280
- material.alpha = transparency.opacity;
281
- transparent = true;
282
- }
283
- }
284
- if (transparencyTexture) {
285
- material.opacityTexture = transparencyTexture;
286
- transparent = true;
287
- }
288
- }
289
- if (transparent) {
290
- // This is a tradeoff. Most 3D engines will by default render with depth write off for
291
- // alpha blended materials, but keep it on for alpha tested materials. We don't have
292
- // such a distinction, yet.
293
- //
294
- // For products like the forklift, it is much better with no depth write.
295
- // On the flip side, without it, the plants and other similar meshes look like crap.
296
- material.forceDepthWrite = true;
297
- // Default to alpha blend + alpha test
298
- material.transparencyMode = PBRMaterial.PBRMATERIAL_ALPHATESTANDBLEND;
299
- // Do not draw very, very transparent pixels since forceDepthWrite is enabled
300
- material.alphaCutOff = 0.05;
301
- // Transparent materials usually needs "back then front" rendering.
302
- material.separateCullingPass = true;
303
- }
304
- /*
305
- * In Babylon.js (and previous Three.js) we use PBR (physically based rendering) and
306
- * use the metalness-roughness way to describe the reflective properties of the materials.
307
- * Here is good overview: https://threejs.org/examples/?q=var#webgl_materials_variations_standard
308
- *
309
- * Specular amount => reduced specular => reduce metalness
310
- * Specular area / exponent => reduced exponent => reduce roughness
311
- * Specular exponent => 0.01 = 10000 and 1 = 0 see gmEditor.cm for more details.
312
- *
313
- * Reflection amount => reduced amount => reduced metalness
314
- * Reflection sharpness => reduced sharpness => increase roughness
315
- * In cmsym sharpness is saved as a customProperty with the name reflectionGlossiness
316
- *
317
- * In our conversion we weigh specular and reflection from cmsym together
318
- * if they are both defined, otherwise we convert them by them self.
319
- */
320
- let specularContribution;
321
- if (specular !== undefined && specular.exponent !== undefined) {
322
- const spec = toColor3(specular.c);
323
- const [, , specularColorLightness] = rgb2hsl(spec.r, spec.g, spec.b);
324
- specularContribution =
325
- Math.sqrt(1 / (specular.exponent + 1)) +
326
- 1 -
327
- specular.amount * specularColorLightness;
328
- }
329
- let reflectionSharpness;
330
- if (redEngineCustomProperties !== undefined) {
331
- const reflectionGlossiness = redEngineCustomProperties.get("reflectionGlossiness");
332
- if (reflectionGlossiness && typeof reflectionGlossiness === "number") {
333
- reflectionSharpness = 1 - reflectionGlossiness;
334
- }
335
- }
336
- if (specularContribution !== undefined) {
337
- if (reflectionSharpness !== undefined) {
338
- material.roughness = (reflectionSharpness + specularContribution) / 2;
339
- }
340
- else {
341
- material.roughness = specularContribution;
342
- }
343
- }
344
- else {
345
- material.roughness = 0.9; // We default to non-reflective material to better match CET
346
- }
347
- material.metallic = 0.1; // We default to non-reflective material to better match CET
348
- if (reflection !== undefined && reflection.amount !== undefined) {
349
- const reflectionAmount = reflection.amount;
350
- material.metallic = reflectionAmount;
351
- // Since the Red Engine in CET needs the sum of reflectiveness and color to be max 1
352
- // very reflective materials will have a color close to black. Babylon.js needs very
353
- // reflective materials to have a very light color. See WRD-119 in Jira.
354
- if (diffuseTexture === undefined && material.roughness < reflectionAmount - 0.3) {
355
- // PBR material albedo color is in linear color space. The conversion to gamma
356
- // (normal, sRGB) color space is done to work exactly the same as the previous
357
- // Three.js version of the code. That said, if "reflectionAmount" is in a
358
- // linear space, then perhaps it is more correct without it...
359
- const color = material.albedoColor.toGammaSpace();
360
- const [h, s, l] = rgb2hsl(color.r, color.g, color.b);
361
- if (l < reflectionAmount) {
362
- material.albedoColor = Color3.FromArray(hsl2rgb(h, s, reflectionAmount)).toLinearSpace();
363
- }
364
- }
365
- }
366
- return { material, transparent, doubleSided };
367
- }
368
- indexFromOptions(doubleSided, backThenFront, flipTextures) {
369
- return (doubleSided ? DBL : 0) + (backThenFront ? BTF : 0) + (flipTextures ? FLP : 0);
370
- }
371
- indexFromMaterial(material) {
372
- // Materials are never flipped by default, so the flipTextures (FLP) flag is always zero
373
- return (material.backFaceCulling ? 0 : DBL) + (material.separateCullingPass ? BTF : 0);
374
- }
375
- cloneSuffix(doubleSided, backThenFront, flipTextures) {
376
- return ` (clone${doubleSided ? ",dbl" : ""}${backThenFront ? ",btf" : ""}${flipTextures ? ",flp" : ""});`;
377
- }
378
- /**
379
- * Returns the PBRMaterial associated with this CfgMaterial, optionally modified.
380
- *
381
- * If none of the optional parameters are set, the returned material is the one initially
382
- * supplied when the CfgMaterial was created.
383
- *
384
- * If any of the optional parameters are supplied, you will get back a material that has those
385
- * options set to the given values. The returned material will be a clone if needed, the
386
- * initial material is NOT affected by this.
387
- *
388
- * It is safe to call this method multiple times, this class keeps an internal material cache.
389
- *
390
- * @warning: Do not modify the returned material in any way since it might be shared with
391
- * other parts of the model!
392
- *
393
- * @param doubleSided Makes sure that backFaceCulling is OFF in the returned material.
394
- * @param backThenFront Makes sure that separateCullingPass is ON in the returned material.
395
- * @param flipTextures Flips the textures in the material along the Y-axis.
396
- */
397
- getPBRMaterial(doubleSided, backThenFront, flipTextures) {
398
- if (doubleSided === undefined) {
399
- doubleSided = !this._material.backFaceCulling;
400
- }
401
- if (backThenFront === undefined) {
402
- backThenFront = this._material.separateCullingPass;
403
- }
404
- if (flipTextures === undefined) {
405
- // Materials are never flipped by default.
406
- flipTextures = false;
407
- }
408
- const index = this.indexFromOptions(doubleSided, backThenFront, flipTextures);
409
- if (this._variants[index] !== undefined) {
410
- return this._variants[index];
411
- }
412
- const clone = this._material.clone(this._material.name + this.cloneSuffix(doubleSided, backThenFront, flipTextures));
413
- clone.separateCullingPass = backThenFront;
414
- makeMaterialDoubleSided(clone, doubleSided);
415
- if (flipTextures) {
416
- flipMaterialTextures(clone);
417
- }
418
- this._variants[index] = clone;
419
- return clone;
420
- }
421
- }
422
- /**
423
- * The exact changes this method makes depends on the material's separateCullingPass setting, so
424
- * make sure make any changes to that property before calling this method.
425
- */
426
- export function makeMaterialDoubleSided(material, doubleSided) {
427
- if (doubleSided) {
428
- // SeparateCullingPass breaks twoSidedLightning by not flipping the normals when
429
- // rendering the back faces of the triangles. forceNormalForward works around that
430
- // but is a bit slower due to more complex shader calculations so only use when needed.
431
- material.backFaceCulling = false;
432
- material.twoSidedLighting = !material.separateCullingPass;
433
- material.forceNormalForward = material.separateCullingPass;
434
- }
435
- else {
436
- material.backFaceCulling = true; // Don't render backside of triangles
437
- material.twoSidedLighting = false; // Don't flip normals on back face of triangles
438
- material.forceNormalForward = false; // Don't change the normals to front facing
439
- }
440
- }
441
- /**
442
- * Clones all the textures used in the material and flips them along the y-axis by inverting the
443
- * texture's vScale, vOffset and wRotation.
444
- *
445
- * @note: The flipped state is not stored in the material it self, so calling this method twice on
446
- * the same material will undo the flip.
447
- */
448
- function flipMaterialTextures(material) {
449
- material.albedoTexture = cloneAndFlipTexture(material.albedoTexture);
450
- material.bumpTexture = cloneAndFlipTexture(material.bumpTexture);
451
- material.ambientTexture = cloneAndFlipTexture(material.ambientTexture);
452
- material.opacityTexture = cloneAndFlipTexture(material.opacityTexture);
453
- material.emissiveTexture = cloneAndFlipTexture(material.emissiveTexture);
454
- material.metallicTexture = cloneAndFlipTexture(material.metallicTexture);
455
- material.lightmapTexture = cloneAndFlipTexture(material.lightmapTexture);
456
- if (material.refractionTexture !== null) {
457
- // The refraction texture is a bit special since it is a getter/setter with side effects.
458
- // As of this writing (Babylon 4.1.0), the only side effect is to change the value of
459
- // isRefractionEnabled so we save and reapply the value after the flip.
460
- const enabled = material.subSurface.isRefractionEnabled;
461
- material.refractionTexture = cloneAndFlipTexture(material.refractionTexture);
462
- material.subSurface.isRefractionEnabled = enabled;
463
- }
464
- material.reflectivityTexture = cloneAndFlipTexture(material.reflectivityTexture);
465
- material.microSurfaceTexture = cloneAndFlipTexture(material.microSurfaceTexture);
466
- }
467
- /**
468
- * Returns undefined if texture is undefined.
469
- * Returns original texture if it isn't of type "Texture".
470
- * Otherwise, Clones and flips the texture along the y-axis by inverting vScale, vOffset and wAng.
471
- */
472
- function cloneAndFlipTexture(texture) {
473
- if (texture instanceof Texture) {
474
- texture = texture.clone();
475
- if (texture instanceof Texture) {
476
- texture.vScale *= -1; // Flips along y-axis
477
- texture.vOffset *= -1; // Adjust texture offset due to flip
478
- texture.wAng *= -1; // Adjust rotation direction due to the flip
479
- }
480
- }
481
- return texture;
482
- }
1
+ import { PBRMaterial } from "@babylonjs/core/Materials/PBR/pbrMaterial.js";
2
+ import { Texture } from "@babylonjs/core/Materials/Textures/texture.js";
3
+ import { Color3 } from "@babylonjs/core/Maths/math.color.js";
4
+ import { Color } from "@configura/web-core/dist/cm/basic/Color.js";
5
+ import { GMaterialPBR } from "@configura/web-core/dist/cm/core3D/GMaterialPBR.js";
6
+ import { LogObservable } from "@configura/web-utilities";
7
+ import { hsl2rgb, rgb2hsl, toColor3 } from "../utilities/utilitiesColor.js";
8
+ function findTexture(textures, texture) {
9
+ var _a;
10
+ if (texture === undefined) {
11
+ return undefined;
12
+ }
13
+ return (_a = textures.find((gmv) => gmv.gm.type === texture.type)) === null || _a === void 0 ? void 0 : _a.tx;
14
+ }
15
+ // Constants (power of two) used to create the index for the variant
16
+ const DBL = 1;
17
+ const BTF = 2;
18
+ const FLP = 4;
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
+ */
29
+ export class CfgMaterial {
30
+ constructor(material, maxSimultaneousLights) {
31
+ this.isTransparent = false;
32
+ this.logger = new LogObservable();
33
+ // Side orientation needs to be set, or the materials will be inside-out
34
+ material.sideOrientation = PBRMaterial.CounterClockWiseSideOrientation;
35
+ material.maxSimultaneousLights = maxSimultaneousLights;
36
+ material.allowShaderHotSwapping = true;
37
+ // Depending on which environment map is used, you might want to disable the radiance over
38
+ // alpha setting since a very strong light can cause too much of the effect in for example
39
+ // glass surfaces causing glitch-like effects. You can read more about it here:
40
+ // https://doc.babylonjs.com/how_to/physically_based_rendering_master
41
+ material.useRadianceOverAlpha = false;
42
+ this._material = material;
43
+ this._variants = new Array(8);
44
+ this._variants[this.indexFromMaterial(material)] = material;
45
+ }
46
+ /** This material is supposed to be rendered as double sided by default. */
47
+ isDoubleSided() {
48
+ return !this._material.backFaceCulling;
49
+ }
50
+ static makeFromTexture(renderEnvironment, texture, sourcePath) {
51
+ var _a, _b;
52
+ sourcePath.push("makeFromTexture");
53
+ let name = "(Img)";
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();
55
+ if (fileName) {
56
+ name += " " + fileName;
57
+ }
58
+ const material = new PBRMaterial(name, renderEnvironment.scene);
59
+ material.albedoTexture = texture;
60
+ material.roughness = 1;
61
+ material.metallic = 0;
62
+ // TODO Babylon: What happens if the texture has an alpha map? Compare to Three.js and CET
63
+ return new this(material, renderEnvironment.lightRig.lightCount);
64
+ }
65
+ static makeFromGm(renderEnvironment, meta, gMaterial, textures) {
66
+ var _a, _b;
67
+ // Use the materials given name or fallback to the materialKey, if any.
68
+ const miscName = (_a = gMaterial.misc) === null || _a === void 0 ? void 0 : _a.get("name");
69
+ const name = typeof miscName == "string" && miscName.length > 0
70
+ ? miscName
71
+ : (_b = gMaterial.materialKey) !== null && _b !== void 0 ? _b : "";
72
+ const { material, transparent, doubleSided } = gMaterial instanceof GMaterialPBR
73
+ ? this.makeFromGmPBR(renderEnvironment, meta, gMaterial, textures, name)
74
+ : this.makeFromGmClassic(renderEnvironment, meta, gMaterial, textures, name);
75
+ makeMaterialDoubleSided(material, doubleSided);
76
+ const cfgMaterial = new this(material, renderEnvironment.lightRig.lightCount);
77
+ cfgMaterial.isTransparent = transparent;
78
+ return cfgMaterial;
79
+ }
80
+ static makeFromGmPBR(renderEnvironment, meta, gMaterial, textures, name) {
81
+ var _a;
82
+ meta.sourcePath.push("makeFromGmPBR");
83
+ name = "(PBR) " + name;
84
+ const { doubleSided, base, emissive, metallic, normal, occlusion, opacity, refraction, roughness, } = gMaterial;
85
+ const material = new PBRMaterial(name, renderEnvironment.scene);
86
+ // Specular AA can help a lot with for example edges of very shiny materials, like the
87
+ // base of our demo chair when the base overlaps / is overlapped by other geometry.
88
+ material.enableSpecularAntiAliasing = true;
89
+ material.albedoColor = Color3.White();
90
+ if (base !== undefined) {
91
+ // TODO: Double check if the color space used in CmSym is linear or gamma
92
+ material.albedoColor = toColor3(base.factor).toLinearSpace();
93
+ const texture = findTexture(textures, base.texture);
94
+ if (texture !== undefined) {
95
+ material.albedoTexture = texture;
96
+ }
97
+ }
98
+ if (emissive !== undefined) {
99
+ material.emissiveColor = toColor3((base === null || base === void 0 ? void 0 : base.factor) || Color.BLACK).toLinearSpace();
100
+ const texture = findTexture(textures, emissive.texture);
101
+ if (texture !== undefined) {
102
+ material.emissiveTexture = texture;
103
+ }
104
+ }
105
+ // Classic defaults to 0.1, but materials should either be 1 (metallic) or 0 (dielectric).
106
+ // PBR has been defined to use 0.0 by default.
107
+ material.metallic = 0;
108
+ if (metallic !== undefined) {
109
+ material.metallic = metallic.factor;
110
+ const channel = metallic.channel;
111
+ if (channel !== undefined) {
112
+ const texture = findTexture(textures, metallic.texture);
113
+ if (texture !== undefined) {
114
+ material.metallicTexture = texture;
115
+ material.useMetallnessFromMetallicTextureBlue = channel === "b"; // false is "r"
116
+ }
117
+ }
118
+ }
119
+ if (normal !== undefined) {
120
+ const texture = findTexture(textures, normal.texture);
121
+ if (texture !== undefined) {
122
+ texture.level = normal.scale;
123
+ material.bumpTexture = texture;
124
+ }
125
+ }
126
+ /**
127
+ * This flag indicates that a material might contain semi or fully transparent pixels and
128
+ * that alpha blending / testing has been enabled.
129
+ *
130
+ * For a PBR material to be considered transparent it must fulfill the following:
131
+ * 1) An opacity section is defined.
132
+ * 2) The opacity section must specify a non-opaque blending mode.
133
+ * 3A) The "factor" must be < 1,
134
+ * 3B) OR an opacity texture is supplied,
135
+ * 3C) OR "useAlphaFromBase" is set and an albedo (base) texture exists.
136
+ */
137
+ let transparent = false;
138
+ if ((opacity === null || opacity === void 0 ? void 0 : opacity.mode) === "translucent" ||
139
+ (opacity === null || opacity === void 0 ? void 0 : opacity.mode) === "mask" ||
140
+ (opacity === null || opacity === void 0 ? void 0 : opacity.mode) === "translucentAndMask") {
141
+ material.alpha = opacity.factor;
142
+ transparent = material.alpha < 1;
143
+ if (opacity.useAlphaFromBase === true) {
144
+ if (material.albedoTexture !== null) {
145
+ material.albedoTexture.hasAlpha = true; // Or alpha blending won't be enabled
146
+ material.useAlphaFromAlbedoTexture = true;
147
+ transparent = true;
148
+ }
149
+ }
150
+ else {
151
+ const texture = findTexture(textures, opacity.texture);
152
+ if (texture !== undefined) {
153
+ material.opacityTexture = texture;
154
+ material.opacityTexture.getAlphaFromRGB = opacity.channels === "r";
155
+ transparent = true;
156
+ }
157
+ }
158
+ if (transparent) {
159
+ // Enabling separateCullingPass will first render any back facing triangles, then
160
+ // any front facing. This type of "back then front" rendering helps with some common
161
+ // rendering issues for meshes with transparent materials.
162
+ material.separateCullingPass = true;
163
+ if (opacity.mode === "mask") {
164
+ // Pure mask mode, only do alpha testing and no blending
165
+ material.transparencyMode = PBRMaterial.PBRMATERIAL_ALPHATEST;
166
+ // Masked pixels are either visible or not visible and should be rendered with
167
+ // the depth buffer enabled. This solves some alpha blending issues.
168
+ material.forceDepthWrite = true;
169
+ }
170
+ else {
171
+ // "translucent" and "translucentAndMask" are both rendered as "alpha test and
172
+ // blend". The proper transparency mode for "translucent" is really "alpha
173
+ // blend" but it's not much use to render very, very transparent pixels to the
174
+ // frame buffer. Rejecting them early can speed up performance by reducing
175
+ // overdraw and can also help with some alpha blending issues.
176
+ material.transparencyMode = PBRMaterial.PBRMATERIAL_ALPHATESTANDBLEND;
177
+ }
178
+ // Since we use alpha testing in all three modes above, make sure it is turned on.
179
+ // WARNING: Babylon.js (at least 4.1.0) will not do pure alpha testing without this.
180
+ material.forceAlphaTest = true;
181
+ material.alphaCutOff =
182
+ opacity.mode === "translucent" ? 0.01 : opacity.maskThreshold;
183
+ }
184
+ }
185
+ if (occlusion !== undefined) {
186
+ material.ambientTextureStrength = occlusion.strength;
187
+ const channel = occlusion.channel;
188
+ const texture = occlusion.texture;
189
+ if (channel !== undefined && texture !== undefined) {
190
+ // Use useAmbientOcclusionFromMetallicTextureRed if using a linked ORM texture
191
+ material.useAmbientInGrayScale = channel === "r"; // false means alpha ("a")
192
+ const texture = findTexture(textures, occlusion.texture);
193
+ if (texture !== undefined) {
194
+ material.ambientTexture = texture;
195
+ }
196
+ }
197
+ }
198
+ if (refraction !== undefined) {
199
+ // TODO: Double check that this is defined the same as CET and not the inverse.
200
+ // For example, use a sphere with ior >= 1.5 and check to see if it turns the
201
+ // background up-side-down.
202
+ material.subSurface.refractionIntensity = 1.0;
203
+ if (refraction.ior !== undefined) {
204
+ material.subSurface.indexOfRefraction = refraction.ior;
205
+ }
206
+ // Hack, see KNOWN ISSUE below.
207
+ material.subSurface.isRefractionEnabled = transparent;
208
+ // By default the refraction intensity is 1.0 which means that the refraction fully
209
+ // fully takes over the color of the material, which is not what we want. Setting it to
210
+ // 0.5 for now which looks like a good compromise to bring base color back and at the
211
+ // same time show the refraction.
212
+ material.subSurface.refractionIntensity = 0.5;
213
+ /* KNOWN ISSUE
214
+ *
215
+ * Enabling refraction on the white test materials will make them very dark. You can
216
+ * adjust that by enabling subSurface.linkRefractionWithTransparency, but then the
217
+ * material gets quite dark when it is very transparent. Using a lower
218
+ * refractionIntensity above works around that somewhat.
219
+ *
220
+ * It is not yet known what the "correct" way is to do this, and unfortunately glTF 2.0
221
+ * does not support refraction (without an extension) so we don't have a good reference
222
+ * to lean on.
223
+ *
224
+ * More work is needed here comparing Babylon, RedSDK (CET) and perhaps Blender.
225
+ * Reading up more on the maths behind refraction and PBR could also be good.
226
+ *
227
+ * Note: Predicating subSurface.isRefractionEnabled on say transparent (which we do
228
+ * above) is NOT correct since the IOR setting affects non-transparent materials in
229
+ * RedSDK. And in any case, transparency is really per-pixel, not per material.
230
+ */
231
+ }
232
+ // Classic uses a default roughness value of 0.9. PBR is defined to use 0.5
233
+ // NOTE: The roughness code below needs to run AFTER metallic due to dependencies.
234
+ material.roughness = 0.5;
235
+ if (roughness !== undefined) {
236
+ material.roughness = roughness.factor;
237
+ const channel = roughness.channel;
238
+ const texture = findTexture(textures, roughness.texture);
239
+ if (channel !== undefined && texture !== undefined) {
240
+ material.useRoughnessFromMetallicTextureGreen = channel === "g";
241
+ material.useRoughnessFromMetallicTextureAlpha = channel === "a";
242
+ // KNOWN ISSUE
243
+ // Babylon.js (as well as glTF) only supports a single texture for both metallness
244
+ // and roughness. See comments in GMaterialPBR.ts Give priority to metallic texture.
245
+ material.metallicTexture = (_a = material.metallicTexture) !== null && _a !== void 0 ? _a : texture;
246
+ }
247
+ }
248
+ return { material, transparent, doubleSided };
249
+ }
250
+ static makeFromGmClassic(renderEnvironment, meta, gMaterial, textures, name) {
251
+ meta.sourcePath.push("makeFromGmClassic");
252
+ name = "(GM) " + name;
253
+ const { doubleSided, redCustomProperties: redEngineCustomProperties, reflection, bump, diffuse, specular, transparency, } = gMaterial;
254
+ const bumpTexture = findTexture(textures, bump);
255
+ const diffuseTexture = findTexture(textures, diffuse);
256
+ const transparencyTexture = findTexture(textures, transparency);
257
+ const material = new PBRMaterial(name, renderEnvironment.scene);
258
+ // Specular AA can help a lot with for example edges of very shiny materials, like the
259
+ // base of our demo chair when the base overlaps / is overlapped by other geometry.
260
+ material.enableSpecularAntiAliasing = true;
261
+ if (bump !== undefined && bumpTexture !== undefined) {
262
+ material.bumpTexture = bumpTexture;
263
+ // Bump the bump strength slightly to visually better match the render results in CET.
264
+ bumpTexture.level = 1.2;
265
+ }
266
+ if (diffuse !== undefined) {
267
+ if (diffuseTexture !== undefined) {
268
+ diffuseTexture.hasAlpha = false;
269
+ material.albedoTexture = diffuseTexture;
270
+ }
271
+ else {
272
+ // From CmSym spec: "If an image url is specified it overrides the color."
273
+ material.albedoColor = toColor3(diffuse.c || Color.BLACK).toLinearSpace();
274
+ }
275
+ }
276
+ let transparent = false;
277
+ if (transparency !== undefined) {
278
+ if (transparency.opacity) {
279
+ if (transparency.opacity < 1) {
280
+ material.alpha = transparency.opacity;
281
+ transparent = true;
282
+ }
283
+ }
284
+ if (transparencyTexture) {
285
+ material.opacityTexture = transparencyTexture;
286
+ transparent = true;
287
+ }
288
+ }
289
+ if (transparent) {
290
+ // This is a tradeoff. Most 3D engines will by default render with depth write off for
291
+ // alpha blended materials, but keep it on for alpha tested materials. We don't have
292
+ // such a distinction, yet.
293
+ //
294
+ // For products like the forklift, it is much better with no depth write.
295
+ // On the flip side, without it, the plants and other similar meshes look like crap.
296
+ material.forceDepthWrite = true;
297
+ // Default to alpha blend + alpha test
298
+ material.transparencyMode = PBRMaterial.PBRMATERIAL_ALPHATESTANDBLEND;
299
+ // Do not draw very, very transparent pixels since forceDepthWrite is enabled
300
+ material.alphaCutOff = 0.05;
301
+ // Transparent materials usually needs "back then front" rendering.
302
+ material.separateCullingPass = true;
303
+ }
304
+ /*
305
+ * In Babylon.js (and previous Three.js) we use PBR (physically based rendering) and
306
+ * use the metalness-roughness way to describe the reflective properties of the materials.
307
+ * Here is good overview: https://threejs.org/examples/?q=var#webgl_materials_variations_standard
308
+ *
309
+ * Specular amount => reduced specular => reduce metalness
310
+ * Specular area / exponent => reduced exponent => reduce roughness
311
+ * Specular exponent => 0.01 = 10000 and 1 = 0 see gmEditor.cm for more details.
312
+ *
313
+ * Reflection amount => reduced amount => reduced metalness
314
+ * Reflection sharpness => reduced sharpness => increase roughness
315
+ * In cmsym sharpness is saved as a customProperty with the name reflectionGlossiness
316
+ *
317
+ * In our conversion we weigh specular and reflection from cmsym together
318
+ * if they are both defined, otherwise we convert them by them self.
319
+ */
320
+ let specularContribution;
321
+ if (specular !== undefined && specular.exponent !== undefined) {
322
+ const spec = toColor3(specular.c);
323
+ const [, , specularColorLightness] = rgb2hsl(spec.r, spec.g, spec.b);
324
+ specularContribution =
325
+ Math.sqrt(1 / (specular.exponent + 1)) +
326
+ 1 -
327
+ specular.amount * specularColorLightness;
328
+ }
329
+ let reflectionSharpness;
330
+ if (redEngineCustomProperties !== undefined) {
331
+ const reflectionGlossiness = redEngineCustomProperties.get("reflectionGlossiness");
332
+ if (reflectionGlossiness && typeof reflectionGlossiness === "number") {
333
+ reflectionSharpness = 1 - reflectionGlossiness;
334
+ }
335
+ }
336
+ if (specularContribution !== undefined) {
337
+ if (reflectionSharpness !== undefined) {
338
+ material.roughness = (reflectionSharpness + specularContribution) / 2;
339
+ }
340
+ else {
341
+ material.roughness = specularContribution;
342
+ }
343
+ }
344
+ else {
345
+ material.roughness = 0.9; // We default to non-reflective material to better match CET
346
+ }
347
+ material.metallic = 0.1; // We default to non-reflective material to better match CET
348
+ if (reflection !== undefined && reflection.amount !== undefined) {
349
+ const reflectionAmount = reflection.amount;
350
+ material.metallic = reflectionAmount;
351
+ // Since the Red Engine in CET needs the sum of reflectiveness and color to be max 1
352
+ // very reflective materials will have a color close to black. Babylon.js needs very
353
+ // reflective materials to have a very light color. See WRD-119 in Jira.
354
+ if (diffuseTexture === undefined && material.roughness < reflectionAmount - 0.3) {
355
+ // PBR material albedo color is in linear color space. The conversion to gamma
356
+ // (normal, sRGB) color space is done to work exactly the same as the previous
357
+ // Three.js version of the code. That said, if "reflectionAmount" is in a
358
+ // linear space, then perhaps it is more correct without it...
359
+ const color = material.albedoColor.toGammaSpace();
360
+ const [h, s, l] = rgb2hsl(color.r, color.g, color.b);
361
+ if (l < reflectionAmount) {
362
+ material.albedoColor = Color3.FromArray(hsl2rgb(h, s, reflectionAmount)).toLinearSpace();
363
+ }
364
+ }
365
+ }
366
+ return { material, transparent, doubleSided };
367
+ }
368
+ indexFromOptions(doubleSided, backThenFront, flipTextures) {
369
+ return (doubleSided ? DBL : 0) + (backThenFront ? BTF : 0) + (flipTextures ? FLP : 0);
370
+ }
371
+ indexFromMaterial(material) {
372
+ // Materials are never flipped by default, so the flipTextures (FLP) flag is always zero
373
+ return (material.backFaceCulling ? 0 : DBL) + (material.separateCullingPass ? BTF : 0);
374
+ }
375
+ cloneSuffix(doubleSided, backThenFront, flipTextures) {
376
+ return ` (clone${doubleSided ? ",dbl" : ""}${backThenFront ? ",btf" : ""}${flipTextures ? ",flp" : ""});`;
377
+ }
378
+ /**
379
+ * Returns the PBRMaterial associated with this CfgMaterial, optionally modified.
380
+ *
381
+ * If none of the optional parameters are set, the returned material is the one initially
382
+ * supplied when the CfgMaterial was created.
383
+ *
384
+ * If any of the optional parameters are supplied, you will get back a material that has those
385
+ * options set to the given values. The returned material will be a clone if needed, the
386
+ * initial material is NOT affected by this.
387
+ *
388
+ * It is safe to call this method multiple times, this class keeps an internal material cache.
389
+ *
390
+ * @warning: Do not modify the returned material in any way since it might be shared with
391
+ * other parts of the model!
392
+ *
393
+ * @param doubleSided Makes sure that backFaceCulling is OFF in the returned material.
394
+ * @param backThenFront Makes sure that separateCullingPass is ON in the returned material.
395
+ * @param flipTextures Flips the textures in the material along the Y-axis.
396
+ */
397
+ getPBRMaterial(doubleSided, backThenFront, flipTextures) {
398
+ if (doubleSided === undefined) {
399
+ doubleSided = !this._material.backFaceCulling;
400
+ }
401
+ if (backThenFront === undefined) {
402
+ backThenFront = this._material.separateCullingPass;
403
+ }
404
+ if (flipTextures === undefined) {
405
+ // Materials are never flipped by default.
406
+ flipTextures = false;
407
+ }
408
+ const index = this.indexFromOptions(doubleSided, backThenFront, flipTextures);
409
+ if (this._variants[index] !== undefined) {
410
+ return this._variants[index];
411
+ }
412
+ const clone = this._material.clone(this._material.name + this.cloneSuffix(doubleSided, backThenFront, flipTextures));
413
+ clone.separateCullingPass = backThenFront;
414
+ makeMaterialDoubleSided(clone, doubleSided);
415
+ if (flipTextures) {
416
+ flipMaterialTextures(clone);
417
+ }
418
+ this._variants[index] = clone;
419
+ return clone;
420
+ }
421
+ }
422
+ /**
423
+ * The exact changes this method makes depends on the material's separateCullingPass setting, so
424
+ * make sure make any changes to that property before calling this method.
425
+ */
426
+ export function makeMaterialDoubleSided(material, doubleSided) {
427
+ if (doubleSided) {
428
+ // SeparateCullingPass breaks twoSidedLightning by not flipping the normals when
429
+ // rendering the back faces of the triangles. forceNormalForward works around that
430
+ // but is a bit slower due to more complex shader calculations so only use when needed.
431
+ material.backFaceCulling = false;
432
+ material.twoSidedLighting = !material.separateCullingPass;
433
+ material.forceNormalForward = material.separateCullingPass;
434
+ }
435
+ else {
436
+ material.backFaceCulling = true; // Don't render backside of triangles
437
+ material.twoSidedLighting = false; // Don't flip normals on back face of triangles
438
+ material.forceNormalForward = false; // Don't change the normals to front facing
439
+ }
440
+ }
441
+ /**
442
+ * Clones all the textures used in the material and flips them along the y-axis by inverting the
443
+ * texture's vScale, vOffset and wRotation.
444
+ *
445
+ * @note: The flipped state is not stored in the material it self, so calling this method twice on
446
+ * the same material will undo the flip.
447
+ */
448
+ function flipMaterialTextures(material) {
449
+ material.albedoTexture = cloneAndFlipTexture(material.albedoTexture);
450
+ material.bumpTexture = cloneAndFlipTexture(material.bumpTexture);
451
+ material.ambientTexture = cloneAndFlipTexture(material.ambientTexture);
452
+ material.opacityTexture = cloneAndFlipTexture(material.opacityTexture);
453
+ material.emissiveTexture = cloneAndFlipTexture(material.emissiveTexture);
454
+ material.metallicTexture = cloneAndFlipTexture(material.metallicTexture);
455
+ material.lightmapTexture = cloneAndFlipTexture(material.lightmapTexture);
456
+ if (material.refractionTexture !== null) {
457
+ // The refraction texture is a bit special since it is a getter/setter with side effects.
458
+ // As of this writing (Babylon 4.1.0), the only side effect is to change the value of
459
+ // isRefractionEnabled so we save and reapply the value after the flip.
460
+ const enabled = material.subSurface.isRefractionEnabled;
461
+ material.refractionTexture = cloneAndFlipTexture(material.refractionTexture);
462
+ material.subSurface.isRefractionEnabled = enabled;
463
+ }
464
+ material.reflectivityTexture = cloneAndFlipTexture(material.reflectivityTexture);
465
+ material.microSurfaceTexture = cloneAndFlipTexture(material.microSurfaceTexture);
466
+ }
467
+ /**
468
+ * Returns undefined if texture is undefined.
469
+ * Returns original texture if it isn't of type "Texture".
470
+ * Otherwise, Clones and flips the texture along the y-axis by inverting vScale, vOffset and wAng.
471
+ */
472
+ function cloneAndFlipTexture(texture) {
473
+ if (texture instanceof Texture) {
474
+ texture = texture.clone();
475
+ if (texture instanceof Texture) {
476
+ texture.vScale *= -1; // Flips along y-axis
477
+ texture.vOffset *= -1; // Adjust texture offset due to flip
478
+ texture.wAng *= -1; // Adjust rotation direction due to the flip
479
+ }
480
+ }
481
+ return texture;
482
+ }