Skip to content

Examples

Guangyan Cai edited this page Jan 5, 2025 · 2 revisions

Examples

By default, isoext uses pytorch cuda tensors for most inputs and outputs. Read the API reference for more details.

A Basic Example

This example demonstrates how to extract the isosurface of a complex shape using marching cubes and dual contouring.

import isoext
from isoext.sdf import *

# Create grid
grid = isoext.UniformGrid([256, 256, 256])

# Create composite SDF shape - a sphere with three orthogonal toroidal holes
torus_a = TorusSDF(R=0.75, r=0.15)  # Base torus in xy plane
torus_b = RotationOp(sdf=torus_a, axis=[1, 0, 0], angle=90)  # Rotated to xz plane
torus_c = RotationOp(sdf=torus_a, axis=[0, 1, 0], angle=90)  # Rotated to yz plane
sphere_a = SphereSDF(radius=0.75)
sdf = IntersectionOp([sphere_a, NegationOp(UnionOp([torus_a, torus_b, torus_c]))])

# Evaluate SDF and extract isosurface
points = grid.get_points() # sample locations
sdf_v = sdf(points) # evaluate SDF at sample locations
grid.set_values(sdf_v) # store SDF values in grid

# Run marching cubes
print("Running marching cubes")
v, f = isoext.marching_cubes(grid)
print("Writing obj")
isoext.write_obj("mc.obj", v, f)
print("Done")

# Run dual contouring
print("Running dual contouring")
# Dual contouring requires a set of intersection points and normals to constrcut the QEF
its = isoext.get_intersection(grid)
points = its.get_points()
normals = get_sdf_normal(sdf, points)
its.set_normals(normals)
v, f = isoext.dual_contouring(grid, its)
print("Writing obj")
isoext.write_obj("dc.obj", v, f)
print("Done")

Notes:

  • You can specify the aabb bounds as follows:
grid = isoext.UniformGrid([256, 256, 256], aabb_min=[-1, -1, -1], aabb_max=[1, 1, 1])
  • You can use a neural network to generate the SDF values, or use a pre-computed SDF grid. If you have the grid of values already, you can skip the grid.get_points() step and directly call grid.set_values().
  • Set the isolevel and method for marching cubes as follows:
    • isoext.marching_cubes(grid, level=0.0, method="nagae")
    • See Marching Cubes for more details.
  • Dual contouring requires a set of intersection points and normals to constrcut the QEF. You can set the isolevel, regularization weight, and SVD tolerance (for QEF solver) for dual contouring as follows:
    • isoext.dual_contouring(grid, its, level=0.0, reg_weight=1e-2, svd_tol=1e-6)
    • See Dual Contouring for more details.

Use SparseGrid for saving memory

import isoext
from isoext.sdf import *

# Create grid
grid = isoext.SparseGrid([512, 512, 512])

# Create composite SDF shape - a sphere with three orthogonal toroidal holes
torus_a = TorusSDF(R=0.75, r=0.15)  # Base torus in xy plane
torus_b = RotationOp(sdf=torus_a, axis=[1, 0, 0], angle=90)  # Rotated to xz plane
torus_c = RotationOp(sdf=torus_a, axis=[0, 1, 0], angle=90)  # Rotated to yz plane
sphere_a = SphereSDF(radius=0.75)
sdf = IntersectionOp([sphere_a, NegationOp(UnionOp([torus_a, torus_b, torus_c]))])

# Find active cells for sparse grid
max_chunk_size = 128**3
cell_indices_chunks = grid.get_potential_cell_indices(max_chunk_size)
active_cell_indices = []
for cell_indices in cell_indices_chunks:
    points = grid.get_points_by_cell_indices(cell_indices)
    values = sdf(points)
    # Filter out inactive cells
    filtered_cell_indices = grid.filter_cell_indices(cell_indices, values, level=0.0)
    if filtered_cell_indices is not None:
        active_cell_indices.append(filtered_cell_indices)
# Update grid with active cells
active_cell_indices = torch.cat(active_cell_indices)
grid.add_cells(active_cell_indices)

# Evaluate SDF and extract isosurface
sdf_v = sdf(grid.get_points())
grid.set_values(sdf_v)

# Run marching cubes
print("Running marching cubes")
v, f = isoext.marching_cubes(grid, level=0.0)
print("Writing obj")
isoext.write_obj("mc_sparse.obj", v, f)
print("Done")

# Run dual contouring
print("Running dual contouring")
its = isoext.get_intersection(grid)
points = its.get_points()
normals = get_sdf_normal(sdf, points)
its.set_normals(normals)
v, f = isoext.dual_contouring(grid, its, level=0.0)
print("Writing obj")
isoext.write_obj("dc_sparse.obj", v, f)
print("Done")

Clone this wiki locally