I’m developing a Rhino plugin. A command of the plugin takes an input mesh and then generates an output mesh. I receive the following warning for an output mesh, when it’s being added to Rhino document:
Warning: New mesh is not valid.
ON_Mesh.m_F[9203].vi[] has invalid vertex indices.
However, the output mesh is saved as STL and it’s opened without any problem on MeshLab and other 3D software.
I studied this post and made sure that my output mesh has its local coordinate system origin i.e. (0, 0, 0) located at its bounding-box center. But the warning persists:
I have attached the input mesh mesh.stl and the output mesh mesh-hollowed.stl saved as STL:
When I import the output mesh saved as STL, i.e. mesh-hollowed.stl, into the Rhino again, I observe that Rhino considers it as two separate meshes, rather than a single one. It’s a hollowed mesh, so it has an outer surface and an inner surface. But both external and internal surfaces are part of the same mesh, not separate.
I’m not sure why Rhino considers them two separate meshes, but not other software like MeshLab. Maybe this observation is related to the warning I’m receiving.
Thanks You’re right, without that checkmark, the imported STL is just a single mesh:
_Check command
Now, I’m going to try your suggested _Check command on my mesh before adding it to the Rhino document. Let’s see if the warning gets resolved by _Check command…
I tried the Check method of C# Mesh class and enabled all of its checks:
// Run the CheckValidity method on the mesh.
MeshCheckParameters parameters = new MeshCheckParameters();
// Enable all the checks:
parameters.CheckForBadNormals = true;
parameters.CheckForDegenerateFaces = true;
parameters.CheckForDisjointMeshes = true;
parameters.CheckForDuplicateFaces = true;
parameters.CheckForExtremelyShortEdges = true;
parameters.CheckForInvalidNgons = true;
parameters.CheckForNakedEdges = true;
parameters.CheckForNonManifoldEdges = true;
parameters.CheckForRandomFaceNormals = true;
parameters.CheckForSelfIntersection = true;
parameters.CheckForUnusedVertices = true;
// Create TextLog object
Rhino.FileIO.TextLog log = new Rhino.FileIO.TextLog("log--mesh-checks--hollowing.txt");
bool isValid = meshOut.Check(log, ref parameters);
RhinoApp.WriteLine("Is output mesh valid? {0}", isValid);
Surprisingly, this is the prompt message:
And this is the content of the mesh check log file:
Implication
The bool isValid = meshOut.Check(log, ref parameters); statement returns true. It means there is nothing critical wrong with the output mesh. But the same warning is thrown:
Warning: New mesh is not valid.
ON_Mesh.m_F[9203].vi has invalid vertex indices.
And after the output mesh is added to the Rhino document, as soon as I zoom in or out, the mesh disappears.
Maybe a reasonable thing to do is to implement a custom logic for detecting the issue with the vertex indices. I mean a method that is designed to check for this specific type of issue. A separate validation mechanism that is able to detect the issue with the vertex indices.
I added this separate validation mechanism that is able to detect the issue with the vertex indices:
public static bool HasInvalidVertexIndices(Mesh mesh)
{
// Check each face in the mesh
for (int i = 0; i < mesh.Faces.Count; i++)
{
MeshFace face = mesh.Faces[i];
// Check each vertex index in the face
if (face.A < 0 || face.A >= mesh.Vertices.Count ||
face.B < 0 || face.B >= mesh.Vertices.Count ||
face.C < 0 || face.C >= mesh.Vertices.Count)
{
// If any vertex index is invalid, return true
return true;
}
}
// If all face vertex indices are valid, return false
return false;
}
Hi @Megidd, print out F[9203] and see if it references 3 unique vertex indices which you have in your mesh vertex list.
Btw. Check just give you the log of errors, it does not repair anything. You’ll need to do this on your own using eg. CullDegenerateFaces, RemoveZeroAreaFaces and get rid of non manifold edges. The display problem you see after adding the mesh to the doc is related to the errors in the mesh. Either try to avoid generating a bad mesh or repair it.
Mesh has 21 pairs of faces that intersect each other.
This can cause problems if you’re doing mesh boolean operations with it.
Mesh has 6 faces where the face normal differs substantially from the vertex normals.
These normals can cause problems if the ultimate goal is for rendering or boolean purposes.
There are also a bunch of floating vertices not creating any face directly below it. Maybe using MeshVertexList.CullUnused method gets rid of this mess. Don’t be confused by the
face numbers, i’ve splitted the whole mesh into disjoint pieces so the numers are different.
I implemented this method to remove triangles with zero area:
public static int[] RemoveZeroAreaTriangles(int[] indexBuffer, float[] vertexBuffer)
{
// Remove the zero-area triangles from the index buffer
List<int> newIndexBuffer = new List<int>();
for (int i = 0; i < indexBuffer.Length; i += 3)
{
// Get the vertex positions for the three vertices of the triangle
int indexA = indexBuffer[i];
int indexB = indexBuffer[i + 1];
int indexC = indexBuffer[i + 2];
Vector3d vertexA = new Vector3d(vertexBuffer[indexA * 3], vertexBuffer[indexA * 3 + 1], vertexBuffer[indexA * 3 + 2]);
Vector3d vertexB = new Vector3d(vertexBuffer[indexB * 3], vertexBuffer[indexB * 3 + 1], vertexBuffer[indexB * 3 + 2]);
Vector3d vertexC = new Vector3d(vertexBuffer[indexC * 3], vertexBuffer[indexC * 3 + 1], vertexBuffer[indexC * 3 + 2]);
// Compute the cross product of two edges of the triangle
Vector3d edge1 = vertexB - vertexA;
Vector3d edge2 = vertexC - vertexA;
Vector3d crossProduct = Vector3d.CrossProduct(edge1, edge2);
// Check if the cross product has zero length
if (crossProduct.Length < RhinoMath.ZeroTolerance)
{
// If the cross product has zero length, skip this triangle
continue;
}
// Add the indices of the non-zero-area triangle to the new index buffer
newIndexBuffer.Add(indexA);
newIndexBuffer.Add(indexB);
newIndexBuffer.Add(indexC);
}
// Convert the new index buffer to an array and return it
return newIndexBuffer.ToArray();
}
Now, the output mesh is added to the scene just fine:
Showing the cross section by ClippingPlane command: