@configura/babylon-view 2.1.0-alpha.0 → 2.1.0-alpha.2
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.
- package/.eslintrc.json +5 -5
- package/LICENSE +201 -201
- package/README.md +1 -1
- package/dist/animation/AnimatableObject.d.ts +8 -8
- package/dist/animation/AnimatableObject.js +3 -3
- package/dist/animation/animator/Animator.d.ts +33 -33
- package/dist/animation/animator/Animator.js +58 -58
- package/dist/animation/animator/AnimatorEasing.d.ts +16 -16
- package/dist/animation/animator/AnimatorEasing.js +31 -31
- package/dist/animation/animator/AnimatorEasingMatrix.d.ts +14 -14
- package/dist/animation/animator/AnimatorEasingMatrix.js +16 -16
- package/dist/animation/animator/AnimatorHighlight.d.ts +16 -16
- package/dist/animation/animator/AnimatorHighlight.js +32 -32
- package/dist/animation/animator/AnimatorPointToPoint.d.ts +8 -8
- package/dist/animation/animator/AnimatorPointToPoint.js +14 -14
- package/dist/animation/animator/AnimatorQueue.d.ts +13 -13
- package/dist/animation/animator/AnimatorQueue.js +57 -57
- package/dist/animation/animator/AnimatorScale.d.ts +8 -8
- package/dist/animation/animator/AnimatorScale.js +13 -13
- package/dist/animation/animator/AnimatorSpin.d.ts +10 -10
- package/dist/animation/animator/AnimatorSpin.js +13 -13
- package/dist/animation/animator/EasingFunctions.d.ts +35 -35
- package/dist/animation/animator/EasingFunctions.js +137 -137
- package/dist/animation/coordinator/Coordinator.d.ts +28 -28
- package/dist/animation/coordinator/Coordinator.js +53 -53
- package/dist/animation/coordinator/CoordinatorDropAndSpin.d.ts +22 -22
- package/dist/animation/coordinator/CoordinatorDropAndSpin.js +138 -138
- package/dist/animation/coordinator/CoordinatorIdentity.d.ts +11 -11
- package/dist/animation/coordinator/CoordinatorIdentity.js +14 -14
- package/dist/animation/coordinator/CoordinatorNodeQueues.d.ts +18 -18
- package/dist/animation/coordinator/CoordinatorNodeQueues.js +50 -50
- package/dist/animation/coordinator/CoordinatorPulse.d.ts +21 -21
- package/dist/animation/coordinator/CoordinatorPulse.js +47 -47
- package/dist/animation/coordinator/CoordinatorPulseBounce.d.ts +14 -14
- package/dist/animation/coordinator/CoordinatorPulseBounce.js +35 -35
- package/dist/animation/coordinator/CoordinatorPulseHighlight.d.ts +13 -13
- package/dist/animation/coordinator/CoordinatorPulseHighlight.js +29 -29
- package/dist/animation/coordinator/CoordinatorPulseInflate.d.ts +14 -14
- package/dist/animation/coordinator/CoordinatorPulseInflate.js +23 -23
- package/dist/camera/CameraCreator.d.ts +5 -5
- package/dist/camera/CameraCreator.js +4 -4
- package/dist/camera/CfgArcRotateCameraPointersInput.d.ts +26 -26
- package/dist/camera/CfgArcRotateCameraPointersInput.js +266 -266
- package/dist/camera/CfgOrbitalCamera.d.ts +76 -76
- package/dist/camera/CfgOrbitalCamera.js +281 -281
- package/dist/camera/CfgOrbitalCameraControlProps.d.ts +14 -14
- package/dist/camera/CfgOrbitalCameraControlProps.js +7 -7
- package/dist/camera/GradingApplier.d.ts +3 -3
- package/dist/camera/GradingApplier.js +48 -48
- package/dist/engine/EngineCreator.d.ts +3 -3
- package/dist/engine/EngineCreator.js +10 -10
- package/dist/geometry/CfgGeometry.d.ts +29 -29
- package/dist/geometry/CfgGeometry.js +146 -146
- package/dist/geometry/CfgMesh.d.ts +10 -10
- package/dist/geometry/CfgMesh.js +38 -38
- package/dist/geometry/geoSplitter.d.ts +8 -8
- package/dist/geometry/geoSplitter.js +192 -192
- package/dist/geometry/stretch/CfgMorphTarget.d.ts +15 -15
- package/dist/geometry/stretch/CfgMorphTarget.js +65 -65
- package/dist/geometry/stretch/CfgStretchData.d.ts +116 -116
- package/dist/geometry/stretch/CfgStretchData.js +350 -350
- package/dist/geometry/stretch/CfgStretchMorphGeometry.d.ts +16 -16
- package/dist/geometry/stretch/CfgStretchMorphGeometry.js +95 -95
- package/dist/index.d.ts +16 -16
- package/dist/index.js +16 -16
- package/dist/io/CfgHistoryToCameraConfConnector.d.ts +31 -31
- package/dist/io/CfgHistoryToCameraConfConnector.js +90 -90
- package/dist/io/CfgIOCameraConfConnector.d.ts +35 -35
- package/dist/io/CfgIOCameraConfConnector.js +81 -81
- package/dist/io/CfgObservableStateToCameraConfConnector.d.ts +10 -10
- package/dist/io/CfgObservableStateToCameraConfConnector.js +11 -11
- package/dist/io/CfgWindowMessageToCameraConfConnector.d.ts +10 -10
- package/dist/io/CfgWindowMessageToCameraConfConnector.js +11 -11
- package/dist/light/CfgDirectionalLight.d.ts +8 -8
- package/dist/light/CfgDirectionalLight.js +18 -18
- package/dist/light/CfgHemisphericLight.d.ts +7 -7
- package/dist/light/CfgHemisphericLight.js +17 -17
- package/dist/light/CfgPointLight.d.ts +8 -8
- package/dist/light/CfgPointLight.js +18 -18
- package/dist/light/DefaultLightRig.d.ts +19 -19
- package/dist/light/DefaultLightRig.js +77 -77
- package/dist/light/LightRigCreator.d.ts +9 -9
- package/dist/light/LightRigCreator.js +3 -3
- package/dist/material/CfgMaterial.d.ts +68 -68
- package/dist/material/CfgMaterial.js +482 -482
- package/dist/material/DummyMaterialCreator.d.ts +4 -4
- package/dist/material/DummyMaterialCreator.js +15 -15
- package/dist/material/material.d.ts +18 -18
- package/dist/material/material.js +128 -128
- package/dist/material/texture.d.ts +14 -14
- package/dist/material/texture.js +306 -306
- package/dist/nodes/CfgContentRootNode.d.ts +19 -19
- package/dist/nodes/CfgContentRootNode.js +75 -75
- package/dist/nodes/CfgDeferredMeshNode.d.ts +55 -55
- package/dist/nodes/CfgDeferredMeshNode.js +378 -378
- package/dist/nodes/CfgProductNode.d.ts +127 -127
- package/dist/nodes/CfgProductNode.js +598 -598
- package/dist/nodes/CfgSymNode.d.ts +50 -50
- package/dist/nodes/CfgSymNode.js +249 -249
- package/dist/nodes/CfgSymRootNode.d.ts +45 -45
- package/dist/nodes/CfgSymRootNode.js +229 -229
- package/dist/nodes/CfgTransformNode.d.ts +33 -33
- package/dist/nodes/CfgTransformNode.js +83 -83
- package/dist/scene/SceneCreator.d.ts +6 -6
- package/dist/scene/SceneCreator.js +22 -22
- package/dist/utilities/CfgBoundingBox.d.ts +21 -21
- package/dist/utilities/CfgBoundingBox.js +81 -81
- package/dist/utilities/anchor/anchor.d.ts +50 -50
- package/dist/utilities/anchor/anchor.js +133 -133
- package/dist/utilities/anchor/anchorMap.d.ts +20 -20
- package/dist/utilities/anchor/anchorMap.js +111 -111
- package/dist/utilities/utilities3D.d.ts +70 -70
- package/dist/utilities/utilities3D.js +265 -265
- package/dist/utilities/utilitiesColor.d.ts +18 -18
- package/dist/utilities/utilitiesColor.js +50 -50
- package/dist/utilities/utilitiesImage.d.ts +6 -6
- package/dist/utilities/utilitiesImage.js +107 -107
- package/dist/utilities/utilitiesSymRootIdentifier.d.ts +7 -7
- package/dist/utilities/utilitiesSymRootIdentifier.js +26 -26
- package/dist/view/BaseView.d.ts +78 -78
- package/dist/view/BaseView.js +303 -303
- package/dist/view/BaseViewConfiguration.d.ts +32 -32
- package/dist/view/BaseViewConfiguration.js +10 -10
- package/dist/view/RenderEnv.d.ts +43 -43
- package/dist/view/RenderEnv.js +7 -7
- package/dist/view/SingleProductDefaultCameraView.d.ts +38 -38
- package/dist/view/SingleProductDefaultCameraView.js +149 -149
- package/dist/view/SingleProductDefaultCameraViewConfiguration.d.ts +44 -44
- package/dist/view/SingleProductDefaultCameraViewConfiguration.js +11 -11
- package/dist/view/SingleProductView.d.ts +44 -44
- package/dist/view/SingleProductView.js +212 -212
- package/dist/view/SingleProductViewConfiguration.d.ts +32 -32
- package/dist/view/SingleProductViewConfiguration.js +19 -19
- 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
|
+
}
|