XelToFab
Guides

Extraction Methods

Choosing between Marching Cubes, Dual Contouring, Surface Nets, and manifold3d

Overview

XelToFab supports four mesh extraction methods, each with different trade-offs for sharp feature preservation, manifold guarantees, and performance:

MethodBackendSharp FeaturesManifoldGPUBest For
mcscikit-imageNoGenerally yesNoDensity fields, general use
dcsdftoolbox (CPU) / isoext (GPU)Yes (QEF)NoYesSDF fields with sharp features
surfnetssdftoolboxNo (smoother)NoNoSDF fields, smooth output
manifoldmanifold3dNoWatertight by designNoFEA/CAD workflows, neural SDFs

Smart defaults

The pipeline auto-selects extraction methods based on field type:

  • SDF fields (field_type="sdf") auto-select dc (Dual Contouring) to leverage gradient information
  • Density fields (field_type="density") keep mc (Marching Cubes) as the default

When DC or Surface Nets are selected, the smoothing parameters are also adjusted:

  • smoothing_method switches to bilateral (feature-preserving)
  • taubin_iterations reduces to 5 (from 20 for MC)

All defaults are overridable by explicitly setting the parameter.

Dual Contouring and sharp features

Comparison: Marching Cubes rounds corners, Dual Contouring preserves sharp edges, Surface Nets produces smooth output

Marching Cubes places vertices on voxel edges via linear interpolation. This means sharp corners and edges are always rounded — MC cannot represent features sharper than a voxel.

Dual Contouring places one vertex per active voxel using a QEF (Quadric Error Function) solver. The QEF finds the point where tangent planes (derived from the SDF gradient) intersect, allowing DC to reconstruct sharp corners and edges that MC misses. This is why DC is the default for SDF fields.

Surface Nets places vertices at the centroid of edge crossings per voxel. This produces smoother results than MC's staircase pattern, but doesn't preserve sharp features like DC.

The smoothing reduction for DC/SurfNets (bilateral, 5 iterations instead of taubin, 20) prevents post-extraction smoothing from eroding the sharp features that DC recovered.

Gradient quality matters

DC's QEF solver relies on SDF gradients to determine vertex placement. The quality of DC output is directly proportional to the quality of the input gradients. With a proper SDF (e.g., from a neural model or compute_implicit_distance), gradients are smooth and consistent, producing clean DC meshes. With a noisy or approximate SDF (e.g., binary voxelization followed by distance transform), gradients are noisy and DC can produce rougher surfaces than MC.

SDF sourceGradient qualityDC result
Neural SDF (DeepSDF, NITO)Smooth (learned)Excellent — DC > MC
Analytic SDF (sphere, box)ExactExcellent — DC preserves sharp features
Mesh → signed distance (VTK)GoodGood — DC ≈ MC
Binary voxelization → EDTPoor (staircase)Poor — DC < MC

The same Dual Contouring algorithm on the same Stanford bunny, with the only difference being SDF quality:

DC with noisy EDT SDF (left, rough surface) vs DC with true signed distance (right, clean surface)

Recommendation: If your SDF comes from a binary voxelization or distance transform, use surfnets (robust averaging) or mc instead of dc. For neural SDF models, see the SDF Functions guide for extraction method selection advice.

GPU acceleration

DC extraction supports GPU acceleration via the isoext backend, which is auto-detected when available. The GPU path uses the same QEF-based Dual Contouring algorithm but runs the grid intersection and vertex placement on the GPU via CUDA.

Benchmark: Sphere SDF extraction (NVIDIA RTX 5080, 16 GB VRAM — results vary by GPU):

GridVoxelsGPU (isoext)CPU (vendored)SpeedupOutput
100^31M0.02s*0.38s11.5K verts
256^316.8M0.08s4.68s61x76.8K verts
500^3125M0.52s35.6s68x293K verts
*At 100^3, GPU overhead (kernel launch, data transfer) exceeds the computation — CPU is faster for small grids. First GPU call includes ~1.5s CUDA context initialization (not shown). Grids above 500^3 may exceed GPU VRAM depending on available memory.

When to use GPU: Grids ≥ 200^3 where DC extraction takes multiple seconds on CPU. Below that, the CPU path is fast enough.

Install the GPU backend:

uv sync --extra cuda  # requires CUDA toolkit + PyTorch with matching CUDA version

If the GPU is unavailable (no CUDA, no isoext, or insufficient VRAM), DC automatically falls back to the CPU implementation with a logged warning:

UserWarning: GPU DC unavailable (...), falling back to CPU

manifold3d

The manifold method uses manifold3d's marching tetrahedra on a body-centered cubic grid. Its output is watertight by design, so the pipeline auto-skips the repair stage.

Trade-offs:

  • Slower for pre-computed grid data (8-88x vs MC) due to Python callback overhead
  • No sharp feature preservation (vertices placed on tetrahedra edges)
  • Excellent for callable neural SDF functions (no callback overhead)

Install:

uv sync --extra manifold

When to use each method

  • Density field from a TO solvermc (default, no action needed)
  • SDF with sharp edges/cornersdc (auto-selected for SDF fields)
  • SDF wanting smooth outputsurfnets
  • Need guaranteed watertight mesh for FEAmanifold
  • Large grid + GPU availabledc (auto-uses GPU when isoext is installed)

Interactive comparison

Stanford bunny extracted with Marching Cubes (left) vs Dual Contouring (right). Rotate and zoom to compare surface quality:

Marching Cubes (15.5K verts)

Dual Contouring (15.4K verts)

Examples

Python API

from xeltofab.state import PipelineParams, PipelineState
from xeltofab.pipeline import process

# SDF field → auto-selects DC with bilateral smoothing
params = PipelineParams(field_type="sdf")
state = PipelineState(field=sdf_array, params=params)
result = process(state)

# Explicit Surface Nets
params = PipelineParams(extraction_method="surfnets")

# Guaranteed watertight output
params = PipelineParams(extraction_method="manifold")

# Override DC smoothing defaults
params = PipelineParams(
    field_type="sdf",
    taubin_iterations=20,       # back to full smoothing
    smoothing_method="taubin",  # override bilateral
)

CLI

# SDF field → auto DC
xtf process neural_sdf.npy -o mesh.stl --field-type sdf

# Explicit extraction method
xtf process field.npy -o mesh.stl --extraction-method dc

# Manifold output
xtf process sdf.npy -o mesh.stl --extraction-method manifold --field-type sdf

# Surface Nets
xtf process sdf.npy -o mesh.stl --extraction-method surfnets --field-type sdf

Outline