public PbrGameMaterialDecorator(IIGameMaterial node) : base(node)
 {
     _babylonCAT = new BabylonCustomAttributeDecorator(node);
 }
        /// <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);
            }
        }
        /// <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);
            }
        }