Ejemplo n.º 1
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="mDagPath">DAG path to the transform above mesh</param>
        /// <param name="babylonScene"></param>
        /// <returns></returns>
        private BabylonNode ExportMesh(MDagPath mDagPath, BabylonScene babylonScene)
        {
            RaiseMessage(mDagPath.partialPathName, 1);

            // Transform above mesh
            MFnTransform mFnTransform = new MFnTransform(mDagPath);

            // Mesh direct child of the transform
            MFnMesh mFnMesh = null;

            for (uint i = 0; i < mFnTransform.childCount; i++)
            {
                MObject childObject = mFnTransform.child(i);
                if (childObject.apiType == MFn.Type.kMesh)
                {
                    var _mFnMesh = new MFnMesh(childObject);
                    if (!_mFnMesh.isIntermediateObject)
                    {
                        mFnMesh = _mFnMesh;
                    }
                }
            }
            if (mFnMesh == null)
            {
                RaiseError("No mesh found has child of " + mDagPath.fullPathName);
                return(null);
            }

            RaiseMessage("mFnMesh.fullPathName=" + mFnMesh.fullPathName, 2);

            // --- prints ---
            #region prints

            Action <MFnDagNode> printMFnDagNode = (MFnDagNode mFnDagNode) =>
            {
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.name=" + mFnDagNode.name, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.absoluteName=" + mFnDagNode.absoluteName, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.fullPathName=" + mFnDagNode.fullPathName, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.partialPathName=" + mFnDagNode.partialPathName, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.activeColor=" + mFnDagNode.activeColor.toString(), 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.attributeCount=" + mFnDagNode.attributeCount, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.childCount=" + mFnDagNode.childCount, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.dormantColor=" + mFnDagNode.dormantColor, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.hasUniqueName=" + mFnDagNode.hasUniqueName, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.inUnderWorld=" + mFnDagNode.inUnderWorld, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isDefaultNode=" + mFnDagNode.isDefaultNode, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanceable=" + mFnDagNode.isInstanceable, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanced(true)=" + mFnDagNode.isInstanced(true), 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanced(false)=" + mFnDagNode.isInstanced(false), 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanced()=" + mFnDagNode.isInstanced(), 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.instanceCount(true)=" + mFnDagNode.instanceCount(true), 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.instanceCount(false)=" + mFnDagNode.instanceCount(false), 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isIntermediateObject=" + mFnDagNode.isIntermediateObject, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isShared=" + mFnDagNode.isShared, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.objectColor=" + mFnDagNode.objectColor, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.parentCount=" + mFnDagNode.parentCount, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.parentNamespace=" + mFnDagNode.parentNamespace, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.uuid().asString()=" + mFnDagNode.uuid().asString(), 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.dagRoot().apiType=" + mFnDagNode.dagRoot().apiType, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.model.equalEqual(mFnDagNode.objectProperty)=" + mFnDagNode.model.equalEqual(mFnDagNode.objectProperty), 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.transformationMatrix.toString()=" + mFnDagNode.transformationMatrix.toString(), 3);
            };

            Action <MFnMesh> printMFnMesh = (MFnMesh _mFnMesh) =>
            {
                printMFnDagNode(mFnMesh);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numVertices=" + _mFnMesh.numVertices, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numEdges=" + _mFnMesh.numEdges, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numPolygons=" + _mFnMesh.numPolygons, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numFaceVertices=" + _mFnMesh.numFaceVertices, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numNormals=" + _mFnMesh.numNormals, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numUVSets=" + _mFnMesh.numUVSets, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numUVsProperty=" + _mFnMesh.numUVsProperty, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.displayColors=" + _mFnMesh.displayColors, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numColorSets=" + _mFnMesh.numColorSets, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numColorsProperty=" + _mFnMesh.numColorsProperty, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.currentUVSetName()=" + _mFnMesh.currentUVSetName(), 3);

                var _uvSetNames = new MStringArray();
                mFnMesh.getUVSetNames(_uvSetNames);
                foreach (var uvSetName in _uvSetNames)
                {
                    RaiseVerbose("BabylonExporter.Mesh | uvSetName=" + uvSetName, 3);
                    RaiseVerbose("BabylonExporter.Mesh | mFnMesh.numUVs(uvSetName)=" + mFnMesh.numUVs(uvSetName), 4);
                    MFloatArray us = new MFloatArray();
                    MFloatArray vs = new MFloatArray();
                    mFnMesh.getUVs(us, vs, uvSetName);
                    RaiseVerbose("BabylonExporter.Mesh | us.Count=" + us.Count, 4);
                }
            };

            Action <MFnTransform> printMFnTransform = (MFnTransform _mFnMesh) =>
            {
                printMFnDagNode(mFnMesh);
            };

            RaiseVerbose("BabylonExporter.Mesh | mFnMesh data", 2);
            printMFnMesh(mFnMesh);

            RaiseVerbose("BabylonExporter.Mesh | mFnTransform data", 2);
            printMFnTransform(mFnTransform);

            Print(mFnTransform, 2, "Print ExportMesh mFnTransform");

            Print(mFnMesh, 2, "Print ExportMesh mFnMesh");

            //// Geometry
            //MIntArray triangleCounts = new MIntArray();
            //MIntArray trianglesVertices = new MIntArray();
            //mFnMesh.getTriangles(triangleCounts, trianglesVertices);
            //RaiseVerbose("BabylonExporter.Mesh | triangleCounts.ToArray()=" + triangleCounts.ToArray().toString(), 3);
            //RaiseVerbose("BabylonExporter.Mesh | trianglesVertices.ToArray()=" + trianglesVertices.ToArray().toString(), 3);
            //int[] polygonsVertexCount = new int[mFnMesh.numPolygons];
            //for (int polygonId = 0; polygonId < mFnMesh.numPolygons; polygonId++)
            //{
            //    polygonsVertexCount[polygonId] = mFnMesh.polygonVertexCount(polygonId);
            //}
            //RaiseVerbose("BabylonExporter.Mesh | polygonsVertexCount=" + polygonsVertexCount.toString(), 3);

            ////MFloatPointArray points = new MFloatPointArray();
            ////mFnMesh.getPoints(points);
            ////RaiseVerbose("BabylonExporter.Mesh | points.ToArray()=" + points.ToArray().Select(mFloatPoint => mFloatPoint.toString()), 3);

            ////MFloatVectorArray normals = new MFloatVectorArray();
            ////mFnMesh.getNormals(normals);
            ////RaiseVerbose("BabylonExporter.Mesh | normals.ToArray()=" + normals.ToArray().Select(mFloatPoint => mFloatPoint.toString()), 3);

            //for (int polygonId = 0; polygonId < mFnMesh.numPolygons; polygonId++)
            //{
            //    MIntArray verticesId = new MIntArray();
            //    RaiseVerbose("BabylonExporter.Mesh | polygonId=" + polygonId, 3);

            //    int nbTriangles = triangleCounts[polygonId];
            //    RaiseVerbose("BabylonExporter.Mesh | nbTriangles=" + nbTriangles, 3);

            //    for (int triangleIndex = 0; triangleIndex < triangleCounts[polygonId]; triangleIndex++)
            //    {
            //        RaiseVerbose("BabylonExporter.Mesh | triangleIndex=" + triangleIndex, 3);
            //        int[] triangleVertices = new int[3];
            //        mFnMesh.getPolygonTriangleVertices(polygonId, triangleIndex, triangleVertices);
            //        RaiseVerbose("BabylonExporter.Mesh | triangleVertices=" + triangleVertices.toString(), 3);

            //        foreach (int vertexId in triangleVertices)
            //        {
            //            RaiseVerbose("BabylonExporter.Mesh | vertexId=" + vertexId, 3);
            //            MPoint point = new MPoint();
            //            mFnMesh.getPoint(vertexId, point);
            //            RaiseVerbose("BabylonExporter.Mesh | point=" + point.toString(), 3);

            //            MVector normal = new MVector();
            //            mFnMesh.getFaceVertexNormal(polygonId, vertexId, normal);
            //            RaiseVerbose("BabylonExporter.Mesh | normal=" + normal.toString(), 3);
            //        }
            //    }
            //}

            #endregion


            if (IsMeshExportable(mFnMesh, mDagPath) == false)
            {
                return(null);
            }

            var babylonMesh = new BabylonMesh {
                name = mFnTransform.name, id = mFnTransform.uuid().asString()
            };

            // Position / rotation / scaling / hierarchy
            ExportNode(babylonMesh, mFnTransform, babylonScene);

            // Misc.
            // TODO - Retreive from Maya
            // TODO - What is the difference between isVisible and visibility?
            // TODO - Fix fatal error: Attempting to save in C:/Users/Fabrice/AppData/Local/Temp/Fabrice.20171205.1613.ma
            //babylonMesh.isVisible = mDagPath.isVisible;
            //babylonMesh.visibility = meshNode.MaxNode.GetVisibility(0, Tools.Forever);
            //babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows == 1;
            //babylonMesh.applyFog = meshNode.MaxNode.ApplyAtmospherics == 1;

            if (mFnMesh.numPolygons < 1)
            {
                RaiseError($"Mesh {babylonMesh.name} has no face", 2);
            }

            if (mFnMesh.numVertices < 3)
            {
                RaiseError($"Mesh {babylonMesh.name} has not enough vertices", 2);
            }

            if (mFnMesh.numVertices >= 65536)
            {
                RaiseWarning($"Mesh {babylonMesh.name} has more than 65536 vertices which means that it will require specific WebGL extension to be rendered. This may impact portability of your scene on low end devices.", 2);
            }

            // Material
            MObjectArray shaders = new MObjectArray();
            mFnMesh.getConnectedShaders(0, shaders, new MIntArray());
            if (shaders.Count > 0)
            {
                List <MFnDependencyNode> materials = new List <MFnDependencyNode>();
                foreach (MObject shader in shaders)
                {
                    // Retreive material
                    MFnDependencyNode shadingEngine      = new MFnDependencyNode(shader);
                    MPlug             mPlugSurfaceShader = shadingEngine.findPlug("surfaceShader");
                    MObject           materialObject     = mPlugSurfaceShader.source.node;
                    MFnDependencyNode material           = new MFnDependencyNode(materialObject);

                    materials.Add(material);
                }

                if (shaders.Count == 1)
                {
                    MFnDependencyNode material = materials[0];

                    // Material is referenced by id
                    babylonMesh.materialId = material.uuid().asString();

                    // Register material for export if not already done
                    if (!referencedMaterials.Contains(material, new MFnDependencyNodeEqualityComparer()))
                    {
                        referencedMaterials.Add(material);
                    }
                }
                else
                {
                    // Create a new id for the group of sub materials
                    string uuidMultiMaterial = GetMultimaterialUUID(materials);

                    // Multi material is referenced by id
                    babylonMesh.materialId = uuidMultiMaterial;

                    // Register multi material for export if not already done
                    if (!multiMaterials.ContainsKey(uuidMultiMaterial))
                    {
                        multiMaterials.Add(uuidMultiMaterial, materials);
                    }
                }
            }

            var vertices = new List <GlobalVertex>();
            var indices  = new List <int>();

            var uvSetNames = new MStringArray();
            mFnMesh.getUVSetNames(uvSetNames);
            bool[] isUVExportSuccess = new bool[Math.Min(uvSetNames.Count, 2)];
            for (int indexUVSet = 0; indexUVSet < isUVExportSuccess.Length; indexUVSet++)
            {
                isUVExportSuccess[indexUVSet] = true;
            }

            // TODO - color, alpha
            //var hasColor = unskinnedMesh.NumberOfColorVerts > 0;
            //var hasAlpha = unskinnedMesh.GetNumberOfMapVerts(-2) > 0;

            // TODO - Add custom properties
            var optimizeVertices = false; // meshNode.MaxNode.GetBoolProperty("babylonjs_optimizevertices");

            // Compute normals
            var subMeshes = new List <BabylonSubMesh>();
            ExtractGeometry(mFnMesh, vertices, indices, subMeshes, uvSetNames, ref isUVExportSuccess, optimizeVertices);

            if (vertices.Count >= 65536)
            {
                RaiseWarning($"Mesh {babylonMesh.name} has {vertices.Count} vertices. This may prevent your scene to work on low end devices where 32 bits indice are not supported", 2);

                if (!optimizeVertices)
                {
                    RaiseError("You can try to optimize your object using [Try to optimize vertices] option", 2);
                }
            }

            for (int indexUVSet = 0; indexUVSet < isUVExportSuccess.Length; indexUVSet++)
            {
                string uvSetName = uvSetNames[indexUVSet];
                // If at least one vertex is mapped to an UV coordinate but some have failed to be exported
                if (isUVExportSuccess[indexUVSet] == false && mFnMesh.numUVs(uvSetName) > 0)
                {
                    RaiseWarning($"Failed to export UV set named {uvSetName}. Ensure all vertices are mapped to a UV coordinate.", 2);
                }
            }

            RaiseMessage($"{vertices.Count} vertices, {indices.Count / 3} faces", 2);

            // Buffers
            babylonMesh.positions = vertices.SelectMany(v => v.Position).ToArray();
            babylonMesh.normals   = vertices.SelectMany(v => v.Normal).ToArray();
            // TODO - Export colors ?
            //babylonMesh.colors = vertices.SelectMany(v => v.Color).ToArray();

            if (uvSetNames.Count > 0 && isUVExportSuccess[0])
            {
                babylonMesh.uvs = vertices.SelectMany(v => v.UV).ToArray();
            }
            if (uvSetNames.Count > 1 && isUVExportSuccess[1])
            {
                babylonMesh.uvs2 = vertices.SelectMany(v => v.UV2).ToArray();
            }

            babylonMesh.subMeshes = subMeshes.ToArray();

            // Buffers - Indices
            babylonMesh.indices = indices.ToArray();


            babylonScene.MeshesList.Add(babylonMesh);
            RaiseMessage("BabylonExporter.Mesh | done", 2);

            return(babylonMesh);
        }
Ejemplo n.º 2
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);
            }
        }
Ejemplo n.º 3
0
        private BabylonTexture _ExportTexture(MFnDependencyNode textureDependencyNode, BabylonScene babylonScene, List <MFnDependencyNode> textureModifiers = null, bool allowCube = false, bool forceAlpha = false, bool updateCoordinatesMode = false, float amount = 1.0f)
        {
            if (textureDependencyNode == null)
            {
                return(null);
            }

            Print(textureDependencyNode, logRankTexture, "Print _ExportTexture textureDependencyNode");

            // Retreive texture file path
            string sourcePath = getSourcePathFromFileTexture(textureDependencyNode);

            if (sourcePath == null)
            {
                RaiseError("Texture path is not a valid string.", logRankTexture + 1);
                return(null);
            }
            if (sourcePath == "")
            {
                RaiseError("Texture path is missing.", logRankTexture + 1);
                return(null);
            }

            // Check format
            var validImageFormat = GetValidImageFormat(Path.GetExtension(sourcePath));

            if (validImageFormat == null)
            {
                // Image format is not supported by the exporter
                RaiseWarning(string.Format("Format of texture {0} is not supported by the exporter. Consider using a standard image format like jpg or png.", Path.GetFileName(sourcePath)), logRankTexture + 1);
                return(null);
            }
            RaiseVerbose("validImageFormat=" + validImageFormat, logRankTexture + 1);

            var id = textureDependencyNode.uuid().asString();

            var babylonTexture = new BabylonTexture(id)
            {
                name = Path.GetFileNameWithoutExtension(sourcePath) + "." + validImageFormat
            };

            babylonTexture.name = babylonTexture.name.Replace(":", "_");

            // Level
            babylonTexture.level = amount;

            // Alpha
            babylonTexture.hasAlpha = forceAlpha;
            // When fileHasAlpha = true:
            // - texture's format has alpha (png, tif, tga...)
            // - and at least one pixel has an alpha value < 255
            babylonTexture.getAlphaFromRGB = !textureDependencyNode.findPlug("fileHasAlpha").asBool();

            // UVs
            _exportUV(textureDependencyNode, babylonTexture, textureModifiers, updateCoordinatesMode);

            // TODO - Animations

            // Copy texture to output
            if (isBabylonExported)
            {
                var destPath = Path.Combine(babylonScene.OutputPath, babylonTexture.name);

                if (textureModifiers == null || textureModifiers.FindAll(textureModifier => textureModifier.objectProperty.hasFn(MFn.Type.kReverse)).Count % 2 == 0)
                {
                    CopyTexture(sourcePath, destPath);
                }
                else
                {
                    // TODO - Reverse texture
                    CopyTexture(sourcePath, destPath);
                }

                // Is cube
                _exportIsCube(Path.Combine(babylonScene.OutputPath, babylonTexture.name), babylonTexture, allowCube);
            }
            else
            {
                babylonTexture.originalPath = sourcePath;
                babylonTexture.isCube       = false;
            }

            return(babylonTexture);
        }
Ejemplo n.º 4
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="mDagPath">DAG path to the transform above mesh</param>
        /// <param name="babylonScene"></param>
        /// <returns></returns>
        private BabylonNode ExportMesh(MDagPath mDagPath, BabylonScene babylonScene)
        {
            RaiseMessage(mDagPath.partialPathName, 1);

            // Transform above mesh
            mFnTransform = new MFnTransform(mDagPath);

            // Mesh direct child of the transform
            // TODO get the original one rather than the modified?
            MFnMesh mFnMesh = null;

            for (uint i = 0; i < mFnTransform.childCount; i++)
            {
                MObject childObject = mFnTransform.child(i);
                if (childObject.apiType == MFn.Type.kMesh)
                {
                    var _mFnMesh = new MFnMesh(childObject);
                    if (!_mFnMesh.isIntermediateObject)
                    {
                        mFnMesh = _mFnMesh;
                    }
                }
            }
            if (mFnMesh == null)
            {
                RaiseError("No mesh found has child of " + mDagPath.fullPathName);
                return(null);
            }

            RaiseMessage("mFnMesh.fullPathName=" + mFnMesh.fullPathName, 2);

            // --- prints ---
            #region prints

            Action <MFnDagNode> printMFnDagNode = (MFnDagNode mFnDagNode) =>
            {
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.name=" + mFnDagNode.name, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.absoluteName=" + mFnDagNode.absoluteName, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.fullPathName=" + mFnDagNode.fullPathName, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.partialPathName=" + mFnDagNode.partialPathName, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.activeColor=" + mFnDagNode.activeColor.toString(), 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.attributeCount=" + mFnDagNode.attributeCount, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.childCount=" + mFnDagNode.childCount, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.dormantColor=" + mFnDagNode.dormantColor, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.hasUniqueName=" + mFnDagNode.hasUniqueName, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.inUnderWorld=" + mFnDagNode.inUnderWorld, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isDefaultNode=" + mFnDagNode.isDefaultNode, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanceable=" + mFnDagNode.isInstanceable, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanced(true)=" + mFnDagNode.isInstanced(true), 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanced(false)=" + mFnDagNode.isInstanced(false), 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanced()=" + mFnDagNode.isInstanced(), 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.instanceCount(true)=" + mFnDagNode.instanceCount(true), 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.instanceCount(false)=" + mFnDagNode.instanceCount(false), 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isIntermediateObject=" + mFnDagNode.isIntermediateObject, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isShared=" + mFnDagNode.isShared, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.objectColor=" + mFnDagNode.objectColor, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.parentCount=" + mFnDagNode.parentCount, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.parentNamespace=" + mFnDagNode.parentNamespace, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.uuid().asString()=" + mFnDagNode.uuid().asString(), 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.dagRoot().apiType=" + mFnDagNode.dagRoot().apiType, 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.model.equalEqual(mFnDagNode.objectProperty)=" + mFnDagNode.model.equalEqual(mFnDagNode.objectProperty), 3);
                RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.transformationMatrix.toString()=" + mFnDagNode.transformationMatrix.toString(), 3);
            };

            Action <MFnMesh> printMFnMesh = (MFnMesh _mFnMesh) =>
            {
                printMFnDagNode(mFnMesh);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numVertices=" + _mFnMesh.numVertices, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numEdges=" + _mFnMesh.numEdges, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numPolygons=" + _mFnMesh.numPolygons, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numFaceVertices=" + _mFnMesh.numFaceVertices, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numNormals=" + _mFnMesh.numNormals, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numUVSets=" + _mFnMesh.numUVSets, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numUVsProperty=" + _mFnMesh.numUVsProperty, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.displayColors=" + _mFnMesh.displayColors, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numColorSets=" + _mFnMesh.numColorSets, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numColorsProperty=" + _mFnMesh.numColorsProperty, 3);
                RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.currentUVSetName()=" + _mFnMesh.currentUVSetName(), 3);

                var _uvSetNames = new MStringArray();
                mFnMesh.getUVSetNames(_uvSetNames);
                foreach (var uvSetName in _uvSetNames)
                {
                    RaiseVerbose("BabylonExporter.Mesh | uvSetName=" + uvSetName, 3);
                    RaiseVerbose("BabylonExporter.Mesh | mFnMesh.numUVs(uvSetName)=" + mFnMesh.numUVs(uvSetName), 4);
                    MFloatArray us = new MFloatArray();
                    MFloatArray vs = new MFloatArray();
                    mFnMesh.getUVs(us, vs, uvSetName);
                    RaiseVerbose("BabylonExporter.Mesh | us.Count=" + us.Count, 4);
                }
            };

            Action <MFnTransform> printMFnTransform = (MFnTransform _mFnMesh) =>
            {
                printMFnDagNode(mFnMesh);
            };

            RaiseVerbose("BabylonExporter.Mesh | mFnMesh data", 2);
            printMFnMesh(mFnMesh);

            RaiseVerbose("BabylonExporter.Mesh | mFnTransform data", 2);
            printMFnTransform(mFnTransform);

            Print(mFnTransform, 2, "Print ExportMesh mFnTransform");

            Print(mFnMesh, 2, "Print ExportMesh mFnMesh");

            //// Geometry
            //MIntArray triangleCounts = new MIntArray();
            //MIntArray trianglesVertices = new MIntArray();
            //mFnMesh.getTriangles(triangleCounts, trianglesVertices);
            //RaiseVerbose("BabylonExporter.Mesh | triangleCounts.ToArray()=" + triangleCounts.ToArray().toString(), 3);
            //RaiseVerbose("BabylonExporter.Mesh | trianglesVertices.ToArray()=" + trianglesVertices.ToArray().toString(), 3);
            //int[] polygonsVertexCount = new int[mFnMesh.numPolygons];
            //for (int polygonId = 0; polygonId < mFnMesh.numPolygons; polygonId++)
            //{
            //    polygonsVertexCount[polygonId] = mFnMesh.polygonVertexCount(polygonId);
            //}
            //RaiseVerbose("BabylonExporter.Mesh | polygonsVertexCount=" + polygonsVertexCount.toString(), 3);

            ////MFloatPointArray points = new MFloatPointArray();
            ////mFnMesh.getPoints(points);
            ////RaiseVerbose("BabylonExporter.Mesh | points.ToArray()=" + points.ToArray().Select(mFloatPoint => mFloatPoint.toString()), 3);

            ////MFloatVectorArray normals = new MFloatVectorArray();
            ////mFnMesh.getNormals(normals);
            ////RaiseVerbose("BabylonExporter.Mesh | normals.ToArray()=" + normals.ToArray().Select(mFloatPoint => mFloatPoint.toString()), 3);

            //for (int polygonId = 0; polygonId < mFnMesh.numPolygons; polygonId++)
            //{
            //    MIntArray verticesId = new MIntArray();
            //    RaiseVerbose("BabylonExporter.Mesh | polygonId=" + polygonId, 3);

            //    int nbTriangles = triangleCounts[polygonId];
            //    RaiseVerbose("BabylonExporter.Mesh | nbTriangles=" + nbTriangles, 3);

            //    for (int triangleIndex = 0; triangleIndex < triangleCounts[polygonId]; triangleIndex++)
            //    {
            //        RaiseVerbose("BabylonExporter.Mesh | triangleIndex=" + triangleIndex, 3);
            //        int[] triangleVertices = new int[3];
            //        mFnMesh.getPolygonTriangleVertices(polygonId, triangleIndex, triangleVertices);
            //        RaiseVerbose("BabylonExporter.Mesh | triangleVertices=" + triangleVertices.toString(), 3);

            //        foreach (int vertexId in triangleVertices)
            //        {
            //            RaiseVerbose("BabylonExporter.Mesh | vertexId=" + vertexId, 3);
            //            MPoint point = new MPoint();
            //            mFnMesh.getPoint(vertexId, point);
            //            RaiseVerbose("BabylonExporter.Mesh | point=" + point.toString(), 3);

            //            MVector normal = new MVector();
            //            mFnMesh.getFaceVertexNormal(polygonId, vertexId, normal);
            //            RaiseVerbose("BabylonExporter.Mesh | normal=" + normal.toString(), 3);
            //        }
            //    }
            //}

            #endregion

            if (IsMeshExportable(mFnMesh, mDagPath) == false)
            {
                return(null);
            }

            var babylonMesh = new BabylonMesh {
                name       = mFnTransform.name,
                id         = mFnTransform.uuid().asString(),
                visibility = Loader.GetVisibility(mFnTransform.fullPathName)
            };

            // Instance
            // For a mesh with instances, we distinguish between master and instance meshes:
            //      - a master mesh stores all the info of the mesh (transform, hierarchy, animations + vertices, indices, materials, bones...)
            //      - an instance mesh only stores the info of the node (transform, hierarchy, animations)

            // Check if this mesh has already been exported as a master mesh
            BabylonMesh babylonMasterMesh = GetMasterMesh(mFnMesh, babylonMesh);
            if (babylonMasterMesh != null)
            {
                RaiseMessage($"The master mesh {babylonMasterMesh.name} was already exported. This one will be exported as an instance.", 2);

                // Export this node as instance
                var babylonInstanceMesh = new BabylonAbstractMesh {
                    name = mFnTransform.name, id = mFnTransform.uuid().asString()
                };

                //// Add instance to master mesh
                List <BabylonAbstractMesh> instances = babylonMasterMesh.instances != null?babylonMasterMesh.instances.ToList() : new List <BabylonAbstractMesh>();

                instances.Add(babylonInstanceMesh);
                babylonMasterMesh.instances = instances.ToArray();

                // Export transform / hierarchy / animations
                ExportNode(babylonInstanceMesh, mFnTransform, babylonScene);

                // Animations
                ExportNodeAnimation(babylonInstanceMesh, mFnTransform);

                return(babylonInstanceMesh);
            }

            // Position / rotation / scaling / hierarchy
            ExportNode(babylonMesh, mFnTransform, babylonScene);

            // Misc.
            // TODO - Retreive from Maya
            //babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows == 1;
            //babylonMesh.applyFog = meshNode.MaxNode.ApplyAtmospherics == 1;

            if (mFnMesh.numPolygons < 1)
            {
                RaiseError($"Mesh {babylonMesh.name} has no face", 2);
            }

            if (mFnMesh.numVertices < 3)
            {
                RaiseError($"Mesh {babylonMesh.name} has not enough vertices", 2);
            }

            if (mFnMesh.numVertices >= 65536)
            {
                RaiseWarning($"Mesh {babylonMesh.name} has more than 65536 vertices which means that it will require specific WebGL extension to be rendered. This may impact portability of your scene on low end devices.", 2);
            }

            // Animations
            ExportNodeAnimation(babylonMesh, mFnTransform);

            // Material
            MObjectArray shaders = new MObjectArray();
            mFnMesh.getConnectedShaders(0, shaders, new MIntArray());
            if (shaders.Count > 0)
            {
                List <MFnDependencyNode> materials = new List <MFnDependencyNode>();
                foreach (MObject shader in shaders)
                {
                    // Retreive material
                    MFnDependencyNode shadingEngine      = new MFnDependencyNode(shader);
                    MPlug             mPlugSurfaceShader = shadingEngine.findPlug("surfaceShader");
                    MObject           materialObject     = mPlugSurfaceShader.source.node;
                    MFnDependencyNode material           = new MFnDependencyNode(materialObject);

                    materials.Add(material);
                }

                if (shaders.Count == 1)
                {
                    MFnDependencyNode material = materials[0];

                    // Material is referenced by id
                    babylonMesh.materialId = material.uuid().asString();

                    // Register material for export if not already done
                    if (!referencedMaterials.Contains(material, new MFnDependencyNodeEqualityComparer()))
                    {
                        referencedMaterials.Add(material);
                    }
                }
                else
                {
                    // Create a new id for the group of sub materials
                    string uuidMultiMaterial = GetMultimaterialUUID(materials);

                    // Multi material is referenced by id
                    babylonMesh.materialId = uuidMultiMaterial;

                    // Register multi material for export if not already done
                    if (!multiMaterials.ContainsKey(uuidMultiMaterial))
                    {
                        multiMaterials.Add(uuidMultiMaterial, materials);
                    }
                }
            }

            var vertices = new List <GlobalVertex>();
            var indices  = new List <int>();

            var uvSetNames = new MStringArray();
            mFnMesh.getUVSetNames(uvSetNames);
            bool[] isUVExportSuccess = new bool[Math.Min(uvSetNames.Count, 2)];
            for (int indexUVSet = 0; indexUVSet < isUVExportSuccess.Length; indexUVSet++)
            {
                isUVExportSuccess[indexUVSet] = true;
            }

            // skin
            if (_exportSkin)
            {
                mFnSkinCluster = getMFnSkinCluster(mFnMesh);
            }
            int maxNbBones = 0;
            if (mFnSkinCluster != null)
            {
                RaiseMessage($"mFnSkinCluster.name | {mFnSkinCluster.name}", 2);
                Print(mFnSkinCluster, 3, $"Print {mFnSkinCluster.name}");

                // Get the bones dictionary<name, index> => it represents all the bones in the skeleton
                indexByNodeName = GetIndexByFullPathNameDictionary(mFnSkinCluster);

                // Get the joint names that influence this mesh
                allMayaInfluenceNames = GetBoneFullPathName(mFnSkinCluster, mFnTransform);

                // Get the max number of joints acting on a vertex
                int maxNumInfluences = GetMaxInfluence(mFnSkinCluster, mFnTransform, mFnMesh);

                RaiseMessage($"Max influences : {maxNumInfluences}", 2);
                if (maxNumInfluences > 8)
                {
                    RaiseWarning($"Too many bones influences per vertex: {maxNumInfluences}. Babylon.js only support up to 8 bones influences per vertex.", 2);
                    RaiseWarning("The result may not be as expected.", 2);
                }
                maxNbBones = Math.Min(maxNumInfluences, 8);

                if (indexByNodeName != null && allMayaInfluenceNames != null)
                {
                    babylonMesh.skeletonId = GetSkeletonIndex(mFnSkinCluster);
                }
                else
                {
                    mFnSkinCluster = null;
                }
            }
            // Export tangents if option is checked and mesh have tangents
            bool isTangentExportSuccess = _exportTangents;

            // TODO - color, alpha
            //var hasColor = unskinnedMesh.NumberOfColorVerts > 0;
            //var hasAlpha = unskinnedMesh.GetNumberOfMapVerts(-2) > 0;

            // TODO - Add custom properties
            //var optimizeVertices = false; // meshNode.MaxNode.GetBoolProperty("babylonjs_optimizevertices");
            var optimizeVertices = _optimizeVertices; // global option

            // Compute normals
            var subMeshes = new List <BabylonSubMesh>();
            ExtractGeometry(mFnMesh, vertices, indices, subMeshes, uvSetNames, ref isUVExportSuccess, ref isTangentExportSuccess, optimizeVertices);

            if (vertices.Count >= 65536)
            {
                RaiseWarning($"Mesh {babylonMesh.name} has {vertices.Count} vertices. This may prevent your scene to work on low end devices where 32 bits indice are not supported", 2);

                if (!optimizeVertices)
                {
                    RaiseError("You can try to optimize your object using [Try to optimize vertices] option", 2);
                }
            }

            for (int indexUVSet = 0; indexUVSet < isUVExportSuccess.Length; indexUVSet++)
            {
                string uvSetName = uvSetNames[indexUVSet];
                // If at least one vertex is mapped to an UV coordinate but some have failed to be exported
                if (isUVExportSuccess[indexUVSet] == false && mFnMesh.numUVs(uvSetName) > 0)
                {
                    RaiseWarning($"Failed to export UV set named {uvSetName}. Ensure all vertices are mapped to a UV coordinate.", 2);
                }
            }

            RaiseMessage($"{vertices.Count} vertices, {indices.Count / 3} faces", 2);

            // Buffers
            babylonMesh.positions = vertices.SelectMany(v => v.Position).ToArray();
            babylonMesh.normals   = vertices.SelectMany(v => v.Normal).ToArray();

            // export the skin
            if (mFnSkinCluster != null)
            {
                babylonMesh.matricesWeights = vertices.SelectMany(v => v.Weights.ToArray()).ToArray();
                babylonMesh.matricesIndices = vertices.Select(v => v.BonesIndices).ToArray();

                babylonMesh.numBoneInfluencers = maxNbBones;
                if (maxNbBones > 4)
                {
                    babylonMesh.matricesWeightsExtra = vertices.SelectMany(v => v.WeightsExtra != null ? v.WeightsExtra.ToArray() : new[] { 0.0f, 0.0f, 0.0f, 0.0f }).ToArray();
                    babylonMesh.matricesIndicesExtra = vertices.Select(v => v.BonesIndicesExtra).ToArray();
                }
            }

            // Tangent
            if (isTangentExportSuccess)
            {
                babylonMesh.tangents = vertices.SelectMany(v => v.Tangent).ToArray();
            }
            // Color
            string colorSetName;
            mFnMesh.getCurrentColorSetName(out colorSetName);
            if (mFnMesh.numColors(colorSetName) > 0)
            {
                babylonMesh.colors = vertices.SelectMany(v => v.Color).ToArray();
            }
            // UVs
            if (uvSetNames.Count > 0 && isUVExportSuccess[0])
            {
                babylonMesh.uvs = vertices.SelectMany(v => v.UV).ToArray();
            }
            if (uvSetNames.Count > 1 && isUVExportSuccess[1])
            {
                babylonMesh.uvs2 = vertices.SelectMany(v => v.UV2).ToArray();
            }

            babylonMesh.subMeshes = subMeshes.ToArray();

            // Buffers - Indices
            babylonMesh.indices = indices.ToArray();


            babylonScene.MeshesList.Add(babylonMesh);
            RaiseMessage("BabylonExporter.Mesh | done", 2);

            return(babylonMesh);
        }
        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);
            }
        }
Ejemplo n.º 6
0
        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))
            {
                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,
                    ambient  = lambertShader.ambientColor.toArrayRGB(),
                    diffuse  = lambertShader.color.toArrayRGB(),
                    emissive = lambertShader.incandescence.toArrayRGB(),
                    alpha    = 1.0f - lambertShader.transparency[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.backFaceCulling = !stdMat.TwoSided;
                //babylonMaterial.wireframe = stdMat.Wire;

                // Textures
                babylonMaterial.diffuseTexture  = ExportTexture(materialDependencyNode, "color", babylonScene);
                babylonMaterial.ambientTexture  = ExportTexture(materialDependencyNode, "ambientColor", babylonScene);
                babylonMaterial.emissiveTexture = ExportTexture(materialDependencyNode, "incandescence", babylonScene);
                babylonMaterial.bumpTexture     = ExportTexture(materialDependencyNode, "normalCamera", babylonScene);
                // TODO - Convert transparency to opacity?
                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);
            }
            // PBR material
            else if (isPBRMaterial(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();
                for (int i = 0; i < babylonMaterial.emissive.Length; i++)
                {
                    babylonMaterial.emissive[i] *= 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
                bool useMetallicMap  = materialDependencyNode.findPlug("use_metallic_map").asBoolProperty;
                bool useRoughnessMap = materialDependencyNode.findPlug("use_roughness_map").asBoolProperty;
                babylonMaterial.metallicRoughnessTexture = ExportMetallicRoughnessTexture(materialDependencyNode, useMetallicMap, useRoughnessMap, babylonScene, name);

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

                // Ambient occlusion
                string useAOMapAttributeName = "use_ao_map";
                if (materialDependencyNode.hasAttribute(useAOMapAttributeName) && materialDependencyNode.findPlug(useAOMapAttributeName).asBoolProperty)
                {
                    babylonMaterial.occlusionTexture = ExportTexture(materialDependencyNode, "TEX_ao_map", babylonScene);
                }

                // 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);
            }
            else
            {
                RaiseWarning("Unsupported material type '" + materialObject.apiType + "' for material named '" + materialDependencyNode.name + "'", 2);
            }
        }