-
Notifications
You must be signed in to change notification settings - Fork 308
/
BabylonExporter.Material.Pbr.cs
424 lines (373 loc) · 21.2 KB
/
BabylonExporter.Material.Pbr.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
using System.Collections.Generic;
using System.Linq;
using Autodesk.Max;
using BabylonExport.Entities;
namespace Max2Babylon
{
/// <summary>
/// PbrGameMaterial decorator homogenizes and simplifies PbrGameMaterial properties.
/// </summary>
public class PbrGameMaterialDecorator : MaterialDecorator
{
BabylonCustomAttributeDecorator _babylonCAT;
public PbrGameMaterialDecorator(IIGameMaterial node) : base(node)
{
_babylonCAT = new BabylonCustomAttributeDecorator(node);
}
public virtual bool AmbientOcclusionAffectsDiffuse => Properties?.GetBoolProperty("ao_affects_diffuse", false) ?? false;
public virtual bool AmbientOcclusionAffectsReflection => Properties?.GetBoolProperty("ao_affects_reflection", false) ?? false;
public virtual bool NormalFlipRed => Properties?.GetBoolProperty("normal_flip_red", false) ?? false;
public virtual bool NormalFlipGreen => Properties?.GetBoolProperty("normal_flip_green", false) ?? false;
public virtual IColor BaseColor => _node.MaxMaterial.GetDiffuse(0, false);
public virtual ITexmap BaseColorMap => _getTexMap(_node, "base_color_map");
public virtual int UseGlossiness => Properties?.GetIntProperty("useGlossiness", 0) ?? 0;
public virtual ITexmap AmbientOcclusionMap => _getTexMap(_node, "ao_map");
public virtual float BumpMapAmount => Properties?.GetFloatProperty("bump_map_amt", 1.0f) ?? 1.0f;
public virtual ITexmap NormalMap => _getTexMap(_node, "norm_map");
public virtual IColor EmitColor => _node.MaxMaterial.GetSelfIllumColor(0, false);
public virtual ITexmap EmitColormMap => _getTexMap(_node, "emit_color_map");
public ITexmap OpacityMap => _getTexMap(_node, "opacity_map");
public BabylonCustomAttributeDecorator BabylonCustomAttributes => _babylonCAT;
}
public class PbrMetalRoughDecorator : PbrGameMaterialDecorator
{
public PbrMetalRoughDecorator(IIGameMaterial node) : base(node)
{
}
public float Metalness => Properties?.GetFloatProperty("metalness", 0) ?? 0;
public ITexmap MetalnessMap => _getTexMap(_node, "metalness_map");
public float Roughness => Properties?.GetFloatProperty("roughness", 0) ?? 0;
public ITexmap RoughnessMap => _getTexMap(_node, "roughness_map");
}
public class PbrSpecGlossDecorator : PbrGameMaterialDecorator
{
public PbrSpecGlossDecorator(IIGameMaterial node) : base(node)
{
}
public IPoint3 SpecularColor => Properties?.GetPoint3Property("Specular");
public ITexmap SpecularMap => _getTexMap(_node, "specular_map");
public float Glossiness => Properties?.GetFloatProperty("glossiness", 0) ?? 0;
public ITexmap GlossinessMap => _getTexMap(_node, "glossiness_map");
}
/// <summary>
/// The Exporter
/// </summary>
partial class BabylonExporter
{
/// <summary>
/// Export dedicated to PbrMetalRough Material
/// </summary>
/// <param name="materialNode">the material node interface</param>
/// <param name="babylonScene">the scene to export the material</param>
private void ExportPbrMetalRoughMaterial(IIGameMaterial materialNode, BabylonScene babylonScene)
{
// build material decorator
PbrMetalRoughDecorator maxDecorator = new PbrMetalRoughDecorator(materialNode);
// get the custom babylon attribute decorator
BabylonCustomAttributeDecorator babylonDecorator = maxDecorator.BabylonCustomAttributes;
// the target material
var babylonMaterial = new BabylonPBRMetallicRoughnessMaterial(maxDecorator.Id)
{
maxGameMaterial = materialNode,
name = maxDecorator.Name,
backFaceCulling = babylonDecorator.BackFaceCulling,
doubleSided = !babylonDecorator.BackFaceCulling,
separateCullingPass = babylonDecorator.SeparateCullingPass,
isUnlit = babylonDecorator.IsUnlit,
_unlit = babylonDecorator.IsUnlit,
baseColor = maxDecorator.BaseColor.ToArray()
};
// --- Global ---
if (babylonMaterial.isUnlit)
{
// Ignore values
babylonMaterial.metallic = 0;
babylonMaterial.roughness = 0.9f;
}
else
{
babylonMaterial.metallic = maxDecorator.Metalness;
babylonMaterial.roughness = maxDecorator.Roughness;
babylonMaterial.emissive = maxDecorator.EmitColor.ToArray();
}
// --- Textures ---
float[] multiplyColor = null;
if (exportParameters.exportTextures)
{
ITexmap baseColorTexMap = maxDecorator.BaseColorMap;
ITexmap alphaTexMap = maxDecorator.OpacityMap;
bool isOpacity = true;
babylonMaterial.baseTexture = ExportBaseColorAlphaTexture(baseColorTexMap, alphaTexMap, babylonMaterial.baseColor, babylonMaterial.alpha, babylonScene, out multiplyColor, isOpacity);
if (multiplyColor != null)
{
babylonMaterial.baseColor = multiplyColor;
}
if (!babylonMaterial.isUnlit)
{
// Metallic, roughness, ambient occlusion
ITexmap metalnessTexMap = maxDecorator.MetalnessMap;
ITexmap roughnessTexMap = maxDecorator.RoughnessMap;
ITexmap ambientOcclusionTexMap = maxDecorator.AmbientOcclusionMap;
// Check if MR or ORM textures are already merged
bool areTexturesAlreadyMerged = false;
if (metalnessTexMap != null && roughnessTexMap != null)
{
string sourcePathMetallic = getSourcePath(metalnessTexMap);
string sourcePathRoughness = getSourcePath(roughnessTexMap);
if (sourcePathMetallic == sourcePathRoughness)
{
if (ambientOcclusionTexMap != null && exportParameters.mergeAO)
{
string sourcePathAmbientOcclusion = getSourcePath(ambientOcclusionTexMap);
if (sourcePathMetallic == sourcePathAmbientOcclusion)
{
// Metallic, roughness and ambient occlusion are already merged
RaiseVerbose("Metallic, roughness and ambient occlusion are already merged", 2);
BabylonTexture ormTexture = ExportTexture(metalnessTexMap, babylonScene);
babylonMaterial.metallicRoughnessTexture = ormTexture;
babylonMaterial.occlusionTexture = ormTexture;
areTexturesAlreadyMerged = true;
}
}
else
{
// Metallic and roughness are already merged
RaiseVerbose("Metallic and roughness are already merged", 2);
BabylonTexture ormTexture = ExportTexture(metalnessTexMap, babylonScene);
babylonMaterial.metallicRoughnessTexture = ormTexture;
areTexturesAlreadyMerged = true;
}
}
}
if (areTexturesAlreadyMerged == false)
{
if (metalnessTexMap != null || roughnessTexMap != null)
{
// Merge metallic, roughness and ambient occlusion
RaiseVerbose("Merge metallic and roughness (and ambient occlusion if `mergeAOwithMR` is enabled)", 2);
BabylonTexture ormTexture = ExportORMTexture(exportParameters.mergeAO ? ambientOcclusionTexMap : null, roughnessTexMap, metalnessTexMap, babylonMaterial.metallic, babylonMaterial.roughness, babylonScene, false);
babylonMaterial.metallicRoughnessTexture = ormTexture;
if (ambientOcclusionTexMap != null)
{
if (exportParameters.mergeAO)
{
// if the ambient occlusion texture map uses a different set of texture coordinates than
// metallic roughness, create a new instance of the ORM BabylonTexture with the different texture
// coordinate indices
var ambientOcclusionTexture = _getBitmapTex(ambientOcclusionTexMap);
var texCoordIndex = ambientOcclusionTexture.UVGen.MapChannel - 1;
if (texCoordIndex != ormTexture.coordinatesIndex)
{
babylonMaterial.occlusionTexture = new BabylonTexture(ormTexture);
babylonMaterial.occlusionTexture.coordinatesIndex = texCoordIndex;
// Set UVs/texture transform for the ambient occlusion texture
var uvGen = _exportUV(ambientOcclusionTexture.UVGen, babylonMaterial.occlusionTexture);
}
else
{
babylonMaterial.occlusionTexture = ormTexture;
}
}
else
{
babylonMaterial.occlusionTexture = ExportTexture(ambientOcclusionTexMap, babylonScene);
}
}
}
else if (ambientOcclusionTexMap != null)
{
// Simply export occlusion texture
RaiseVerbose("Simply export occlusion texture", 2);
babylonMaterial.occlusionTexture = ExportTexture(ambientOcclusionTexMap, babylonScene);
}
}
if (ambientOcclusionTexMap != null && !exportParameters.mergeAO && babylonMaterial.occlusionTexture == null)
{
RaiseVerbose("Exporting occlusion texture without merging with metallic roughness", 2);
babylonMaterial.occlusionTexture = ExportTexture(ambientOcclusionTexMap, babylonScene);
}
var normalMapAmount = maxDecorator.BumpMapAmount;
ITexmap normalTexMap = maxDecorator.NormalMap;
babylonMaterial.normalTexture = ExportTexture(normalTexMap, babylonScene, normalMapAmount);
ITexmap emitTexMap = maxDecorator.EmitColormMap;
babylonMaterial.emissiveTexture = ExportTexture(emitTexMap , babylonScene);
if (babylonMaterial.metallicRoughnessTexture != null && !babylonDecorator.UseMaxFactor)
{
// Change the factor to zero if combining partial channel to avoid issue (in case of image compression).
// ie - if no metallic map, then b MUST be fully black. However channel of jpeg MAY not beeing fully black
// cause of the compression algorithm. Keeping MetallicFactor to 1 will make visible artifact onto texture. So set to Zero instead.
babylonMaterial.metallic = areTexturesAlreadyMerged || metalnessTexMap != null ? 1.0f : 0.0f;
babylonMaterial.roughness = areTexturesAlreadyMerged || roughnessTexMap != null ? 1.0f : 0.0f;
}
}
}
if (babylonMaterial.alpha != 1.0f || (babylonMaterial.baseTexture != null && babylonMaterial.baseTexture.hasAlpha))
{
babylonMaterial.transparencyMode = (int)BabylonMaterial.TransparencyMode.ALPHABLEND;
}
if (babylonMaterial.transparencyMode == (int)BabylonMaterial.TransparencyMode.ALPHATEST)
{
// Set the alphaCutOff value explicitely to avoid different interpretations on different engines
// Use the glTF default value rather than the babylon one
babylonMaterial.alphaCutOff = 0.5f;
}
if (babylonMaterial.emissiveTexture != null)
{
babylonMaterial.emissive = new[] { 1.0f, 1.0f, 1.0f };
}
// Add babylon attributes
if (babylonDecorator.Properties == null)
{
AddPhysicalBabylonAttributes(materialNode.MaterialName, babylonMaterial);
}
// List all babylon material attributes
// Those attributes are currently stored into the native material
// They should not be exported as extra attributes
var doNotExport = BabylonCustomAttributeDecorator.ListPrivatePropertyNames().ToList();
// Export the custom attributes of this material
babylonMaterial.metadata = ExportExtraAttributes(materialNode, babylonScene, doNotExport);
if (exportParameters.pbrFull)
{
var fullPBR = new BabylonPBRMaterial(babylonMaterial)
{
directIntensity = babylonDecorator.DirectIntensity,
emissiveIntensity = babylonDecorator.EmissiveIntensity,
environmentIntensity = babylonDecorator.EnvironementIntensity,
specularIntensity = babylonDecorator.SpecularIntensity,
maxGameMaterial = babylonMaterial.maxGameMaterial
};
babylonScene.MaterialsList.Add(fullPBR);
}
else
{
// Add the material to the scene
babylonScene.MaterialsList.Add(babylonMaterial);
}
}
/// <summary>
/// Export dedicated to SpecGloss Material
/// </summary>
/// <param name="materialNode">the material node interface</param>
/// <param name="babylonScene">the scene to export the material</param>
private void ExportPbrSpecGlossMaterial(IIGameMaterial materialNode, BabylonScene babylonScene)
{
// build material decorator
PbrSpecGlossDecorator maxDecorator = new PbrSpecGlossDecorator(materialNode);
// get the custom babylon attribute decorator
BabylonCustomAttributeDecorator babylonDecorator = maxDecorator.BabylonCustomAttributes;
// the target material
var babylonMaterial = new BabylonPBRSpecularGlossinessMaterial(maxDecorator.Id)
{
maxGameMaterial = materialNode,
name = maxDecorator.Name,
backFaceCulling = babylonDecorator.BackFaceCulling,
doubleSided = !babylonDecorator.BackFaceCulling,
separateCullingPass = babylonDecorator.SeparateCullingPass,
isUnlit = babylonDecorator.IsUnlit,
baseColor = maxDecorator.BaseColor.ToArray(),
};
// --- Global ---
if (babylonMaterial.isUnlit)
{
// Ignore values
babylonMaterial.specularColor = BabylonPBRBaseSimpleMaterial.BlackColor();
babylonMaterial.glossiness = 0;
}
else
{
babylonMaterial.glossiness = maxDecorator.Glossiness;
babylonMaterial.specularColor = maxDecorator.SpecularColor.ToArray();
babylonMaterial.emissive = maxDecorator.EmitColor.ToArray();
}
// --- Textures ---
float[] multiplyColor = null;
if (exportParameters.exportTextures)
{
ITexmap diffuseTexMap = maxDecorator.BaseColorMap;
ITexmap alphaTexMap = maxDecorator.OpacityMap;
bool isOpacity = true;
babylonMaterial.diffuseTexture = ExportBaseColorAlphaTexture(diffuseTexMap, alphaTexMap, babylonMaterial.baseColor, babylonMaterial.alpha, babylonScene, out multiplyColor, isOpacity);
if (multiplyColor != null)
{
babylonMaterial.baseColor = multiplyColor;
}
if (!babylonMaterial.isUnlit)
{
// Metallic, roughness, ambient occlusion
ITexmap specularTexMap = maxDecorator.SpecularMap;
ITexmap glossinessTexMap = maxDecorator.GlossinessMap;
ITexmap ambientOcclusionTexMap = maxDecorator.AmbientOcclusionMap;
if (specularTexMap != null || glossinessTexMap != null)
{
// Merge Specular and Glossiness
RaiseVerbose("Merge Specular and Glossiness", 2);
BabylonTexture specularGlossinessTexture = ExportSpecularGlossinessTexture(babylonMaterial.specularColor, specularTexMap, babylonMaterial.glossiness, glossinessTexMap, babylonScene);
babylonMaterial.specularGlossinessTexture = specularGlossinessTexture;
}
if (ambientOcclusionTexMap != null)
{
// Simply export occlusion texture
RaiseVerbose("Export occlusion texture", 2);
babylonMaterial.occlusionTexture = ExportTexture(ambientOcclusionTexMap, babylonScene);
}
var normalMapAmount = maxDecorator.BumpMapAmount;
ITexmap normalTexMap = maxDecorator.NormalMap;
babylonMaterial.normalTexture = ExportTexture(normalTexMap, babylonScene, normalMapAmount);
ITexmap emitTexMap = maxDecorator.EmitColormMap;
babylonMaterial.emissiveTexture = ExportTexture(emitTexMap, babylonScene);
if (babylonMaterial.specularGlossinessTexture != null && !babylonDecorator.UseMaxFactor)
{
babylonMaterial.glossiness = glossinessTexMap != null ? 1.0f : 0.0f;
babylonMaterial.specularColor = specularTexMap != null ? BabylonPBRBaseSimpleMaterial.WhiteColor() : BabylonPBRBaseSimpleMaterial.BlackColor();
}
}
}
// --- Finalize ---
if (babylonMaterial.alpha != 1.0f || (babylonMaterial.diffuseTexture != null && babylonMaterial.diffuseTexture.hasAlpha))
{
babylonMaterial.transparencyMode = (int)BabylonMaterial.TransparencyMode.ALPHABLEND;
}
if (babylonMaterial.transparencyMode == (int)BabylonMaterial.TransparencyMode.ALPHATEST)
{
// Set the alphaCutOff value explicitely to avoid different interpretations on different engines
// Use the glTF default value rather than the babylon one
babylonMaterial.alphaCutOff = 0.5f;
}
if (babylonMaterial.emissiveTexture != null)
{
babylonMaterial.emissive = new[] { 1.0f, 1.0f, 1.0f };
}
// List all babylon material attributes
// Those attributes are currently stored into the native material
// They should not be exported as extra attributes
var doNotExport = BabylonCustomAttributeDecorator.ListPrivatePropertyNames().ToList();
// Export the custom attributes of this material
babylonMaterial.metadata = ExportExtraAttributes(materialNode, babylonScene, doNotExport);
if (exportParameters.pbrFull)
{
var fullPBR = new BabylonPBRMaterial(babylonMaterial)
{
directIntensity = babylonDecorator.DirectIntensity,
emissiveIntensity = babylonDecorator.EmissiveIntensity,
environmentIntensity = babylonDecorator.EnvironementIntensity,
specularIntensity = babylonDecorator.SpecularIntensity,
maxGameMaterial = babylonMaterial.maxGameMaterial
};
babylonScene.MaterialsList.Add(fullPBR);
}
else
{
// Add the material to the scene
babylonScene.MaterialsList.Add(babylonMaterial);
}
}
public bool isPbrMetalRoughMaterial(IIGameMaterial materialNode)
{
return ClassIDWrapper.PBR_MetalRough_Material.Equals(materialNode.MaxMaterial.ClassID);
}
public bool isPbrSpecGlossMaterial(IIGameMaterial materialNode)
{
return ClassIDWrapper.PBR_SpecGloss_Material.Equals(materialNode.MaxMaterial.ClassID);
}
}
}