// ------------------------- // --------- Utils --------- // ------------------------- private IBitmapTex _getBitmapTex(ITexmap texMap) { if (texMap == null || texMap.GetParamBlock(0) == null || texMap.GetParamBlock(0).Owner == null) { return(null); } return(texMap.GetParamBlock(0).Owner as IBitmapTex); }
private ITexmap _exportFresnelParameters(ITexmap texMap, out BabylonFresnelParameters fresnelParameters) { fresnelParameters = null; // Fallout if (texMap.ClassName == "Falloff") // This is the only way I found to detect it. This is crappy but it works { RaiseMessage("fresnelParameters", 3); fresnelParameters = new BabylonFresnelParameters(); var paramBlock = texMap.GetParamBlock(0); var color1 = paramBlock.GetColor(0, 0, 0); var color2 = paramBlock.GetColor(4, 0, 0); fresnelParameters.isEnabled = true; fresnelParameters.leftColor = color2.ToArray(); fresnelParameters.rightColor = color1.ToArray(); if (paramBlock.GetInt(8, 0, 0) == 2) { fresnelParameters.power = paramBlock.GetFloat(12, 0, 0); } else { fresnelParameters.power = 1; } var texMap1 = paramBlock.GetTexmap(2, 0, 0); var texMap1On = paramBlock.GetInt(3, 0, 0); var texMap2 = paramBlock.GetTexmap(6, 0, 0); var texMap2On = paramBlock.GetInt(7, 0, 0); if (texMap1 != null && texMap1On != 0) { texMap = texMap1; fresnelParameters.rightColor = new float[] { 1, 1, 1 }; if (texMap2 != null && texMap2On != 0) { RaiseWarning(string.Format("You cannot specify two textures for falloff. Only one is supported"), 3); } } else if (texMap2 != null && texMap2On != 0) { fresnelParameters.leftColor = new float[] { 1, 1, 1 }; texMap = texMap2; } else { return(null); } } return(texMap); }
private BabylonTexture ExportEnvironmnentTexture(ITexmap texMap, BabylonScene babylonScene) { if (texMap.GetParamBlock(0) == null || texMap.GetParamBlock(0).Owner == null) { RaiseWarning("Failed to export environment texture. Uncheck \"Use Map\" option to fix this warning."); return(null); } var texture = texMap.GetParamBlock(0).Owner as IBitmapTex; if (texture == null) { RaiseWarning("Failed to export environment texture. Uncheck \"Use Map\" option to fix this warning."); return(null); } var sourcePath = texture.Map.FullFilePath; var fileName = Path.GetFileName(sourcePath); // Allow only dds file format if (!fileName.EndsWith(".dds")) { RaiseWarning("Failed to export environment texture: only .dds format is supported. Uncheck \"Use map\" to fix this warning."); return(null); } var babylonTexture = new BabylonTexture { name = fileName }; // Copy texture to output if (isBabylonExported) { var destPath = Path.Combine(babylonScene.OutputPath, babylonTexture.name); if (CopyTexturesToOutput) { try { if (File.Exists(sourcePath)) { File.Copy(sourcePath, destPath, true); } } catch { // silently fails } } } return(babylonTexture); }
private Bitmap _loadTexture(ITexmap texMap) { IBitmapTex texture = _getBitmapTex(texMap); if (texture == null) { return(null); } return(TextureUtilities.LoadTexture(texture.Map.FullFilePath, this)); }
private Bitmap _loadTexture(ITexmap texMap) { IBitmapTex texture = _getBitmapTex(texMap); if (texture == null) { return(null); } return(LoadTexture(texture.Map.FullFilePath)); }
private string getSourcePath(ITexmap texMap) { IBitmapTex bitmapTex = _getBitmapTex(texMap); if (bitmapTex != null) { return(bitmapTex.Map.FullFilePath); } else { return(null); } }
private Bitmap _loadTexture(ITexmap texMap) { if (texMap == null || texMap.GetParamBlock(0) == null || texMap.GetParamBlock(0).Owner == null) { return(null); } var texture = texMap.GetParamBlock(0).Owner as IBitmapTex; if (texture == null) { return(null); } return(LoadTexture(texture.Map.FullFilePath)); }
// ------------------------- // --------- Utils --------- // ------------------------- private IBitmapTex _getBitmapTex(ITexmap texMap, bool raiseError = true) { if (texMap == null || texMap.GetParamBlock(0) == null || texMap.GetParamBlock(0).Owner == null) { return(null); } var texture = texMap.GetParamBlock(0).Owner as IBitmapTex; if (texture == null && raiseError) { RaiseError($"Texture type is not supported. Use a Bitmap instead.", 2); } return(texture); }
private bool isTextureOk(ITexmap texMap) { var texture = _getBitmapTex(texMap); if (texture == null) { return(false); } if (!File.Exists(texture.Map.FullFilePath)) { return(false); } return(true); }
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); }
private BabylonTexture ExportEnvironmnentTexture(ITexmap texMap, BabylonScene babylonScene) { if (texMap.GetParamBlock(0) == null || texMap.GetParamBlock(0).Owner == null) { return(null); } var texture = texMap.GetParamBlock(0).Owner as IBitmapTex; if (texture == null) { return(null); } var sourcePath = texture.Map.FullFilePath; var babylonTexture = new BabylonTexture { name = Path.GetFileName(sourcePath) }; // Copy texture to output if (isBabylonExported) { var destPath = Path.Combine(babylonScene.OutputPath, babylonTexture.name); if (CopyTexturesToOutput) { try { if (File.Exists(sourcePath)) { File.Copy(sourcePath, destPath, true); } } catch { // silently fails } } } return(babylonTexture); }
private BabylonFurMaterial ExportFurModifier(IModifier modifier, String sourceMeshName, BabylonScene babylonScene) { RaiseMessage("Export Fur Modifier", 2); var paramBlock = modifier.GetParamBlock(0); // 3dsMax "Cut Length" is in percentages - "100%" will be "20" babylon spacing // (babylon Fur length means the distance from the obj, while the length of the hair is the spacing) var cutLength = paramBlock.GetFloat(CUT_LENGTH_PARAM_ID, 0, 0); var spacing = (int)Math.Round(cutLength / 5); // 3dsMax "Root Thick" is in percentages - "100%" will be "1" babylon density // (lower density in babylon is thicker hair - lower root thick in 3dsMax is thinner) var rootThickness = paramBlock.GetFloat(ROOT_THICKNESS_PARAM_ID, 0, 0); var density = (int)Math.Ceiling((100.1f - rootThickness) / 5); var rootColor = paramBlock.GetColor(ROOT_COLOR_PARAM_ID, 0, 0); var furColor = new float[] { rootColor.R, rootColor.G, rootColor.B }; if (paramBlock.GetTexmap(MAPS_PARAM_ID, 0, 11) != null) { RaiseWarning("Tip texture is not supported. Use root texture instead", 2); } BabylonTexture diffuseTexture = null; ITexmap rootColorTexmap = paramBlock.GetTexmap(MAPS_PARAM_ID, 0, 14); if (rootColorTexmap != null) { diffuseTexture = ExportTexture(rootColorTexmap, 0f, babylonScene); diffuseTexture.level = 1; } return(new BabylonFurMaterial { id = modifier.GetGuid().ToString(), name = modifier.GetGuid().ToString(), sourceMeshName = sourceMeshName, furDensity = density, furSpacing = spacing, diffuseTexture = diffuseTexture, furColor = furColor, }); }
private bool isTextureOk(ITexmap texMap) { if (texMap == null || texMap.GetParamBlock(0) == null || texMap.GetParamBlock(0).Owner == null) { return(false); } var texture = texMap.GetParamBlock(0).Owner as IBitmapTex; if (texture == null) { return(false); } if (!File.Exists(texture.Map.FullFilePath)) { return(false); } return(true); }
private BabylonTexture ExportORMTexture(ITexmap ambientOcclusionTexMap, ITexmap roughnessTexMap, ITexmap metallicTexMap, float metallic, float roughness, BabylonScene babylonScene, bool invertRoughness) { // --- Babylon texture --- var metallicTexture = _getBitmapTex(metallicTexMap); var roughnessTexture = _getBitmapTex(roughnessTexMap); var ambientOcclusionTexture = _getBitmapTex(ambientOcclusionTexMap); // Use metallic or roughness texture as a reference for UVs parameters var texture = metallicTexture != null ? metallicTexture : roughnessTexture; if (texture == null) { return(null); } RaiseMessage("Export ORM texture", 2); var textureID = texture.GetGuid().ToString(); if (textureMap.ContainsKey(textureID)) { return(textureMap[textureID]); } else { var babylonTexture = new BabylonTexture(textureID) { name = (ambientOcclusionTexMap != null ? Path.GetFileNameWithoutExtension(ambientOcclusionTexture.Map.FileName) : "") + (roughnessTexMap != null ? Path.GetFileNameWithoutExtension(roughnessTexture.Map.FileName) : ("" + (int)(roughness * 255))) + (metallicTexMap != null ? Path.GetFileNameWithoutExtension(metallicTexture.Map.FileName) : ("" + (int)(metallic * 255))) + ".jpg" // TODO - unsafe name, may conflict with another texture name }; // 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 (exportParameters.writeTextures) { // Load bitmaps var metallicBitmap = _loadTexture(metallicTexMap); var roughnessBitmap = _loadTexture(roughnessTexMap); var ambientOcclusionBitmap = _loadTexture(ambientOcclusionTexMap); // Retreive dimensions int width = 0; int height = 0; var haveSameDimensions = TextureUtilities.GetMinimalBitmapDimensions(out width, out height, metallicBitmap, roughnessBitmap, ambientOcclusionBitmap); if (!haveSameDimensions) { RaiseError((ambientOcclusionBitmap != null ? "Occlusion, roughness and metallic " : "Metallic and roughness") + " maps should have same dimensions", 3); } // Create ORM map Bitmap ormBitmap = new Bitmap(width, height); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { int _occlusion = ambientOcclusionBitmap != null?ambientOcclusionBitmap.GetPixel(x, y).R : 0; int _roughness = roughnessBitmap != null ? (invertRoughness ? 255 - roughnessBitmap.GetPixel(x, y).G : roughnessBitmap.GetPixel(x, y).G) : (int)(roughness * 255.0f); int _metallic = metallicBitmap != null?metallicBitmap.GetPixel(x, y).B : (int)(metallic * 255.0f); // The occlusion values are sampled from the R channel. // The roughness values are sampled from the G channel. // The metalness values are sampled from the B channel. Color colorMetallicRoughness = Color.FromArgb(_occlusion, _roughness, _metallic); ormBitmap.SetPixel(x, y, colorMetallicRoughness); } } // Write bitmap if (isBabylonExported) { RaiseMessage($"Texture | write image '{babylonTexture.name}'", 3); TextureUtilities.SaveBitmap(ormBitmap, babylonScene.OutputPath, babylonTexture.name, ImageFormat.Jpeg, exportParameters.txtQuality, this); } else { // Store created bitmap for further use in gltf export babylonTexture.bitmap = ormBitmap; } } textureMap[babylonTexture.Id] = babylonTexture; return(babylonTexture); } }
/// <returns></returns> private BabylonTexture ExportBaseColorAlphaTexture(ITexmap baseColorTexMap, ITexmap alphaTexMap, float[] baseColor, float alpha, BabylonScene babylonScene, string materialName, bool isOpacity = false) { // --- Babylon texture --- var baseColorTexture = _getBitmapTex(baseColorTexMap); var alphaTexture = _getBitmapTex(alphaTexMap); var texture = baseColorTexture != null ? baseColorTexture : alphaTexture; if (texture == null) { return(null); } var baseColorTextureMapExtension = Path.GetExtension(baseColorTexture.Map.FullFilePath).ToLower(); if (alphaTexture == null && baseColorTexture != null && alpha == 1) { if (baseColorTexture.AlphaSource == 0 && (baseColorTextureMapExtension == ".tif" || baseColorTextureMapExtension == ".tiff")) { RaiseWarning($"Diffuse texture named {baseColorTexture.Map.FullFilePath} is a .tif file and its Alpha Source is 'Image Alpha' by default.", 3); RaiseWarning($"If you don't want material to be in BLEND mode, set diffuse texture Alpha Source to 'None (Opaque)'", 3); } if (baseColorTexture.AlphaSource == 3 && // 'None (Opaque)' baseColorTextureMapExtension == ".jpg" || baseColorTextureMapExtension == ".jpeg" || baseColorTextureMapExtension == ".bmp" || baseColorTextureMapExtension == ".png") { // Copy base color image return(ExportTexture(baseColorTexture, babylonScene)); } } // Use one as a reference for UVs parameters RaiseMessage("Export baseColor+Alpha texture", 2); string nameText = null; nameText = (baseColorTexture != null ? Path.GetFileNameWithoutExtension(baseColorTexture.Map.FullFilePath) : TextureUtilities.ColorToStringName(baseColor)); var textureID = texture.GetGuid().ToString(); if (textureMap.ContainsKey(textureID)) { return(textureMap[textureID]); } else { var babylonTexture = new BabylonTexture(textureID) { name = nameText // 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 // If the texture file format does not traditionally support an alpha channel, export the base texture as opaque if (baseColorTextureMapExtension == ".jpg" || baseColorTextureMapExtension == ".jpeg" || baseColorTextureMapExtension == ".bmp") { babylonTexture.hasAlpha = false; } else { babylonTexture.hasAlpha = isTextureOk(alphaTexMap) || (isTextureOk(baseColorTexMap) && baseColorTexture.AlphaSource == 0) || alpha < 1.0f; } babylonTexture.getAlphaFromRGB = false; if ((!isTextureOk(alphaTexMap) && alpha == 1.0f && (isTextureOk(baseColorTexMap) && baseColorTexture.AlphaSource == 0)) && (baseColorTextureMapExtension == ".tif" || baseColorTextureMapExtension == ".tiff")) { RaiseWarning($"Diffuse texture named {baseColorTexture.Map.FullFilePath} is a .tif file and its Alpha Source is 'Image Alpha' by default.", 3); RaiseWarning($"If you don't want material to be in BLEND mode, set diffuse texture Alpha Source to 'None (Opaque)'", 3); } if (!hasBaseColor && !hasAlpha) { return(null); } // Set image format ImageFormat imageFormat = babylonTexture.hasAlpha ? ImageFormat.Png : ImageFormat.Jpeg; babylonTexture.name += imageFormat == ImageFormat.Png ? ".png" : ".jpg"; // --- Merge baseColor and alpha maps --- if (exportParameters.writeTextures) { // Load bitmaps var baseColorBitmap = _loadTexture(baseColorTexMap); var alphaBitmap = _loadTexture(alphaTexMap); // Retreive dimensions int width = 0; int height = 0; var haveSameDimensions = TextureUtilities.GetMinimalBitmapDimensions(out width, out height, baseColorBitmap, alphaBitmap); if (!haveSameDimensions) { RaiseError("Base color and transparency color maps should have same dimensions", 3); } var getAlphaFromRGB = alphaTexture != null && ((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 Color alphaColor = alphaBitmap.GetPixel(x, y); int alphaAtPixel = getAlphaFromRGB ? alphaColor.R : alphaColor.A; if (isOpacity == false) { // Convert transparency to opacity alphaAtPixel = 255 - alphaAtPixel; } 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) { RaiseMessage($"Texture | write image '{babylonTexture.name}'", 3); TextureUtilities.SaveBitmap(baseColorAlphaBitmap, babylonScene.OutputPath, babylonTexture.name, imageFormat, exportParameters.txtQuality, this); } else { // Store created bitmap for further use in gltf export babylonTexture.bitmap = baseColorAlphaBitmap; } } return(babylonTexture); } }
private BabylonTexture ExportClearCoatTexture(ITexmap intensityTexMap, ITexmap roughnessTexMap, float coatWeight, float coatRoughness, BabylonScene babylonScene, string materialName, bool invertRoughness) { // --- Babylon texture --- var intensityTexture = _getBitmapTex(intensityTexMap); var roughnessTexture = _getBitmapTex(roughnessTexMap); var texture = intensityTexture != null ? intensityTexture : roughnessTexture; if (texture == null) { return(null); } // Use one as a reference for UVs parameters RaiseMessage("Export Clear Coat weight+roughness texture", 2); string nameText = Path.GetFileNameWithoutExtension(texture.Map.FullFilePath); var textureID = texture.GetGuid().ToString(); if (textureMap.ContainsKey(textureID)) { return(textureMap[textureID]); } else { var babylonTexture = new BabylonTexture(textureID) { name = nameText // 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 maps --- var hasIntensity = isTextureOk(intensityTexture); var hasRoughness = isTextureOk(roughnessTexture); if (!hasIntensity && !hasRoughness) { return(null); } // Set image format ImageFormat imageFormat = ImageFormat.Jpeg; babylonTexture.name += ".jpg"; if (exportParameters.writeTextures) { // Load bitmaps var intensityBitmap = _loadTexture(intensityTexture); var roughnessBitmap = _loadTexture(roughnessTexture); // Retreive dimensions int width = 0; int height = 0; var haveSameDimensions = TextureUtilities.GetMinimalBitmapDimensions(out width, out height, intensityBitmap, roughnessBitmap); if (!haveSameDimensions) { RaiseError("Base color and transparency color maps should have same dimensions", 3); } // Create map var _intensity = (int)(coatWeight * 255); var _roughness = (int)(coatRoughness * 255); Bitmap intensityRoughnessBitmap = new Bitmap(width, height); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { var intensityAtPixel = (intensityBitmap == null) ? _intensity : intensityBitmap.GetPixel(x, y).R; Color intensityRoughness; if (roughnessBitmap == null) { intensityRoughness = Color.FromArgb(intensityAtPixel, _roughness, 0); } else { var roughnessAtPixel = (roughnessBitmap == null) ? _roughness : invertRoughness ? 255 - roughnessBitmap.GetPixel(x, y).G : roughnessBitmap.GetPixel(x, y).G; intensityRoughness = Color.FromArgb(intensityAtPixel, roughnessAtPixel, 0); } intensityRoughnessBitmap.SetPixel(x, y, intensityRoughness); } } // Write bitmap if (isBabylonExported) { RaiseMessage($"Texture | write image '{babylonTexture.name}'", 3); TextureUtilities.SaveBitmap(intensityRoughnessBitmap, babylonScene.OutputPath, babylonTexture.name, imageFormat, exportParameters.txtQuality, this); } else { // Store created bitmap for further use in gltf export babylonTexture.bitmap = intensityRoughnessBitmap; } } return(babylonTexture); } }
/// <summary> /// Export dedicated to PbrMetalRough Material /// </summary> /// <param name="materialNode">the material node interface</param> /// <param name="babylonScene">the scene to export the material</param> private void ExportPbrMetalRoughMaterial(IIGameMaterial materialNode, BabylonScene babylonScene) { // build material decorator PbrMetalRoughDecorator maxDecorator = new PbrMetalRoughDecorator(materialNode); // get the custom babylon attribute decorator BabylonCustomAttributeDecorator babylonDecorator = maxDecorator.BabylonCustomAttributes; // the target material var babylonMaterial = new BabylonPBRMetallicRoughnessMaterial(maxDecorator.Id) { maxGameMaterial = materialNode, name = maxDecorator.Name, backFaceCulling = babylonDecorator.BackFaceCulling, doubleSided = !babylonDecorator.BackFaceCulling, separateCullingPass = babylonDecorator.SeparateCullingPass, isUnlit = babylonDecorator.IsUnlit, _unlit = babylonDecorator.IsUnlit, baseColor = maxDecorator.BaseColor.ToArray() }; // --- Global --- if (babylonMaterial.isUnlit) { // Ignore values babylonMaterial.metallic = 0; babylonMaterial.roughness = 0.9f; } else { babylonMaterial.metallic = maxDecorator.Metalness; babylonMaterial.roughness = maxDecorator.Roughness; babylonMaterial.emissive = maxDecorator.EmitColor.ToArray(); } // --- Textures --- float[] multiplyColor = null; if (exportParameters.exportTextures) { ITexmap baseColorTexMap = maxDecorator.BaseColorMap; ITexmap alphaTexMap = maxDecorator.OpacityMap; bool isOpacity = true; babylonMaterial.baseTexture = ExportBaseColorAlphaTexture(baseColorTexMap, alphaTexMap, babylonMaterial.baseColor, babylonMaterial.alpha, babylonScene, out multiplyColor, isOpacity); if (multiplyColor != null) { babylonMaterial.baseColor = multiplyColor; } if (!babylonMaterial.isUnlit) { // Metallic, roughness, ambient occlusion ITexmap metalnessTexMap = maxDecorator.MetalnessMap; ITexmap roughnessTexMap = maxDecorator.RoughnessMap; ITexmap ambientOcclusionTexMap = maxDecorator.AmbientOcclusionMap; // Check if MR or ORM textures are already merged bool areTexturesAlreadyMerged = false; if (metalnessTexMap != null && roughnessTexMap != null) { string sourcePathMetallic = getSourcePath(metalnessTexMap); string sourcePathRoughness = getSourcePath(roughnessTexMap); if (sourcePathMetallic == sourcePathRoughness) { if (ambientOcclusionTexMap != null && exportParameters.mergeAO) { string sourcePathAmbientOcclusion = getSourcePath(ambientOcclusionTexMap); if (sourcePathMetallic == sourcePathAmbientOcclusion) { // Metallic, roughness and ambient occlusion are already merged RaiseVerbose("Metallic, roughness and ambient occlusion are already merged", 2); BabylonTexture ormTexture = ExportTexture(metalnessTexMap, babylonScene); babylonMaterial.metallicRoughnessTexture = ormTexture; babylonMaterial.occlusionTexture = ormTexture; areTexturesAlreadyMerged = true; } } else { // Metallic and roughness are already merged RaiseVerbose("Metallic and roughness are already merged", 2); BabylonTexture ormTexture = ExportTexture(metalnessTexMap, babylonScene); babylonMaterial.metallicRoughnessTexture = ormTexture; areTexturesAlreadyMerged = true; } } } if (areTexturesAlreadyMerged == false) { if (metalnessTexMap != null || roughnessTexMap != null) { // Merge metallic, roughness and ambient occlusion RaiseVerbose("Merge metallic and roughness (and ambient occlusion if `mergeAOwithMR` is enabled)", 2); BabylonTexture ormTexture = ExportORMTexture(exportParameters.mergeAO ? ambientOcclusionTexMap : null, roughnessTexMap, metalnessTexMap, babylonMaterial.metallic, babylonMaterial.roughness, babylonScene, false); babylonMaterial.metallicRoughnessTexture = ormTexture; if (ambientOcclusionTexMap != null) { if (exportParameters.mergeAO) { // if the ambient occlusion texture map uses a different set of texture coordinates than // metallic roughness, create a new instance of the ORM BabylonTexture with the different texture // coordinate indices var ambientOcclusionTexture = _getBitmapTex(ambientOcclusionTexMap); var texCoordIndex = ambientOcclusionTexture.UVGen.MapChannel - 1; if (texCoordIndex != ormTexture.coordinatesIndex) { babylonMaterial.occlusionTexture = new BabylonTexture(ormTexture); babylonMaterial.occlusionTexture.coordinatesIndex = texCoordIndex; // Set UVs/texture transform for the ambient occlusion texture var uvGen = _exportUV(ambientOcclusionTexture.UVGen, babylonMaterial.occlusionTexture); } else { babylonMaterial.occlusionTexture = ormTexture; } } else { babylonMaterial.occlusionTexture = ExportTexture(ambientOcclusionTexMap, babylonScene); } } } else if (ambientOcclusionTexMap != null) { // Simply export occlusion texture RaiseVerbose("Simply export occlusion texture", 2); babylonMaterial.occlusionTexture = ExportTexture(ambientOcclusionTexMap, babylonScene); } } if (ambientOcclusionTexMap != null && !exportParameters.mergeAO && babylonMaterial.occlusionTexture == null) { RaiseVerbose("Exporting occlusion texture without merging with metallic roughness", 2); babylonMaterial.occlusionTexture = ExportTexture(ambientOcclusionTexMap, babylonScene); } var normalMapAmount = maxDecorator.BumpMapAmount; ITexmap normalTexMap = maxDecorator.NormalMap; babylonMaterial.normalTexture = ExportTexture(normalTexMap, babylonScene, normalMapAmount); ITexmap emitTexMap = maxDecorator.EmitColormMap; babylonMaterial.emissiveTexture = ExportTexture(emitTexMap, babylonScene); if (babylonMaterial.metallicRoughnessTexture != null && !babylonDecorator.UseMaxFactor) { // Change the factor to zero if combining partial channel to avoid issue (in case of image compression). // ie - if no metallic map, then b MUST be fully black. However channel of jpeg MAY not beeing fully black // cause of the compression algorithm. Keeping MetallicFactor to 1 will make visible artifact onto texture. So set to Zero instead. babylonMaterial.metallic = areTexturesAlreadyMerged || metalnessTexMap != null ? 1.0f : 0.0f; babylonMaterial.roughness = areTexturesAlreadyMerged || roughnessTexMap != null ? 1.0f : 0.0f; } } } if (babylonMaterial.alpha != 1.0f || (babylonMaterial.baseTexture != null && babylonMaterial.baseTexture.hasAlpha)) { babylonMaterial.transparencyMode = (int)BabylonMaterial.TransparencyMode.ALPHABLEND; } if (babylonMaterial.transparencyMode == (int)BabylonMaterial.TransparencyMode.ALPHATEST) { // Set the alphaCutOff value explicitely to avoid different interpretations on different engines // Use the glTF default value rather than the babylon one babylonMaterial.alphaCutOff = 0.5f; } if (babylonMaterial.emissiveTexture != null) { babylonMaterial.emissive = new[] { 1.0f, 1.0f, 1.0f }; } // Add babylon attributes if (babylonDecorator.Properties == null) { AddPhysicalBabylonAttributes(materialNode.MaterialName, babylonMaterial); } // List all babylon material attributes // Those attributes are currently stored into the native material // They should not be exported as extra attributes var doNotExport = BabylonCustomAttributeDecorator.ListPrivatePropertyNames().ToList(); // Export the custom attributes of this material babylonMaterial.metadata = ExportExtraAttributes(materialNode, babylonScene, doNotExport); if (exportParameters.pbrFull) { var fullPBR = new BabylonPBRMaterial(babylonMaterial) { directIntensity = babylonDecorator.DirectIntensity, emissiveIntensity = babylonDecorator.EmissiveIntensity, environmentIntensity = babylonDecorator.EnvironementIntensity, specularIntensity = babylonDecorator.SpecularIntensity, maxGameMaterial = babylonMaterial.maxGameMaterial }; babylonScene.MaterialsList.Add(fullPBR); } else { // Add the material to the scene babylonScene.MaterialsList.Add(babylonMaterial); } }
/// <summary> /// Export dedicated to SpecGloss Material /// </summary> /// <param name="materialNode">the material node interface</param> /// <param name="babylonScene">the scene to export the material</param> private void ExportPbrSpecGlossMaterial(IIGameMaterial materialNode, BabylonScene babylonScene) { // build material decorator PbrSpecGlossDecorator maxDecorator = new PbrSpecGlossDecorator(materialNode); // get the custom babylon attribute decorator BabylonCustomAttributeDecorator babylonDecorator = maxDecorator.BabylonCustomAttributes; // the target material var babylonMaterial = new BabylonPBRSpecularGlossinessMaterial(maxDecorator.Id) { maxGameMaterial = materialNode, name = maxDecorator.Name, backFaceCulling = babylonDecorator.BackFaceCulling, doubleSided = !babylonDecorator.BackFaceCulling, separateCullingPass = babylonDecorator.SeparateCullingPass, isUnlit = babylonDecorator.IsUnlit, baseColor = maxDecorator.BaseColor.ToArray(), }; // --- Global --- if (babylonMaterial.isUnlit) { // Ignore values babylonMaterial.specularColor = BabylonPBRBaseSimpleMaterial.BlackColor(); babylonMaterial.glossiness = 0; } else { babylonMaterial.glossiness = maxDecorator.Glossiness; babylonMaterial.specularColor = maxDecorator.SpecularColor.ToArray(); babylonMaterial.emissive = maxDecorator.EmitColor.ToArray(); } // --- Textures --- float[] multiplyColor = null; if (exportParameters.exportTextures) { ITexmap diffuseTexMap = maxDecorator.BaseColorMap; ITexmap alphaTexMap = maxDecorator.OpacityMap; bool isOpacity = true; babylonMaterial.diffuseTexture = ExportBaseColorAlphaTexture(diffuseTexMap, alphaTexMap, babylonMaterial.baseColor, babylonMaterial.alpha, babylonScene, out multiplyColor, isOpacity); if (multiplyColor != null) { babylonMaterial.baseColor = multiplyColor; } if (!babylonMaterial.isUnlit) { // Metallic, roughness, ambient occlusion ITexmap specularTexMap = maxDecorator.SpecularMap; ITexmap glossinessTexMap = maxDecorator.GlossinessMap; ITexmap ambientOcclusionTexMap = maxDecorator.AmbientOcclusionMap; if (specularTexMap != null || glossinessTexMap != null) { // Merge Specular and Glossiness RaiseVerbose("Merge Specular and Glossiness", 2); BabylonTexture specularGlossinessTexture = ExportSpecularGlossinessTexture(babylonMaterial.specularColor, specularTexMap, babylonMaterial.glossiness, glossinessTexMap, babylonScene); babylonMaterial.specularGlossinessTexture = specularGlossinessTexture; } if (ambientOcclusionTexMap != null) { // Simply export occlusion texture RaiseVerbose("Export occlusion texture", 2); babylonMaterial.occlusionTexture = ExportTexture(ambientOcclusionTexMap, babylonScene); } var normalMapAmount = maxDecorator.BumpMapAmount; ITexmap normalTexMap = maxDecorator.NormalMap; babylonMaterial.normalTexture = ExportTexture(normalTexMap, babylonScene, normalMapAmount); ITexmap emitTexMap = maxDecorator.EmitColormMap; babylonMaterial.emissiveTexture = ExportTexture(emitTexMap, babylonScene); if (babylonMaterial.specularGlossinessTexture != null && !babylonDecorator.UseMaxFactor) { babylonMaterial.glossiness = glossinessTexMap != null ? 1.0f : 0.0f; babylonMaterial.specularColor = specularTexMap != null?BabylonPBRBaseSimpleMaterial.WhiteColor() : BabylonPBRBaseSimpleMaterial.BlackColor(); } } } // --- Finalize --- if (babylonMaterial.alpha != 1.0f || (babylonMaterial.diffuseTexture != null && babylonMaterial.diffuseTexture.hasAlpha)) { babylonMaterial.transparencyMode = (int)BabylonMaterial.TransparencyMode.ALPHABLEND; } if (babylonMaterial.transparencyMode == (int)BabylonMaterial.TransparencyMode.ALPHATEST) { // Set the alphaCutOff value explicitely to avoid different interpretations on different engines // Use the glTF default value rather than the babylon one babylonMaterial.alphaCutOff = 0.5f; } if (babylonMaterial.emissiveTexture != null) { babylonMaterial.emissive = new[] { 1.0f, 1.0f, 1.0f }; } // List all babylon material attributes // Those attributes are currently stored into the native material // They should not be exported as extra attributes var doNotExport = BabylonCustomAttributeDecorator.ListPrivatePropertyNames().ToList(); // Export the custom attributes of this material babylonMaterial.metadata = ExportExtraAttributes(materialNode, babylonScene, doNotExport); if (exportParameters.pbrFull) { var fullPBR = new BabylonPBRMaterial(babylonMaterial) { directIntensity = babylonDecorator.DirectIntensity, emissiveIntensity = babylonDecorator.EmissiveIntensity, environmentIntensity = babylonDecorator.EnvironementIntensity, specularIntensity = babylonDecorator.SpecularIntensity, maxGameMaterial = babylonMaterial.maxGameMaterial }; babylonScene.MaterialsList.Add(fullPBR); } else { // Add the material to the scene babylonScene.MaterialsList.Add(babylonMaterial); } }
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 BabylonTexture ExportTexture(ITexmap texMap, BabylonScene babylonScene, float amount = 1.0f, bool allowCube = false, bool forceAlpha = false) { IBitmapTex texture = _getBitmapTex(texMap, false); if (texture == null) { float specialAmount; var specialTexMap = _getSpecialTexmap(texMap, out specialAmount); texture = _getBitmapTex(specialTexMap, false); amount *= specialAmount; } if (texture == null) { return(null); } var sourcePath = texture.Map.FullFilePath; if (sourcePath == null || sourcePath == "") { RaiseWarning("Texture path is missing.", 2); return(null); } RaiseMessage("Export texture named: " + Path.GetFileName(sourcePath), 2); var validImageFormat = TextureUtilities.GetValidImageFormat(Path.GetExtension(sourcePath)); if (validImageFormat == null) { // Image format is not supported by the exporter RaiseWarning(string.Format("Format of texture {0} is not supported by the exporter. Consider using a standard image format like jpg or png.", Path.GetFileName(sourcePath)), 3); return(null); } var textureID = texture.GetGuid().ToString(); if (textureMap.ContainsKey(textureID)) { return(textureMap[textureID]); } else { var babylonTexture = new BabylonTexture(textureID) { name = Path.GetFileNameWithoutExtension(texture.MapName) + "." + validImageFormat }; RaiseMessage($"texture id = {babylonTexture.Id}", 2); // Level babylonTexture.level = amount; // Alpha if (forceAlpha) { babylonTexture.hasAlpha = true; babylonTexture.getAlphaFromRGB = (texture.AlphaSource == 2) || (texture.AlphaSource == 3); // 'RGB intensity' or 'None (Opaque)' } else { babylonTexture.hasAlpha = (texture.AlphaSource != 3); // Not 'None (Opaque)' babylonTexture.getAlphaFromRGB = (texture.AlphaSource == 2); // 'RGB intensity' } // UVs var uvGen = _exportUV(texture.UVGen, babylonTexture); // Animations var animations = new List <BabylonAnimation>(); ExportFloatAnimation("uOffset", animations, key => new[] { uvGen.GetUOffs(key) }); ExportFloatAnimation("vOffset", animations, key => new[] { -uvGen.GetVOffs(key) }); ExportFloatAnimation("uScale", animations, key => new[] { uvGen.GetUScl(key) }); ExportFloatAnimation("vScale", animations, key => new[] { uvGen.GetVScl(key) }); ExportFloatAnimation("uAng", animations, key => new[] { uvGen.GetUAng(key) }); ExportFloatAnimation("vAng", animations, key => new[] { uvGen.GetVAng(key) }); ExportFloatAnimation("wAng", animations, key => new[] { uvGen.GetWAng(key) }); babylonTexture.animations = animations.ToArray(); // Copy texture to output if (isBabylonExported) { var destPath = Path.Combine(babylonScene.OutputPath, babylonTexture.name); TextureUtilities.CopyTexture(sourcePath, destPath, exportParameters.txtQuality, this); // Is cube _exportIsCube(Path.Combine(babylonScene.OutputPath, babylonTexture.name), babylonTexture, allowCube); } else { babylonTexture.isCube = false; } babylonTexture.originalPath = sourcePath; return(babylonTexture); } }
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); }
// ------------------------- // -- Export sub methods --- // ------------------------- private BabylonTexture _exportTexture(ITexmap texMap, float amount, BabylonScene babylonScene, bool allowCube = false, bool forceAlpha = false) { if (texMap.GetParamBlock(0) == null || texMap.GetParamBlock(0).Owner == null) { return(null); } var texture = texMap.GetParamBlock(0).Owner as IBitmapTex; if (texture == null) { return(null); } var babylonTexture = new BabylonTexture { name = Path.GetFileName(texture.MapName) }; // Level babylonTexture.level = amount; // Alpha if (forceAlpha) { babylonTexture.hasAlpha = true; babylonTexture.getAlphaFromRGB = (texture.AlphaSource == 2) || (texture.AlphaSource == 3); // 'RGB intensity' or 'None (Opaque)' } else { babylonTexture.hasAlpha = (texture.AlphaSource != 3); // Not 'None (Opaque)' babylonTexture.getAlphaFromRGB = (texture.AlphaSource == 2); // 'RGB intensity' } // UVs var uvGen = _exportUV(texture, babylonTexture); // Animations var animations = new List <BabylonAnimation>(); ExportFloatAnimation("uOffset", animations, key => new[] { uvGen.GetUOffs(key) }); ExportFloatAnimation("vOffset", animations, key => new[] { -uvGen.GetVOffs(key) }); ExportFloatAnimation("uScale", animations, key => new[] { uvGen.GetUScl(key) }); ExportFloatAnimation("vScale", animations, key => new[] { uvGen.GetVScl(key) }); ExportFloatAnimation("uAng", animations, key => new[] { uvGen.GetUAng(key) }); ExportFloatAnimation("vAng", animations, key => new[] { uvGen.GetVAng(key) }); ExportFloatAnimation("wAng", animations, key => new[] { uvGen.GetWAng(key) }); babylonTexture.animations = animations.ToArray(); // Is cube _exportIsCube(texture, babylonTexture, allowCube); // Copy texture to output var absolutePath = texture.Map.FullFilePath; try { if (File.Exists(absolutePath)) { if (CopyTexturesToOutput) { File.Copy(absolutePath, Path.Combine(babylonScene.OutputPath, babylonTexture.name), true); } } } catch { // silently fails } return(babylonTexture); }
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); }
/// <returns></returns> private BabylonTexture ExportBaseColorAlphaTexture(ITexmap baseColorTexMap, ITexmap alphaTexMap, float[] baseColor, float alpha, BabylonScene babylonScene, string materialName, bool isOpacity = false) { // --- Babylon texture --- var baseColorTexture = _getBitmapTex(baseColorTexMap); var alphaTexture = _getBitmapTex(alphaTexMap); string baseColorTextureMapExtension = null; // If we don't retrieve any textures from Max, return null. if (baseColorTexture == null && alphaTexture == null) { return(null); } // If we only have a base color texture, and we are using an opaque texture, export the base color image only. if (baseColorTexture != null && alphaTexture == null) { baseColorTextureMapExtension = Path.GetExtension(baseColorTexture.Map.FullFilePath).ToLower(); if (alpha == 1) { if (baseColorTexture.AlphaSource == MaxConstants.IMAGE_ALPHA_FILE && (baseColorTextureMapExtension == ".tif" || baseColorTextureMapExtension == ".tiff")) { RaiseWarning($"Diffuse texture named {baseColorTexture.Map.FullFilePath} is a .tif file and its Alpha Source is 'Image Alpha' by default.", 3); RaiseWarning($"If you don't want material to be in BLEND mode, set diffuse texture Alpha Source to 'None (Opaque)'", 3); } // Copy base color image var outTexture = ExportTexture(baseColorTexture, babylonScene); textureMap[outTexture.Id] = outTexture; return(outTexture); } } // Otherwise combine base color and alpha textures to a single output texture RaiseMessage("Export baseColor+Alpha texture", 2); var hasBaseColor = baseColorTexture != null && isTextureOk(baseColorTexMap); var hasAlpha = isTextureOk(alphaTexMap); var texture = hasBaseColor ? baseColorTexture : alphaTexture; ImageFormat imageFormat = null; if (hasBaseColor) { imageFormat = TextureUtilities.GetImageFormat(Path.GetExtension(baseColorTexture.Map.FullFilePath)); } if (hasAlpha || imageFormat == null) { baseColorTextureMapExtension = ".png"; // since we are adding an alpha channel, export as png. This will convert any other input base texture format to PNG. imageFormat = ImageFormat.Png; } // since we are creating a new texture, give it a unique ID based on the base color and alpha maps. var nameText = (hasBaseColor ? Path.GetFileNameWithoutExtension(baseColorTexture.Map.FullFilePath) + (hasAlpha ? "_" + Path.GetFileNameWithoutExtension(alphaTexture.Map.FullFilePath) : "") : TextureUtilities.ColorToStringName(baseColor)); var textureID = hasBaseColor ? texture.GetGuid().ToString() + (hasAlpha ? "_" + alphaTexture.GetGuid().ToString() : "") : string.Format("{0}_{1}", texture.GetGuid().ToString(), nameText); if (textureMap.ContainsKey(textureID)) { return(textureMap[textureID]); } BabylonTexture babylonTexture = null; babylonTexture = new BabylonTexture(textureID) { name = nameText }; // Alpha babylonTexture.hasAlpha = hasAlpha || (hasBaseColor && (baseColorTexture.AlphaSource == MaxConstants.IMAGE_ALPHA_FILE || baseColorTexture.AlphaSource == MaxConstants.IMAGE_ALPHA_RGB)) || alpha < 1.0f; babylonTexture.getAlphaFromRGB = false; if (!hasBaseColor && !hasAlpha) { return(null); } if ((!isTextureOk(alphaTexMap) && alpha == 1.0f && (isTextureOk(baseColorTexMap) && baseColorTexture.AlphaSource == MaxConstants.IMAGE_ALPHA_FILE)) && (baseColorTextureMapExtension == ".tif" || baseColorTextureMapExtension == ".tiff")) { RaiseWarning($"Diffuse texture named {baseColorTexture.Map.FullFilePath} is a .tif file and its Alpha Source is 'Image Alpha' by default.", 3); RaiseWarning($"If you don't want material to be in BLEND mode, set diffuse texture Alpha Source to 'None (Opaque)'", 3); } // Set image format if (hasAlpha) { babylonTexture.name += "_alpha_" + alphaTexture.Name; } if (imageFormat == ImageFormat.Jpeg) { babylonTexture.name += ".jpg"; } else { babylonTexture.name += "." + imageFormat.ToString(); } // 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 --- if (exportParameters.writeTextures && baseColorTexture != alphaTexture && alphaTexture != null) { // Load bitmaps var baseColorBitmap = _loadTexture(baseColorTexMap); var alphaBitmap = _loadTexture(alphaTexMap); // Retreive dimensions int width = 0; int height = 0; var haveSameDimensions = TextureUtilities.GetMinimalBitmapDimensions(out width, out height, baseColorBitmap, alphaBitmap); if (!haveSameDimensions) { RaiseError("Base color and transparency color maps should have same dimensions", 3); } var getAlphaFromRGB = alphaTexture != null && ((alphaTexture.AlphaSource == MaxConstants.IMAGE_ALPHA_RGB) || (alphaTexture.AlphaSource == MaxConstants.IMAGE_ALPHA_NONE)); // '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 Color alphaColor = alphaBitmap.GetPixel(x, y); int alphaAtPixel = getAlphaFromRGB ? alphaColor.R : alphaColor.A; if (isOpacity == false) { // Convert transparency to opacity alphaAtPixel = 255 - alphaAtPixel; } baseColorAlpha = Color.FromArgb(alphaAtPixel, baseColorAtPixel); } else if (baseColorTexture != null && baseColorTexture.AlphaSource == MaxConstants.IMAGE_ALPHA_FILE) // 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) { RaiseMessage($"Texture | write image '{babylonTexture.name}'", 3); TextureUtilities.SaveBitmap(baseColorAlphaBitmap, babylonScene.OutputPath, babylonTexture.name, imageFormat, exportParameters.txtQuality, this); } else { // Store created bitmap for further use in gltf export babylonTexture.bitmap = baseColorAlphaBitmap; } } return(babylonTexture); }
// ------------------------- // -- Export sub methods --- // ------------------------- private BabylonTexture ExportTexture(ITexmap texMap, float amount, BabylonScene babylonScene, bool allowCube = false, bool forceAlpha = false) { if (texMap.GetParamBlock(0) == null || texMap.GetParamBlock(0).Owner == null) { return(null); } var texture = texMap.GetParamBlock(0).Owner as IBitmapTex; if (texture == null) { return(null); } var sourcePath = texture.Map.FullFilePath; if (sourcePath == null || sourcePath == "") { RaiseWarning("Texture path is missing.", 2); return(null); } var validImageFormat = GetValidImageFormat(Path.GetExtension(sourcePath)); if (validImageFormat == null) { // Image format is not supported by the exporter RaiseWarning(string.Format("Format of texture {0} is not supported by the exporter. Consider using a standard image format like jpg or png.", Path.GetFileName(sourcePath)), 2); return(null); } var babylonTexture = new BabylonTexture { name = Path.GetFileNameWithoutExtension(texture.MapName) + "." + validImageFormat }; // Level babylonTexture.level = amount; // Alpha if (forceAlpha) { babylonTexture.hasAlpha = true; babylonTexture.getAlphaFromRGB = (texture.AlphaSource == 2) || (texture.AlphaSource == 3); // 'RGB intensity' or 'None (Opaque)' } else { babylonTexture.hasAlpha = (texture.AlphaSource != 3); // Not 'None (Opaque)' babylonTexture.getAlphaFromRGB = (texture.AlphaSource == 2); // 'RGB intensity' } // UVs var uvGen = _exportUV(texture.UVGen, babylonTexture); // Animations var animations = new List <BabylonAnimation>(); ExportFloatAnimation("uOffset", animations, key => new[] { uvGen.GetUOffs(key) }); ExportFloatAnimation("vOffset", animations, key => new[] { -uvGen.GetVOffs(key) }); ExportFloatAnimation("uScale", animations, key => new[] { uvGen.GetUScl(key) }); ExportFloatAnimation("vScale", animations, key => new[] { uvGen.GetVScl(key) }); ExportFloatAnimation("uAng", animations, key => new[] { uvGen.GetUAng(key) }); ExportFloatAnimation("vAng", animations, key => new[] { uvGen.GetVAng(key) }); ExportFloatAnimation("wAng", animations, key => new[] { uvGen.GetWAng(key) }); babylonTexture.animations = animations.ToArray(); // Copy texture to output if (isBabylonExported) { var destPath = Path.Combine(babylonScene.OutputPath, babylonTexture.name); CopyTexture(sourcePath, destPath); // Is cube _exportIsCube(Path.Combine(babylonScene.OutputPath, babylonTexture.name), babylonTexture, allowCube); } else { babylonTexture.originalPath = sourcePath; babylonTexture.isCube = false; } return(babylonTexture); }
/// <summary> /// Return custom attributes retreive from a max object named "obj" /// </summary> /// <param name="metadata"></param> /// <param name="propertyContainer"></param> /// <param name="babylonScene"></param> /// <param name="excludeAttributes">Attribute names to not export</param> private Dictionary <string, object> _ExportExtraAttributes(IIPropertyContainer propertyContainer, BabylonScene babylonScene, List <string> excludeAttributes = null) { logger?.RaiseMessage("ExportExtraAttributes", 2); // Return a string encoded with 2 separators // Parameter separator: _$€PParam_ // Name/Type separator: _$€PType_ string cmd = "s = \"\"" + "\r\n" + "for objDef in (custAttributes.getDefs obj) do" + "\r\n" + "(" + "\r\n" + "pbArray = custAttributes.getPBlockDefs objdef" + "\r\n" + "for indexPBlock = 1 to pbArray.count do" + "\r\n" + "(" + "\r\n" + "itms = pbArray[indexPBlock]" + "\r\n" + "for y = 5 to itms.Count do" + "\r\n" + "(" + "\r\n" + "s = s + \"_$€PParam_\" + itms[y][1]" + "\r\n" + "for z = 1 to itms[y][2].Count by 2 do" + "\r\n" + "(" + "\r\n" + "key = itms[y][2][z] as string" + "\r\n" + "if (findString key \"type\") != undefined then" + "\r\n" + "(" + "\r\n" + "s = s + \"_$€PType_\" + itms[y][2][z+1]" + "\r\n" + ")" + "\r\n" + ")" + "\r\n" + ")" + "\r\n" + ")" + "\r\n" + ")" + "\r\n" + "s"; string result = ScriptsUtilities.ExecuteMaxScriptQuery(cmd); if (result == null || result == "") { return(null); } // Parse the result into a dictionary string[] parameters = result.Split(new string[] { "_$€PParam_" }, StringSplitOptions.RemoveEmptyEntries); Dictionary <string, string> customAttributesTypeByName = new Dictionary <string, string>(); foreach (string parameter in parameters) { string[] customAttribute = parameter.Split(new string[] { "_$€PType_" }, StringSplitOptions.RemoveEmptyEntries); string key = customAttribute[0]; if (customAttributesTypeByName.ContainsKey(key) == false) { customAttributesTypeByName.Add(key, customAttribute[1]); } } // Remove preset custom attributes customAttributesTypeByName.Remove("presetName_str"); customAttributesTypeByName.Remove("preset_str"); customAttributesTypeByName.Remove("rampOn"); // Remove specified attributes if (excludeAttributes != null) { foreach (string excludeAttribute in excludeAttributes) { customAttributesTypeByName.Remove(excludeAttribute); } } // Handle each attribute type Dictionary <string, object> metadata = new Dictionary <string, object>(); foreach (KeyValuePair <string, string> entry in customAttributesTypeByName) { object obj = null; logger?.RaiseMessage(entry.Key + "=" + entry.Value, 2); switch (entry.Value.ToLowerInvariant()) { case "float": case "angle": // in rad units case "worldunits": obj = propertyContainer.GetFloatProperty(entry.Key); break; case "percent": // in base 1 (80% => 0.8) obj = propertyContainer.GetFloatProperty(entry.Key) / 100f; break; case "boolean": obj = propertyContainer.GetBoolProperty(entry.Key); break; case "integer": case "array": // selected enum value expressed as int starting from 1 obj = propertyContainer.GetIntProperty(entry.Key); break; case "string": obj = propertyContainer.GetStringProperty(entry.Key); break; case "color": // Color RGB in base 1 (not 255) obj = propertyContainer.GetPoint3Property(entry.Key).ToArray(); break; case "frgba": // Color RGBA in base 1 (not 255) obj = propertyContainer.GetPoint4Property(entry.Key).ToArray(); break; case "texturemap": IIGameProperty gameProperty = propertyContainer.QueryProperty(entry.Key); ITexmap texmap = gameProperty.MaxParamBlock2.GetTexmap(gameProperty.ParamID, 0, 0); obj = ExportTexture(texmap, babylonScene); break; case "node": // Currently not exported break; case "material": // Currently not exported break; default: logger?.RaiseWarning("Unknown type '" + entry.Value + "' for custom attribute named '" + entry.Key + "'", 2); break; } if (obj != null) { metadata.Add(entry.Key, obj); } } // Print all extra attributes foreach (KeyValuePair <string, object> entry in metadata) { logger?.RaiseVerbose(entry.Key + "=" + entry.Value, 2); } return(metadata); }
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); } }
internal Texture(ITexmap x) : base(x) { }
// ------------------------- // -- Export sub methods --- // ------------------------- private ITexmap _getSpecialTexmap(ITexmap texMap, out float amount) { if (texMap == null) { amount = 0.0f; return(null); } if (texMap.ClassName == "Normal Bump") { var block = texMap.GetParamBlockByID(0); // General Block if (block != null) { amount = block.GetFloat(0, 0, 0); // Normal texture Mult Spin var map = block.GetTexmap(2, 0, 0); // Normal texture var mapEnabled = block.GetInt(4, 0, 0); // Normal texture Enable if (mapEnabled == 0) { RaiseError($"Only Normal Bump Texture with Normal enabled are supported.", 2); return(null); } var method = block.GetInt(6, 0, 0); // Normal texture mode (Tangent, screen...) if (method != 0) { RaiseError($"Only Normal Bump Texture in tangent space are supported.", 2); return(null); } var flipR = block.GetInt(7, 0, 0); // Normal texture Red chanel Flip if (flipR != 0) { RaiseError($"Only Normal Bump Texture without R flip are supported.", 2); return(null); } var flipG = block.GetInt(8, 0, 0); // Normal texture Green chanel Flip if (flipG != 0) { RaiseError($"Only Normal Bump Texture without G flip are supported.", 2); return(null); } var swapRG = block.GetInt(9, 0, 0); // Normal texture swap R and G channels if (swapRG != 0) { RaiseError($"Only Normal Bump Texture without R and G swap are supported.", 2); return(null); } // var bumpAmount = block.GetFloat(1, 0, 0); // Bump texture Mult Spin // var bumpMap = block.GetMap(3, 0, 0); // Bump texture var bumpMapEnable = block.GetInt(5, 0, 0); // Bump texture Enable if (bumpMapEnable == 1) { RaiseError($"Only Normal Bump Texture without Bump are supported.", 2); return(null); } return(map); } } amount = 0.0f; RaiseError($"Texture type is not supported. Use a Bitmap or Normal Bump map instead.", 2); return(null); }
public TextureMapWrapper(ITexmap textureMap) { this.TextureMap = textureMap; }
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 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); } }