Esempio n. 1
0
        private void ExportMaterial(MFnDependencyNode materialDependencyNode, BabylonScene babylonScene, bool fullPBR)
        {
            MObject materialObject = materialDependencyNode.objectProperty;
            var     name           = materialDependencyNode.name;
            var     id             = materialDependencyNode.uuid().asString();

            RaiseMessage(name, 1);
            RaiseMessage(materialObject.apiType.ToString(), 1);

            RaiseVerbose("materialObject.hasFn(MFn.Type.kBlinn)=" + materialObject.hasFn(MFn.Type.kBlinn), 2);
            RaiseVerbose("materialObject.hasFn(MFn.Type.kPhong)=" + materialObject.hasFn(MFn.Type.kPhong), 2);
            RaiseVerbose("materialObject.hasFn(MFn.Type.kPhongExplorer)=" + materialObject.hasFn(MFn.Type.kPhongExplorer), 2);

            Print(materialDependencyNode, 2, "Print ExportMaterial materialDependencyNode");

            // Retreive Babylon Material dependency node
            MFnDependencyNode babylonAttributesDependencyNode = getBabylonMaterialNode(materialDependencyNode);

            // Standard material
            if (materialObject.hasFn(MFn.Type.kLambert))
            {
                if (materialObject.hasFn(MFn.Type.kBlinn))
                {
                    RaiseMessage("Blinn shader", 2);
                }
                else if (materialObject.hasFn(MFn.Type.kPhong))
                {
                    RaiseMessage("Phong shader", 2);
                }
                else if (materialObject.hasFn(MFn.Type.kPhongExplorer))
                {
                    RaiseMessage("Phong E shader", 2);
                }
                else
                {
                    RaiseMessage("Lambert shader", 2);
                }

                var lambertShader = new MFnLambertShader(materialObject);

                RaiseVerbose("typeId=" + lambertShader.typeId, 2);
                RaiseVerbose("typeName=" + lambertShader.typeName, 2);
                RaiseVerbose("color=" + lambertShader.color.toString(), 2);
                RaiseVerbose("transparency=" + lambertShader.transparency.toString(), 2);
                RaiseVerbose("ambientColor=" + lambertShader.ambientColor.toString(), 2);
                RaiseVerbose("incandescence=" + lambertShader.incandescence.toString(), 2);
                RaiseVerbose("diffuseCoeff=" + lambertShader.diffuseCoeff, 2);
                RaiseVerbose("translucenceCoeff=" + lambertShader.translucenceCoeff, 2);

                BabylonStandardMaterial babylonMaterial = new BabylonStandardMaterial(id)
                {
                    name    = name,
                    diffuse = lambertShader.color.toArrayRGB()
                };

                // User custom attributes
                babylonMaterial.metadata = ExportCustomAttributeFromMaterial(babylonMaterial);

                bool isTransparencyModeFromBabylonMaterialNode = false;
                if (babylonAttributesDependencyNode != null)
                {
                    // Transparency mode
                    if (babylonAttributesDependencyNode.hasAttribute("babylonTransparencyMode"))
                    {
                        int transparencyMode = babylonAttributesDependencyNode.findPlug("babylonTransparencyMode").asInt();
                        babylonMaterial.transparencyMode = transparencyMode;

                        isTransparencyModeFromBabylonMaterialNode = true;
                    }
                }

                // Maya ambient <=> babylon emissive
                babylonMaterial.emissive = lambertShader.ambientColor.toArrayRGB();
                babylonMaterial.linkEmissiveWithDiffuse = true; // Incandescence (or Illumination) is not exported

                if (isTransparencyModeFromBabylonMaterialNode == false || babylonMaterial.transparencyMode != 0)
                {
                    // If transparency is not a shade of grey (shade of grey <=> R=G=B)
                    if (lambertShader.transparency[0] != lambertShader.transparency[1] ||
                        lambertShader.transparency[0] != lambertShader.transparency[2])
                    {
                        RaiseWarning("Transparency color is not a shade of grey. Only it's R channel is used.", 2);
                    }
                    // Convert transparency to opacity
                    babylonMaterial.alpha = 1.0f - lambertShader.transparency[0];
                }

                // Specular power
                if (materialObject.hasFn(MFn.Type.kReflect))
                {
                    var reflectShader = new MFnReflectShader(materialObject);

                    RaiseVerbose("specularColor=" + reflectShader.specularColor.toString(), 2);
                    RaiseVerbose("reflectivity=" + reflectShader.reflectivity, 2);
                    RaiseVerbose("reflectedColor=" + reflectShader.reflectedColor.toString(), 2);

                    babylonMaterial.specular = reflectShader.specularColor.toArrayRGB();

                    if (materialObject.hasFn(MFn.Type.kBlinn))
                    {
                        MFnBlinnShader blinnShader = new MFnBlinnShader(materialObject);
                        babylonMaterial.specularPower = (1.0f - blinnShader.eccentricity) * 256;
                    }
                    else if (materialObject.hasFn(MFn.Type.kPhong))
                    {
                        MFnPhongShader phongShader = new MFnPhongShader(materialObject);

                        float glossiness = (float)Math.Log(phongShader.cosPower, 2) * 10;
                        babylonMaterial.specularPower = glossiness / 100 * 256;
                    }
                    else if (materialObject.hasFn(MFn.Type.kPhongExplorer))
                    {
                        MFnPhongEShader phongEShader = new MFnPhongEShader(materialObject);
                        // No use of phongE.whiteness and phongE.highlightSize
                        babylonMaterial.specularPower = (1.0f - phongEShader.roughness) * 256;
                    }
                    else
                    {
                        RaiseWarning("Unknown reflect shader type: " + reflectShader.typeName + ". Specular power is default 64. Consider using a Blinn or Phong shader instead.", 2);
                    }
                }

                // TODO
                //babylonMaterial.wireframe = stdMat.Wire;

                // --- Textures ---

                babylonMaterial.diffuseTexture  = ExportTexture(materialDependencyNode, "color", babylonScene);
                babylonMaterial.emissiveTexture = ExportTexture(materialDependencyNode, "ambientColor", babylonScene); // Maya ambient <=> babylon emissive
                babylonMaterial.bumpTexture     = ExportTexture(materialDependencyNode, "normalCamera", babylonScene);
                if (isTransparencyModeFromBabylonMaterialNode == false || babylonMaterial.transparencyMode != 0)
                {
                    babylonMaterial.opacityTexture = ExportTexture(materialDependencyNode, "transparency", babylonScene, false, true);
                }
                if (materialObject.hasFn(MFn.Type.kReflect))
                {
                    babylonMaterial.specularTexture   = ExportTexture(materialDependencyNode, "specularColor", babylonScene);
                    babylonMaterial.reflectionTexture = ExportTexture(materialDependencyNode, "reflectedColor", babylonScene, true, false, true);
                }

                if (isTransparencyModeFromBabylonMaterialNode == false && (babylonMaterial.alpha != 1.0f || (babylonMaterial.diffuseTexture != null && babylonMaterial.diffuseTexture.hasAlpha) || babylonMaterial.opacityTexture != null))
                {
                    babylonMaterial.transparencyMode = (int)BabylonPBRMetallicRoughnessMaterial.TransparencyMode.ALPHABLEND;
                }

                // 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.transparencyMode == (int)BabylonPBRMetallicRoughnessMaterial.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 (babylonAttributesDependencyNode == null)
                {
                    // Create Babylon Material dependency node
                    babylonStandardMaterialNode.Create(materialDependencyNode);

                    // Retreive Babylon Material dependency node
                    babylonAttributesDependencyNode = getBabylonMaterialNode(materialDependencyNode);
                }

                if (babylonAttributesDependencyNode != null)
                {
                    // Ensure all attributes are setup
                    babylonStandardMaterialNode.Init(babylonAttributesDependencyNode, babylonMaterial);

                    RaiseVerbose("Babylon Attributes of " + babylonAttributesDependencyNode.name, 2);

                    // Common attributes
                    ExportCommonBabylonAttributes(babylonAttributesDependencyNode, babylonMaterial);

                    // Special treatment for Unlit
                    if (babylonMaterial.isUnlit)
                    {
                        if ((babylonMaterial.emissive != null && (babylonMaterial.emissive[0] != 0 || babylonMaterial.emissive[1] != 0 || babylonMaterial.emissive[2] != 0)) ||
                            (babylonMaterial.emissiveTexture != null) ||
                            (babylonMaterial.emissiveFresnelParameters != null))
                        {
                            RaiseWarning("Material is unlit. Emission is discarded and replaced by diffuse.", 2);
                        }
                        // Copy diffuse to emissive
                        babylonMaterial.emissive                  = babylonMaterial.diffuse;
                        babylonMaterial.emissiveTexture           = babylonMaterial.diffuseTexture;
                        babylonMaterial.emissiveFresnelParameters = babylonMaterial.diffuseFresnelParameters;

                        babylonMaterial.disableLighting         = true;
                        babylonMaterial.linkEmissiveWithDiffuse = false;
                    }
                    // Special treatment for "Alpha test" transparency mode
                    if (babylonMaterial.transparencyMode == (int)BabylonPBRMetallicRoughnessMaterial.TransparencyMode.ALPHATEST &&
                        ((babylonMaterial.diffuseTexture != null && babylonMaterial.opacityTexture != null && babylonMaterial.diffuseTexture.originalPath != babylonMaterial.opacityTexture.originalPath) ||
                         (babylonMaterial.diffuseTexture == null && babylonMaterial.opacityTexture != null)))
                    {
                        // Base color and alpha files need to be merged into a single file
                        Color             defaultColor = Color.FromArgb((int)(babylonMaterial.diffuse[0] * 255), (int)(babylonMaterial.diffuse[1] * 255), (int)(babylonMaterial.diffuse[2] * 255));
                        MFnDependencyNode baseColorTextureDependencyNode = getTextureDependencyNode(materialDependencyNode, "color");
                        MFnDependencyNode opacityTextureDependencyNode   = getTextureDependencyNode(materialDependencyNode, "transparency");
                        babylonMaterial.diffuseTexture = ExportBaseColorAlphaTexture(baseColorTextureDependencyNode, opacityTextureDependencyNode, babylonScene, name, defaultColor, babylonMaterial.alpha);
                        babylonMaterial.opacityTexture = null;
                        babylonMaterial.alpha          = 1.0f;
                    }
                }

                babylonScene.MaterialsList.Add(babylonMaterial);
            }
            // Stingray PBS material
            else if (isStingrayPBSMaterial(materialDependencyNode))
            {
                RaiseMessage("Stingray shader", 2);

                var babylonMaterial = new BabylonPBRMetallicRoughnessMaterial(id)
                {
                    name = name
                };

                // --- Global ---

                // Color3
                babylonMaterial.baseColor = materialDependencyNode.findPlug("base_color").asFloatArray();

                // Alpha
                string opacityAttributeName = "opacity";
                if (materialDependencyNode.hasAttribute(opacityAttributeName))
                {
                    float opacityAttributeValue = materialDependencyNode.findPlug(opacityAttributeName).asFloat();
                    babylonMaterial.alpha = 1.0f - opacityAttributeValue;
                }

                // Metallic & roughness
                babylonMaterial.metallic  = materialDependencyNode.findPlug("metallic").asFloat();
                babylonMaterial.roughness = materialDependencyNode.findPlug("roughness").asFloat();

                // Emissive
                float emissiveIntensity = materialDependencyNode.findPlug("emissive_intensity").asFloat();
                // Factor emissive color with emissive intensity
                emissiveIntensity        = Tools.Clamp(emissiveIntensity, 0f, 1f);
                babylonMaterial.emissive = materialDependencyNode.findPlug("emissive").asFloatArray().Multiply(emissiveIntensity);

                // --- Textures ---

                // Base color & alpha
                bool   useColorMap   = materialDependencyNode.findPlug("use_color_map").asBool();
                bool   useOpacityMap = false;
                string useOpacityMapAttributeName = "use_opacity_map";
                if (materialDependencyNode.hasAttribute(useOpacityMapAttributeName))
                {
                    useOpacityMap = materialDependencyNode.findPlug(useOpacityMapAttributeName).asBool();
                }
                if (materialDependencyNode.hasAttribute("mask_threshold")) // Preset "Masked"
                {
                    if (useColorMap && useOpacityMap)
                    {
                        // Texture is assumed to be already merged
                        babylonMaterial.baseTexture = ExportTexture(materialDependencyNode, "TEX_color_map", babylonScene, false, true);
                    }
                    else if (useColorMap || useOpacityMap)
                    {
                        // Merge Diffuse and Mask
                        Color defaultColor = Color.FromArgb((int)(babylonMaterial.baseColor[0] * 255), (int)(babylonMaterial.baseColor[1] * 255), (int)(babylonMaterial.baseColor[2] * 255));
                        // In Maya, a Masked StingrayPBS material without opacity or mask textures is counted as being fully transparent
                        // Such material is visible only when the mask threshold is set to 0
                        float defaultOpacity = 0;

                        // Either use the color map
                        MFnDependencyNode baseColorTextureDependencyNode = null;
                        if (useColorMap)
                        {
                            baseColorTextureDependencyNode = getTextureDependencyNode(materialDependencyNode, "TEX_color_map");
                        }
                        // Or the opacity map
                        MFnDependencyNode opacityTextureDependencyNode = null;
                        if (useOpacityMap)
                        {
                            opacityTextureDependencyNode = getTextureDependencyNode(materialDependencyNode, "TEX_color_map");
                        }

                        // Merge default value and texture
                        babylonMaterial.baseTexture = ExportBaseColorAlphaTexture(baseColorTextureDependencyNode, opacityTextureDependencyNode, babylonScene, babylonMaterial.name, defaultColor, defaultOpacity);
                    }
                    else
                    {
                        // In Maya, a Masked StingrayPBS material without opacity or mask textures is counted as being fully transparent
                        // Such material is visible only when the mask threshold is set to 0
                        babylonMaterial.alpha = 0;
                    }
                }
                else
                {
                    if (useColorMap || useOpacityMap)
                    {
                        // Force non use map to default value
                        // Ex: if useOpacityMap == false, force alpha = 255 for all pixels.
                        babylonMaterial.baseTexture = ExportTexture(materialDependencyNode, "TEX_color_map", babylonScene, false, useOpacityMap);
                    }
                }

                if (babylonMaterial.transparencyMode == (int)BabylonPBRMetallicRoughnessMaterial.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;
                }

                // Alpha cuttoff
                if (materialDependencyNode.hasAttribute("mask_threshold")) // Preset "Masked"
                {
                    babylonMaterial.alphaCutOff = materialDependencyNode.findPlug("mask_threshold").asFloat();
                }

                // Metallic, roughness, ambient occlusion
                bool   useMetallicMap        = materialDependencyNode.findPlug("use_metallic_map").asBool();
                bool   useRoughnessMap       = materialDependencyNode.findPlug("use_roughness_map").asBool();
                string useAOMapAttributeName = "use_ao_map";
                bool   useAOMap = materialDependencyNode.hasAttribute(useAOMapAttributeName) && materialDependencyNode.findPlug(useAOMapAttributeName).asBool();

                MFnDependencyNode metallicTextureDependencyNode         = useMetallicMap ? getTextureDependencyNode(materialDependencyNode, "TEX_metallic_map") : null;
                MFnDependencyNode roughnessTextureDependencyNode        = useRoughnessMap ? getTextureDependencyNode(materialDependencyNode, "TEX_roughness_map") : null;
                MFnDependencyNode ambientOcclusionTextureDependencyNode = useAOMap ? getTextureDependencyNode(materialDependencyNode, "TEX_ao_map") : null;

                // Check if MR or ORM textures are already merged
                bool areTexturesAlreadyMerged = false;
                if (metallicTextureDependencyNode != null && roughnessTextureDependencyNode != null)
                {
                    string sourcePathMetallic  = getSourcePathFromFileTexture(metallicTextureDependencyNode);
                    string sourcePathRoughness = getSourcePathFromFileTexture(roughnessTextureDependencyNode);

                    if (sourcePathMetallic == sourcePathRoughness)
                    {
                        if (ambientOcclusionTextureDependencyNode != null)
                        {
                            string sourcePathAmbientOcclusion = getSourcePathFromFileTexture(ambientOcclusionTextureDependencyNode);
                            if (sourcePathMetallic == sourcePathAmbientOcclusion)
                            {
                                // Metallic, roughness and ambient occlusion are already merged
                                RaiseVerbose("Metallic, roughness and ambient occlusion are already merged", 2);
                                BabylonTexture ormTexture = ExportTexture(metallicTextureDependencyNode, 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(metallicTextureDependencyNode, babylonScene);
                            babylonMaterial.metallicRoughnessTexture = ormTexture;
                            areTexturesAlreadyMerged = true;
                        }
                    }
                }
                if (areTexturesAlreadyMerged == false)
                {
                    if (metallicTextureDependencyNode != null || roughnessTextureDependencyNode != null)
                    {
                        // Merge metallic, roughness and ambient occlusion
                        RaiseVerbose("Merge metallic, roughness and ambient occlusion", 2);
                        BabylonTexture ormTexture = ExportORMTexture(babylonScene, metallicTextureDependencyNode, roughnessTextureDependencyNode, ambientOcclusionTextureDependencyNode, babylonMaterial.metallic, babylonMaterial.roughness);
                        babylonMaterial.metallicRoughnessTexture = ormTexture;

                        if (ambientOcclusionTextureDependencyNode != null)
                        {
                            babylonMaterial.occlusionTexture = ormTexture;
                        }
                    }
                    else if (ambientOcclusionTextureDependencyNode != null)
                    {
                        // Simply export occlusion texture
                        RaiseVerbose("Simply export occlusion texture", 2);
                        babylonMaterial.occlusionTexture = ExportTexture(ambientOcclusionTextureDependencyNode, babylonScene);
                    }
                }

                // Normal
                if (materialDependencyNode.findPlug("use_normal_map").asBool())
                {
                    babylonMaterial.normalTexture = ExportTexture(materialDependencyNode, "TEX_normal_map", babylonScene);
                }

                // Emissive
                bool useEmissiveMap = materialDependencyNode.findPlug("use_emissive_map").asBool();
                if (useEmissiveMap)
                {
                    babylonMaterial.emissiveTexture = ExportTexture(materialDependencyNode, "TEX_emissive_map", babylonScene, false, false, false, emissiveIntensity);
                }

                // Constraints
                if (useColorMap)
                {
                    babylonMaterial.baseColor = new[] { 1.0f, 1.0f, 1.0f };
                }
                if (useOpacityMap)
                {
                    babylonMaterial.alpha = 1.0f;
                }
                if (babylonMaterial.alpha != 1.0f || (babylonMaterial.baseTexture != null && babylonMaterial.baseTexture.hasAlpha))
                {
                    if (materialDependencyNode.hasAttribute("mask_threshold"))
                    {
                        babylonMaterial.transparencyMode = (int)BabylonPBRMetallicRoughnessMaterial.TransparencyMode.ALPHATEST;
                    }
                    else
                    {
                        babylonMaterial.transparencyMode = (int)BabylonPBRMetallicRoughnessMaterial.TransparencyMode.ALPHABLEND;
                    }
                }
                if (useMetallicMap)
                {
                    babylonMaterial.metallic = 1.0f;
                }
                if (useRoughnessMap)
                {
                    babylonMaterial.roughness = 1.0f;
                }
                if (useEmissiveMap)
                {
                    babylonMaterial.emissive = new[] { 1.0f, 1.0f, 1.0f };
                }

                // User custom attributes
                babylonMaterial.metadata = ExportCustomAttributeFromMaterial(babylonMaterial);

                if (babylonAttributesDependencyNode == null)
                {
                    // Create Babylon Material dependency node
                    babylonStingrayPBSMaterialNode.Create(materialDependencyNode);

                    // Retreive Babylon Material dependency node
                    babylonAttributesDependencyNode = getBabylonMaterialNode(materialDependencyNode);
                }

                if (babylonAttributesDependencyNode != null)
                {
                    // Ensure all attributes are setup
                    babylonStingrayPBSMaterialNode.Init(babylonAttributesDependencyNode, babylonMaterial);

                    RaiseVerbose("Babylon Attributes of " + babylonAttributesDependencyNode.name, 2);

                    // Common attributes
                    ExportCommonBabylonAttributes(babylonAttributesDependencyNode, babylonMaterial);
                    babylonMaterial.doubleSided = !babylonMaterial.backFaceCulling;
                    babylonMaterial._unlit      = babylonMaterial.isUnlit;

                    // Update displayed Transparency mode value based on StingrayPBS preset material
                    MGlobal.executeCommand($"setAttr - l false {{ \"{babylonAttributesDependencyNode.name}.babylonTransparencyMode\" }}"); // Unlock attribute
                    int babylonTransparencyMode = 0;
                    if (materialDependencyNode.hasAttribute("mask_threshold"))
                    {
                        babylonTransparencyMode = 1;
                    }
                    else if (materialDependencyNode.hasAttribute("use_opacity_map"))
                    {
                        babylonTransparencyMode = 2;
                    }
                    MGlobal.executeCommand($"setAttr \"{babylonAttributesDependencyNode.name}.babylonTransparencyMode\" {babylonTransparencyMode};");
                    MGlobal.executeCommand($"setAttr - l true {{ \"{babylonAttributesDependencyNode.name}.babylonTransparencyMode\" }}"); // Lock it afterwards
                }

                babylonScene.MaterialsList.Add(babylonMaterial);
            }
            // Arnold Ai Standard Surface
            else if (isAiStandardSurface(materialDependencyNode))
            {
                RaiseMessage("Ai Standard Surface shader", 2);

                var babylonMaterial = new BabylonPBRMetallicRoughnessMaterial(id)
                {
                    name = name
                };

                // User custom attributes
                babylonMaterial.metadata = ExportCustomAttributeFromMaterial(babylonMaterial);

                // --- Global ---

                bool isTransparencyModeFromBabylonMaterialNode = false;
                if (babylonAttributesDependencyNode != null)
                {
                    // Transparency mode
                    if (babylonAttributesDependencyNode.hasAttribute("babylonTransparencyMode"))
                    {
                        babylonMaterial.transparencyMode          = babylonAttributesDependencyNode.findPlug("babylonTransparencyMode").asInt();
                        isTransparencyModeFromBabylonMaterialNode = true;
                    }
                }

                // Color3
                float   baseWeight = materialDependencyNode.findPlug("base").asFloat();
                float[] baseColor  = materialDependencyNode.findPlug("baseColor").asFloatArray();
                babylonMaterial.baseColor = baseColor.Multiply(baseWeight);

                // Alpha
                MaterialDuplicationData materialDuplicationData = materialDuplicationDatas[id];
                // If at least one mesh is Transparent and is using this material either directly or as a sub material
                if ((isTransparencyModeFromBabylonMaterialNode == false || babylonMaterial.transparencyMode != 0) && materialDuplicationData.isArnoldTransparent())
                {
                    float[] opacityAttributeValue = materialDependencyNode.findPlug("opacity").asFloatArray();
                    babylonMaterial.alpha = opacityAttributeValue[0];
                }
                else
                {
                    // Do not bother about alpha
                    babylonMaterial.alpha = 1.0f;
                }

                // Metallic & roughness
                babylonMaterial.metallic  = materialDependencyNode.findPlug("metalness").asFloat();
                babylonMaterial.roughness = materialDependencyNode.findPlug("specularRoughness").asFloat();

                // Emissive
                float emissionWeight = materialDependencyNode.findPlug("emission").asFloat();
                babylonMaterial.emissive = materialDependencyNode.findPlug("emissionColor").asFloatArray().Multiply(emissionWeight);


                var list = new List <string>();

                for (int i = 0; i < materialDependencyNode.attributeCount; i++)
                {
                    var attr = materialDependencyNode.attribute((uint)i);
                    var plug = materialDependencyNode.findPlug(attr);
                    //string aliasName;
                    //materialDependencyNode.getPlugsAlias(plug, out aliasName);
                    System.Diagnostics.Debug.WriteLine(plug.name + i.ToString());
                }

                // --- Clear Coat ---
                float             coatWeight = materialDependencyNode.findPlug("coat").asFloat();
                MFnDependencyNode intensityCoatTextureDependencyNode = getTextureDependencyNode(materialDependencyNode, "coat");
                if (coatWeight > 0.0f || intensityCoatTextureDependencyNode != null)
                {
                    babylonMaterial.clearCoat.isEnabled         = true;
                    babylonMaterial.clearCoat.indexOfRefraction = materialDependencyNode.findPlug("coatIOR").asFloat();

                    var coatRoughness = materialDependencyNode.findPlug("coatRoughness").asFloat();
                    MFnDependencyNode roughnessCoatTextureDependencyNode = getTextureDependencyNode(materialDependencyNode, "coatRoughness");
                    var coatTexture = ExportCoatTexture(intensityCoatTextureDependencyNode, roughnessCoatTextureDependencyNode, babylonScene, name, coatWeight, coatRoughness);
                    if (coatTexture != null)
                    {
                        babylonMaterial.clearCoat.texture   = coatTexture;
                        babylonMaterial.clearCoat.roughness = 1.0f;
                        babylonMaterial.clearCoat.intensity = 1.0f;
                    }
                    else
                    {
                        babylonMaterial.clearCoat.intensity = coatWeight;
                        babylonMaterial.clearCoat.roughness = coatRoughness;
                    }

                    float[] coatColor = materialDependencyNode.findPlug("coatColor").asFloatArray();
                    if (coatColor[0] != 1.0f || coatColor[1] != 1.0f || coatColor[2] != 1.0f)
                    {
                        babylonMaterial.clearCoat.isTintEnabled = true;
                        babylonMaterial.clearCoat.tintColor     = coatColor;
                    }

                    babylonMaterial.clearCoat.tintTexture = ExportTexture(materialDependencyNode, "coatColor", babylonScene);
                    if (babylonMaterial.clearCoat.tintTexture != null)
                    {
                        babylonMaterial.clearCoat.tintColor     = new[] { 1.0f, 1.0f, 1.0f };
                        babylonMaterial.clearCoat.isTintEnabled = true;
                    }

                    // EyeBall deduction...
                    babylonMaterial.clearCoat.tintThickness = 0.65f;

                    babylonMaterial.clearCoat.bumpTexture = ExportTexture(materialDependencyNode, "coatNormal", babylonScene);
                }

                // --- Textures ---

                // Base color & alpha
                if ((isTransparencyModeFromBabylonMaterialNode == false || babylonMaterial.transparencyMode != 0) && materialDuplicationData.isArnoldTransparent())
                {
                    MFnDependencyNode baseColorTextureDependencyNode = getTextureDependencyNode(materialDependencyNode, "baseColor");
                    MFnDependencyNode opacityTextureDependencyNode   = getTextureDependencyNode(materialDependencyNode, "opacity");
                    if (baseColorTextureDependencyNode != null && opacityTextureDependencyNode != null &&
                        getSourcePathFromFileTexture(baseColorTextureDependencyNode) == getSourcePathFromFileTexture(opacityTextureDependencyNode))
                    {
                        // If the same file is used for base color and opacity
                        // Base color and alpha are already merged into a single file
                        babylonMaterial.baseTexture = ExportTexture(baseColorTextureDependencyNode, babylonScene, false, true);
                    }
                    else
                    {
                        // Base color and alpha files need to be merged into a single file
                        Color _baseColor = Color.FromArgb((int)(baseColor[0] * 255), (int)(baseColor[1] * 255), (int)(baseColor[2] * 255));
                        babylonMaterial.baseTexture = ExportBaseColorAlphaTexture(baseColorTextureDependencyNode, opacityTextureDependencyNode, babylonScene, name, _baseColor, babylonMaterial.alpha);
                    }
                }
                else
                {
                    // Base color only
                    // Do not bother about alpha
                    babylonMaterial.baseTexture = ExportTexture(materialDependencyNode, "baseColor", babylonScene);
                }

                // Metallic & roughness
                MFnDependencyNode metallicTextureDependencyNode  = getTextureDependencyNode(materialDependencyNode, "metalness");
                MFnDependencyNode roughnessTextureDependencyNode = getTextureDependencyNode(materialDependencyNode, "specularRoughness");
                if (metallicTextureDependencyNode != null && roughnessTextureDependencyNode != null &&
                    getSourcePathFromFileTexture(metallicTextureDependencyNode) == getSourcePathFromFileTexture(roughnessTextureDependencyNode))
                {
                    // If the same file is used for metallic and roughness
                    // Then we assume it's an ORM file (Red=Occlusion, Green=Roughness, Blue=Metallic)

                    // Metallic and roughness are already merged into a single file
                    babylonMaterial.metallicRoughnessTexture = ExportTexture(metallicTextureDependencyNode, babylonScene);

                    // Use same file for Ambient occlusion
                    babylonMaterial.occlusionTexture = babylonMaterial.metallicRoughnessTexture;
                }
                else
                {
                    // Metallic and roughness files need to be merged into a single file
                    // Occlusion texture is not exported since aiStandardSurface material doesn't provide input for it
                    babylonMaterial.metallicRoughnessTexture = ExportORMTexture(babylonScene, metallicTextureDependencyNode, roughnessTextureDependencyNode, null, babylonMaterial.metallic, babylonMaterial.roughness);
                }

                // Normal
                babylonMaterial.normalTexture = ExportTexture(materialDependencyNode, "normalCamera", babylonScene);

                // Emissive
                babylonMaterial.emissiveTexture = ExportTexture(materialDependencyNode, "emissionColor", babylonScene);

                // Constraints
                if (babylonMaterial.baseTexture != null)
                {
                    babylonMaterial.baseColor = new[] { baseWeight, baseWeight, baseWeight };
                    babylonMaterial.alpha     = 1.0f;
                }
                if (babylonMaterial.metallicRoughnessTexture != null)
                {
                    babylonMaterial.metallic  = 1.0f;
                    babylonMaterial.roughness = 1.0f;
                }
                if (babylonMaterial.emissiveTexture != null)
                {
                    babylonMaterial.emissive = new[] { emissionWeight, emissionWeight, emissionWeight };
                }

                // If this material is containing alpha data
                if (babylonMaterial.alpha != 1.0f || (babylonMaterial.baseTexture != null && babylonMaterial.baseTexture.hasAlpha))
                {
                    if (isTransparencyModeFromBabylonMaterialNode == false)
                    {
                        babylonMaterial.transparencyMode = (int)BabylonPBRMetallicRoughnessMaterial.TransparencyMode.ALPHABLEND;
                    }

                    // If this material is assigned to both Transparent and Opaque meshes (either directly or as a sub material)
                    if (materialDuplicationData.isDuplicationRequired())
                    {
                        // Duplicate material
                        BabylonPBRMetallicRoughnessMaterial babylonMaterialCloned = DuplicateMaterial(babylonMaterial, materialDuplicationData);

                        // Store duplicated material too
                        babylonScene.MaterialsList.Add(babylonMaterialCloned);
                    }
                }

                if (babylonMaterial.transparencyMode == (int)BabylonPBRMetallicRoughnessMaterial.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 (babylonAttributesDependencyNode == null)
                {
                    // Create Babylon Material dependency node
                    babylonAiStandardSurfaceMaterialNode.Create(materialDependencyNode);

                    // Retreive Babylon Material dependency node
                    babylonAttributesDependencyNode = getBabylonMaterialNode(materialDependencyNode);
                }

                if (babylonAttributesDependencyNode != null)
                {
                    // Ensure all attributes are setup
                    babylonAiStandardSurfaceMaterialNode.Init(babylonAttributesDependencyNode, babylonMaterial);

                    RaiseVerbose("Babylon Attributes of " + babylonAttributesDependencyNode.name, 2);

                    // Common attributes
                    ExportCommonBabylonAttributes(babylonAttributesDependencyNode, babylonMaterial);
                    babylonMaterial.doubleSided = !babylonMaterial.backFaceCulling;
                    babylonMaterial._unlit      = babylonMaterial.isUnlit;
                }

                if (fullPBR)
                {
                    var fullPBRMaterial = new BabylonPBRMaterial(babylonMaterial);
                    babylonScene.MaterialsList.Add(fullPBRMaterial);
                }
                else
                {
                    babylonScene.MaterialsList.Add(babylonMaterial);
                }
            }
            else
            {
                RaiseWarning("Unsupported material type '" + materialObject.apiType + "' for material named '" + materialDependencyNode.name + "'", 2);
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Analyse each material to know if it is assigned to Opaque meshes, Transparent ones, or both
        /// Generate an ID for each material requiring to be duplicated. This ID is used later.
        /// </summary>
        /// <param name="babylonScene"></param>
        private void GenerateMaterialDuplicationDatas(BabylonScene babylonScene)
        {
            materialDuplicationDatas = new Dictionary <string, MaterialDuplicationData>();

            // --- Get the meshes binded to each simple AiStandardSurface material ---
            foreach (MFnDependencyNode material in referencedMaterials)
            {
                if (isAiStandardSurfaceNotStdNotPBS(material))
                {
                    string multiMaterialId = material.uuid().asString();

                    // Retreive meshes using this material directly
                    // Meshes are split into 2 categories: Opaque or Transparent
                    List <BabylonMesh> meshesOpaque      = new List <BabylonMesh>();
                    List <BabylonMesh> meshesTransparent = new List <BabylonMesh>();
                    getMeshesByMaterialId(babylonScene, material.uuid().asString(), meshesOpaque, meshesTransparent);

                    // Get material duplication data or create new ones
                    if (!materialDuplicationDatas.ContainsKey(multiMaterialId))
                    {
                        materialDuplicationDatas.Add(multiMaterialId, new MaterialDuplicationData());
                    }
                    MaterialDuplicationData materialDuplicationData = materialDuplicationDatas[multiMaterialId];

                    // Store meshes
                    materialDuplicationData.meshesOpaque      = meshesOpaque;
                    materialDuplicationData.meshesTransparent = meshesTransparent;
                }
            }

            // --- Get the meshes binded to each multimaterial ---
            meshesOpaqueMultis      = new Dictionary <string, List <BabylonMesh> >();
            meshesTransparentMultis = new Dictionary <string, List <BabylonMesh> >();
            foreach (KeyValuePair <string, List <MFnDependencyNode> > multiMaterialKeyValue in multiMaterials)
            {
                string multiMaterialId = multiMaterialKeyValue.Key;
                List <MFnDependencyNode> subMaterials = multiMaterialKeyValue.Value;

                // Retreive meshes using this material as a multi material with at least one AiStandardSurface sub material
                // Meshes are split into 2 categories: Opaque or Transparent
                List <BabylonMesh> meshesOpaqueMulti      = new List <BabylonMesh>();
                List <BabylonMesh> meshesTransparentMulti = new List <BabylonMesh>();
                if (subMaterials.Find(isAiStandardSurfaceNotStdNotPBS) != null)
                {
                    getMeshesByMaterialId(babylonScene, multiMaterialId, meshesOpaqueMulti, meshesTransparentMulti);
                }

                meshesOpaqueMultis.Add(multiMaterialId, meshesOpaqueMulti);
                meshesTransparentMultis.Add(multiMaterialId, meshesTransparentMulti);
            }

            // --- Compute the number of opaque and transparent meshes from multimaterials ---
            foreach (KeyValuePair <string, List <MFnDependencyNode> > multiMaterialKeyValue in multiMaterials)
            {
                string multiMaterialId = multiMaterialKeyValue.Key;
                List <MFnDependencyNode> subMaterials = multiMaterialKeyValue.Value;

                // For each multi material, get the number of opaque and transparent meshes using it
                int nbMeshesOpaqueMulti      = meshesOpaqueMultis[multiMaterialId].Count;
                int nbMeshesTransparentMulti = meshesTransparentMultis[multiMaterialId].Count;

                // For each AiStandardSurface submaterial
                foreach (MFnDependencyNode subMaterial in subMaterials.FindAll(isAiStandardSurfaceNotStdNotPBS))
                {
                    string subMaterialId = subMaterial.uuid().asString();

                    // Get material duplication data or create new ones
                    if (!materialDuplicationDatas.ContainsKey(subMaterialId))
                    {
                        materialDuplicationDatas.Add(subMaterialId, new MaterialDuplicationData());
                    }
                    MaterialDuplicationData materialDuplicationData = materialDuplicationDatas[subMaterialId];

                    // Update the number of opaque and transparent meshes using submaterial as part of the multi material
                    materialDuplicationData.nbMeshesOpaqueMulti      += nbMeshesOpaqueMulti;
                    materialDuplicationData.nbMeshesTransparentMulti += nbMeshesTransparentMulti;
                }
            }

            // Generate here a new UUID for each AiStandardSurface material assigned to both opaque and transparent meshes
            // This UUID is used later when exporting (simple) materials
            foreach (KeyValuePair <string, MaterialDuplicationData> materialDuplicationDataKeyValue in materialDuplicationDatas)
            {
                MaterialDuplicationData materialDuplicationValue = materialDuplicationDataKeyValue.Value;

                if (materialDuplicationValue.isDuplicationRequired())
                {
                    string duplicationId = Tools.GenerateUUID();
                    materialDuplicationValue.idOpaque = duplicationId;
                }
            }
        }
Esempio n. 3
0
        private void UpdateMeshesMaterialId(BabylonScene babylonScene)
        {
            // --- Update meshes using simple materials ---
            foreach (MFnDependencyNode material in referencedMaterials)
            {
                string materialId = material.uuid().asString();

                // If it's an AiStandardSurface material
                if (materialDuplicationDatas.ContainsKey(materialId))
                {
                    MaterialDuplicationData materialDuplicationValue = materialDuplicationDatas[materialId];

                    // If both Opaque and Transparent meshes are using this material
                    // And the material contains alpha data
                    if (materialDuplicationValue.idOpaque != null && materialDuplicationValue.isDuplicationSuccess)
                    {
                        // The duplicated material is used for Opaque meshes instead of the original one
                        foreach (BabylonMesh meshOpaque in materialDuplicationValue.meshesOpaque)
                        {
                            meshOpaque.materialId = materialDuplicationValue.idOpaque;
                        }
                    }
                }
            }

            // --- Update meshes using multimaterials ---
            var multiMaterialsList = new List <BabylonMultiMaterial>(babylonScene.MultiMaterialsList);

            foreach (BabylonMultiMaterial babylonMultiMaterial in multiMaterialsList)
            {
                string multiMaterialId = babylonMultiMaterial.id;

                // If at least one Opaque mesh is using this multimaterial
                if (meshesOpaqueMultis[multiMaterialId].Count > 0)
                {
                    List <MFnDependencyNode> subMaterials = multiMaterials[multiMaterialId];

                    // Build a list of all opaque sub material ids
                    List <string> opaqueSubMaterials = new List <string>();
                    foreach (MFnDependencyNode subMaterial in subMaterials)
                    {
                        string subMaterialId = subMaterial.uuid().asString();

                        // If it's an AiStandardSurface material
                        if (materialDuplicationDatas.ContainsKey(subMaterialId))
                        {
                            MaterialDuplicationData materialDuplicationValue = materialDuplicationDatas[subMaterialId];

                            // If both Opaque and Transparent meshes are using this sub material
                            // And the sub material contains alpha data
                            if (materialDuplicationValue.idOpaque != null && materialDuplicationValue.isDuplicationSuccess)
                            {
                                // The duplicated material is used for Opaque meshes instead of the original one
                                subMaterialId = materialDuplicationValue.idOpaque;
                            }
                        }

                        opaqueSubMaterials.Add(subMaterialId);
                    }

                    // If both Opaque and Transparent meshes are using this multimaterial
                    if (meshesTransparentMultis[multiMaterialId].Count > 0)
                    {
                        // Duplicate multimaterial
                        BabylonMultiMaterial multiMaterialCloned = new BabylonMultiMaterial(babylonMultiMaterial);

                        // Update id
                        string idMultiMaterialCloned = Tools.GenerateUUID();
                        multiMaterialCloned.id = idMultiMaterialCloned;

                        // Update sub materials
                        multiMaterialCloned.materials = opaqueSubMaterials.ToArray();

                        // Store duplication
                        babylonScene.MultiMaterialsList.Add(multiMaterialCloned);

                        // Update meshes id
                        foreach (BabylonMesh meshOpaque in meshesOpaqueMultis[multiMaterialId])
                        {
                            meshOpaque.materialId = idMultiMaterialCloned;
                        }
                    }
                    else
                    {
                        // Only Opaque meshes are using this multimaterial
                        // Simply override sub materials with all opaque ones
                        babylonMultiMaterial.materials = opaqueSubMaterials.ToArray();
                    }
                }
            }
        }
Esempio n. 4
0
        private BabylonPBRMetallicRoughnessMaterial DuplicateMaterial(BabylonPBRMetallicRoughnessMaterial babylonMaterial, MaterialDuplicationData materialDuplicationData)
        {
            // Duplicate material
            BabylonPBRMetallicRoughnessMaterial babylonMaterialCloned = new BabylonPBRMetallicRoughnessMaterial(babylonMaterial);

            // Give it a new UUID
            babylonMaterialCloned.id = materialDuplicationData.idOpaque;

            // This material is used for opaque meshes
            // Reset its alpha
            babylonMaterialCloned.alpha = 1;
            if (babylonMaterialCloned.baseTexture != null)
            {
                babylonMaterialCloned.baseTexture          = new BabylonTexture(babylonMaterialCloned.baseTexture);
                babylonMaterialCloned.baseTexture.hasAlpha = false;
            }

            // Set transparency for opaque material
            babylonMaterialCloned.transparencyMode = (int)BabylonPBRMetallicRoughnessMaterial.TransparencyMode.OPAQUE;

            // Duplication is done correctly, thus meshes material id can be updated accordingly
            materialDuplicationData.isDuplicationSuccess = true;

            return(babylonMaterialCloned);
        }
        private void ExportMaterial(MFnDependencyNode materialDependencyNode, BabylonScene babylonScene)
        {
            MObject materialObject = materialDependencyNode.objectProperty;
            var     name           = materialDependencyNode.name;
            var     id             = materialDependencyNode.uuid().asString();

            RaiseMessage(name, 1);

            RaiseVerbose("materialObject.hasFn(MFn.Type.kBlinn)=" + materialObject.hasFn(MFn.Type.kBlinn), 2);
            RaiseVerbose("materialObject.hasFn(MFn.Type.kPhong)=" + materialObject.hasFn(MFn.Type.kPhong), 2);
            RaiseVerbose("materialObject.hasFn(MFn.Type.kPhongExplorer)=" + materialObject.hasFn(MFn.Type.kPhongExplorer), 2);

            Print(materialDependencyNode, 2, "Print ExportMaterial materialDependencyNode");

            // Standard material
            if (materialObject.hasFn(MFn.Type.kLambert))
            {
                if (materialObject.hasFn(MFn.Type.kBlinn))
                {
                    RaiseMessage("Blinn shader", 2);
                }
                else if (materialObject.hasFn(MFn.Type.kPhong))
                {
                    RaiseMessage("Phong shader", 2);
                }
                else if (materialObject.hasFn(MFn.Type.kPhongExplorer))
                {
                    RaiseMessage("Phong E shader", 2);
                }
                else
                {
                    RaiseMessage("Lambert shader", 2);
                }

                var lambertShader = new MFnLambertShader(materialObject);

                RaiseVerbose("typeId=" + lambertShader.typeId, 2);
                RaiseVerbose("typeName=" + lambertShader.typeName, 2);
                RaiseVerbose("color=" + lambertShader.color.toString(), 2);
                RaiseVerbose("transparency=" + lambertShader.transparency.toString(), 2);
                RaiseVerbose("ambientColor=" + lambertShader.ambientColor.toString(), 2);
                RaiseVerbose("incandescence=" + lambertShader.incandescence.toString(), 2);
                RaiseVerbose("diffuseCoeff=" + lambertShader.diffuseCoeff, 2);
                RaiseVerbose("translucenceCoeff=" + lambertShader.translucenceCoeff, 2);

                var babylonMaterial = new BabylonStandardMaterial
                {
                    name    = name,
                    id      = id,
                    diffuse = lambertShader.color.toArrayRGB(),
                    alpha   = 1.0f - lambertShader.transparency[0]
                };

                // Maya ambient <=> babylon emissive
                babylonMaterial.emissive = lambertShader.ambientColor.toArrayRGB();
                babylonMaterial.linkEmissiveWithDiffuse = true; // Incandescence (or Illumination) is not exported

                // If transparency is not a shade of grey (shade of grey <=> R=G=B)
                if (lambertShader.transparency[0] != lambertShader.transparency[1] ||
                    lambertShader.transparency[0] != lambertShader.transparency[2])
                {
                    RaiseWarning("Transparency color is not a shade of grey. Only it's R channel is used.", 2);
                }
                // Convert transparency to opacity
                babylonMaterial.alpha = 1.0f - lambertShader.transparency[0];

                // Specular power
                if (materialObject.hasFn(MFn.Type.kReflect))
                {
                    var reflectShader = new MFnReflectShader(materialObject);

                    RaiseVerbose("specularColor=" + reflectShader.specularColor.toString(), 2);
                    RaiseVerbose("reflectivity=" + reflectShader.reflectivity, 2);
                    RaiseVerbose("reflectedColor=" + reflectShader.reflectedColor.toString(), 2);

                    babylonMaterial.specular = reflectShader.specularColor.toArrayRGB();

                    if (materialObject.hasFn(MFn.Type.kBlinn))
                    {
                        MFnBlinnShader blinnShader = new MFnBlinnShader(materialObject);
                        babylonMaterial.specularPower = (1.0f - blinnShader.eccentricity) * 256;
                    }
                    else if (materialObject.hasFn(MFn.Type.kPhong))
                    {
                        MFnPhongShader phongShader = new MFnPhongShader(materialObject);

                        float glossiness = (float)Math.Log(phongShader.cosPower, 2) * 10;
                        babylonMaterial.specularPower = glossiness / 100 * 256;
                    }
                    else if (materialObject.hasFn(MFn.Type.kPhongExplorer))
                    {
                        MFnPhongEShader phongEShader = new MFnPhongEShader(materialObject);
                        // No use of phongE.whiteness and phongE.highlightSize
                        babylonMaterial.specularPower = (1.0f - phongEShader.roughness) * 256;
                    }
                    else
                    {
                        RaiseWarning("Unknown reflect shader type: " + reflectShader.typeName + ". Specular power is default 64. Consider using a Blinn or Phong shader instead.", 2);
                    }
                }

                // TODO
                //babylonMaterial.backFaceCulling = !stdMat.TwoSided;
                //babylonMaterial.wireframe = stdMat.Wire;

                // --- Textures ---

                babylonMaterial.diffuseTexture  = ExportTexture(materialDependencyNode, "color", babylonScene);
                babylonMaterial.emissiveTexture = ExportTexture(materialDependencyNode, "ambientColor", babylonScene); // Maya ambient <=> babylon emissive
                babylonMaterial.bumpTexture     = ExportTexture(materialDependencyNode, "normalCamera", babylonScene);
                babylonMaterial.opacityTexture  = ExportTexture(materialDependencyNode, "transparency", babylonScene, false, true);
                if (materialObject.hasFn(MFn.Type.kReflect))
                {
                    babylonMaterial.specularTexture   = ExportTexture(materialDependencyNode, "specularColor", babylonScene);
                    babylonMaterial.reflectionTexture = ExportTexture(materialDependencyNode, "reflectedColor", babylonScene, true, false, true);
                }

                // 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 };
                }

                babylonScene.MaterialsList.Add(babylonMaterial);
            }
            // Stingray PBS material
            else if (isStingrayPBSMaterial(materialDependencyNode))
            {
                RaiseMessage("Stingray shader", 2);

                var babylonMaterial = new BabylonPBRMetallicRoughnessMaterial
                {
                    name = name,
                    id   = id
                };

                // --- Global ---

                // Color3
                babylonMaterial.baseColor = materialDependencyNode.findPlug("base_color").asFloatArray();

                // Alpha
                string opacityAttributeName = "opacity";
                if (materialDependencyNode.hasAttribute(opacityAttributeName))
                {
                    float opacityAttributeValue = materialDependencyNode.findPlug(opacityAttributeName).asFloatProperty;
                    babylonMaterial.alpha = 1.0f - opacityAttributeValue;
                }

                // Metallic & roughness
                babylonMaterial.metallic  = materialDependencyNode.findPlug("metallic").asFloatProperty;
                babylonMaterial.roughness = materialDependencyNode.findPlug("roughness").asFloatProperty;

                // Emissive
                float emissiveIntensity = materialDependencyNode.findPlug("emissive_intensity").asFloatProperty;
                // Factor emissive color with emissive intensity
                emissiveIntensity        = Tools.Clamp(emissiveIntensity, 0f, 1f);
                babylonMaterial.emissive = materialDependencyNode.findPlug("emissive").asFloatArray().Multiply(emissiveIntensity);

                // --- Textures ---

                // Base color & alpha
                bool   useColorMap   = materialDependencyNode.findPlug("use_color_map").asBoolProperty;
                bool   useOpacityMap = false;
                string useOpacityMapAttributeName = "use_opacity_map";
                if (materialDependencyNode.hasAttribute(useOpacityMapAttributeName))
                {
                    useOpacityMap = materialDependencyNode.findPlug(useOpacityMapAttributeName).asBoolProperty;
                }
                if (useColorMap || useOpacityMap)
                {
                    // TODO - Force non use map to default value ?
                    // Ex: if useOpacityMap == false, force alpha = 255 for all pixels.
                    //babylonMaterial.baseTexture = ExportBaseColorAlphaTexture(materialDependencyNode, useColorMap, useOpacityMap, babylonMaterial.baseColor, babylonMaterial.alpha, babylonScene);
                    babylonMaterial.baseTexture = ExportTexture(materialDependencyNode, "TEX_color_map", babylonScene, false, useOpacityMap);
                }

                // Metallic, roughness, ambient occlusion
                bool   useMetallicMap        = materialDependencyNode.findPlug("use_metallic_map").asBoolProperty;
                bool   useRoughnessMap       = materialDependencyNode.findPlug("use_roughness_map").asBoolProperty;
                string useAOMapAttributeName = "use_ao_map";
                bool   useAOMap = materialDependencyNode.hasAttribute(useAOMapAttributeName) && materialDependencyNode.findPlug(useAOMapAttributeName).asBoolProperty;

                MFnDependencyNode metallicTextureDependencyNode         = useMetallicMap ? getTextureDependencyNode(materialDependencyNode, "TEX_metallic_map") : null;
                MFnDependencyNode roughnessTextureDependencyNode        = useRoughnessMap ? getTextureDependencyNode(materialDependencyNode, "TEX_roughness_map") : null;
                MFnDependencyNode ambientOcclusionTextureDependencyNode = useAOMap ? getTextureDependencyNode(materialDependencyNode, "TEX_ao_map") : null;

                // Check if MR or ORM textures are already merged
                bool areTexturesAlreadyMerged = false;
                if (metallicTextureDependencyNode != null && roughnessTextureDependencyNode != null)
                {
                    string sourcePathMetallic  = getSourcePathFromFileTexture(metallicTextureDependencyNode);
                    string sourcePathRoughness = getSourcePathFromFileTexture(roughnessTextureDependencyNode);

                    if (sourcePathMetallic == sourcePathRoughness)
                    {
                        if (ambientOcclusionTextureDependencyNode != null)
                        {
                            string sourcePathAmbientOcclusion = getSourcePathFromFileTexture(ambientOcclusionTextureDependencyNode);
                            if (sourcePathMetallic == sourcePathAmbientOcclusion)
                            {
                                // Metallic, roughness and ambient occlusion are already merged
                                RaiseVerbose("Metallic, roughness and ambient occlusion are already merged", 2);
                                BabylonTexture ormTexture = ExportTexture(metallicTextureDependencyNode, 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(metallicTextureDependencyNode, babylonScene);
                            babylonMaterial.metallicRoughnessTexture = ormTexture;
                            areTexturesAlreadyMerged = true;
                        }
                    }
                }
                if (areTexturesAlreadyMerged == false)
                {
                    if (metallicTextureDependencyNode != null || roughnessTextureDependencyNode != null)
                    {
                        // Merge metallic, roughness and ambient occlusion
                        RaiseVerbose("Merge metallic, roughness and ambient occlusion", 2);
                        BabylonTexture ormTexture = ExportORMTexture(babylonScene, metallicTextureDependencyNode, roughnessTextureDependencyNode, ambientOcclusionTextureDependencyNode, babylonMaterial.metallic, babylonMaterial.roughness);
                        babylonMaterial.metallicRoughnessTexture = ormTexture;

                        if (ambientOcclusionTextureDependencyNode != null)
                        {
                            babylonMaterial.occlusionTexture = ormTexture;
                        }
                    }
                    else if (ambientOcclusionTextureDependencyNode != null)
                    {
                        // Simply export occlusion texture
                        RaiseVerbose("Simply export occlusion texture", 2);
                        babylonMaterial.occlusionTexture = ExportTexture(ambientOcclusionTextureDependencyNode, babylonScene);
                    }
                }

                // Normal
                if (materialDependencyNode.findPlug("use_normal_map").asBoolProperty)
                {
                    babylonMaterial.normalTexture = ExportTexture(materialDependencyNode, "TEX_normal_map", babylonScene);
                }

                // Emissive
                bool useEmissiveMap = materialDependencyNode.findPlug("use_emissive_map").asBoolProperty;
                if (useEmissiveMap)
                {
                    babylonMaterial.emissiveTexture = ExportTexture(materialDependencyNode, "TEX_emissive_map", babylonScene, false, false, false, emissiveIntensity);
                }

                // Constraints
                if (useColorMap)
                {
                    babylonMaterial.baseColor = new[] { 1.0f, 1.0f, 1.0f };
                }
                if (useOpacityMap)
                {
                    babylonMaterial.alpha = 1.0f;
                }
                if (babylonMaterial.alpha != 1.0f || (babylonMaterial.baseTexture != null && babylonMaterial.baseTexture.hasAlpha))
                {
                    babylonMaterial.transparencyMode = (int)BabylonPBRMetallicRoughnessMaterial.TransparencyMode.ALPHABLEND;
                }
                if (useMetallicMap)
                {
                    babylonMaterial.metallic = 1.0f;
                }
                if (useRoughnessMap)
                {
                    babylonMaterial.roughness = 1.0f;
                }
                if (useEmissiveMap)
                {
                    babylonMaterial.emissive = new[] { 1.0f, 1.0f, 1.0f };
                }

                babylonScene.MaterialsList.Add(babylonMaterial);
            }
            // Arnold Ai Standard Surface
            else if (isAiStandardSurface(materialDependencyNode))
            {
                RaiseMessage("Ai Standard Surface shader", 2);

                var babylonMaterial = new BabylonPBRMetallicRoughnessMaterial
                {
                    name = name,
                    id   = id
                };

                // --- Global ---

                // Color3
                float   baseWeight = materialDependencyNode.findPlug("base").asFloatProperty;
                float[] baseColor  = materialDependencyNode.findPlug("baseColor").asFloatArray();
                babylonMaterial.baseColor = baseColor.Multiply(baseWeight);

                // Alpha
                MaterialDuplicationData materialDuplicationData = materialDuplicationDatas[id];
                // If at least one mesh is Transparent and is using this material either directly or as a sub material
                if (materialDuplicationData.isArnoldTransparent())
                {
                    float[] opacityAttributeValue = materialDependencyNode.findPlug("opacity").asFloatArray();
                    babylonMaterial.alpha = opacityAttributeValue[0];
                }
                else
                {
                    // Do not bother about alpha
                    babylonMaterial.alpha = 1.0f;
                }

                // Metallic & roughness
                babylonMaterial.metallic  = materialDependencyNode.findPlug("metalness").asFloatProperty;
                babylonMaterial.roughness = materialDependencyNode.findPlug("specularRoughness").asFloatProperty;

                // Emissive
                float emissionWeight = materialDependencyNode.findPlug("emission").asFloatProperty;
                babylonMaterial.emissive = materialDependencyNode.findPlug("emissionColor").asFloatArray().Multiply(emissionWeight);

                // --- Textures ---

                // Base color & alpha
                if (materialDuplicationData.isArnoldTransparent())
                {
                    MFnDependencyNode baseColorTextureDependencyNode = getTextureDependencyNode(materialDependencyNode, "baseColor");
                    MFnDependencyNode opacityTextureDependencyNode   = getTextureDependencyNode(materialDependencyNode, "opacity");
                    if (baseColorTextureDependencyNode != null && opacityTextureDependencyNode != null &&
                        getSourcePathFromFileTexture(baseColorTextureDependencyNode) == getSourcePathFromFileTexture(opacityTextureDependencyNode))
                    {
                        // If the same file is used for base color and opacity
                        // Base color and alpha are already merged into a single file
                        babylonMaterial.baseTexture = ExportTexture(baseColorTextureDependencyNode, babylonScene, false, true);
                    }
                    else
                    {
                        // Base color and alpha files need to be merged into a single file
                        Color _baseColor = Color.FromArgb((int)baseColor[0] * 255, (int)baseColor[1] * 255, (int)baseColor[2] * 255);
                        babylonMaterial.baseTexture = ExportBaseColorAlphaTexture(baseColorTextureDependencyNode, opacityTextureDependencyNode, babylonScene, name, _baseColor, babylonMaterial.alpha);
                    }
                }
                else
                {
                    // Base color only
                    // Do not bother about alpha
                    babylonMaterial.baseTexture = ExportTexture(materialDependencyNode, "baseColor", babylonScene);
                }

                // Metallic & roughness
                MFnDependencyNode metallicTextureDependencyNode  = getTextureDependencyNode(materialDependencyNode, "metalness");
                MFnDependencyNode roughnessTextureDependencyNode = getTextureDependencyNode(materialDependencyNode, "specularRoughness");
                if (metallicTextureDependencyNode != null && roughnessTextureDependencyNode != null &&
                    getSourcePathFromFileTexture(metallicTextureDependencyNode) == getSourcePathFromFileTexture(roughnessTextureDependencyNode))
                {
                    // If the same file is used for metallic and roughness
                    // Then we assume it's an ORM file (Red=Occlusion, Green=Roughness, Blue=Metallic)

                    // Metallic and roughness are already merged into a single file
                    babylonMaterial.metallicRoughnessTexture = ExportTexture(metallicTextureDependencyNode, babylonScene);

                    // Use same file for Ambient occlusion
                    babylonMaterial.occlusionTexture = babylonMaterial.metallicRoughnessTexture;
                }
                else
                {
                    // Metallic and roughness files need to be merged into a single file
                    // Occlusion texture is not exported since aiStandardSurface material doesn't provide input for it
                    babylonMaterial.metallicRoughnessTexture = ExportORMTexture(babylonScene, metallicTextureDependencyNode, roughnessTextureDependencyNode, null, babylonMaterial.metallic, babylonMaterial.roughness);
                }

                // Normal
                babylonMaterial.normalTexture = ExportTexture(materialDependencyNode, "normalCamera", babylonScene);

                // Emissive
                babylonMaterial.emissiveTexture = ExportTexture(materialDependencyNode, "emissionColor", babylonScene);

                // Constraints
                if (babylonMaterial.baseTexture != null)
                {
                    babylonMaterial.baseColor = new[] { baseWeight, baseWeight, baseWeight };
                    babylonMaterial.alpha     = 1.0f;
                }
                if (babylonMaterial.metallicRoughnessTexture != null)
                {
                    babylonMaterial.metallic  = 1.0f;
                    babylonMaterial.roughness = 1.0f;
                }
                if (babylonMaterial.emissiveTexture != null)
                {
                    babylonMaterial.emissive = new[] { emissionWeight, emissionWeight, emissionWeight };
                }

                // If this material is containing alpha data
                if (babylonMaterial.alpha != 1.0f || (babylonMaterial.baseTexture != null && babylonMaterial.baseTexture.hasAlpha))
                {
                    babylonMaterial.transparencyMode = (int)BabylonPBRMetallicRoughnessMaterial.TransparencyMode.ALPHABLEND;

                    // If this material is assigned to both Transparent and Opaque meshes (either directly or as a sub material)
                    if (materialDuplicationData.isDuplicationRequired())
                    {
                        // Duplicate material
                        BabylonPBRMetallicRoughnessMaterial babylonMaterialCloned = DuplicateMaterial(babylonMaterial, materialDuplicationData);

                        // Store duplicated material too
                        babylonScene.MaterialsList.Add(babylonMaterialCloned);
                    }
                }

                babylonScene.MaterialsList.Add(babylonMaterial);
            }
            else
            {
                RaiseWarning("Unsupported material type '" + materialObject.apiType + "' for material named '" + materialDependencyNode.name + "'", 2);
            }
        }