RhinoCommon Mesh Reduce

Hello guys. Im making a Rhino plugin that needs to reduce a mesh if its above a certain face count. However I’m running into an issue where im getting a nullreferece error on the reduce process. I cannot seem to figure this out. Any help would be appreciated!

DEBUG: docMesh.Reduce(…) threw an exception: NullReferenceException => Object reference not set to an instance of an object.DEBUG: docMesh.Reduce(…) threw an exception: NullReferenceException => Object reference not set to an instance of an object.

  // STEP 7: Reduce if facecount > 5k (document-owned approach, 90% reduction) with extra debug

  foreach (var meshPiece in pieces)
  {
      // Debug: Checking face count of meshPiece
      if (meshPiece == null)
      {
          RhinoApp.WriteLine("DEBUG: meshPiece is null at the start of the loop.");
          continue;
      }
      int faceCountDebug = meshPiece.Faces.Count;
      RhinoApp.WriteLine($"DEBUG: meshPiece is NOT null. It currently has {faceCountDebug} faces.");
      // Now do the threshold check
      RhinoApp.WriteLine($"FaceCount checked {faceCountDebug}");
      if (faceCountDebug <= 5000)
      {
          RhinoApp.WriteLine($"Low Polygon Count (<=5k). We skip reduce. FaceCount is {faceCountDebug}.");
          continue; // skip smaller meshes
      }

      RhinoApp.WriteLine($"Before reduction: {faceCountDebug} faces");

      // 1) Create a doc object from meshPiece
      RhinoApp.WriteLine("DEBUG: About to add meshPiece to doc as a new mesh object.");
      Guid pieceId = doc.Objects.AddMesh(meshPiece);
      RhinoApp.WriteLine($"DEBUG: Added doc object with ID: {pieceId}");
      doc.Views.Redraw();


      // Confirm meshPiece is still not null
      if (meshPiece == null)
      {
          RhinoApp.WriteLine("DEBUG: meshPiece became null AFTER adding to doc (unexpected). Skipping reduce.");
          continue; // or handle differently
      }


      // 2) Retrieve the Mesh from the doc
      RhinoApp.WriteLine($"DEBUG: Attempting to find doc object by pieceId: {pieceId}");
      RhinoObject docObj = doc.Objects.Find(pieceId);
      if (docObj == null)
      {
          RhinoApp.WriteLine("DEBUG: docObj is null => we cannot find the newly added object in doc.");
          continue;
      }
      // Check if docObj.Geometry is a Mesh
      var docMesh = docObj.Geometry as Mesh;
      if (docMesh == null)
      {
          RhinoApp.WriteLine("Warning: docObj is not a Mesh. Possibly invalid geometry?");
          continue;
      }
      RhinoApp.WriteLine($"DEBUG: docMesh is valid. docMesh.Faces.Count = {docMesh.Faces.Count}");

      // 3) (Optional) Clean up docMesh
      RhinoApp.WriteLine("DEBUG: About to run docMesh cleanup steps (Compact, CullDegenerate, etc.)");
      docMesh.Compact();
      docMesh.Faces.CullDegenerateFaces();
      docMesh.Vertices.CombineIdentical(true, true);
      docMesh.Vertices.CullUnused();
      docMesh.Normals.ComputeNormals();
      docMesh.UnifyNormals();
      docMesh.Compact();
      RhinoApp.WriteLine($"DEBUG: docMesh cleanup done. Faces now = {docMesh.Faces.Count}.");

      // 4) Prepare reduce parameters
      int faceCount = docMesh.Faces.Count;
      RhinoApp.WriteLine($"DEBUG: Current docMesh faceCount after cleanup: {faceCount}");
      int targetCount = (int)(faceCount * 0.1); // keep 10%, reduce by 90%
      var reduceParams = new ReduceMeshParameters
      {
          DesiredPolygonCount = targetCount
          // e.g. reduceParams.AllowDistortion = false;
          // reduceParams.Accuracy = 5;
      };
      RhinoApp.WriteLine($"DEBUG: reduceParams set => DesiredPolygonCount = {targetCount}.");

      // Double-check docMesh validity
      if (!docMesh.IsValid || docMesh.Faces.Count == 0)
      {
          RhinoApp.WriteLine($"DEBUG: docMesh invalid or no faces before reduce. docMesh.Faces.Count={docMesh.Faces.Count}");
          continue;
      }

      // 5) Attempt to reduce docMesh
      RhinoApp.WriteLine("DEBUG: About to call docMesh.Reduce(...) now. Cross fingers!");
      bool ok = false;
      try
      {
          ok = docMesh.Reduce(reduceParams);
      }
      catch (Exception ex)
      {
          RhinoApp.WriteLine($"DEBUG: docMesh.Reduce(...) threw an exception: {ex.GetType().Name} => {ex.Message}");
          continue;
      }

      if (!ok)
      {
          RhinoApp.WriteLine("Warning: reduce method returned false or was unable to reduce.");
          continue;
      }

      // 6) Print updated face count
      int newCount = docMesh.Faces.Count;
      RhinoApp.WriteLine($"After reduction: {newCount} faces");

      // 7) Overwrite meshPiece if we want to keep the final geometry in memory
      RhinoApp.WriteLine("DEBUG: About to copy docMesh changes back into meshPiece.");
      try
      {
          meshPiece.CopyFrom(docMesh);
      }
      catch (Exception ex)
      {
          RhinoApp.WriteLine($"DEBUG: meshPiece.CopyFrom(docMesh) threw: {ex.GetType().Name} => {ex.Message}");
      }

      // 8) Optionally, remove from doc if you only want the final pieces[] in memory
      // doc.Objects.Delete(pieceId, true);
      // doc.Views.Redraw();
      RhinoApp.WriteLine("DEBUG: Done with reduce process for this meshPiece.\n");
  }

Dear @Andre7
please edit your post and make sure you script is nicely formatted.
it is horrible to read it at the moment.

add ```csharp at the beginning

// start with ```csharp
// Your C# code here
public class HelloWorld
{
    public static void Main()
    {
        Console.WriteLine("Hello, world!");
    }
}

to find errors within the mesh itself, there is Mesh.Check Method

Also notice that

Guid pieceId = doc.Objects.AddMesh(meshPiece);

pieceId might be Guid.Empty indicating that adding to the doc failed, which might be caused by an incorrect / bad mesh.

… hard to tell without any geometry.

can you reproduce the error in your logic, if you use Mesh.CreateFromSphere() as a starting mesh ?

Hi @Andre7,

It might be easier for us if you just upload the full sample command that exhibits the error. You can attach a .cs file to a reply.

– Dale