public bool isPhysicalMaterial(IIGameMaterial materialNode) { // TODO - Find another way to detect if material is physical return(materialNode.MaterialClass.ToLower() == "physical material" || // English materialNode.MaterialClass.ToLower() == "physikalisches material" || // German materialNode.MaterialClass.ToLower() == "matériau physique"); // French }
public bool isMultiSubObjectMaterial(IIGameMaterial materialNode) { // TODO - Find another way to detect if material is a multi/sub-object return(materialNode.MaterialClass.ToLower() == "multi/sub-object" || // English materialNode.MaterialClass.ToLower() == "multi-/unterobjekt" || // German materialNode.MaterialClass.ToLower() == "multi/sous-objet"); // French }
public bool GetGltfMaterial(BabylonMaterial babylonMaterial, GLTF gltf, ILoggingProvider logger, out GLTFMaterial gltfMaterial) { gltfMaterial = null; IIGameMaterial gameMtl = babylonMaterial.maxGameMaterial; if (gameMtl == null || gameMtl.MaxMaterial == null) { return(false); } IMtl maxMtl = gameMtl.MaxMaterial; IMaxMaterialExporter materialExporter; if (materialExporters.TryGetValue(new ClassIDWrapper(maxMtl.ClassID), out materialExporter) && materialExporter is IMaxGLTFMaterialExporter) { gltfMaterial = ((IMaxGLTFMaterialExporter)materialExporter).ExportGLTFMaterial(this.exportParameters, gltf, gameMtl, (string sourcePath, string textureName) => { return(this.TryWriteImage(gltf, sourcePath, textureName, exportParameters, logger)); }, (string message, Color color) => { logger.RaiseMessage(message, color, 2); }, (string message) => { logger.RaiseWarning(message, 2); }, (string message) => { logger.RaiseError(message, 2); }); if (gltfMaterial == null) { string message = string.Format("Custom glTF material exporter failed to export | Exporter: '{0}' | Material Name: '{1}' | Material Class: '{2}'", materialExporter.GetType().ToString(), gameMtl.MaterialName, gameMtl.ClassName); logger.RaiseWarning(message, 2); return(false); } return(true); } return(false); }
private IIGameMaterial GetBakedMaterialFromShellMaterial(IIGameMaterial materialNode) { if (isShellMaterial(materialNode)) { // Shell Material Parameters // Original Material not exported => only for the offline rendering in 3DS Max // Baked Material => used for the export IMtl bakedMtl = materialNode.IPropertyContainer.GetProperty(1).MaxParamBlock2.GetMtl(3, 0, 0); if (bakedMtl != null) { Guid guid = bakedMtl.GetGuid(); for (int indexSubMaterial = 0; indexSubMaterial < materialNode.SubMaterialCount; indexSubMaterial++) { IIGameMaterial subMaterialNode = materialNode.GetSubMaterial(indexSubMaterial); if (guid.Equals(subMaterialNode.MaxMaterial.GetGuid())) { return(subMaterialNode); } } } } return(null); }
/// <summary> /// Return the custom attributes of a material /// </summary> /// <param name="materialNode"></param> /// <param name="babylonScene"></param> /// <param name="excludeAttributes">Attribute names to not export</param> public Dictionary <string, object> ExportExtraAttributes(IIGameMaterial gameMaterial, BabylonScene babylonScene, List <string> excludeAttributes = null) { // Retreive the max object ScriptsUtilities.ExecuteMaxScriptCommand("obj = sceneMaterials[\"" + gameMaterial.MaterialName + "\"];"); return(_ExportExtraAttributes(gameMaterial.IPropertyContainer, babylonScene, excludeAttributes)); }
private BabylonTexture ExportPBRTexture(IIGameMaterial materialNode, int index, BabylonScene babylonScene, float amount = 1.0f) { var texMap = _getTexMap(materialNode, index); if (texMap != null) { return(_exportTexture(texMap, amount, babylonScene)); } return(null); }
private BabylonTexture ExportPBRTexture(IIGameMaterial materialNode, int index, BabylonScene babylonScene, float amount = 1.0f, bool allowCube = false) { var texMap = _getTexMap(materialNode, index); if (texMap != null) { return(ExportTexture(texMap, babylonScene, amount, allowCube)); } return(null); }
private ITexmap _getTexMap(IIGameMaterial materialNode, string name) { for (int i = 0; i < materialNode.MaxMaterial.NumSubTexmaps; i++) { if (materialNode.MaxMaterial.GetSubTexmapSlotName(i) == name) { return(_getTexMap(materialNode, i)); } } return(null); }
private IIGameMaterial GetRenderMaterialFromDirectXShader(IIGameMaterial materialNode) { IIGameMaterial renderMaterial = null; if (isDirectXShaderMaterial(materialNode)) { var gameScene = Loader.Global.IGameInterface; IMtl renderMtl = materialNode.IPropertyContainer.GetProperty(35).MaxParamBlock2.GetMtl(4, 0, 0); if (renderMtl != null) { renderMaterial = gameScene.GetIGameMaterial(renderMtl); } } return(renderMaterial); }
private ITexmap _getTexMap(IIGameMaterial materialNode, int index) { ITexmap texMap = null; if (materialNode.MaxMaterial.SubTexmapOn(index) == 1) { texMap = materialNode.MaxMaterial.GetSubTexmap(index); // No warning displayed because by default, physical material in 3ds Max have all maps on // Would be tedious for the user to uncheck all unused maps //if (texMap == null) //{ // RaiseWarning("Texture channel " + index + " activated but no texture found.", 2); //} } return(texMap); }
/// <summary> /// Return null if the material is supported. /// Otherwise return the unsupported material (himself or one of its sub-materials) /// </summary> /// <param name="materialNode"></param> /// <returns></returns> public IIGameMaterial isMaterialSupported(IIGameMaterial materialNode) { if (materialNode.SubMaterialCount > 0) { // Check sub materials recursively for (int indexSubMaterial = 0; indexSubMaterial < materialNode.SubMaterialCount; indexSubMaterial++) { IIGameMaterial subMaterialNode = materialNode.GetSubMaterial(indexSubMaterial); IIGameMaterial unsupportedSubMaterial = isMaterialSupported(subMaterialNode); if (unsupportedSubMaterial != null) { return(unsupportedSubMaterial); } } // Multi/sub-object material if (isMultiSubObjectMaterial(materialNode)) { return(null); } } else { // Standard material var stdMat = materialNode.MaxMaterial.GetParamBlock(0).Owner as IStdMat2; if (stdMat != null) { return(null); } // Physical material if (isPhysicalMaterial(materialNode)) { return(null); } // Arnold material if (isArnoldMaterial(materialNode)) { return(null); } } return(materialNode); }
protected ITexmap _getTexMap(IIGameMaterial materialNode, string name, bool cache = true) { if (cache) { return(_getTexMapWithCache(materialNode, name)); } for (int k = 0; k < 2; k++) { for (int i = 0; i < materialNode.MaxMaterial.NumSubTexmaps; i++) { if (materialNode.MaxMaterial.GetSubTexmapSlotName(i) == name) { return(materialNode.MaxMaterial.GetSubTexmap(i)); } } // max 2022 maj introduce a change into the naming of the map. // the SDK do not return the name of the map anymore but a display name with camel style and space // Here a fix which maintain the old style and transform the name for a second try if failed. name = string.Join(" ", name.Split('_').Select(s => char.ToUpper(s[0]) + s.Substring(1))); } return(null); }
protected ITexmap _getTexMapWithCache(IIGameMaterial materialNode, string name) { var materialName = materialNode.MaterialName; if (_mapCaches == null) { _mapCaches = new Dictionary <string, ITexmap>(); for (int i = 0; i < materialNode.MaxMaterial.NumSubTexmaps; i++) { var mn = materialNode.MaxMaterial.GetSubTexmapSlotName(i); _mapCaches.Add(mn, materialNode.MaxMaterial.GetSubTexmap(i)); } } if (_mapCaches.TryGetValue(name, out ITexmap texmap)) { return(texmap); } // max 2022 maj introduce a change into the naming of the map. // the SDK do not return the name of the map anymore but a display name with camel style and space // Here a fix which maintain the old style and transform the name for a second try if failed. name = string.Join(" ", name.Split('_').Select(s => char.ToUpper(s[0]) + s.Substring(1))); return(_mapCaches.TryGetValue(name, out texmap) ? texmap : null); }
public static float GetFloatMaterialProperty(this IIGameMaterial material, string propName, int key, IInterval interval) { float result = 0; for (int i = 0; i < material.IPropertyContainer.NumberOfProperties; ++i) { IIGameProperty property = material.IPropertyContainer.GetProperty(i); if (property == null) { continue; } string propertyName = property.Name.ToUpperInvariant(); string targetProp = propName.ToUpperInvariant(); if (propertyName == targetProp) { property.GetPropertyValue(ref result, key, true); } } return(result); }
private IIGameMaterial GetRenderMaterialFromDirectXShader(IIGameMaterial materialNode) { IIGameMaterial renderMaterial = null; if (isDirectXShaderMaterial(materialNode)) { var gameScene = Loader.Global.IGameInterface; IIGameProperty property = materialNode.IPropertyContainer.GetProperty(35); if (property != null) { IMtl renderMtl = materialNode.IPropertyContainer.GetProperty(35).MaxParamBlock2.GetMtl(4, 0, 0); if (renderMtl != null) { renderMaterial = gameScene.GetIGameMaterial(renderMtl); } } else { RaiseWarning($"DirectX material property for {materialNode.MaterialName} is null...", 2); } } return(renderMaterial); }
/// <summary> /// Return null if the material is supported. /// Otherwise return the unsupported material (himself or one of its sub-materials) /// </summary> /// <param name="materialNode"></param> /// <returns></returns> public IIGameMaterial isMaterialSupported(IIGameMaterial materialNode) { // Shell material if (isShellMaterial(materialNode)) { var bakedMaterial = GetBakedMaterialFromShellMaterial(materialNode); if (bakedMaterial == null) { return(materialNode); } return(isMaterialSupported(bakedMaterial)); } if (materialNode.SubMaterialCount > 0) { // Check sub materials recursively for (int indexSubMaterial = 0; indexSubMaterial < materialNode.SubMaterialCount; indexSubMaterial++) { IIGameMaterial subMaterialNode = materialNode.GetSubMaterial(indexSubMaterial); IIGameMaterial unsupportedSubMaterial = isMaterialSupported(subMaterialNode); if (unsupportedSubMaterial != null) { return(unsupportedSubMaterial); } } // Multi/sub-object material if (isMultiSubObjectMaterial(materialNode)) { return(null); } } else { // Standard material var stdMat = materialNode.MaxMaterial.NumParamBlocks > 0 ? materialNode.MaxMaterial.GetParamBlock(0).Owner as IStdMat2 : null; if (stdMat != null) { return(null); } // Physical material if (isPhysicalMaterial(materialNode)) { return(null); } // Custom material exporters IMaxMaterialExporter materialExporter; if (materialExporters.TryGetValue(new ClassIDWrapper(materialNode.MaxMaterial.ClassID), out materialExporter)) { if (isGltfExported && materialExporter is IMaxGLTFMaterialExporter) { return(null); } else if (isBabylonExported && materialExporter is IMaxBabylonMaterialExporter) { return(null); } } // Arnold material if (isArnoldMaterial(materialNode)) { return(null); } // DirectX Shader if (isDirectXShaderMaterial(materialNode)) { return(isMaterialSupported(GetRenderMaterialFromDirectXShader(materialNode))); } } return(materialNode); }
private BabylonTexture ExportBaseColorAlphaTexture(IIGameMaterial materialNode, float[] baseColor, float alpha, BabylonScene babylonScene, string materialName) { ITexmap baseColorTexMap = _getTexMap(materialNode, 1); ITexmap alphaTexMap = _getTexMap(materialNode, 9); // Transparency weight map // --- Babylon texture --- var baseColorTexture = _getBitmapTex(baseColorTexMap); var alphaTexture = _getBitmapTex(alphaTexMap); // Use one as a reference for UVs parameters var texture = baseColorTexture != null ? baseColorTexture : alphaTexture; if (texture == null) { return(null); } var babylonTexture = new BabylonTexture { name = materialName + "_baseColor.png" // TODO - unsafe name, may conflict with another texture name }; // Level babylonTexture.level = 1.0f; // UVs var uvGen = _exportUV(texture.UVGen, babylonTexture); // Is cube _exportIsCube(texture.Map.FullFilePath, babylonTexture, false); // --- Merge baseColor and alpha maps --- var hasBaseColor = isTextureOk(baseColorTexMap); var hasAlpha = isTextureOk(alphaTexMap); // Alpha babylonTexture.hasAlpha = isTextureOk(alphaTexMap) || (isTextureOk(baseColorTexMap) && baseColorTexture.AlphaSource == 0); babylonTexture.getAlphaFromRGB = false; if (!hasBaseColor && !hasAlpha) { return(null); } if (CopyTexturesToOutput) { // Load bitmaps var baseColorBitmap = _loadTexture(baseColorTexMap); var alphaBitmap = _loadTexture(alphaTexMap); // Retreive dimensions int width = 0; int height = 0; var haveSameDimensions = _getMinimalBitmapDimensions(out width, out height, baseColorBitmap, alphaBitmap); if (!haveSameDimensions) { RaiseError("Base color and transparency color maps should have same dimensions", 2); } var getAlphaFromRGB = false; if (alphaTexture != null) { getAlphaFromRGB = (alphaTexture.AlphaSource == 2) || (alphaTexture.AlphaSource == 3); // 'RGB intensity' or 'None (Opaque)' } // Create baseColor+alpha map var _baseColor = Color.FromArgb( (int)(baseColor[0] * 255), (int)(baseColor[1] * 255), (int)(baseColor[2] * 255)); var _alpha = (int)(alpha * 255); Bitmap baseColorAlphaBitmap = new Bitmap(width, height); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { var baseColorAtPixel = baseColorBitmap != null?baseColorBitmap.GetPixel(x, y) : _baseColor; Color baseColorAlpha; if (alphaBitmap != null) { // Retreive alpha from alpha texture var alphaColor = alphaBitmap.GetPixel(x, y); var alphaAtPixel = 255 - (getAlphaFromRGB ? alphaColor.R : alphaColor.A); baseColorAlpha = Color.FromArgb(alphaAtPixel, baseColorAtPixel); } else if (baseColorTexture != null && baseColorTexture.AlphaSource == 0) // Alpha source is 'Image Alpha' { // Use all channels from base color baseColorAlpha = baseColorAtPixel; } else { // Use RGB channels from base color and default alpha baseColorAlpha = Color.FromArgb(_alpha, baseColorAtPixel.R, baseColorAtPixel.G, baseColorAtPixel.B); } baseColorAlphaBitmap.SetPixel(x, y, baseColorAlpha); } } // Write bitmap if (isBabylonExported) { var absolutePath = Path.Combine(babylonScene.OutputPath, babylonTexture.name); RaiseMessage($"Texture | write image '{babylonTexture.name}'", 2); baseColorAlphaBitmap.Save(absolutePath, System.Drawing.Imaging.ImageFormat.Png); // Explicit image format even though png is default } else { // Store created bitmap for further use in gltf export babylonTexture.bitmap = baseColorAlphaBitmap; } } return(babylonTexture); }
private BabylonTexture ExportMetallicRoughnessTexture(IIGameMaterial materialNode, float metallic, float roughness, BabylonScene babylonScene, string materialName, bool invertRoughness) { ITexmap metallicTexMap = _getTexMap(materialNode, 5); ITexmap roughnessTexMap = _getTexMap(materialNode, 4); // --- Babylon texture --- var metallicTexture = _getBitmapTex(metallicTexMap); var roughnessTexture = _getBitmapTex(roughnessTexMap); // Use one as a reference for UVs parameters var texture = metallicTexture != null ? metallicTexture : roughnessTexture; if (texture == null) { return(null); } var babylonTexture = new BabylonTexture { name = materialName + "_metallicRoughness" + ".jpg" // TODO - unsafe name, may conflict with another texture name }; // Level babylonTexture.level = 1.0f; // No alpha babylonTexture.hasAlpha = false; babylonTexture.getAlphaFromRGB = false; // UVs var uvGen = _exportUV(texture.UVGen, babylonTexture); // Is cube _exportIsCube(texture.Map.FullFilePath, babylonTexture, false); // --- Merge metallic and roughness maps --- if (!isTextureOk(metallicTexMap) && !isTextureOk(roughnessTexMap)) { return(null); } if (CopyTexturesToOutput) { // Load bitmaps var metallicBitmap = _loadTexture(metallicTexMap); var roughnessBitmap = _loadTexture(roughnessTexMap); // Retreive dimensions int width = 0; int height = 0; var haveSameDimensions = _getMinimalBitmapDimensions(out width, out height, metallicBitmap, roughnessBitmap); if (!haveSameDimensions) { RaiseError("Metallic and roughness maps should have same dimensions", 2); } // Create metallic+roughness map Bitmap metallicRoughnessBitmap = new Bitmap(width, height); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { var _metallic = metallicBitmap != null?metallicBitmap.GetPixel(x, y).B: metallic * 255.0f; var _roughness = roughnessBitmap != null ? (invertRoughness ? 255 - roughnessBitmap.GetPixel(x, y).G : roughnessBitmap.GetPixel(x, y).G) : roughness * 255.0f; // The metalness values are sampled from the B channel. // The roughness values are sampled from the G channel. // These values are linear. If other channels are present (R or A), they are ignored for metallic-roughness calculations. Color colorMetallicRoughness = Color.FromArgb( 0, (int)_roughness, (int)_metallic ); metallicRoughnessBitmap.SetPixel(x, y, colorMetallicRoughness); } } // Write bitmap if (isBabylonExported) { var absolutePath = Path.Combine(babylonScene.OutputPath, babylonTexture.name); RaiseMessage($"Texture | write image '{babylonTexture.name}'", 2); metallicRoughnessBitmap.Save(absolutePath, System.Drawing.Imaging.ImageFormat.Jpeg); } else { // Store created bitmap for further use in gltf export babylonTexture.bitmap = metallicRoughnessBitmap; } } return(babylonTexture); }
private void ExportMaterial(IIGameMaterial materialNode, BabylonScene babylonScene) { var name = materialNode.MaterialName; var id = materialNode.MaxMaterial.GetGuid().ToString(); RaiseMessage(name, 1); if (materialNode.SubMaterialCount > 0) { var babylonMultimaterial = new BabylonMultiMaterial { name = name, id = id }; var guids = new List <string>(); for (var index = 0; index < materialNode.SubMaterialCount; index++) { var subMat = materialNode.GetSubMaterial(index); if (subMat != null) { guids.Add(subMat.MaxMaterial.GetGuid().ToString()); if (!referencedMaterials.Contains(subMat)) { referencedMaterials.Add(subMat); ExportMaterial(subMat, babylonScene); } } else { guids.Add(Guid.Empty.ToString()); } } babylonMultimaterial.materials = guids.ToArray(); babylonScene.MultiMaterialsList.Add(babylonMultimaterial); return; } var babylonMaterial = new BabylonStandardMaterial { name = name, id = id, ambient = materialNode.MaxMaterial.GetAmbient(0, false).ToArray(), diffuse = materialNode.MaxMaterial.GetDiffuse(0, false).ToArray(), specular = materialNode.MaxMaterial.GetSpecular(0, false).Scale(materialNode.MaxMaterial.GetShinStr(0, false)), specularPower = materialNode.MaxMaterial.GetShininess(0, false) * 256, emissive = materialNode.MaxMaterial.GetSelfIllumColorOn(0, false) ? materialNode.MaxMaterial.GetSelfIllumColor(0, false).ToArray() : materialNode.MaxMaterial.GetDiffuse(0, false).Scale(materialNode.MaxMaterial.GetSelfIllum(0, false)), alpha = 1.0f - materialNode.MaxMaterial.GetXParency(0, false) }; var stdMat = materialNode.MaxMaterial.GetParamBlock(0).Owner as IStdMat2; if (stdMat != null) { babylonMaterial.backFaceCulling = !stdMat.TwoSided; babylonMaterial.wireframe = stdMat.Wire; // Textures BabylonFresnelParameters fresnelParameters; babylonMaterial.ambientTexture = ExportTexture(stdMat, 0, out fresnelParameters, babylonScene); // Ambient babylonMaterial.diffuseTexture = ExportTexture(stdMat, 1, out fresnelParameters, babylonScene); // Diffuse if (fresnelParameters != null) { babylonMaterial.diffuseFresnelParameters = fresnelParameters; } babylonMaterial.specularTexture = ExportTexture(stdMat, 2, out fresnelParameters, babylonScene); // Specular babylonMaterial.emissiveTexture = ExportTexture(stdMat, 5, out fresnelParameters, babylonScene); // Emissive if (fresnelParameters != null) { babylonMaterial.emissiveFresnelParameters = fresnelParameters; if (babylonMaterial.emissive[0] == 0 && babylonMaterial.emissive[1] == 0 && babylonMaterial.emissive[2] == 0 && babylonMaterial.emissiveTexture == null) { babylonMaterial.emissive = new float[] { 1, 1, 1 }; } } babylonMaterial.opacityTexture = ExportTexture(stdMat, 6, out fresnelParameters, babylonScene, false, true); // Opacity if (fresnelParameters != null) { babylonMaterial.opacityFresnelParameters = fresnelParameters; if (babylonMaterial.alpha == 1 && babylonMaterial.opacityTexture == null) { babylonMaterial.alpha = 0; } } babylonMaterial.bumpTexture = ExportTexture(stdMat, 8, out fresnelParameters, babylonScene); // Bump babylonMaterial.reflectionTexture = ExportTexture(stdMat, 9, out fresnelParameters, babylonScene, true); // Reflection if (fresnelParameters != null) { if (babylonMaterial.reflectionTexture == null) { RaiseWarning("Fallout cannot be used with reflection channel without a texture", 2); } else { babylonMaterial.reflectionFresnelParameters = fresnelParameters; } } // Constraints if (babylonMaterial.diffuseTexture != null) { babylonMaterial.diffuse = new[] { 1.0f, 1.0f, 1.0f }; } if (babylonMaterial.emissiveTexture != null) { babylonMaterial.emissive = new float[] { 0, 0, 0 }; } if (babylonMaterial.opacityTexture != null && babylonMaterial.diffuseTexture != null && babylonMaterial.diffuseTexture.name == babylonMaterial.opacityTexture.name && babylonMaterial.diffuseTexture.hasAlpha && !babylonMaterial.opacityTexture.getAlphaFromRGB) { // This is a alpha testing purpose babylonMaterial.opacityTexture = null; babylonMaterial.diffuseTexture.hasAlpha = true; RaiseWarning("Opacity texture was removed because alpha from diffuse texture can be use instead", 2); RaiseWarning("If you do not want this behavior, just set Alpha Source = None on your diffuse texture", 2); } } babylonScene.MaterialsList.Add(babylonMaterial); }
private void ExportMaterial(BabylonMaterial babylonMaterial, GLTF gltf) { var name = babylonMaterial.name; var id = babylonMaterial.id; RaiseMessage("GLTFExporter.Material | Export material named: " + name, 1); GLTFMaterial gltfMaterial = null; IIGameMaterial gameMtl = babylonMaterial.maxGameMaterial; IMtl maxMtl = gameMtl.MaxMaterial; IMaterialExporter materialExporter; if (materialExporters.TryGetValue(new ClassIDWrapper(maxMtl.ClassID), out materialExporter) && materialExporter is IGLTFMaterialExporter) { gltfMaterial = ((IGLTFMaterialExporter)materialExporter).ExportGLTFMaterial(this, gltf, gameMtl, (string sourcePath, string textureName) => { return TryWriteImage(gltf, sourcePath, textureName); }, (string message, Color color) => { RaiseMessage(message, color, 2); }, (string message) => { RaiseWarning(message, 2); }, (string message) => { RaiseError(message, 2); }); if (gltfMaterial == null) { string message = string.Format("Custom glTF material exporter failed to export | Exporter: '{0}' | Material Name: '{1}' | Material Class: '{2}'", materialExporter.GetType().ToString(), gameMtl.MaterialName, gameMtl.ClassName); RaiseWarning(message, 2); } else { gltfMaterial.index = gltf.MaterialsList.Count; gltf.MaterialsList.Add(gltfMaterial); } } else if (babylonMaterial.GetType() == typeof(BabylonStandardMaterial)) { var babylonStandardMaterial = babylonMaterial as BabylonStandardMaterial; // --- prints --- #region prints RaiseVerbose("GLTFExporter.Material | babylonMaterial data", 2); RaiseVerbose("GLTFExporter.Material | babylonMaterial.alpha=" + babylonMaterial.alpha, 3); RaiseVerbose("GLTFExporter.Material | babylonMaterial.alphaMode=" + babylonMaterial.alphaMode, 3); RaiseVerbose("GLTFExporter.Material | babylonMaterial.backFaceCulling=" + babylonMaterial.backFaceCulling, 3); RaiseVerbose("GLTFExporter.Material | babylonMaterial.wireframe=" + babylonMaterial.wireframe, 3); // Ambient for (int i = 0; i < babylonStandardMaterial.ambient.Length; i++) { RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.ambient[" + i + "]=" + babylonStandardMaterial.ambient[i], 3); } // Diffuse RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.diffuse.Length=" + babylonStandardMaterial.diffuse.Length, 3); for (int i = 0; i < babylonStandardMaterial.diffuse.Length; i++) { RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.diffuse[" + i + "]=" + babylonStandardMaterial.diffuse[i], 3); } if (babylonStandardMaterial.diffuseTexture == null) { RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.diffuseTexture=null", 3); } else { RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.diffuseTexture.name=" + babylonStandardMaterial.diffuseTexture.name, 3); } // Normal / bump if (babylonStandardMaterial.bumpTexture == null) { RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.bumpTexture=null", 3); } else { RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.bumpTexture.name=" + babylonStandardMaterial.bumpTexture.name, 3); } // Opacity if (babylonStandardMaterial.opacityTexture == null) { RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.opacityTexture=null", 3); } else { RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.opacityTexture.name=" + babylonStandardMaterial.opacityTexture.name, 3); } // Specular for (int i = 0; i < babylonStandardMaterial.specular.Length; i++) { RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.specular[" + i + "]=" + babylonStandardMaterial.specular[i], 3); } RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.specularPower=" + babylonStandardMaterial.specularPower, 3); if (babylonStandardMaterial.specularTexture == null) { RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.specularTexture=null", 3); } else { RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.specularTexture.name=" + babylonStandardMaterial.specularTexture.name, 3); } // Occlusion if (babylonStandardMaterial.ambientTexture == null) { RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.ambientTexture=null", 3); } else { RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.ambientTexture.name=" + babylonStandardMaterial.ambientTexture.name, 3); } // Emissive for (int i = 0; i < babylonStandardMaterial.emissive.Length; i++) { RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.emissive[" + i + "]=" + babylonStandardMaterial.emissive[i], 3); } if (babylonStandardMaterial.emissiveTexture == null) { RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.emissiveTexture=null", 3); } else { RaiseVerbose("GLTFExporter.Material | babylonStandardMaterial.emissiveTexture.name=" + babylonStandardMaterial.emissiveTexture.name, 3); } #endregion // -------------------------------- // --------- gltfMaterial --------- // -------------------------------- RaiseMessage("GLTFExporter.Material | create gltfMaterial", 2); gltfMaterial = new GLTFMaterial { name = name }; gltfMaterial.id = babylonMaterial.id; gltfMaterial.index = gltf.MaterialsList.Count; gltf.MaterialsList.Add(gltfMaterial); // Alpha string alphaMode; float? alphaCutoff; getAlphaMode(babylonStandardMaterial, out alphaMode, out alphaCutoff); gltfMaterial.alphaMode = alphaMode; gltfMaterial.alphaCutoff = alphaCutoff; // DoubleSided gltfMaterial.doubleSided = !babylonMaterial.backFaceCulling; // Normal gltfMaterial.normalTexture = ExportTexture(babylonStandardMaterial.bumpTexture, gltf); // Occulison gltfMaterial.occlusionTexture = ExportTexture(babylonStandardMaterial.ambientTexture, gltf); // Emissive gltfMaterial.emissiveFactor = babylonStandardMaterial.emissive; // linkEmissiveWithDiffuse attribute doesn't have an equivalent in gltf format // When true, the emissive texture needs to be manually multiplied with diffuse texture // Otherwise, the emissive texture is assumed to be already pre-multiplied if (babylonStandardMaterial.linkEmissiveWithDiffuse) { // Even when no emissive texture is provided, the self illumination value needs to be multiplied to the diffuse texture in order to get the pre-multiplied emissive (texture) if (babylonStandardMaterial.emissiveTexture != null || babylonStandardMaterial.selfIllum > 0) { // Default emissive is the raw value of the self illumination // It is not the babylon emissive value which is already pre-multiplied with diffuse color float[] defaultEmissive = new float[] { 1, 1, 1 }.Multiply(babylonStandardMaterial.selfIllum); gltfMaterial.emissiveTexture = ExportEmissiveTexture(babylonStandardMaterial, gltf, defaultEmissive, babylonStandardMaterial.diffuse); } } else { gltfMaterial.emissiveTexture = ExportTexture(babylonStandardMaterial.emissiveTexture, gltf); } // Constraints if (gltfMaterial.emissiveTexture != null) { gltfMaterial.emissiveFactor = new[] { 1.0f, 1.0f, 1.0f }; } // -------------------------------- // --- gltfPbrMetallicRoughness --- // -------------------------------- RaiseMessage("GLTFExporter.Material | create gltfPbrMetallicRoughness", 2); var gltfPbrMetallicRoughness = new GLTFPBRMetallicRoughness(); gltfMaterial.pbrMetallicRoughness = gltfPbrMetallicRoughness; // --- Global --- // Eye Ball correction to limit overall brightness from std to PBR. // This only impacts the factors. var correctedDiffuse = new BabylonColor3(babylonStandardMaterial.diffuse).scale(0.5f); SpecularGlossiness _specularGlossiness = new SpecularGlossiness { diffuse = correctedDiffuse, opacity = babylonMaterial.alpha, specular = new BabylonColor3(babylonStandardMaterial.specular), glossiness = babylonStandardMaterial.specularPower / 256 }; MetallicRoughness _metallicRoughness = ConvertToMetallicRoughness(_specularGlossiness, true); // Base color gltfPbrMetallicRoughness.baseColorFactor = new float[4] { _metallicRoughness.baseColor.r, _metallicRoughness.baseColor.g, _metallicRoughness.baseColor.b, _metallicRoughness.opacity }; // Metallic roughness gltfPbrMetallicRoughness.metallicFactor = _metallicRoughness.metallic; gltfPbrMetallicRoughness.roughnessFactor = _metallicRoughness.roughness; // --- Textures --- var babylonTexture = babylonStandardMaterial.diffuseTexture != null ? babylonStandardMaterial.diffuseTexture : babylonStandardMaterial.specularTexture != null ? babylonStandardMaterial.specularTexture : babylonStandardMaterial.opacityTexture != null ? babylonStandardMaterial.opacityTexture : null; if (babylonTexture != null) { //Check if the texture already exist var _key = SetStandText(babylonStandardMaterial); bool isAlphaInTexture = (isTextureOk(babylonStandardMaterial.diffuseTexture) && babylonStandardMaterial.diffuseTexture.hasAlpha) || isTextureOk(babylonStandardMaterial.opacityTexture); Bitmap baseColorBitmap = null; Bitmap metallicRoughnessBitmap = null; GLTFTextureInfo textureInfoBC = new GLTFTextureInfo(); GLTFTextureInfo textureInfoMR = new GLTFTextureInfo(); if (exportParameters.writeTextures) { // Diffuse Bitmap diffuseBitmap = null; if (babylonStandardMaterial.diffuseTexture != null) { diffuseBitmap = LoadTexture(babylonStandardMaterial.diffuseTexture.originalPath); } // Specular Bitmap specularBitmap = null; if (babylonStandardMaterial.specularTexture != null) { if (babylonStandardMaterial.specularTexture.bitmap != null) { // Specular color map has been computed by the exporter specularBitmap = babylonStandardMaterial.specularTexture.bitmap; } else { // Specular color map is straight input specularBitmap = LoadTexture(babylonStandardMaterial.specularTexture.originalPath); } } // Opacity / Alpha / Transparency Bitmap opacityBitmap = null; if ((babylonStandardMaterial.diffuseTexture == null || babylonStandardMaterial.diffuseTexture.hasAlpha == false) && babylonStandardMaterial.opacityTexture != null) { opacityBitmap = LoadTexture(babylonStandardMaterial.opacityTexture.originalPath); } if (diffuseBitmap != null || specularBitmap != null || opacityBitmap != null) { // Retreive dimensions int width = 0; int height = 0; var haveSameDimensions = _getMinimalBitmapDimensions(out width, out height, diffuseBitmap, specularBitmap, opacityBitmap); if (!haveSameDimensions) { RaiseError("Diffuse, specular and opacity maps should have same dimensions", 2); } // Create baseColor+alpha and metallic+roughness maps baseColorBitmap = new Bitmap(width, height); metallicRoughnessBitmap = new Bitmap(width, height); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { SpecularGlossiness specularGlossinessTexture = new SpecularGlossiness { diffuse = diffuseBitmap != null ? new BabylonColor3(diffuseBitmap.GetPixel(x, y)) : _specularGlossiness.diffuse, opacity = diffuseBitmap != null && babylonStandardMaterial.diffuseTexture.hasAlpha ? diffuseBitmap.GetPixel(x, y).A / 255.0f : opacityBitmap != null && babylonStandardMaterial.opacityTexture.getAlphaFromRGB ? opacityBitmap.GetPixel(x, y).R / 255.0f : opacityBitmap != null && babylonStandardMaterial.opacityTexture.getAlphaFromRGB == false ? opacityBitmap.GetPixel(x, y).A / 255.0f : _specularGlossiness.opacity, specular = specularBitmap != null ? new BabylonColor3(specularBitmap.GetPixel(x, y)) : _specularGlossiness.specular, glossiness = babylonStandardMaterial.useGlossinessFromSpecularMapAlpha && specularBitmap != null ? specularBitmap.GetPixel(x, y).A / 255.0f : _specularGlossiness.glossiness }; var displayPrints = x == width / 2 && y == height / 2; MetallicRoughness metallicRoughnessTexture = ConvertToMetallicRoughness(specularGlossinessTexture, displayPrints); Color colorBase = Color.FromArgb( (int)(metallicRoughnessTexture.opacity * 255), (int)(metallicRoughnessTexture.baseColor.r * 255), (int)(metallicRoughnessTexture.baseColor.g * 255), (int)(metallicRoughnessTexture.baseColor.b * 255) ); baseColorBitmap.SetPixel(x, y, colorBase); // The metalness values are sampled from the B channel. // The roughness values are sampled from the G channel. // These values are linear. If other channels are present (R or A), they are ignored for metallic-roughness calculations. Color colorMetallicRoughness = Color.FromArgb( 0, (int)(metallicRoughnessTexture.roughness * 255), (int)(metallicRoughnessTexture.metallic * 255) ); metallicRoughnessBitmap.SetPixel(x, y, colorMetallicRoughness); } } } } //export textures textureInfoBC = ExportBitmapTexture(gltf, babylonTexture, baseColorBitmap, babylonTexture.name); gltfPbrMetallicRoughness.baseColorTexture = textureInfoBC; // If no specular map is defined, the metallic and roughness values are be driven by the global parameters if (babylonStandardMaterial.specularTexture != null) { textureInfoMR = ExportBitmapTexture(gltf, babylonTexture, metallicRoughnessBitmap, babylonMaterial.name + "_metallicRoughness" + ".jpg"); gltfPbrMetallicRoughness.metallicRoughnessTexture = textureInfoMR; } //register the texture AddStandText(_key, textureInfoBC, textureInfoMR); } } else if (babylonMaterial.GetType() == typeof(BabylonPBRMetallicRoughnessMaterial)) { var babylonPBRMetallicRoughnessMaterial = babylonMaterial as BabylonPBRMetallicRoughnessMaterial; // --- prints --- #region prints RaiseVerbose("GLTFExporter.Material | babylonMaterial data", 2); RaiseVerbose("GLTFExporter.Material | babylonMaterial.alpha=" + babylonMaterial.alpha, 3); RaiseVerbose("GLTFExporter.Material | babylonMaterial.alphaMode=" + babylonMaterial.alphaMode, 3); RaiseVerbose("GLTFExporter.Material | babylonMaterial.backFaceCulling=" + babylonMaterial.backFaceCulling, 3); RaiseVerbose("GLTFExporter.Material | babylonMaterial.wireframe=" + babylonMaterial.wireframe, 3); // Global RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.maxSimultaneousLights=" + babylonPBRMetallicRoughnessMaterial.maxSimultaneousLights, 3); RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.disableLighting=" + babylonPBRMetallicRoughnessMaterial.disableLighting, 3); RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.alphaCutOff=" + babylonPBRMetallicRoughnessMaterial.alphaCutOff, 3); RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.transparencyMode=" + babylonPBRMetallicRoughnessMaterial.transparencyMode, 3); RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.doubleSided=" + babylonPBRMetallicRoughnessMaterial.doubleSided, 3); // Base color RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.baseColor.Length=" + babylonPBRMetallicRoughnessMaterial.baseColor.Length, 3); for (int i = 0; i < babylonPBRMetallicRoughnessMaterial.baseColor.Length; i++) { RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.baseColor[" + i + "]=" + babylonPBRMetallicRoughnessMaterial.baseColor[i], 3); } if (babylonPBRMetallicRoughnessMaterial.baseTexture == null) { RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.baseTexture=null", 3); } // Metallic+roughness RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.metallic=" + babylonPBRMetallicRoughnessMaterial.metallic, 3); RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.roughness=" + babylonPBRMetallicRoughnessMaterial.roughness, 3); if (babylonPBRMetallicRoughnessMaterial.metallicRoughnessTexture == null) { RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.metallicRoughnessTexture=null", 3); } // Normal / bump if (babylonPBRMetallicRoughnessMaterial.normalTexture == null) { RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.normalTexture=null", 3); } RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.invertNormalMapX=" + babylonPBRMetallicRoughnessMaterial.invertNormalMapX, 3); RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.invertNormalMapY=" + babylonPBRMetallicRoughnessMaterial.invertNormalMapY, 3); // Emissive for (int i = 0; i < babylonPBRMetallicRoughnessMaterial.emissive.Length; i++) { RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.emissiveColor[" + i + "]=" + babylonPBRMetallicRoughnessMaterial.emissive[i], 3); } if (babylonPBRMetallicRoughnessMaterial.emissiveTexture == null) { RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.emissiveTexture=null", 3); } // Ambient occlusion RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.occlusionStrength=" + babylonPBRMetallicRoughnessMaterial.occlusionStrength, 3); if (babylonPBRMetallicRoughnessMaterial.occlusionTexture == null) { RaiseVerbose("GLTFExporter.Material | babylonPBRMetallicRoughnessMaterial.occlusionTexture=null", 3); } #endregion // -------------------------------- // --------- gltfMaterial --------- // -------------------------------- RaiseMessage("GLTFExporter.Material | create gltfMaterial", 2); gltfMaterial = new GLTFMaterial { name = name }; gltfMaterial.id = babylonMaterial.id; gltfMaterial.index = gltf.MaterialsList.Count; gltf.MaterialsList.Add(gltfMaterial); // Alpha string alphaMode; float? alphaCutoff; getAlphaMode(babylonPBRMetallicRoughnessMaterial, out alphaMode, out alphaCutoff); gltfMaterial.alphaMode = alphaMode; gltfMaterial.alphaCutoff = alphaCutoff; // DoubleSided gltfMaterial.doubleSided = babylonPBRMetallicRoughnessMaterial.doubleSided; // Normal gltfMaterial.normalTexture = ExportTexture(babylonPBRMetallicRoughnessMaterial.normalTexture, gltf); // Occlusion if (babylonPBRMetallicRoughnessMaterial.occlusionTexture != null) { if (babylonPBRMetallicRoughnessMaterial.occlusionTexture.bitmap != null) { // ORM texture has been merged manually by the exporter // Occlusion is defined as well as metallic and/or roughness RaiseVerbose("Occlusion is defined as well as metallic and/or roughness", 2); gltfMaterial.occlusionTexture = ExportBitmapTexture(gltf, babylonPBRMetallicRoughnessMaterial.occlusionTexture); } else { // ORM texture was already merged or only occlusion is defined RaiseVerbose("ORM texture was already merged or only occlusion is defined", 2); gltfMaterial.occlusionTexture = ExportTexture(babylonPBRMetallicRoughnessMaterial.occlusionTexture, gltf); } } // Emissive gltfMaterial.emissiveFactor = babylonPBRMetallicRoughnessMaterial.emissive; gltfMaterial.emissiveTexture = ExportTexture(babylonPBRMetallicRoughnessMaterial.emissiveTexture, gltf); // -------------------------------- // --- gltfPbrMetallicRoughness --- // -------------------------------- RaiseMessage("GLTFExporter.Material | create gltfPbrMetallicRoughness", 2); var gltfPbrMetallicRoughness = new GLTFPBRMetallicRoughness(); gltfMaterial.pbrMetallicRoughness = gltfPbrMetallicRoughness; // --- Global --- // Base color gltfPbrMetallicRoughness.baseColorFactor = new float[4] { babylonPBRMetallicRoughnessMaterial.baseColor[0], babylonPBRMetallicRoughnessMaterial.baseColor[1], babylonPBRMetallicRoughnessMaterial.baseColor[2], babylonPBRMetallicRoughnessMaterial.alpha }; if (babylonPBRMetallicRoughnessMaterial.baseTexture != null) { if (babylonPBRMetallicRoughnessMaterial.baseTexture.bitmap != null) { gltfPbrMetallicRoughness.baseColorTexture = ExportBitmapTexture(gltf, babylonPBRMetallicRoughnessMaterial.baseTexture); } else { gltfPbrMetallicRoughness.baseColorTexture = ExportTexture(babylonPBRMetallicRoughnessMaterial.baseTexture, gltf); } } // Metallic roughness gltfPbrMetallicRoughness.metallicFactor = babylonPBRMetallicRoughnessMaterial.metallic; gltfPbrMetallicRoughness.roughnessFactor = babylonPBRMetallicRoughnessMaterial.roughness; if (babylonPBRMetallicRoughnessMaterial.metallicRoughnessTexture != null) { if (babylonPBRMetallicRoughnessMaterial.metallicRoughnessTexture == babylonPBRMetallicRoughnessMaterial.occlusionTexture) { // Occlusion is defined as well as metallic and/or roughness // Use same texture RaiseVerbose("Occlusion is defined as well as metallic and/or roughness", 2); gltfPbrMetallicRoughness.metallicRoughnessTexture = gltfMaterial.occlusionTexture; } else { // Occlusion is not defined, only metallic and/or roughness RaiseVerbose("Occlusion is not defined, only metallic and/or roughness", 2); if (babylonPBRMetallicRoughnessMaterial.metallicRoughnessTexture.bitmap != null) { // Metallic & roughness texture has been merged manually by the exporter // Write bitmap file RaiseVerbose("Metallic & roughness texture has been merged manually by the exporter", 2); gltfPbrMetallicRoughness.metallicRoughnessTexture = ExportBitmapTexture(gltf, babylonPBRMetallicRoughnessMaterial.metallicRoughnessTexture); } else { // Metallic & roughness texture was already merged // Copy file RaiseVerbose("Metallic & roughness texture was already merged", 2); gltfPbrMetallicRoughness.metallicRoughnessTexture = ExportTexture(babylonPBRMetallicRoughnessMaterial.metallicRoughnessTexture, gltf); } } } } else { RaiseWarning("GLTFExporter.Material | Unsupported material type: " + babylonMaterial.GetType() + " | Max MaterialClass: " + babylonMaterial.maxGameMaterial.ClassName, 2); } if (gltfMaterial != null && babylonMaterial.isUnlit) { // Add Unlit extension if (!exportParameters.enableKHRMaterialsUnlit) { RaiseWarning("GLTFExporter.Material | KHR_materials_unlit has not been enabled for export!", 2); } else { if (gltfMaterial.extensions == null) { gltfMaterial.extensions = new GLTFExtensions(); } if (gltf.extensionsUsed == null) { gltf.extensionsUsed = new System.Collections.Generic.List<string>(); } if (!gltf.extensionsUsed.Contains("KHR_materials_unlit")) { gltf.extensionsUsed.Add("KHR_materials_unlit"); } gltfMaterial.extensions["KHR_materials_unlit"] = new object(); } } }
/// <summary> /// Return null if the material is supported. /// Otherwise return the unsupported material (himself or one of its sub-materials) /// </summary> /// <param name="materialNode"></param> /// <returns></returns> public IIGameMaterial isMaterialSupported(IIGameMaterial materialNode) { // Shell material if (isShellMaterial(materialNode)) { var bakedMaterial = GetBakedMaterialFromShellMaterial(materialNode); if (bakedMaterial == null) { return(materialNode); } return(isMaterialSupported(bakedMaterial)); } if (materialNode.SubMaterialCount > 0) { // Check sub materials recursively for (int indexSubMaterial = 0; indexSubMaterial < materialNode.SubMaterialCount; indexSubMaterial++) { IIGameMaterial subMaterialNode = materialNode.GetSubMaterial(indexSubMaterial); IIGameMaterial unsupportedSubMaterial = isMaterialSupported(subMaterialNode); if (unsupportedSubMaterial != null) { return(unsupportedSubMaterial); } } // Multi/sub-object material if (isMultiSubObjectMaterial(materialNode)) { return(null); } } else { // Standard material var stdMat = materialNode.MaxMaterial.NumParamBlocks > 0 ? materialNode.MaxMaterial.GetParamBlock(0).Owner as IStdMat2 : null; if (stdMat != null) { return(null); } // Physical material if (isPhysicalMaterial(materialNode)) { return(null); } // Arnold material if (isArnoldMaterial(materialNode)) { return(null); } // DirectX Shader if (isDirectXShaderMaterial(materialNode)) { return(isMaterialSupported(GetRenderMaterialFromDirectXShader(materialNode))); } } return(materialNode); }
public bool isShellMaterial(IIGameMaterial materialNode) { return(materialNode.MaterialClass.ToLower() == "shell material" || // English materialNode.MaterialClass.ToLower() == "hüllenmaterial" || // German materialNode.MaterialClass.ToLower() == "matériau coque"); // French }
public bool isArnoldMaterial(IIGameMaterial materialNode) { return(materialNode.MaterialClass.ToLower() == "standard surface"); // English, German and French }
private void ExportMaterial(IIGameMaterial materialNode, BabylonScene babylonScene) { var name = materialNode.MaterialName; var id = materialNode.MaxMaterial.GetGuid().ToString(); RaiseMessage(name, 1); if (materialNode.SubMaterialCount > 0) { var babylonMultimaterial = new BabylonMultiMaterial { name = name, id = id }; var guids = new List<string>(); for (var index = 0; index < materialNode.SubMaterialCount; index++) { var subMat = materialNode.GetSubMaterial(index); if (subMat != null) { guids.Add(subMat.MaxMaterial.GetGuid().ToString()); if (!referencedMaterials.Contains(subMat)) { referencedMaterials.Add(subMat); ExportMaterial(subMat, babylonScene); } } else { guids.Add(Guid.Empty.ToString()); } } babylonMultimaterial.materials = guids.ToArray(); babylonScene.MultiMaterialsList.Add(babylonMultimaterial); return; } var babylonMaterial = new BabylonStandardMaterial { name = name, id = id, ambient = materialNode.MaxMaterial.GetAmbient(0, false).ToArray(), diffuse = materialNode.MaxMaterial.GetDiffuse(0, false).ToArray(), specular = materialNode.MaxMaterial.GetSpecular(0, false).Scale(materialNode.MaxMaterial.GetShinStr(0, false)), specularPower = materialNode.MaxMaterial.GetShininess(0, false) * 256, emissive = materialNode.MaxMaterial.GetSelfIllumColorOn(0, false) ? materialNode.MaxMaterial.GetSelfIllumColor(0, false).ToArray() : materialNode.MaxMaterial.GetDiffuse(0, false).Scale(materialNode.MaxMaterial.GetSelfIllum(0, false)), alpha = 1.0f - materialNode.MaxMaterial.GetXParency(0, false) }; var stdMat = materialNode.MaxMaterial.GetParamBlock(0).Owner as IStdMat2; if (stdMat != null) { babylonMaterial.backFaceCulling = !stdMat.TwoSided; babylonMaterial.wireframe = stdMat.Wire; // Textures BabylonFresnelParameters fresnelParameters; babylonMaterial.ambientTexture = ExportTexture(stdMat, 0, out fresnelParameters, babylonScene); // Ambient babylonMaterial.diffuseTexture = ExportTexture(stdMat, 1, out fresnelParameters, babylonScene); // Diffuse if (fresnelParameters != null) { babylonMaterial.diffuseFresnelParameters = fresnelParameters; } babylonMaterial.specularTexture = ExportTexture(stdMat, 2, out fresnelParameters, babylonScene); // Specular babylonMaterial.emissiveTexture = ExportTexture(stdMat, 5, out fresnelParameters, babylonScene); // Emissive if (fresnelParameters != null) { babylonMaterial.emissiveFresnelParameters = fresnelParameters; if (babylonMaterial.emissive[0] == 0 && babylonMaterial.emissive[1] == 0 && babylonMaterial.emissive[2] == 0 && babylonMaterial.emissiveTexture == null) { babylonMaterial.emissive = new float[] { 1, 1, 1 }; } } babylonMaterial.opacityTexture = ExportTexture(stdMat, 6, out fresnelParameters, babylonScene, false, true); // Opacity if (fresnelParameters != null) { babylonMaterial.opacityFresnelParameters = fresnelParameters; if (babylonMaterial.alpha == 1 && babylonMaterial.opacityTexture == null) { babylonMaterial.alpha = 0; } } babylonMaterial.bumpTexture = ExportTexture(stdMat, 8, out fresnelParameters, babylonScene); // Bump babylonMaterial.reflectionTexture = ExportTexture(stdMat, 9, out fresnelParameters, babylonScene, true); // Reflection if (fresnelParameters != null) { if (babylonMaterial.reflectionTexture == null) { RaiseWarning("Fallout cannot be used with reflection channel without a texture", 2); } else { babylonMaterial.reflectionFresnelParameters = fresnelParameters; } } // Constraints if (babylonMaterial.diffuseTexture != null) { babylonMaterial.diffuse = new[] { 1.0f, 1.0f, 1.0f }; } if (babylonMaterial.emissiveTexture != null) { babylonMaterial.emissive = new float[] { 0, 0, 0 }; } if (babylonMaterial.opacityTexture != null && babylonMaterial.diffuseTexture != null && babylonMaterial.diffuseTexture.name == babylonMaterial.opacityTexture.name && babylonMaterial.diffuseTexture.hasAlpha && !babylonMaterial.opacityTexture.getAlphaFromRGB) { // This is a alpha testing purpose babylonMaterial.opacityTexture = null; babylonMaterial.diffuseTexture.hasAlpha = true; RaiseWarning("Opacity texture was removed because alpha from diffuse texture can be use instead", 2); RaiseWarning("If you do not want this behavior, just set Alpha Source = None on your diffuse texture", 2); } } babylonScene.MaterialsList.Add(babylonMaterial); }
private BabylonTexture ExportMetallicRoughnessTexture(IIGameMaterial materialNode, float metallic, float roughness, BabylonScene babylonScene, string materialName) { ITexmap metallicTexMap = _getTexMap(materialNode, 5); ITexmap roughnessTexMap = _getTexMap(materialNode, 4); if (metallicTexMap == null && roughnessTexMap == null) { return(null); } // Use one as a reference for UVs parameters var referenceTexMap = metallicTexMap != null ? metallicTexMap : roughnessTexMap; // --- Babylon texture --- if (referenceTexMap.GetParamBlock(0) == null || referenceTexMap.GetParamBlock(0).Owner == null) { return(null); } var texture = referenceTexMap.GetParamBlock(0).Owner as IBitmapTex; if (texture == null) { return(null); } var babylonTexture = new BabylonTexture { name = materialName + "_metallicRoughness" + ".jpg" // TODO - unsafe name, may conflict with another texture name }; // Level babylonTexture.level = 1.0f; // No alpha babylonTexture.hasAlpha = false; babylonTexture.getAlphaFromRGB = false; // UVs var uvGen = _exportUV(texture, babylonTexture); // Is cube _exportIsCube(texture, babylonTexture, false); // --- Merge metallic and roughness maps --- // Load bitmaps var metallicBitmap = _loadTexture(metallicTexMap); var roughnessBitmap = _loadTexture(roughnessTexMap); // Retreive dimensions int width = 0; int height = 0; var haveSameDimensions = _getMinimalBitmapDimensions(out width, out height, metallicBitmap, roughnessBitmap); if (!haveSameDimensions) { RaiseWarning("Metallic and roughness maps should have same dimensions", 2); } // Create metallic+roughness map Bitmap metallicRoughnessBitmap = new Bitmap(width, height); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { var _metallic = metallicBitmap != null?metallicBitmap.GetPixel(x, y).R: metallic * 255.0f; var _roughness = roughnessBitmap != null?roughnessBitmap.GetPixel(x, y).R: roughness * 255.0f; // The metalness values are sampled from the B channel. // The roughness values are sampled from the G channel. // These values are linear. If other channels are present (R or A), they are ignored for metallic-roughness calculations. Color colorMetallicRoughness = Color.FromArgb( 0, (int)_roughness, (int)_metallic ); metallicRoughnessBitmap.SetPixel(x, y, colorMetallicRoughness); } } // Write bitmap var absolutePath = Path.Combine(babylonScene.OutputPath, babylonTexture.name); RaiseMessage($"Texture | write image '{babylonTexture.name}'", 2); metallicRoughnessBitmap.Save(absolutePath); return(babylonTexture); }
private BabylonNode ExportMesh(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene) { if (IsMeshExportable(meshNode) == false) { return(null); } RaiseMessage(meshNode.Name, 1); // Instances var tabs = Loader.Global.NodeTab.Create(); Loader.Global.IInstanceMgr.InstanceMgr.GetInstances(meshNode.MaxNode, tabs); if (tabs.Count > 1) { // For a mesh with instances, we distinguish between master and instance meshes: // - a master mesh stores all the info of the mesh (transform, hierarchy, animations + vertices, indices, materials, bones...) // - an instance mesh only stores the info of the node (transform, hierarchy, animations) // Check if this mesh has already been exported BabylonMesh babylonMasterMesh = null; var index = 0; while (babylonMasterMesh == null && index < tabs.Count) { #if MAX2017 || MAX2018 var tab = tabs[index]; #else var tab = tabs[new IntPtr(index)]; #endif babylonMasterMesh = babylonScene.MeshesList.Find(_babylonMesh => { // Same id return(_babylonMesh.id == tab.GetGuid().ToString() && // Mesh is not a dummy _babylonMesh.isDummy == false); }); index++; } if (babylonMasterMesh != null) { // Mesh already exported // Export this node as instance meshNode.MaxNode.MarkAsInstance(); var babylonInstanceMesh = new BabylonAbstractMesh { name = meshNode.Name, id = meshNode.MaxNode.GetGuid().ToString() }; // Add instance to master mesh List <BabylonAbstractMesh> list = babylonMasterMesh.instances != null?babylonMasterMesh.instances.ToList() : new List <BabylonAbstractMesh>(); list.Add(babylonInstanceMesh); babylonMasterMesh.instances = list.ToArray(); // Export transform / hierarchy / animations exportNode(babylonInstanceMesh, meshNode, scene, babylonScene); // Animations exportAnimation(babylonInstanceMesh, meshNode); return(babylonInstanceMesh); } } var gameMesh = meshNode.IGameObject.AsGameMesh(); try { bool initialized = gameMesh.InitializeData; // needed, the property is in fact a method initializing the exporter that has wrongly been auto // translated into a property because it has no parameters } catch (Exception e) { RaiseWarning($"Mesh {meshNode.Name} failed to initialize. Mesh is exported as dummy.", 2); return(ExportDummy(scene, meshNode, babylonScene)); } var babylonMesh = new BabylonMesh { name = meshNode.Name, id = meshNode.MaxNode.GetGuid().ToString() }; // Position / rotation / scaling / hierarchy exportNode(babylonMesh, meshNode, scene, babylonScene); // Sounds var soundName = meshNode.MaxNode.GetStringProperty("babylonjs_sound_filename", ""); if (!string.IsNullOrEmpty(soundName)) { var filename = Path.GetFileName(soundName); var meshSound = new BabylonSound { name = filename, autoplay = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_autoplay", 1), loop = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_loop", 1), volume = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_volume", 1.0f), playbackRate = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_playbackrate", 1.0f), connectedMeshId = babylonMesh.id, isDirectional = false, spatialSound = false, distanceModel = meshNode.MaxNode.GetStringProperty("babylonjs_sound_distancemodel", "linear"), maxDistance = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_maxdistance", 100f), rolloffFactor = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_rolloff", 1.0f), refDistance = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_refdistance", 1.0f), }; var isDirectional = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_directional"); if (isDirectional) { meshSound.isDirectional = true; meshSound.coneInnerAngle = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneinnerangle", 360f); meshSound.coneOuterAngle = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneouterangle", 360f); meshSound.coneOuterGain = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneoutergain", 1.0f); } babylonScene.SoundsList.Add(meshSound); if (isBabylonExported) { try { File.Copy(soundName, Path.Combine(babylonScene.OutputPath, filename), true); } catch { } } } // Misc. #if MAX2017 || MAX2018 babylonMesh.isVisible = meshNode.MaxNode.Renderable; babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows; babylonMesh.applyFog = meshNode.MaxNode.ApplyAtmospherics; #else babylonMesh.isVisible = meshNode.MaxNode.Renderable == 1; babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows == 1; babylonMesh.applyFog = meshNode.MaxNode.ApplyAtmospherics == 1; #endif babylonMesh.pickable = meshNode.MaxNode.GetBoolProperty("babylonjs_checkpickable"); babylonMesh.showBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showboundingbox"); babylonMesh.showSubMeshesBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showsubmeshesboundingbox"); babylonMesh.alphaIndex = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_alphaindex", 1000); // Collisions babylonMesh.checkCollisions = meshNode.MaxNode.GetBoolProperty("babylonjs_checkcollisions"); // Skin var isSkinned = gameMesh.IsObjectSkinned; var skin = gameMesh.IGameSkin; var unskinnedMesh = gameMesh; IGMatrix skinInitPoseMatrix = Loader.Global.GMatrix.Create(Loader.Global.Matrix3.Create(true)); List <int> boneIds = null; int maxNbBones = 0; if (isSkinned && GetRevelantNodes(skin).Count > 0) // if the mesh has a skin with at least one bone { bonesCount = skin.TotalSkinBoneCount; var skinAlreadyStored = skins.Find(_skin => IsSkinEqualTo(_skin, skin)); if (skinAlreadyStored == null) { skins.Add(skin); } else { skin = skinAlreadyStored; } babylonMesh.skeletonId = skins.IndexOf(skin); skin.GetInitSkinTM(skinInitPoseMatrix); boneIds = GetNodeIndices(skin); } else { skin = null; } // Mesh if (unskinnedMesh.IGameType == Autodesk.Max.IGameObject.ObjectTypes.Mesh && unskinnedMesh.MaxMesh != null) { if (unskinnedMesh.NumberOfFaces < 1) { RaiseError($"Mesh {babylonMesh.name} has no face", 2); } if (unskinnedMesh.NumberOfVerts < 3) { RaiseError($"Mesh {babylonMesh.name} has not enough vertices", 2); } if (unskinnedMesh.NumberOfVerts >= 65536) { RaiseWarning($"Mesh {babylonMesh.name} has tmore than 65536 vertices which means that it will require specific WebGL extension to be rendered. This may impact portability of your scene on low end devices.", 2); } if (skin != null) { for (var vertexIndex = 0; vertexIndex < unskinnedMesh.NumberOfVerts; vertexIndex++) { maxNbBones = Math.Max(maxNbBones, skin.GetNumberOfBones(vertexIndex)); } } // Physics var impostorText = meshNode.MaxNode.GetStringProperty("babylonjs_impostor", "None"); if (impostorText != "None") { switch (impostorText) { case "Sphere": babylonMesh.physicsImpostor = 1; break; case "Box": babylonMesh.physicsImpostor = 2; break; case "Plane": babylonMesh.physicsImpostor = 3; break; default: babylonMesh.physicsImpostor = 0; break; } babylonMesh.physicsMass = meshNode.MaxNode.GetFloatProperty("babylonjs_mass"); babylonMesh.physicsFriction = meshNode.MaxNode.GetFloatProperty("babylonjs_friction", 0.2f); babylonMesh.physicsRestitution = meshNode.MaxNode.GetFloatProperty("babylonjs_restitution", 0.2f); } // Material var mtl = meshNode.NodeMaterial; var multiMatsCount = 1; if (mtl != null) { IIGameMaterial unsupportedMaterial = isMaterialSupported(mtl); if (unsupportedMaterial == null) { babylonMesh.materialId = mtl.MaxMaterial.GetGuid().ToString(); if (!referencedMaterials.Contains(mtl)) { referencedMaterials.Add(mtl); } multiMatsCount = Math.Max(mtl.SubMaterialCount, 1); } else { if (mtl.SubMaterialCount == 0 || mtl == unsupportedMaterial) { RaiseWarning("Unsupported material type '" + unsupportedMaterial.MaterialClass + "'. Material is ignored.", 2); } else { RaiseWarning("Unsupported sub-material type '" + unsupportedMaterial.MaterialClass + "'. Material is ignored.", 2); } } } babylonMesh.visibility = meshNode.MaxNode.GetVisibility(0, Tools.Forever); var vertices = new List <GlobalVertex>(); var indices = new List <int>(); var mappingChannels = unskinnedMesh.ActiveMapChannelNum; bool hasUV = false; bool hasUV2 = false; for (int i = 0; i < mappingChannels.Count; ++i) { #if MAX2017 || MAX2018 var channelNum = mappingChannels[i]; #else var channelNum = mappingChannels[new IntPtr(i)]; #endif if (channelNum == 1) { hasUV = true; } else if (channelNum == 2) { hasUV2 = true; } } var hasColor = unskinnedMesh.NumberOfColorVerts > 0; var hasAlpha = unskinnedMesh.GetNumberOfMapVerts(-2) > 0; var optimizeVertices = meshNode.MaxNode.GetBoolProperty("babylonjs_optimizevertices"); var invertedWorldMatrix = GetInvertWorldTM(meshNode, 0); // Compute normals var subMeshes = new List <BabylonSubMesh>(); List <int> faceIndexes = null; ExtractGeometry(babylonMesh, vertices, indices, subMeshes, boneIds, skin, unskinnedMesh, invertedWorldMatrix, hasUV, hasUV2, hasColor, hasAlpha, optimizeVertices, multiMatsCount, meshNode, ref faceIndexes); if (vertices.Count >= 65536) { RaiseWarning($"Mesh {babylonMesh.name} has {vertices.Count} vertices. This may prevent your scene to work on low end devices where 32 bits indice are not supported", 2); if (!optimizeVertices) { RaiseError("You can try to optimize your object using [Try to optimize vertices] option", 2); } } // Tangent // Export tangents if option is checked and mesh have tangents if (exportParameters.exportTangents) { babylonMesh.tangents = vertices.SelectMany(v => v.Tangent).ToArray(); } RaiseMessage($"{vertices.Count} vertices, {indices.Count / 3} faces", 2); // Buffers babylonMesh.positions = vertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToArray(); // flip normals depending on parity var parityObject = meshNode.GetObjectTM(0).ExtractMatrix3().Parity; // for cesium, threejs and babylonjs (all the same) if (parityObject) { // flipped case: reverse normals babylonMesh.normals = vertices.SelectMany(v => new[] { -v.Normal.X, -v.Normal.Y, -v.Normal.Z }).ToArray(); } else { // normal case babylonMesh.normals = vertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToArray(); } if (hasUV) { babylonMesh.uvs = vertices.SelectMany(v => new[] { v.UV.X, 1 - v.UV.Y }).ToArray(); } if (hasUV2) { babylonMesh.uvs2 = vertices.SelectMany(v => new[] { v.UV2.X, 1 - v.UV2.Y }).ToArray(); } if (skin != null) { babylonMesh.matricesWeights = vertices.SelectMany(v => v.Weights.ToArray()).ToArray(); babylonMesh.matricesIndices = vertices.Select(v => v.BonesIndices).ToArray(); babylonMesh.numBoneInfluencers = maxNbBones; if (maxNbBones > 4) { babylonMesh.matricesWeightsExtra = vertices.SelectMany(v => v.WeightsExtra != null ? v.WeightsExtra.ToArray() : new[] { 0.0f, 0.0f, 0.0f, 0.0f }).ToArray(); babylonMesh.matricesIndicesExtra = vertices.Select(v => v.BonesIndicesExtra).ToArray(); } } if (hasColor) { babylonMesh.colors = vertices.SelectMany(v => v.Color.ToArray()).ToArray(); babylonMesh.hasVertexAlpha = hasAlpha; } babylonMesh.subMeshes = subMeshes.ToArray(); // Buffers - Indices babylonMesh.indices = indices.ToArray(); // ------------------------ // ---- Morph targets ----- // ------------------------ // Retreive modifiers with morpher flag List <IIGameModifier> modifiers = new List <IIGameModifier>(); for (int i = 0; i < meshNode.IGameObject.NumModifiers; i++) { var modifier = meshNode.IGameObject.GetIGameModifier(i); if (modifier.ModifierType == Autodesk.Max.IGameModifier.ModType.Morpher) { modifiers.Add(modifier); } } // Cast modifiers to morphers List <IIGameMorpher> morphers = modifiers.ConvertAll(new Converter <IIGameModifier, IIGameMorpher>(modifier => modifier.AsGameMorpher())); var hasMorphTarget = false; morphers.ForEach(morpher => { if (morpher.NumberOfMorphTargets > 0) { hasMorphTarget = true; } }); if (hasMorphTarget) { RaiseMessage("Export morph targets", 2); // Morph Target Manager var babylonMorphTargetManager = new BabylonMorphTargetManager(); babylonScene.MorphTargetManagersList.Add(babylonMorphTargetManager); babylonMesh.morphTargetManagerId = babylonMorphTargetManager.id; // Morph Targets var babylonMorphTargets = new List <BabylonMorphTarget>(); // All morphers are considered identical // Their targets are concatenated morphers.ForEach(morpher => { for (int i = 0; i < morpher.NumberOfMorphTargets; i++) { // Morph target var maxMorphTarget = morpher.GetMorphTarget(i); // Ensure target still exists (green color legend) if (maxMorphTarget != null) { var babylonMorphTarget = new BabylonMorphTarget { name = maxMorphTarget.Name }; babylonMorphTargets.Add(babylonMorphTarget); // TODO - Influence babylonMorphTarget.influence = 0f; // Target geometry var targetVertices = ExtractVertices(babylonMesh, maxMorphTarget, optimizeVertices, faceIndexes); babylonMorphTarget.positions = targetVertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToArray(); babylonMorphTarget.normals = targetVertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToArray(); // Tangent if (exportParameters.exportTangents) { babylonMorphTarget.tangents = targetVertices.SelectMany(v => v.Tangent).ToArray(); } // Animations var animations = new List <BabylonAnimation>(); var morphWeight = morpher.GetMorphWeight(i); ExportFloatGameController(morphWeight, "influence", animations); if (animations.Count > 0) { babylonMorphTarget.animations = animations.ToArray(); } } } }); babylonMorphTargetManager.targets = babylonMorphTargets.ToArray(); } } // World Modifiers ExportWorldModifiers(meshNode, babylonScene, babylonMesh); // Animations // Done last to avoid '0 vertex found' error (unkown cause) exportAnimation(babylonMesh, meshNode); babylonScene.MeshesList.Add(babylonMesh); return(babylonMesh); }
private BabylonTexture ExportSpecularTexture(IIGameMaterial materialNode, float[] specularColor, BabylonScene babylonScene) { ITexmap specularColorTexMap = _getTexMap(materialNode, 2); ITexmap specularLevelTexMap = _getTexMap(materialNode, 3); // --- Babylon texture --- var specularColorTexture = _getBitmapTex(specularColorTexMap); var specularLevelTexture = _getBitmapTex(specularLevelTexMap); if (specularLevelTexture == null) { // Copy specular color image // Assume specular color texture is already pre-multiplied by a global specular level value // So do not use global specular level return(ExportTexture(specularColorTexture, babylonScene)); } // Use one as a reference for UVs parameters var texture = specularColorTexture != null ? specularColorTexture : specularLevelTexture; if (texture == null) { return(null); } RaiseMessage("Multiply specular color and level textures", 2); string nameText = null; nameText = (specularColorTexture != null ? Path.GetFileNameWithoutExtension(specularColorTexture.Map.FullFilePath) : TextureUtilities.ColorToStringName(specularColor)) + Path.GetFileNameWithoutExtension(specularLevelTexture.Map.FullFilePath) + "_specularColor"; var textureID = texture.GetGuid().ToString(); if (textureMap.ContainsKey(textureID)) { return(textureMap[textureID]); } else { var babylonTexture = new BabylonTexture(textureID) { name = nameText + ".jpg" // TODO - unsafe name, may conflict with another texture name }; // Level babylonTexture.level = 1.0f; // UVs var uvGen = _exportUV(texture.UVGen, babylonTexture); // Is cube _exportIsCube(texture.Map.FullFilePath, babylonTexture, false); // --- Multiply specular color and level maps --- // Alpha babylonTexture.hasAlpha = false; babylonTexture.getAlphaFromRGB = false; if (exportParameters.writeTextures) { // Load bitmaps var specularColorBitmap = _loadTexture(specularColorTexMap); var specularLevelBitmap = _loadTexture(specularLevelTexMap); if (specularLevelBitmap == null) { // Copy specular color image RaiseError("Failed to load specular level texture. Specular color is exported alone.", 3); return(ExportTexture(specularColorTexture, babylonScene)); } // Retreive dimensions int width = 0; int height = 0; var haveSameDimensions = TextureUtilities.GetMinimalBitmapDimensions(out width, out height, specularColorBitmap, specularLevelBitmap); if (!haveSameDimensions) { RaiseError("Specular color and specular level maps should have same dimensions", 3); } // Create pre-multiplied specular color map var _specularColor = Color.FromArgb( (int)(specularColor[0] * 255), (int)(specularColor[1] * 255), (int)(specularColor[2] * 255)); Bitmap specularColorPreMultipliedBitmap = new Bitmap(width, height); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { var specularColorAtPixel = specularColorBitmap != null?specularColorBitmap.GetPixel(x, y) : _specularColor; var specularLevelAtPixel = specularLevelBitmap.GetPixel(x, y); var specularColorPreMultipliedAtPixel = specularColorAtPixel.multiply(specularLevelAtPixel); specularColorPreMultipliedBitmap.SetPixel(x, y, specularColorPreMultipliedAtPixel); } } // Write bitmap if (isBabylonExported) { RaiseMessage($"Texture | write image '{babylonTexture.name}'", 3); TextureUtilities.SaveBitmap(specularColorPreMultipliedBitmap, babylonScene.OutputPath, babylonTexture.name, ImageFormat.Jpeg, exportParameters.txtQuality, this); } else { // Store created bitmap for further use in gltf export babylonTexture.bitmap = specularColorPreMultipliedBitmap; } } textureMap.Add(babylonTexture.Id, babylonTexture); return(babylonTexture); } }
public bool isDirectXShaderMaterial(IIGameMaterial materialNode) { return(materialNode.MaterialClass.ToLower() == "directx shader" || // English materialNode.MaterialClass.ToLower() == "directx-shader" || // German materialNode.MaterialClass.ToLower() == "ombrage directx"); // French }
private BabylonTexture ExportBaseColorAlphaTexture(IIGameMaterial materialNode, BabylonScene babylonScene, string materialName) { ITexmap baseColorTexMap = _getTexMap(materialNode, 1); ITexmap alphaTexMap = _getTexMap(materialNode, 9); // Transparency weight map // --- Babylon texture --- var baseColorTexture = _getBitmapTex(baseColorTexMap); var alphaTexture = _getBitmapTex(alphaTexMap); // Use one as a reference for UVs parameters var texture = baseColorTexture != null ? baseColorTexture : alphaTexture; if (texture == null) { return(null); } var hasAlpha = alphaTexMap != null || (baseColorTexture != null && baseColorTexture.AlphaSource == 0); // Alpha source is 'Image Alpha' var babylonTexture = new BabylonTexture { name = materialName + "_baseColor" + (hasAlpha ? ".png" : ".jpg") // TODO - unsafe name, may conflict with another texture name }; // Level babylonTexture.level = 1.0f; // Alpha babylonTexture.hasAlpha = hasAlpha; babylonTexture.getAlphaFromRGB = false; // UVs var uvGen = _exportUV(texture, babylonTexture); // Is cube _exportIsCube(texture, babylonTexture, false); // --- Merge baseColor and alpha maps --- // Load bitmaps var baseColorBitmap = _loadTexture(baseColorTexMap); var alphaBitmap = _loadTexture(alphaTexMap); // Retreive dimensions int width = 0; int height = 0; var haveSameDimensions = _getMinimalBitmapDimensions(out width, out height, baseColorBitmap, alphaBitmap); if (!haveSameDimensions) { RaiseWarning("Base color and transparency color maps should have same dimensions", 2); } var getAlphaFromRGB = false; if (alphaTexture != null) { getAlphaFromRGB = (alphaTexture.AlphaSource == 2) || (alphaTexture.AlphaSource == 3); // 'RGB intensity' or 'None (Opaque)' } // Create baseColor+alpha map Bitmap baseColorAlphaBitmap = new Bitmap(width, height); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { var baseColor = baseColorBitmap != null?baseColorBitmap.GetPixel(x, y) : Color.FromArgb(255, 255, 255); Color baseColorAlpha; if (babylonTexture.hasAlpha) { if (alphaBitmap != null) { // Retreive alpha from alpha texture var alphaColor = alphaBitmap.GetPixel(x, y); var alpha = getAlphaFromRGB ? alphaColor.R : alphaColor.A; baseColorAlpha = Color.FromArgb(alpha, baseColor); } else { // Use all channels from base color baseColorAlpha = baseColor; } } else { // Only use RGB channels from base color baseColorAlpha = Color.FromArgb(baseColor.R, baseColor.G, baseColor.B); } baseColorAlphaBitmap.SetPixel(x, y, baseColorAlpha); } } // Write bitmap var absolutePath = Path.Combine(babylonScene.OutputPath, babylonTexture.name); RaiseMessage($"Texture | write image '{babylonTexture.name}'", 2); baseColorAlphaBitmap.Save(absolutePath); return(babylonTexture); }
private void ExportMaterial(IIGameMaterial materialNode, BabylonScene babylonScene) { var name = materialNode.MaterialName; var id = materialNode.MaxMaterial.GetGuid().ToString(); RaiseMessage(name, 1); // --- prints --- #region prints { RaiseVerbose("materialNode.MaterialClass=" + materialNode.MaterialClass, 2); RaiseVerbose("materialNode.NumberOfTextureMaps=" + materialNode.NumberOfTextureMaps, 2); var propertyContainer = materialNode.IPropertyContainer; RaiseVerbose("propertyContainer=" + propertyContainer, 2); if (propertyContainer != null) { RaiseVerbose("propertyContainer.NumberOfProperties=" + propertyContainer.NumberOfProperties, 3); for (int i = 0; i < propertyContainer.NumberOfProperties; i++) { var prop = propertyContainer.GetProperty(i); if (prop != null) { RaiseVerbose("propertyContainer.GetProperty(" + i + ")=" + prop.Name, 3); switch (prop.GetType_) { case PropType.StringProp: string propertyString = ""; RaiseVerbose("prop.GetPropertyValue(ref propertyString, 0)=" + prop.GetPropertyValue(ref propertyString, 0), 4); RaiseVerbose("propertyString=" + propertyString, 4); break; case PropType.IntProp: int propertyInt = 0; RaiseVerbose("prop.GetPropertyValue(ref propertyInt, 0)=" + prop.GetPropertyValue(ref propertyInt, 0), 4); RaiseVerbose("propertyInt=" + propertyInt, 4); break; case PropType.FloatProp: float propertyFloat = 0; RaiseVerbose("prop.GetPropertyValue(ref propertyFloat, 0)=" + prop.GetPropertyValue(ref propertyFloat, 0, true), 4); RaiseVerbose("propertyFloat=" + propertyFloat, 4); RaiseVerbose("prop.GetPropertyValue(ref propertyFloat, 0)=" + prop.GetPropertyValue(ref propertyFloat, 0, false), 4); RaiseVerbose("propertyFloat=" + propertyFloat, 4); break; case PropType.Point3Prop: IPoint3 propertyPoint3 = Loader.Global.Point3.Create(0, 0, 0); RaiseVerbose("prop.GetPropertyValue(ref propertyPoint3, 0)=" + prop.GetPropertyValue(propertyPoint3, 0), 4); RaiseVerbose("propertyPoint3=" + Point3ToString(propertyPoint3), 4); break; case PropType.Point4Prop: IPoint4 propertyPoint4 = Loader.Global.Point4.Create(0, 0, 0, 0); RaiseVerbose("prop.GetPropertyValue(ref propertyPoint4, 0)=" + prop.GetPropertyValue(propertyPoint4, 0), 4); RaiseVerbose("propertyPoint4=" + Point4ToString(propertyPoint4), 4); break; case PropType.UnknownProp: default: RaiseVerbose("Unknown property type", 4); break; } } else { RaiseVerbose("propertyContainer.GetProperty(" + i + ") IS NULL", 3); } } } } #endregion if (materialNode.SubMaterialCount > 0) { var babylonMultimaterial = new BabylonMultiMaterial { name = name, id = id }; var guids = new List <string>(); for (var index = 0; index < materialNode.SubMaterialCount; index++) { var subMat = materialNode.GetSubMaterial(index); if (subMat != null) { guids.Add(subMat.MaxMaterial.GetGuid().ToString()); if (!referencedMaterials.Contains(subMat)) { referencedMaterials.Add(subMat); ExportMaterial(subMat, babylonScene); } } else { guids.Add(Guid.Empty.ToString()); } } babylonMultimaterial.materials = guids.ToArray(); babylonScene.MultiMaterialsList.Add(babylonMultimaterial); return; } var stdMat = materialNode.MaxMaterial.GetParamBlock(0).Owner as IStdMat2; if (stdMat != null) { var isSelfIllumColor = materialNode.MaxMaterial.GetSelfIllumColorOn(0, false); var babylonMaterial = new BabylonStandardMaterial { name = name, id = id, ambient = materialNode.MaxMaterial.GetAmbient(0, false).ToArray(), diffuse = materialNode.MaxMaterial.GetDiffuse(0, false).ToArray(), specular = materialNode.MaxMaterial.GetSpecular(0, false).Scale(materialNode.MaxMaterial.GetShinStr(0, false)), specularPower = materialNode.MaxMaterial.GetShininess(0, false) * 256, emissive = isSelfIllumColor ? materialNode.MaxMaterial.GetSelfIllumColor(0, false).ToArray() : materialNode.MaxMaterial.GetDiffuse(0, false).Scale(materialNode.MaxMaterial.GetSelfIllum(0, false)), // compute the pre-multiplied emissive color alpha = 1.0f - materialNode.MaxMaterial.GetXParency(0, false) }; // If Self-Illumination color checkbox is checked // Then self-illumination is assumed to be pre-multiplied // Otherwise self-illumination needs to be multiplied with diffuse // linkEmissiveWithDiffuse attribute tells the Babylon engine to perform such multiplication babylonMaterial.linkEmissiveWithDiffuse = !isSelfIllumColor; // useEmissiveAsIllumination attribute tells the Babylon engine to use pre-multiplied emissive as illumination babylonMaterial.useEmissiveAsIllumination = isSelfIllumColor; // Store the emissive value (before multiplication) for gltf babylonMaterial.selfIllum = materialNode.MaxMaterial.GetSelfIllum(0, false); babylonMaterial.backFaceCulling = !stdMat.TwoSided; babylonMaterial.wireframe = stdMat.Wire; // Textures BabylonFresnelParameters fresnelParameters; babylonMaterial.ambientTexture = ExportTexture(stdMat, 0, out fresnelParameters, babylonScene); // Ambient babylonMaterial.diffuseTexture = ExportTexture(stdMat, 1, out fresnelParameters, babylonScene); // Diffuse if (fresnelParameters != null) { babylonMaterial.diffuseFresnelParameters = fresnelParameters; } babylonMaterial.specularTexture = ExportTexture(stdMat, 2, out fresnelParameters, babylonScene); // Specular babylonMaterial.emissiveTexture = ExportTexture(stdMat, 5, out fresnelParameters, babylonScene); // Emissive if (fresnelParameters != null) { babylonMaterial.emissiveFresnelParameters = fresnelParameters; if (babylonMaterial.emissive[0] == 0 && babylonMaterial.emissive[1] == 0 && babylonMaterial.emissive[2] == 0 && babylonMaterial.emissiveTexture == null) { babylonMaterial.emissive = new float[] { 1, 1, 1 }; } } babylonMaterial.opacityTexture = ExportTexture(stdMat, 6, out fresnelParameters, babylonScene, false, true); // Opacity if (fresnelParameters != null) { babylonMaterial.opacityFresnelParameters = fresnelParameters; if (babylonMaterial.alpha == 1 && babylonMaterial.opacityTexture == null) { babylonMaterial.alpha = 0; } } babylonMaterial.bumpTexture = ExportTexture(stdMat, 8, out fresnelParameters, babylonScene); // Bump babylonMaterial.reflectionTexture = ExportTexture(stdMat, 9, out fresnelParameters, babylonScene, true); // Reflection if (fresnelParameters != null) { if (babylonMaterial.reflectionTexture == null) { RaiseWarning("Fallout cannot be used with reflection channel without a texture", 2); } else { babylonMaterial.reflectionFresnelParameters = fresnelParameters; } } // Constraints if (babylonMaterial.diffuseTexture != null) { babylonMaterial.diffuse = new[] { 1.0f, 1.0f, 1.0f }; } if (babylonMaterial.emissiveTexture != null) { babylonMaterial.emissive = new float[] { 0, 0, 0 }; } if (babylonMaterial.opacityTexture != null && babylonMaterial.diffuseTexture != null && babylonMaterial.diffuseTexture.name == babylonMaterial.opacityTexture.name && babylonMaterial.diffuseTexture.hasAlpha && !babylonMaterial.opacityTexture.getAlphaFromRGB) { // This is a alpha testing purpose babylonMaterial.opacityTexture = null; babylonMaterial.diffuseTexture.hasAlpha = true; RaiseWarning("Opacity texture was removed because alpha from diffuse texture can be use instead", 2); RaiseWarning("If you do not want this behavior, just set Alpha Source = None on your diffuse texture", 2); } babylonScene.MaterialsList.Add(babylonMaterial); } // TODO - Find another way to detect if material is physical else if (materialNode.MaterialClass.ToLower() == "physical material" || // English materialNode.MaterialClass.ToLower() == "physisches material" || // German // TODO - check if translation is ok materialNode.MaterialClass.ToLower() == "matériau physique") // French { var propertyContainer = materialNode.IPropertyContainer; var babylonMaterial = new BabylonPBRMetallicRoughnessMaterial { name = name, id = id }; // --- Global --- // Alpha //var alphaFromXParency = 1.0f - materialNode.MaxMaterial.GetXParency(0, false); var alphaFromPropertyContainer = 1.0f - propertyContainer.GetFloatProperty(17); //RaiseMessage("alphaFromXParency=" + alphaFromXParency, 2); //RaiseMessage("alphaFromPropertyContainer=" + alphaFromPropertyContainer, 2); babylonMaterial.alpha = alphaFromPropertyContainer; babylonMaterial.baseColor = materialNode.MaxMaterial.GetDiffuse(0, false).ToArray(); babylonMaterial.metallic = propertyContainer.GetFloatProperty(6); babylonMaterial.roughness = propertyContainer.GetFloatProperty(4); var invertRoughness = propertyContainer.GetBoolProperty(5); if (invertRoughness) { // Inverse roughness babylonMaterial.roughness = 1 - babylonMaterial.roughness; } // Self illumination is computed from emission color, luminance, temperature and weight babylonMaterial.emissive = materialNode.MaxMaterial.GetSelfIllumColorOn(0, false) ? materialNode.MaxMaterial.GetSelfIllumColor(0, false).ToArray() : materialNode.MaxMaterial.GetDiffuse(0, false).Scale(materialNode.MaxMaterial.GetSelfIllum(0, false)); // --- Textures --- babylonMaterial.baseTexture = ExportBaseColorAlphaTexture(materialNode, babylonMaterial.baseColor, babylonMaterial.alpha, babylonScene, name); babylonMaterial.metallicRoughnessTexture = ExportMetallicRoughnessTexture(materialNode, babylonMaterial.metallic, babylonMaterial.roughness, babylonScene, name, invertRoughness); var normalMapAmount = propertyContainer.GetFloatProperty(91); babylonMaterial.normalTexture = ExportPBRTexture(materialNode, 30, babylonScene, normalMapAmount); babylonMaterial.emissiveTexture = ExportPBRTexture(materialNode, 17, babylonScene); // Use diffuse roughness map as ambient occlusion babylonMaterial.occlusionTexture = ExportPBRTexture(materialNode, 6, babylonScene); // Constraints if (babylonMaterial.baseTexture != null) { babylonMaterial.baseColor = new[] { 1.0f, 1.0f, 1.0f }; babylonMaterial.alpha = 1.0f; } if (babylonMaterial.alpha != 1.0f || (babylonMaterial.baseTexture != null && babylonMaterial.baseTexture.hasAlpha)) { babylonMaterial.transparencyMode = (int)BabylonPBRMetallicRoughnessMaterial.TransparencyMode.ALPHABLEND; } if (babylonMaterial.emissiveTexture != null) { babylonMaterial.emissive = new[] { 1.0f, 1.0f, 1.0f }; } if (babylonMaterial.metallicRoughnessTexture != null) { babylonMaterial.metallic = 1.0f; babylonMaterial.roughness = 1.0f; } babylonScene.MaterialsList.Add(babylonMaterial); } else { RaiseWarning("Unsupported material type: " + materialNode.MaterialClass, 2); } }
private void ExportMaterial(IIGameMaterial materialNode, BabylonScene babylonScene) { var name = materialNode.MaterialName; var id = materialNode.MaxMaterial.GetGuid().ToString(); // Check if the material was already exported. The material id is unique. if (babylonScene.MaterialsList.FirstOrDefault(m => m.id == id) != null) { return; } RaiseMessage(name, 1); // --- prints --- #region prints { RaiseVerbose("materialNode.MaterialClass=" + materialNode.MaterialClass, 2); RaiseVerbose("materialNode.NumberOfTextureMaps=" + materialNode.NumberOfTextureMaps, 2); var propertyContainer = materialNode.IPropertyContainer; RaiseVerbose("propertyContainer=" + propertyContainer, 2); if (propertyContainer != null) { RaiseVerbose("propertyContainer.NumberOfProperties=" + propertyContainer.NumberOfProperties, 3); for (int i = 0; i < propertyContainer.NumberOfProperties; i++) { var prop = propertyContainer.GetProperty(i); if (prop != null) { RaiseVerbose("propertyContainer.GetProperty(" + i + ")=" + prop.Name, 3); switch (prop.GetType_) { case PropType.StringProp: string propertyString = ""; RaiseVerbose("prop.GetPropertyValue(ref propertyString, 0)=" + prop.GetPropertyValue(ref propertyString, 0), 4); RaiseVerbose("propertyString=" + propertyString, 4); break; case PropType.IntProp: int propertyInt = 0; RaiseVerbose("prop.GetPropertyValue(ref propertyInt, 0)=" + prop.GetPropertyValue(ref propertyInt, 0), 4); RaiseVerbose("propertyInt=" + propertyInt, 4); break; case PropType.FloatProp: float propertyFloat = 0; RaiseVerbose("prop.GetPropertyValue(ref propertyFloat, 0, true)=" + prop.GetPropertyValue(ref propertyFloat, 0, true), 4); RaiseVerbose("propertyFloat=" + propertyFloat, 4); RaiseVerbose("prop.GetPropertyValue(ref propertyFloat, 0, false)=" + prop.GetPropertyValue(ref propertyFloat, 0, false), 4); RaiseVerbose("propertyFloat=" + propertyFloat, 4); break; case PropType.Point3Prop: IPoint3 propertyPoint3 = Loader.Global.Point3.Create(0, 0, 0); RaiseVerbose("prop.GetPropertyValue(ref propertyPoint3, 0)=" + prop.GetPropertyValue(propertyPoint3, 0), 4); RaiseVerbose("propertyPoint3=" + Point3ToString(propertyPoint3), 4); break; case PropType.Point4Prop: IPoint4 propertyPoint4 = Loader.Global.Point4.Create(0, 0, 0, 0); RaiseVerbose("prop.GetPropertyValue(ref propertyPoint4, 0)=" + prop.GetPropertyValue(propertyPoint4, 0), 4); RaiseVerbose("propertyPoint4=" + Point4ToString(propertyPoint4), 4); break; case PropType.UnknownProp: default: RaiseVerbose("Unknown property type", 4); break; } } else { RaiseVerbose("propertyContainer.GetProperty(" + i + ") IS NULL", 3); } } } } #endregion if (materialNode.SubMaterialCount > 0) { var babylonMultimaterial = new BabylonMultiMaterial { name = name, id = id }; var guids = new List <string>(); for (var index = 0; index < materialNode.SubMaterialCount; index++) { var subMat = materialNode.GetSubMaterial(index); if (subMat != null) { guids.Add(subMat.MaxMaterial.GetGuid().ToString()); if (!referencedMaterials.Contains(subMat)) { referencedMaterials.Add(subMat); ExportMaterial(subMat, babylonScene); } } else { guids.Add(Guid.Empty.ToString()); } } babylonMultimaterial.materials = guids.ToArray(); babylonScene.MultiMaterialsList.Add(babylonMultimaterial); return; } var unlitProperty = materialNode.IPropertyContainer.QueryProperty("BabylonUnlit"); bool isUnlit = unlitProperty != null?unlitProperty.GetBoolValue() : false; var stdMat = materialNode.MaxMaterial.NumParamBlocks > 0 ? materialNode.MaxMaterial.GetParamBlock(0).Owner as IStdMat2 : null; if (stdMat != null) { var babylonMaterial = new BabylonStandardMaterial { name = name, id = id, isUnlit = isUnlit, diffuse = materialNode.MaxMaterial.GetDiffuse(0, false).ToArray(), alpha = 1.0f - materialNode.MaxMaterial.GetXParency(0, false) }; babylonMaterial.backFaceCulling = !stdMat.TwoSided; babylonMaterial.wireframe = stdMat.Wire; var isSelfIllumColor = materialNode.MaxMaterial.GetSelfIllumColorOn(0, false); var maxSpecularColor = materialNode.MaxMaterial.GetSpecular(0, false).ToArray(); if (isUnlit == false) { babylonMaterial.ambient = materialNode.MaxMaterial.GetAmbient(0, false).ToArray(); babylonMaterial.specular = maxSpecularColor.Multiply(materialNode.MaxMaterial.GetShinStr(0, false)); babylonMaterial.specularPower = materialNode.MaxMaterial.GetShininess(0, false) * 256; babylonMaterial.emissive = isSelfIllumColor ? materialNode.MaxMaterial.GetSelfIllumColor(0, false).ToArray() : materialNode.MaxMaterial.GetDiffuse(0, false).Scale(materialNode.MaxMaterial.GetSelfIllum(0, false)); // compute the pre-multiplied emissive color // If Self-Illumination color checkbox is checked // Then self-illumination is assumed to be pre-multiplied // Otherwise self-illumination needs to be multiplied with diffuse // linkEmissiveWithDiffuse attribute tells the Babylon engine to perform such multiplication babylonMaterial.linkEmissiveWithDiffuse = !isSelfIllumColor; // useEmissiveAsIllumination attribute tells the Babylon engine to use pre-multiplied emissive as illumination babylonMaterial.useEmissiveAsIllumination = isSelfIllumColor; // Store the emissive value (before multiplication) for gltf babylonMaterial.selfIllum = materialNode.MaxMaterial.GetSelfIllum(0, false); } // Textures BabylonFresnelParameters fresnelParameters; babylonMaterial.diffuseTexture = ExportTexture(stdMat, 1, out fresnelParameters, babylonScene); // Diffuse if (fresnelParameters != null) { babylonMaterial.diffuseFresnelParameters = fresnelParameters; } if ((babylonMaterial.alpha == 1.0f && babylonMaterial.opacityTexture == null) && babylonMaterial.diffuseTexture != null && (babylonMaterial.diffuseTexture.originalPath.EndsWith(".tif") || babylonMaterial.diffuseTexture.originalPath.EndsWith(".tiff")) && babylonMaterial.diffuseTexture.hasAlpha) { RaiseWarning($"Diffuse texture named {babylonMaterial.diffuseTexture.originalPath} is a .tif file and its Alpha Source is 'Image Alpha' by default.", 2); RaiseWarning($"If you don't want material to be in BLEND mode, set diffuse texture Alpha Source to 'None (Opaque)'", 2); } babylonMaterial.opacityTexture = ExportTexture(stdMat, 6, out fresnelParameters, babylonScene, false, true); // Opacity if (fresnelParameters != null) { babylonMaterial.opacityFresnelParameters = fresnelParameters; if (babylonMaterial.alpha == 1 && babylonMaterial.opacityTexture == null) { babylonMaterial.alpha = 0; } } if (isUnlit == false) { babylonMaterial.ambientTexture = ExportTexture(stdMat, 0, out fresnelParameters, babylonScene); // Ambient babylonMaterial.specularTexture = ExportSpecularTexture(materialNode, maxSpecularColor, babylonScene); babylonMaterial.emissiveTexture = ExportTexture(stdMat, 5, out fresnelParameters, babylonScene); // Emissive if (fresnelParameters != null) { babylonMaterial.emissiveFresnelParameters = fresnelParameters; if (babylonMaterial.emissive[0] == 0 && babylonMaterial.emissive[1] == 0 && babylonMaterial.emissive[2] == 0 && babylonMaterial.emissiveTexture == null) { babylonMaterial.emissive = new float[] { 1, 1, 1 }; } } babylonMaterial.bumpTexture = ExportTexture(stdMat, 8, out fresnelParameters, babylonScene); // Bump babylonMaterial.reflectionTexture = ExportTexture(stdMat, 9, out fresnelParameters, babylonScene, true); // Reflection if (fresnelParameters != null) { if (babylonMaterial.reflectionTexture == null) { RaiseWarning("Fallout cannot be used with reflection channel without a texture", 2); } else { babylonMaterial.reflectionFresnelParameters = fresnelParameters; } } } // Constraints if (babylonMaterial.diffuseTexture != null) { babylonMaterial.diffuse = new[] { 1.0f, 1.0f, 1.0f }; } if (babylonMaterial.emissiveTexture != null) { babylonMaterial.emissive = new float[] { 0, 0, 0 }; } if (babylonMaterial.opacityTexture != null && babylonMaterial.diffuseTexture != null && babylonMaterial.diffuseTexture.name == babylonMaterial.opacityTexture.name && babylonMaterial.diffuseTexture.hasAlpha && !babylonMaterial.opacityTexture.getAlphaFromRGB) { // This is a alpha testing purpose babylonMaterial.opacityTexture = null; babylonMaterial.diffuseTexture.hasAlpha = true; RaiseWarning("Opacity texture was removed because alpha from diffuse texture can be use instead", 2); RaiseWarning("If you do not want this behavior, just set Alpha Source = None on your diffuse texture", 2); } babylonScene.MaterialsList.Add(babylonMaterial); } else if (isPhysicalMaterial(materialNode)) { var propertyContainer = materialNode.IPropertyContainer; var babylonMaterial = new BabylonPBRMetallicRoughnessMaterial { name = name, id = id, isUnlit = isUnlit }; // --- Global --- // Alpha //var alphaFromXParency = 1.0f - materialNode.MaxMaterial.GetXParency(0, false); var alphaFromPropertyContainer = 1.0f - propertyContainer.GetFloatProperty(17); //RaiseMessage("alphaFromXParency=" + alphaFromXParency, 2); //RaiseMessage("alphaFromPropertyContainer=" + alphaFromPropertyContainer, 2); babylonMaterial.alpha = alphaFromPropertyContainer; babylonMaterial.baseColor = materialNode.MaxMaterial.GetDiffuse(0, false).ToArray(); var invertRoughness = propertyContainer.GetBoolProperty(5); if (isUnlit == false) { babylonMaterial.metallic = propertyContainer.GetFloatProperty(6); babylonMaterial.roughness = propertyContainer.GetFloatProperty(4); if (invertRoughness) { // Inverse roughness babylonMaterial.roughness = 1 - babylonMaterial.roughness; } // Self illumination is computed from emission color, luminance, temperature and weight babylonMaterial.emissive = materialNode.MaxMaterial.GetSelfIllumColorOn(0, false) ? materialNode.MaxMaterial.GetSelfIllumColor(0, false).ToArray() : materialNode.MaxMaterial.GetDiffuse(0, false).Scale(materialNode.MaxMaterial.GetSelfIllum(0, false)); } else { // Ignore specified roughness and metallic values babylonMaterial.metallic = 0; babylonMaterial.roughness = 0.9f; } // --- Textures --- // 1 - base color ; 9 - transparancy weight ITexmap colorTexmap = _getTexMap(materialNode, 1); ITexmap alphaTexmap = _getTexMap(materialNode, 9); babylonMaterial.baseTexture = ExportBaseColorAlphaTexture(colorTexmap, alphaTexmap, babylonMaterial.baseColor, babylonMaterial.alpha, babylonScene, name); if (isUnlit == false) { // Metallic, roughness, ambient occlusion ITexmap metallicTexmap = _getTexMap(materialNode, 5); ITexmap roughnessTexmap = _getTexMap(materialNode, 4); ITexmap ambientOcclusionTexmap = _getTexMap(materialNode, 6); // Use diffuse roughness map as ambient occlusion // Check if MR or ORM textures are already merged bool areTexturesAlreadyMerged = false; if (metallicTexmap != null && roughnessTexmap != null) { string sourcePathMetallic = getSourcePath(metallicTexmap); string sourcePathRoughness = getSourcePath(roughnessTexmap); if (sourcePathMetallic == sourcePathRoughness) { if (ambientOcclusionTexmap != null && exportParameters.mergeAOwithMR) { 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(metallicTexmap, 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(metallicTexmap, babylonScene); babylonMaterial.metallicRoughnessTexture = ormTexture; areTexturesAlreadyMerged = true; } } } if (areTexturesAlreadyMerged == false) { if (metallicTexmap != 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.mergeAOwithMR ? ambientOcclusionTexmap : null, roughnessTexmap, metallicTexmap, babylonMaterial.metallic, babylonMaterial.roughness, babylonScene, invertRoughness); babylonMaterial.metallicRoughnessTexture = ormTexture; if (ambientOcclusionTexmap != null) { if (exportParameters.mergeAOwithMR) { babylonMaterial.occlusionTexture = ormTexture; } else { babylonMaterial.occlusionTexture = ExportPBRTexture(materialNode, 6, babylonScene); } } } else if (ambientOcclusionTexmap != null) { // Simply export occlusion texture RaiseVerbose("Simply export occlusion texture", 2); babylonMaterial.occlusionTexture = ExportTexture(ambientOcclusionTexmap, babylonScene); } } if (ambientOcclusionTexmap != null && !exportParameters.mergeAOwithMR && babylonMaterial.occlusionTexture == null) { RaiseVerbose("Exporting occlusion texture without merging with metallic roughness", 2); babylonMaterial.occlusionTexture = ExportTexture(ambientOcclusionTexmap, babylonScene); } var normalMapAmount = propertyContainer.GetFloatProperty(91); babylonMaterial.normalTexture = ExportPBRTexture(materialNode, 30, babylonScene, normalMapAmount); babylonMaterial.emissiveTexture = ExportPBRTexture(materialNode, 17, babylonScene); } if (babylonMaterial.alpha != 1.0f || (babylonMaterial.baseTexture != null && babylonMaterial.baseTexture.hasAlpha)) { var alphaTestProperty = materialNode.IPropertyContainer.QueryProperty("BabylonAlphaTest"); bool isAlphaTest = alphaTestProperty != null?alphaTestProperty.GetBoolValue() : false; babylonMaterial.transparencyMode = isAlphaTest ? (int)BabylonPBRMetallicRoughnessMaterial.TransparencyMode.ALPHATEST : (int)BabylonPBRMetallicRoughnessMaterial.TransparencyMode.ALPHABLEND; } if (babylonMaterial.emissiveTexture != null) { babylonMaterial.emissive = new[] { 1.0f, 1.0f, 1.0f }; } if (babylonMaterial.metallicRoughnessTexture != null) { babylonMaterial.metallic = 1.0f; babylonMaterial.roughness = 1.0f; } babylonScene.MaterialsList.Add(babylonMaterial); } else if (isArnoldMaterial(materialNode)) { var propertyContainer = materialNode.IPropertyContainer; var babylonMaterial = new BabylonPBRMetallicRoughnessMaterial { name = name, id = id, isUnlit = isUnlit }; // Alpha babylonMaterial.alpha = 1.0f - propertyContainer.GetFloatProperty(32); // Color: base * weight float[] baseColor = propertyContainer.GetPoint3Property(5).ToArray(); float baseWeight = propertyContainer.GetFloatProperty(2); babylonMaterial.baseColor = baseColor.Multiply(baseWeight); // Metallic & roughness bool invertRoughness = false; babylonMaterial.roughness = propertyContainer.GetFloatProperty(17); // specular_roughness babylonMaterial.metallic = propertyContainer.GetFloatProperty(29); // Emissive: emission_color * emission float[] emissionColor = propertyContainer.GetPoint3Property(94).ToArray(); float emissionWeight = propertyContainer.GetFloatProperty(91); babylonMaterial.emissive = emissionColor.Multiply(emissionWeight); // --- Textures --- // 1 - base_color ; 5 - diffuse_roughness ; 9 - metalness ; 10 - transparent ITexmap colorTexmap = _getTexMap(materialNode, 1); ITexmap alphaTexmap = _getTexMap(materialNode, 10); babylonMaterial.baseTexture = ExportBaseColorAlphaTexture(colorTexmap, alphaTexmap, babylonMaterial.baseColor, babylonMaterial.alpha, babylonScene, name); if (isUnlit == false) { // Metallic, roughness ITexmap metallicTexmap = _getTexMap(materialNode, 9); ITexmap roughnessTexmap = _getTexMap(materialNode, 5); // Check if MR textures are already merged bool areTexturesAlreadyMerged = false; if (metallicTexmap != null && roughnessTexmap != null) { string sourcePathMetallic = getSourcePath(metallicTexmap); string sourcePathRoughness = getSourcePath(roughnessTexmap); if (sourcePathMetallic == sourcePathRoughness) { // Metallic and roughness are already merged RaiseVerbose("Metallic and roughness are already merged", 2); BabylonTexture ormTexture = ExportTexture(metallicTexmap, babylonScene); babylonMaterial.metallicRoughnessTexture = ormTexture; // The already merged map is assumed to contain Ambient Occlusion in R channel babylonMaterial.occlusionTexture = ormTexture; areTexturesAlreadyMerged = true; } } if (areTexturesAlreadyMerged == false) { if (metallicTexmap != null || roughnessTexmap != null) { // Merge metallic, roughness RaiseVerbose("Merge metallic and roughness", 2); BabylonTexture ormTexture = ExportORMTexture(null, roughnessTexmap, metallicTexmap, babylonMaterial.metallic, babylonMaterial.roughness, babylonScene, invertRoughness); babylonMaterial.metallicRoughnessTexture = ormTexture; } } babylonMaterial.normalTexture = ExportPBRTexture(materialNode, 20, babylonScene); babylonMaterial.emissiveTexture = ExportPBRTexture(materialNode, 30, babylonScene); } // Constraints if (babylonMaterial.baseTexture != null) { babylonMaterial.baseColor = new[] { 1.0f, 1.0f, 1.0f }; babylonMaterial.alpha = 1.0f; } if (babylonMaterial.alpha != 1.0f || (babylonMaterial.baseTexture != null && babylonMaterial.baseTexture.hasAlpha)) { babylonMaterial.transparencyMode = (int)BabylonPBRMetallicRoughnessMaterial.TransparencyMode.ALPHABLEND; } if (babylonMaterial.emissiveTexture != null) { babylonMaterial.emissive = new[] { 1.0f, 1.0f, 1.0f }; } if (babylonMaterial.metallicRoughnessTexture != null) { babylonMaterial.metallic = 1.0f; babylonMaterial.roughness = 1.0f; } // Add the material to the scene babylonScene.MaterialsList.Add(babylonMaterial); } else { // isMaterialExportable check should prevent this to happen RaiseError("Unsupported material type: " + materialNode.MaterialClass, 2); } }