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