Skip to content

Commit 7322fc2

Browse files
committed
[MC] First pass at allocation-free meshing for MC
1 parent 1c1a3ee commit 7322fc2

File tree

2 files changed

+151
-0
lines changed

2 files changed

+151
-0
lines changed

src/marching_cubes.jl

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,144 @@ function marching_cubes(sdf::SignedDistanceField{3,ST,FT},
392392
MT(vts,fcs)
393393
end
394394

395+
396+
function marching_cubes(f::Function,
397+
bounds::HyperRectangle,
398+
samples::NTuple{3,Int}=(256,256,256),
399+
iso=0.0,
400+
MT::Type{M}=SimpleMesh{Point{3,Float64},Face{3,Int}},
401+
eps=0.00001) where {ST,FT,M<:AbstractMesh}
402+
nx, ny, nz = samples[1], samples[2], samples[3]
403+
w = widths(bounds)
404+
orig = origin(bounds)
405+
406+
# we subtract one from the length along each axis because
407+
# an NxNxN SDF has N-1 cells on each axis
408+
s = Point{3,Float64}(w[1]/(nx-1), w[2]/(ny-1), w[3]/(nz-1))
409+
410+
# arrays for vertices and faces
411+
vts = Point{3,Float64}[]
412+
fcs = Face{3,Int}[]
413+
mt = max(nx,ny,nz)
414+
sizehint!(vts, mt*mt*6)
415+
sizehint!(fcs, mt*mt*2)
416+
vertlist = Vector{Point{3,Float64}}(undef, 12)
417+
iso_vals = Vector{Float64}(undef,8)
418+
points = Vector{Point{3,Float64}}(undef,8)
419+
@inbounds for xi = 1:nx-1, yi = 1:ny-1, zi = 1:nz-1
420+
421+
422+
if zi == 1
423+
points[1] = Point{3,Float64}(xi-1,yi-1,zi-1) .* s .+ orig
424+
points[2] = Point{3,Float64}(xi,yi-1,zi-1) .* s .+ orig
425+
points[3] = Point{3,Float64}(xi,yi,zi-1) .* s .+ orig
426+
points[4] = Point{3,Float64}(xi-1,yi,zi-1) .* s .+ orig
427+
points[5] = Point{3,Float64}(xi-1,yi-1,zi) .* s .+ orig
428+
points[6] = Point{3,Float64}(xi,yi-1,zi) .* s .+ orig
429+
points[7] = Point{3,Float64}(xi,yi,zi) .* s .+ orig
430+
points[8] = Point{3,Float64}(xi-1,yi,zi) .* s .+ orig
431+
for i = 1:8
432+
iso_vals[i] = f(points[i])
433+
end
434+
else
435+
points[1] = points[5]
436+
points[2] = points[6]
437+
points[3] = points[7]
438+
points[4] = points[8]
439+
points[5] = Point{3,Float64}(xi-1,yi-1,zi) .* s .+ orig
440+
points[6] = Point{3,Float64}(xi,yi-1,zi) .* s .+ orig
441+
points[7] = Point{3,Float64}(xi,yi,zi) .* s .+ orig
442+
points[8] = Point{3,Float64}(xi-1,yi,zi) .* s .+ orig
443+
iso_vals[1] = iso_vals[5]
444+
iso_vals[2] = iso_vals[6]
445+
iso_vals[3] = iso_vals[7]
446+
iso_vals[4] = iso_vals[8]
447+
iso_vals[5] = f(points[5])
448+
iso_vals[6] = f(points[6])
449+
iso_vals[7] = f(points[7])
450+
iso_vals[8] = f(points[8])
451+
end
452+
453+
#Determine the index into the edge table which
454+
#tells us which vertices are inside of the surface
455+
cubeindex = iso_vals[1] < iso ? 1 : 0
456+
iso_vals[2] < iso && (cubeindex |= 2)
457+
iso_vals[3] < iso && (cubeindex |= 4)
458+
iso_vals[4] < iso && (cubeindex |= 8)
459+
iso_vals[5] < iso && (cubeindex |= 16)
460+
iso_vals[6] < iso && (cubeindex |= 32)
461+
iso_vals[7] < iso && (cubeindex |= 64)
462+
iso_vals[8] < iso && (cubeindex |= 128)
463+
cubeindex += 1
464+
465+
# Cube is entirely in/out of the surface
466+
edge_table[cubeindex] == 0 && continue
467+
468+
# Find the vertices where the surface intersects the cube
469+
# TODO this can use the underlying function to find the zero.
470+
# The underlying space is non-linear so there will be error otherwise
471+
if (edge_table[cubeindex] & 1 != 0)
472+
vertlist[1] =
473+
vertex_interp(iso,points[1],points[2],iso_vals[1],iso_vals[2], eps)
474+
end
475+
if (edge_table[cubeindex] & 2 != 0)
476+
vertlist[2] =
477+
vertex_interp(iso,points[2],points[3],iso_vals[2],iso_vals[3], eps)
478+
end
479+
if (edge_table[cubeindex] & 4 != 0)
480+
vertlist[3] =
481+
vertex_interp(iso,points[3],points[4],iso_vals[3],iso_vals[4], eps)
482+
end
483+
if (edge_table[cubeindex] & 8 != 0)
484+
vertlist[4] =
485+
vertex_interp(iso,points[4],points[1],iso_vals[4],iso_vals[1], eps)
486+
end
487+
if (edge_table[cubeindex] & 16 != 0)
488+
vertlist[5] =
489+
vertex_interp(iso,points[5],points[6],iso_vals[5],iso_vals[6], eps)
490+
end
491+
if (edge_table[cubeindex] & 32 != 0)
492+
vertlist[6] =
493+
vertex_interp(iso,points[6],points[7],iso_vals[6],iso_vals[7], eps)
494+
end
495+
if (edge_table[cubeindex] & 64 != 0)
496+
vertlist[7] =
497+
vertex_interp(iso,points[7],points[8],iso_vals[7],iso_vals[8], eps)
498+
end
499+
if (edge_table[cubeindex] & 128 != 0)
500+
vertlist[8] =
501+
vertex_interp(iso,points[8],points[5],iso_vals[8],iso_vals[5], eps)
502+
end
503+
if (edge_table[cubeindex] & 256 != 0)
504+
vertlist[9] =
505+
vertex_interp(iso,points[1],points[5],iso_vals[1],iso_vals[5], eps)
506+
end
507+
if (edge_table[cubeindex] & 512 != 0)
508+
vertlist[10] =
509+
vertex_interp(iso,points[2],points[6],iso_vals[2],iso_vals[6], eps)
510+
end
511+
if (edge_table[cubeindex] & 1024 != 0)
512+
vertlist[11] =
513+
vertex_interp(iso,points[3],points[7],iso_vals[3],iso_vals[7], eps)
514+
end
515+
if (edge_table[cubeindex] & 2048 != 0)
516+
vertlist[12] =
517+
vertex_interp(iso,points[4],points[8],iso_vals[4],iso_vals[8], eps)
518+
end
519+
520+
# Create the triangle
521+
for i = 1:3:13
522+
tri_table[cubeindex][i] == -1 && break
523+
push!(vts, vertlist[tri_table[cubeindex][i ]])
524+
push!(vts, vertlist[tri_table[cubeindex][i+1]])
525+
push!(vts, vertlist[tri_table[cubeindex][i+2]])
526+
fct = length(vts)
527+
push!(fcs, Face{3,Int}(fct, fct-1, fct-2))
528+
end
529+
end
530+
MT(vts,fcs)
531+
end
532+
395533
# Linearly interpolate the position where an isosurface cuts
396534
# an edge between two vertices, each with their own scalar value
397535
function vertex_interp(iso, p1, p2, valp1, valp2, eps = 0.00001)
@@ -415,3 +553,7 @@ MarchingCubes(iso::T1=0.0, eps::T2=1e-3) where {T1, T2} = MarchingCubes{promote_
415553
function (::Type{MT})(df::SignedDistanceField, method::MarchingCubes)::MT where {MT <: AbstractMesh}
416554
marching_cubes(df, method.iso, MT, method.eps)
417555
end
556+
557+
function (::Type{MT})(f::Function, h::HyperRectangle, size::NTuple{3,Int}, method::MarchingCubes)::MT where {MT <: AbstractMesh}
558+
marching_cubes(f, h, size, method.iso, MT, method.eps)
559+
end

test/runtests.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,15 @@ using LinearAlgebra: dot, norm
7676
@test m == m2
7777

7878
end
79+
80+
@testset "marching cubes function" begin
81+
m = marching_cubes(HyperRectangle(Vec(-1,-1,-1.),Vec(2,2,2.)),(21,21,21)) do v
82+
sqrt(sum(dot(v,v))) - 1 # sphere
83+
end
84+
@test length(vertices(m)) == 10968
85+
@test length(faces(m)) == 3656
86+
end
87+
7988
@testset "respect origin" begin
8089
# verify that when we construct a mesh, that mesh:
8190
# a) respects the origin of the SDF

0 commit comments

Comments
 (0)