Table of Contents

Meshing

Renderers, STL and glTF files, and FEM need triangles. BRepMesh_IncrementalMesh triangulates each face and stores the result in the shape; BRep_Tool.Triangulation reads it back.

flowchart LR
    shape[TopoDS_Shape] -->|BRepMesh_IncrementalMesh| stored["a Poly_Triangulation<br/>on each face"]
    stored -->|"BRep_Tool.Triangulation(face, location)"| read["nodes + triangles<br/>in the face's coordinates"]
    read -->|"location, orientation"| buffers["vertex and index buffers"]
    stored --> stl["StlAPI_Writer"]
    stored --> gltf["RWGltf_CafWriter"]
    stored --> view["AIS_Shape<br/><i>shows it, meshes if missing</i>"]

Triangulating

// 0.1: how far a triangle may stray from the surface; 0.5 rad: how far its normals may turn
var mesh = new BRepMesh_IncrementalMesh(sphere, 0.1, false, 0.5, true); // true: in parallel
var done = mesh.IsDone();
Parameter Means
linear deflection the largest distance between a triangle and the surface, in model units
relative true: the deflection is a fraction of each edge's size
angular deflection the largest angle, in radians, between neighbouring triangles' normals on curved faces
parallel mesh faces on all cores

The deflection trades accuracy for size: halving it roughly doubles the triangles on curved faces. For rendering, a deflection of about 0.1% of the model's size looks smooth.

Meshing again

new BRepMesh_IncrementalMesh(sphere, 1.0);
var coarse = Triangles(sphere);

// the triangulation stays on the shape: meshing again with a finer deflection refines it,
// a coarser one keeps what's there. BRepTools.Clean drops it.
new BRepMesh_IncrementalMesh(sphere, 0.01);
var fine = Triangles(sphere);

BRepTools.Clean(sphere);
var cleaned = Triangles(sphere); // 0

Reading the triangles

Each face's nodes are in the face's own coordinates: apply the location BRep_Tool.Triangulation writes. A reversed face points its triangles the other way: swap two indices to keep normals outward.

// one vertex and index list for the whole shape, as a renderer wants it
var vertices = new List<gp_Pnt>();
var indices = new List<int>();
foreach (var item in new TopExp_Explorer(shape, TopAbs_ShapeEnum.TopAbs_FACE))
{
  var face = TopoDS.Face(item);
  var location = new TopLoc_Location();
  var triangulation =
    BRep_Tool.Triangulation(face, location); // writes the face's location into it
  if (triangulation is null)
  {
    continue;
  }

  // the nodes are in the face's own coordinates: move them where the face is
  var trsf = location.Transformation();
  var offset = vertices.Count;
  foreach (var node in triangulation.NodesToArray()) // one native call for all nodes
  {
    vertices.Add(node.Transformed(trsf));
  }

  // a reversed face's triangles turn the other way
  var reversed = face.Orientation() == TopAbs_Orientation.TopAbs_REVERSED;
  foreach (var triangle in triangulation.TrianglesToArray())
  {
    int n1 = 0, n2 = 0, n3 = 0;
    triangle.Get(ref n1, ref n2, ref n3); // node numbers from 1
    indices.Add(offset + n1 - 1);
    indices.Add(offset + (reversed ? n3 : n2) - 1);
    indices.Add(offset + (reversed ? n2 : n3) - 1);
  }
}

NodesToArray() and TrianglesToArray() copy everything in one native call; Node(i) and Triangle(i) cost a call each. With BRepLib_ToolTriangulatedShape.ComputeNormals(face, triangulation) the triangulation gets normals, read with Normal(i).

Note

Edges have their own polygons on the triangulation (BRep_Tool.PolygonOnTriangulation), which line up with the faces' nodes: use them to draw edges that match the shading.

Writing triangles

STL: see Files. glTF and OBJ write XCAF documents, with colors and names: see Files.