Construct mesh export

SDF to triangle mesh export pipeline

Overview

The mesh export pipeline converts data-driven SDF scenes into triangle mesh files (STL, OBJ, PLY, VTK). The scene's field — evaluated through the libeval Native JIT — is the single source of truth: extraction, post-processing and the quality metrics all consume the same evaluator, so the exported mesh can be measured exactly against what was modelled.

Feature-preserving extraction and the post-process pipeline are specified in the construct-mesh-quality internal spec (content/doc/internal/specs/construct-mesh-quality/); this page is the reference summary.

Architecture

Scene snapshot (QVector<SDFNode>)
    -> SdfSceneGraphBuilder -> eval::Graph
    -> eval Backend::Native JIT (CPUFieldEvaluator; GPU batch path for chunks)
    -> Φ(x)  and  āˆ‡Ī¦(x)   (IFieldEvaluator::evaluateWithGradient — analytic
                            where the op tree is capable, FD-tetrahedron floor
                            everywhere else)
    -> extractor: Marching Cubes | Dual Contouring | adaptive octree DC
    -> post-process chain (optional): weld → sdfProject → coplanarMerge → qemDecimate
    -> mesh::Mesh<float> -> mesh::io::IORegistry -> STL/OBJ/PLY/VTK

Extractor family

Selected per export via ExportRequest.extractor (UI: the export dialog's Quality group):

Extractor Behaviour
MarchingCubes Legacy baseline. Vertices constrained to grid edges — sharp features alias into a staircase at any finite resolution. Chunked + parallel.
DualContouring Uniform-grid Ju-2002 DC: one vertex per sign-change cell, positioned by a robust QEF over the cell's Hermite (Φ, āˆ‡Ī¦) samples; bisection-refined edge crossings; exact chunk-seam welds via a shared global grid frame.
DualContouringOctree One global octree over the domain with a conservative manifold-preserving collapse gate: triangles concentrate where the surface has detail (a box collapses to its exact 12-triangle mesh) and the output is guaranteed closed ∧ manifold ∧ orientable on the whole test corpus.

Post-process stages

Declared as an ExportRequest.postprocess string list (also the bench definition's postprocess array); executed at the single buildAndSaveMesh hook. All stages preserve the watertight-manifold invariant — mutating stages re-validate and restore their input on regression.

Stage Does
weld[(tol)] Spatial-hash vertex weld of the extractor's triangle soup (auto-prepended when absent).
sdfProject[(iters)] Newton-snaps vertices onto the exact zero level set x ← x āˆ’ Ī¦Ā·āˆ‡Ī¦/ā€–āˆ‡Ī¦ā€–Ā² with tangential relaxation; crease vertices move only along their feature line, corners are pinned.
coplanarMerge[(angleDeg)] Region-grows near-coplanar patches and retriangulates each from its boundary loop — flat caps become fans.
qemDecimate[(ratio)] Feature-aware quadric decimation: crease constraint planes + corner pinning keep the sharp edges extraction captured; link-condition collapses only.

Quality metrics (SDF ground truth)

construct::bench::computeMeshMetrics measures a mesh against the same field it was extracted from — no reference mesh:

  • Surface error — |Φ| at vertices, edge midpoints and face centroids (mean/p95/max).
  • Feature error — surface error restricted to the field-tagged high-curvature band (the cylinder-rim metric MC fails).
  • Normal error — angle between face normals and āˆ‡Ī¦.
  • Topology — closed ∧ manifold ∧ orientable + boundary-edge count (HalfedgeMesh predicates); this triple is the export quality gate.
  • Triangle quality — min-angle statistics.

The MeshExportBench runner records these per scene Ɨ pipeline definition into a lab::TestDB results database; browse it with the Mesh Lab activity or the mesh.bench.* OPAL tasks.

Source files

construct/eval/CPUFieldEvaluator.*      — libeval Native evaluator (+ analytic āˆ‡Ī¦)
construct/eval/AnalyticGradient.*       — §AR-2.2 capability-gated analytic gradients
construct/mesh/MarchingCubes.*          — baseline extractor
construct/mesh/DualContouring.*         — uniform-grid DC + QEF
construct/mesh/Octree.*                 — adaptive octree DC + manifold gate
construct/mesh/Qef.*                    — robust QEF solver
construct/export/ExportPipeline.*       — async orchestrator + post-process hook
construct/export/PostProcess.*          — §BM-2 spec-string parser/runner
construct/bench/MeshExportBench.*       — corpus Ɨ definitions bench runner
construct/bench/MeshMetrics.*           — SDF-ground-truth metrics
construct/bench/MeshBenchWorker.*       — overnight batch worker, inputs and outputs by hash
construct/bench/MeshBenchJob.*          — submits a bench set and writes its outputs back
construct/ui/MeshExportConfigDialog.*   — export dialog (Quality group)
construct/ui/MeshLabActivity.*          — results browser
libmesh/mesh/process/…                  — weld / SdfProjection / CoplanarMerge / QEM

Tests

  • testMeshExport, testConstructCli — pipeline end-to-end.
  • testQef, testManifoldDC — DC extraction + the watertight-manifold corpus invariant.
  • testSdfProjection, testFeatureQem — post-process stages + the post-decimate invariant.
  • testMeshMetrics, testMeshExportBench — metrics and the per-cell gates.
  • construct-bench mesh-full --scenes <dir> --definitions <dir> — the whole corpus x definitions matrix. It was testMeshBenchFull, which asserted only that the bench ran and had to write QTEST_FUNCTION_TIMEOUT into its own environment to outlive QtTest's watchdog (no-environment-variables row T35).
  • testMeshLab — batch worker → results DB → browser → OPAL tasks.
In this section
Topics
reference construct mesh export