/// <summary>
        /// Extract geometry (position, normal, UVs...) for a specific vertex
        /// </summary>
        /// <param name="mFnMesh"></param>
        /// <param name="polygonId">The polygon (face) to examine</param>
        /// <param name="vertexIndexGlobal">The object-relative (mesh-relative/global) vertex index</param>
        /// <param name="vertexIndexLocal">The face-relative (local) vertex id to examine</param>
        /// <param name="hasUV"></param>
        /// <returns></returns>
        private GlobalVertex ExtractVertex(MFnMesh mFnMesh, int polygonId, int vertexIndexGlobal, int vertexIndexLocal, bool hasUV)
        {
            MPoint point = new MPoint();

            mFnMesh.getPoint(vertexIndexGlobal, point);

            MVector normal = new MVector();

            mFnMesh.getFaceVertexNormal(polygonId, vertexIndexGlobal, normal);

            // Switch coordinate system at object level
            point.z  *= -1;
            normal.z *= -1;

            var vertex = new GlobalVertex
            {
                BaseIndex = vertexIndexGlobal,
                Position  = point.toArray(),
                Normal    = normal.toArray()
            };

            // UV
            if (hasUV)
            {
                float u = 0, v = 0;
                mFnMesh.getPolygonUV(polygonId, vertexIndexLocal, ref u, ref v);
                vertex.UV = new float[] { u, v };
            }

            return(vertex);
        }
        /// <summary>
        /// Extract ordered indices on a triangle basis
        /// Extract position and normal of each vertex per face
        /// </summary>
        /// <param name="mFnMesh"></param>
        /// <param name="vertices"></param>
        /// <param name="indices"></param>
        /// <param name="subMeshes"></param>
        /// <param name="optimizeVertices"></param>
        private void ExtractGeometry(MFnMesh mFnMesh, List <GlobalVertex> vertices, List <int> indices, List <BabylonSubMesh> subMeshes, bool optimizeVertices)
        {
            // TODO - Multimaterials: create a BabylonSubMesh per submaterial
            // TODO - optimizeVertices
            MIntArray triangleCounts    = new MIntArray();
            MIntArray trianglesVertices = new MIntArray();

            mFnMesh.getTriangles(triangleCounts, trianglesVertices);

            // For each polygon of this mesh
            for (int polygonId = 0; polygonId < mFnMesh.numPolygons; polygonId++)
            {
                MIntArray verticesId  = new MIntArray();
                int       nbTriangles = triangleCounts[polygonId];

                // For each triangle of this polygon
                for (int triangleIndex = 0; triangleIndex < triangleCounts[polygonId]; triangleIndex++)
                {
                    int[] triangleVertices = new int[3];
                    mFnMesh.getPolygonTriangleVertices(polygonId, triangleIndex, triangleVertices);

                    // Inverse winding order
                    var tmp = triangleVertices[1];
                    triangleVertices[1] = triangleVertices[2];
                    triangleVertices[2] = tmp;

                    // For each vertex of this triangle (3 vertices per triangle)
                    foreach (int vertexId in triangleVertices)
                    {
                        MPoint point = new MPoint();
                        mFnMesh.getPoint(vertexId, point);

                        MVector normal = new MVector();
                        mFnMesh.getFaceVertexNormal(polygonId, vertexId, normal);

                        var vertex = new GlobalVertex
                        {
                            BaseIndex = vertexId,
                            Position  = point.toArray(),
                            Normal    = normal.toArray()
                        };

                        indices.Add(vertices.Count);
                        vertices.Add(vertex);
                    }
                }
            }

            // BabylonSubMesh
            var subMesh = new BabylonSubMesh {
                indexStart = 0, materialIndex = 0
            };

            subMeshes.Add(subMesh);

            subMesh.indexCount    = indices.Count;
            subMesh.verticesStart = 0;
            subMesh.verticesCount = vertices.Count;
        }
Esempio n. 3
0
        /// <summary>
        /// Extract geometry (position, normal, UVs...) for a specific vertex
        /// </summary>
        /// <param name="mFnMesh"></param>
        /// <param name="polygonId">The polygon (face) to examine</param>
        /// <param name="vertexIndexGlobal">The object-relative (mesh-relative/global) vertex index</param>
        /// <param name="vertexIndexLocal">The face-relative (local) vertex id to examine</param>
        /// <param name="uvSetNames"></param>
        /// <param name="isUVExportSuccess"></param>
        /// <returns></returns>
        private GlobalVertex ExtractVertex(MFnMesh mFnMesh, int polygonId, int vertexIndexGlobal, int vertexIndexLocal, MStringArray uvSetNames, ref bool[] isUVExportSuccess)
        {
            MPoint point = new MPoint();

            mFnMesh.getPoint(vertexIndexGlobal, point);

            MVector normal = new MVector();

            mFnMesh.getFaceVertexNormal(polygonId, vertexIndexGlobal, normal);

            // Switch coordinate system at object level
            point.z  *= -1;
            normal.z *= -1;

            var vertex = new GlobalVertex
            {
                BaseIndex = vertexIndexGlobal,
                Position  = point.toArray(),
                Normal    = normal.toArray(),
            };

            // TODO - Export colors ?
            //// Color
            //int colorIndex;
            //string colorSetName;
            //float[] defaultColor = new float[] { 0.5f, 0.5f, 0.5f, 1 };
            //MColor color = new MColor();
            //mFnMesh.getCurrentColorSetName(out colorSetName);

            //if (mFnMesh.numColors(colorSetName) > 0)
            //{
            //    //Get the color index
            //    mFnMesh.getColorIndex(polygonId, vertexIndexLocal, out colorIndex);

            //    //if a color is set
            //    if (colorIndex != -1)
            //    {
            //        mFnMesh.getColor(colorIndex, color);
            //        vertex.Color = color.toArray();
            //    }
            //    //else set the color to the default one of Maya
            //    else
            //    {
            //        vertex.Color = defaultColor;
            //    }
            //}
            //else
            //{
            //    vertex.Color = defaultColor;
            //}

            // UV
            int indexUVSet = 0;

            if (uvSetNames.Count > indexUVSet && isUVExportSuccess[indexUVSet])
            {
                try
                {
                    float u = 0, v = 0;
                    mFnMesh.getPolygonUV(polygonId, vertexIndexLocal, ref u, ref v, uvSetNames[indexUVSet]);
                    vertex.UV = new float[] { u, v };
                }
                catch (Exception e)
                {
                    // An exception is raised when a vertex isn't mapped to an UV coordinate
                    isUVExportSuccess[indexUVSet] = false;
                }
            }
            indexUVSet = 1;
            if (uvSetNames.Count > indexUVSet && isUVExportSuccess[indexUVSet])
            {
                try
                {
                    float u = 0, v = 0;
                    mFnMesh.getPolygonUV(polygonId, vertexIndexLocal, ref u, ref v, uvSetNames[indexUVSet]);
                    vertex.UV2 = new float[] { u, v };
                }
                catch (Exception e)
                {
                    // An exception is raised when a vertex isn't mapped to an UV coordinate
                    isUVExportSuccess[indexUVSet] = false;
                }
            }


            return(vertex);
        }
Esempio n. 4
0
        private BabylonNode ExportMesh(MObject mObject, BabylonScene babylonScene)
        {
            MFnMesh    mFnMesh    = new MFnMesh(mObject);
            MFnDagNode mFnDagNode = new MFnDagNode(mObject);

            var        mObjectParent    = mFnMesh.parent(0);
            MFnDagNode mFnDagNodeParent = new MFnDagNode(mObjectParent);

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

            RaiseVerbose("BabylonExporter.Mesh | mFnMesh data", 2);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.name=" + mFnMesh.name, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.absoluteName=" + mFnMesh.absoluteName, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.fullPathName=" + mFnMesh.fullPathName, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.partialPathName=" + mFnMesh.partialPathName, 3);
            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.activeColor=" + mFnMesh.activeColor.toString(), 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.attributeCount=" + mFnMesh.attributeCount, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.childCount=" + mFnMesh.childCount, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.displayColors=" + mFnMesh.displayColors, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.dormantColor=" + mFnMesh.dormantColor, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.hasUniqueName=" + mFnMesh.hasUniqueName, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.inUnderWorld=" + mFnMesh.inUnderWorld, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.isDefaultNode=" + mFnMesh.isDefaultNode, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.isInstanceable=" + mFnMesh.isInstanceable, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.isInstanced(true)=" + mFnMesh.isInstanced(true), 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.isInstanced(false)=" + mFnMesh.isInstanced(false), 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.isInstanced()=" + mFnMesh.isInstanced(), 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.isIntermediateObject=" + mFnMesh.isIntermediateObject, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.isShared=" + mFnMesh.isShared, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.numColorSets=" + mFnMesh.numColorSets, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.numColorsProperty=" + mFnMesh.numColorsProperty, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.objectColor=" + mFnMesh.objectColor, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.parentCount=" + mFnMesh.parentCount, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.parentNamespace=" + mFnMesh.parentNamespace, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.uuid().asString()=" + mFnMesh.uuid().asString(), 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.dagRoot().apiType=" + mFnMesh.dagRoot().apiType, 3);
            RaiseVerbose("BabylonExporter.Mesh | mFnMesh.model.equalEqual(mFnMesh.objectProperty)=" + mFnMesh.model.equalEqual(mFnMesh.objectProperty), 3);
            RaiseVerbose("BabylonExporter.Mesh | ToString(mFnMesh.transformationMatrix)=" + mFnMesh.transformationMatrix.toString(), 3);
            var transformationMatrix = new MTransformationMatrix(mFnMesh.transformationMatrix);
            RaiseVerbose("BabylonExporter.Mesh | transformationMatrix.getTranslation().toString()=" + transformationMatrix.getTranslation().toString(), 3);
            var transformationMatrixParent = new MTransformationMatrix(mFnDagNodeParent.transformationMatrix);
            RaiseVerbose("BabylonExporter.Mesh | transformationMatrixParent.getTranslation().toString()=" + transformationMatrixParent.getTranslation().toString(), 3);

            // 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) == false)
            {
                return(null);
            }

            RaiseMessage(mFnMesh.name, 1);

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

            // Position / rotation / scaling / hierarchy
            ExportNode(babylonMesh, mFnDagNode, 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);
            }

            // TODO - Material

            var vertices = new List <GlobalVertex>();
            var indices  = new List <int>();
            // TODO - UV, color, alpha
            //var mappingChannels = unskinnedMesh.ActiveMapChannelNum;
            //bool hasUV = false;
            //bool hasUV2 = false;
            //for (int i = 0; i < mappingChannels.Count; ++i)
            //{
            //    var indexer = i;
            //    var channelNum = mappingChannels[indexer];
            //    if (channelNum == 1)
            //    {
            //        hasUV = true;
            //    }
            //    else if (channelNum == 2)
            //    {
            //        hasUV2 = true;
            //    }
            //}
            //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, 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);
                }
            }

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

            babylonMesh.subMeshes = subMeshes.ToArray();

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


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

            return(babylonMesh);
        }
Esempio n. 5
0
        /// <summary>
        /// Extract geometry (position, normal, UVs...) for a specific vertex
        /// </summary>
        /// <param name="mFnMesh"></param>
        /// <param name="polygonId">The polygon (face) to examine</param>
        /// <param name="vertexIndexGlobal">The object-relative (mesh-relative/global) vertex index</param>
        /// <param name="vertexIndexLocal">The face-relative (local) vertex id to examine</param>
        /// <param name="uvSetNames"></param>
        /// <param name="isUVExportSuccess"></param>
        /// <returns></returns>
        private GlobalVertex ExtractVertex(MFnMesh mFnMesh, int polygonId, int vertexIndexGlobal, int vertexIndexLocal, MStringArray uvSetNames, ref bool[] isUVExportSuccess, ref bool isTangentExportSuccess)
        {
            MPoint point = new MPoint();

            mFnMesh.getPoint(vertexIndexGlobal, point);

            MVector normal = new MVector();

            mFnMesh.getFaceVertexNormal(polygonId, vertexIndexGlobal, normal);

            // Switch coordinate system at object level
            point.z  *= -1;
            normal.z *= -1;

            var vertex = new GlobalVertex
            {
                BaseIndex = vertexIndexGlobal,
                Position  = point.toArray(),
                Normal    = normal.toArray()
            };

            // Tangent
            if (isTangentExportSuccess)
            {
                try
                {
                    MVector tangent = new MVector();
                    mFnMesh.getFaceVertexTangent(polygonId, vertexIndexGlobal, tangent);

                    // Switch coordinate system at object level
                    tangent.z *= -1;

                    int  tangentId            = mFnMesh.getTangentId(polygonId, vertexIndexGlobal);
                    bool isRightHandedTangent = mFnMesh.isRightHandedTangent(tangentId);

                    // Invert W to switch to left handed system
                    vertex.Tangent = new float[] { (float)tangent.x, (float)tangent.y, (float)tangent.z, isRightHandedTangent ? -1 : 1 };
                }
                catch
                {
                    // Exception raised when mesh don't have tangents
                    isTangentExportSuccess = false;
                }
            }

            // Color
            int    colorIndex;
            string colorSetName;

            float[] defaultColor = new float[] { 1, 1, 1, 0 };
            MColor  color        = new MColor();

            mFnMesh.getCurrentColorSetName(out colorSetName);

            if (mFnMesh.numColors(colorSetName) > 0)
            {
                //Get the color index
                mFnMesh.getColorIndex(polygonId, vertexIndexLocal, out colorIndex);

                //if a color is set
                if (colorIndex != -1)
                {
                    mFnMesh.getColor(colorIndex, color);
                    vertex.Color = color.toArray();
                }
                //else set the default color
                else
                {
                    vertex.Color = defaultColor;
                }
            }

            // UV
            int indexUVSet = 0;

            if (uvSetNames.Count > indexUVSet && isUVExportSuccess[indexUVSet])
            {
                try
                {
                    float u = 0, v = 0;
                    mFnMesh.getPolygonUV(polygonId, vertexIndexLocal, ref u, ref v, uvSetNames[indexUVSet]);
                    vertex.UV = new float[] { u, v };
                }
                catch
                {
                    // An exception is raised when a vertex isn't mapped to an UV coordinate
                    isUVExportSuccess[indexUVSet] = false;
                }
            }
            indexUVSet = 1;
            if (uvSetNames.Count > indexUVSet && isUVExportSuccess[indexUVSet])
            {
                try
                {
                    float u = 0, v = 0;
                    mFnMesh.getPolygonUV(polygonId, vertexIndexLocal, ref u, ref v, uvSetNames[indexUVSet]);
                    vertex.UV2 = new float[] { u, v };
                }
                catch
                {
                    // An exception is raised when a vertex isn't mapped to an UV coordinate
                    isUVExportSuccess[indexUVSet] = false;
                }
            }

            // skin
            if (mFnSkinCluster != null)
            {
                // Filter null weights
                Dictionary <int, double> weightByInfluenceIndex = new Dictionary <int, double>(); // contains the influence indices with weight > 0

                // Export Weights and BonesIndices for the vertex
                // Get the weight values of all the influences for this vertex
                allMayaInfluenceWeights = new MDoubleArray();
                MGlobal.executeCommand($"skinPercent -query -value {mFnSkinCluster.name} {mFnTransform.name}.vtx[{vertexIndexGlobal}]",
                                       allMayaInfluenceWeights);
                allMayaInfluenceWeights.get(out double[] allInfluenceWeights);

                for (int influenceIndex = 0; influenceIndex < allInfluenceWeights.Length; influenceIndex++)
                {
                    double weight = allInfluenceWeights[influenceIndex];

                    if (weight > 0)
                    {
                        try
                        {
                            // add indice and weight in the local lists
                            weightByInfluenceIndex.Add(indexByNodeName[allMayaInfluenceNames[influenceIndex]], weight);
                        }
                        catch (Exception e)
                        {
                            RaiseError(e.Message, 2);
                            RaiseError(e.StackTrace, 3);
                        }
                    }
                }

                // normalize weights => Sum weights == 1
                Normalize(ref weightByInfluenceIndex);

                // decreasing sort
                OrderByDescending(ref weightByInfluenceIndex);

                int   bonesCount = indexByNodeName.Count; // number total of bones/influences for the mesh
                float weight0    = 0;
                float weight1    = 0;
                float weight2    = 0;
                float weight3    = 0;
                int   bone0      = bonesCount;
                int   bone1      = bonesCount;
                int   bone2      = bonesCount;
                int   bone3      = bonesCount;
                int   nbBones    = weightByInfluenceIndex.Count; // number of bones/influences for this vertex

                if (nbBones == 0)
                {
                    weight0 = 1.0f;
                    bone0   = bonesCount;
                }

                if (nbBones > 0)
                {
                    bone0   = weightByInfluenceIndex.ElementAt(0).Key;
                    weight0 = (float)weightByInfluenceIndex.ElementAt(0).Value;

                    if (nbBones > 1)
                    {
                        bone1   = weightByInfluenceIndex.ElementAt(1).Key;
                        weight1 = (float)weightByInfluenceIndex.ElementAt(1).Value;

                        if (nbBones > 2)
                        {
                            bone2   = weightByInfluenceIndex.ElementAt(2).Key;
                            weight2 = (float)weightByInfluenceIndex.ElementAt(2).Value;

                            if (nbBones > 3)
                            {
                                bone3   = weightByInfluenceIndex.ElementAt(3).Key;
                                weight3 = (float)weightByInfluenceIndex.ElementAt(3).Value;
                            }
                        }
                    }
                }

                float[] weights = { weight0, weight1, weight2, weight3 };
                vertex.Weights      = weights;
                vertex.BonesIndices = (bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0;

                if (nbBones > 4)
                {
                    bone0   = weightByInfluenceIndex.ElementAt(4).Key;
                    weight0 = (float)weightByInfluenceIndex.ElementAt(4).Value;
                    weight1 = 0;
                    weight2 = 0;
                    weight3 = 0;

                    if (nbBones > 5)
                    {
                        bone1   = weightByInfluenceIndex.ElementAt(5).Key;
                        weight1 = (float)weightByInfluenceIndex.ElementAt(4).Value;

                        if (nbBones > 6)
                        {
                            bone2   = weightByInfluenceIndex.ElementAt(6).Key;
                            weight2 = (float)weightByInfluenceIndex.ElementAt(4).Value;

                            if (nbBones > 7)
                            {
                                bone3   = weightByInfluenceIndex.ElementAt(7).Key;
                                weight3 = (float)weightByInfluenceIndex.ElementAt(7).Value;
                            }
                        }
                    }

                    float[] weightsExtra = { weight0, weight1, weight2, weight3 };
                    vertex.WeightsExtra      = weightsExtra;
                    vertex.BonesIndicesExtra = (bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0;
                }
            }
            return(vertex);
        }
        /// <summary>
        /// Extract geometry (position, normal, UVs...) for a specific vertex
        /// </summary>
        /// <param name="mFnMesh"></param>
        /// <param name="polygonId">The polygon (face) to examine</param>
        /// <param name="vertexIndexGlobal">The object-relative (mesh-relative/global) vertex index</param>
        /// <param name="vertexIndexLocal">The face-relative (local) vertex id to examine</param>
        /// <param name="uvSetNames"></param>
        /// <param name="isUVExportSuccess"></param>
        /// <returns></returns>
        private GlobalVertex ExtractVertex(MFnMesh mFnMesh, int polygonId, int vertexIndexGlobal, int vertexIndexLocal, MStringArray uvSetNames, ref bool[] isUVExportSuccess)
        {
            MPoint point = new MPoint();

            mFnMesh.getPoint(vertexIndexGlobal, point);

            MVector normal = new MVector();

            mFnMesh.getFaceVertexNormal(polygonId, vertexIndexGlobal, normal);

            MVector tangent = new MVector();

            mFnMesh.getFaceVertexTangent(polygonId, vertexIndexGlobal, tangent);

            // Switch coordinate system at object level
            point.z   *= -1;
            normal.z  *= -1;
            tangent.z *= -1;

            float[] tangentVec4 = new float[] { (float)tangent.x, (float)tangent.y, (float)tangent.z, -1 };

            var vertex = new GlobalVertex
            {
                BaseIndex = vertexIndexGlobal,
                Position  = point.toArray(),
                Normal    = normal.toArray(),
                Tangent   = tangentVec4,
            };

            // Color
            int    colorIndex;
            string colorSetName;

            float[] defaultColor = new float[] { 1, 1, 1, 0 };
            MColor  color        = new MColor();

            mFnMesh.getCurrentColorSetName(out colorSetName);

            if (mFnMesh.numColors(colorSetName) > 0)
            {
                //Get the color index
                mFnMesh.getColorIndex(polygonId, vertexIndexLocal, out colorIndex);

                //if a color is set
                if (colorIndex != -1)
                {
                    mFnMesh.getColor(colorIndex, color);
                    vertex.Color = color.toArray();
                }
                //else set the default color
                else
                {
                    vertex.Color = defaultColor;
                }
            }

            // UV
            int indexUVSet = 0;

            if (uvSetNames.Count > indexUVSet && isUVExportSuccess[indexUVSet])
            {
                try
                {
                    float u = 0, v = 0;
                    mFnMesh.getPolygonUV(polygonId, vertexIndexLocal, ref u, ref v, uvSetNames[indexUVSet]);
                    vertex.UV = new float[] { u, v };
                }
                catch (Exception e)
                {
                    // An exception is raised when a vertex isn't mapped to an UV coordinate
                    isUVExportSuccess[indexUVSet] = false;
                }
            }
            indexUVSet = 1;
            if (uvSetNames.Count > indexUVSet && isUVExportSuccess[indexUVSet])
            {
                try
                {
                    float u = 0, v = 0;
                    mFnMesh.getPolygonUV(polygonId, vertexIndexLocal, ref u, ref v, uvSetNames[indexUVSet]);
                    vertex.UV2 = new float[] { u, v };
                }
                catch (Exception e)
                {
                    // An exception is raised when a vertex isn't mapped to an UV coordinate
                    isUVExportSuccess[indexUVSet] = false;
                }
            }

            return(vertex);
        }