Skip to content

Commit f1412a4

Browse files
authored
Merge pull request #25 from JuliaGeometry/sjk/perf
[WIP] non-SDF Meshing
2 parents 1c1a3ee + 70bc91c commit f1412a4

File tree

4 files changed

+420
-84
lines changed

4 files changed

+420
-84
lines changed

README.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,38 @@
33
[![Build Status](https://travis-ci.org/JuliaGeometry/Meshing.jl.svg)](https://travis-ci.org/JuliaGeometry/Meshing.jl)
44
[![codecov.io](http://codecov.io/github/JuliaGeometry/Meshing.jl/coverage.svg?branch=master)](http://codecov.io/github/JuliaGeometry/Meshing.jl?branch=master)
55

6-
This package provides meshing algorithms for use on distance fields.
6+
This package provides a comprehensive suite of meshing algorithms for use on distance fields.
77

8-
Including:
8+
Algorithms included:
99
* [Marching Tetrahedra](https://en.wikipedia.org/wiki/Marching_tetrahedra)
1010
* [Marching Cubes](https://en.wikipedia.org/wiki/Marching_cubes)
1111
* [Naive Surface Nets](https://0fps.net/2012/07/12/smooth-voxel-terrain-part-2/)
1212

1313
## Interface
1414

15-
This package is tightly integrated with [GeometryTypes.jl](https://github.com/JuliaGeometry/GeometryTypes.jl).
15+
This package inherits the [mesh types](http://juliageometry.github.io/GeometryTypes.jl/latest/types.html#Meshes-1)
16+
from [GeometryTypes.jl](https://github.com/JuliaGeometry/GeometryTypes.jl).
1617

17-
All algorithms operate on `SignedDistanceField` and output a concrete `AbstractMesh`. For example:
18+
The algorithms operate on a `Function` or a `SignedDistanceField` and output a concrete `AbstractMesh`. For example:
1819

1920
```
2021
using Meshing
2122
using GeometryTypes
2223
using LinearAlgebra: dot, norm
2324
using FileIO
2425
25-
# generate an SDF of a sphere
26-
sdf_sphere = SignedDistanceField(HyperRectangle(Vec(-1,-1,-1.),Vec(2,2,2.))) do v
27-
sqrt(sum(dot(v,v))) - 1 # sphere
26+
# Mesh an equation of sphere in the Axis-Aligned Bounding box starting
27+
# at -1,-1,-1 and widths of 2,2,2
28+
m = GLNormalMesh(HyperRectangle(Vec(-1,-1,-1.), Vec(2,2,2.)), MarchingCubes()) do v
29+
sqrt(sum(dot(v,v))) - 1
2830
end
2931
30-
m = GLNormalMesh(sdf_sphere, MarchingCubes())
31-
32+
# save the Sphere as a PLY file
3233
save("sphere.ply",m)
3334
```
3435

35-
The general API is ``(::Type{MT})(sdf::SignedDistanceField, method::AbstractMeshingAlgorithm) where {MT <: AbstractMesh}``
36+
The general API is: ```(::Type{MT})(sdf::Function, method::AbstractMeshingAlgorithm) where {MT <: AbstractMesh}``` or ```(::Type{MT})(sdf::SignedDistanceField, method::AbstractMeshingAlgorithm) where {MT <: AbstractMesh}```
37+
3638

3739
For a full listing of concrete `AbstractMesh` types see [GeometryTypes.jl mesh documentation](http://juliageometry.github.io/GeometryTypes.jl/latest/types.html#Meshes-1).
3840

src/marching_cubes.jl

Lines changed: 171 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -305,21 +305,6 @@ function marching_cubes(sdf::SignedDistanceField{3,ST,FT},
305305
vertlist = Vector{Point{3,Float64}}(undef, 12)
306306
@inbounds for xi = 1:nx-1, yi = 1:ny-1, zi = 1:nz-1
307307

308-
#Determine the index into the edge table which
309-
#tells us which vertices are inside of the surface
310-
cubeindex = sdf[xi,yi,zi] < iso ? 1 : 0
311-
sdf[xi+1,yi,zi] < iso && (cubeindex |= 2)
312-
sdf[xi+1,yi+1,zi] < iso && (cubeindex |= 4)
313-
sdf[xi,yi+1,zi] < iso && (cubeindex |= 8)
314-
sdf[xi,yi,zi+1] < iso && (cubeindex |= 16)
315-
sdf[xi+1,yi,zi+1] < iso && (cubeindex |= 32)
316-
sdf[xi+1,yi+1,zi+1] < iso && (cubeindex |= 64)
317-
sdf[xi,yi+1,zi+1] < iso && (cubeindex |= 128)
318-
cubeindex += 1
319-
320-
# Cube is entirely in/out of the surface
321-
edge_table[cubeindex] == 0 && continue
322-
323308
points = (Point{3,Float64}(xi-1,yi-1,zi-1) .* s .+ orig,
324309
Point{3,Float64}(xi,yi-1,zi-1) .* s .+ orig,
325310
Point{3,Float64}(xi,yi,zi-1) .* s .+ orig,
@@ -328,57 +313,123 @@ function marching_cubes(sdf::SignedDistanceField{3,ST,FT},
328313
Point{3,Float64}(xi,yi-1,zi) .* s .+ orig,
329314
Point{3,Float64}(xi,yi,zi) .* s .+ orig,
330315
Point{3,Float64}(xi-1,yi,zi) .* s .+ orig)
316+
iso_vals = (sdf[xi,yi,zi],
317+
sdf[xi+1,yi,zi],
318+
sdf[xi+1,yi+1,zi],
319+
sdf[xi,yi+1,zi],
320+
sdf[xi,yi,zi+1],
321+
sdf[xi+1,yi,zi+1],
322+
sdf[xi+1,yi+1,zi+1],
323+
sdf[xi,yi+1,zi+1])
324+
325+
#Determine the index into the edge table which
326+
#tells us which vertices are inside of the surface
327+
cubeindex = iso_vals[1] < iso ? 1 : 0
328+
iso_vals[2] < iso && (cubeindex |= 2)
329+
iso_vals[3] < iso && (cubeindex |= 4)
330+
iso_vals[4] < iso && (cubeindex |= 8)
331+
iso_vals[5] < iso && (cubeindex |= 16)
332+
iso_vals[6] < iso && (cubeindex |= 32)
333+
iso_vals[7] < iso && (cubeindex |= 64)
334+
iso_vals[8] < iso && (cubeindex |= 128)
335+
cubeindex += 1
336+
337+
# Cube is entirely in/out of the surface
338+
edge_table[cubeindex] == 0 && continue
331339

332340
# Find the vertices where the surface intersects the cube
333-
if (edge_table[cubeindex] & 1 != 0)
334-
vertlist[1] =
335-
vertex_interp(iso,points[1],points[2],sdf[xi,yi,zi],sdf[xi+1,yi,zi], eps)
336-
end
337-
if (edge_table[cubeindex] & 2 != 0)
338-
vertlist[2] =
339-
vertex_interp(iso,points[2],points[3],sdf[xi+1,yi,zi],sdf[xi+1,yi+1,zi], eps)
340-
end
341-
if (edge_table[cubeindex] & 4 != 0)
342-
vertlist[3] =
343-
vertex_interp(iso,points[3],points[4],sdf[xi+1,yi+1,zi],sdf[xi,yi+1,zi], eps)
344-
end
345-
if (edge_table[cubeindex] & 8 != 0)
346-
vertlist[4] =
347-
vertex_interp(iso,points[4],points[1],sdf[xi,yi+1,zi],sdf[xi,yi,zi], eps)
348-
end
349-
if (edge_table[cubeindex] & 16 != 0)
350-
vertlist[5] =
351-
vertex_interp(iso,points[5],points[6],sdf[xi,yi,zi+1],sdf[xi+1,yi,zi+1], eps)
352-
end
353-
if (edge_table[cubeindex] & 32 != 0)
354-
vertlist[6] =
355-
vertex_interp(iso,points[6],points[7],sdf[xi+1,yi,zi+1],sdf[xi+1,yi+1,zi+1], eps)
356-
end
357-
if (edge_table[cubeindex] & 64 != 0)
358-
vertlist[7] =
359-
vertex_interp(iso,points[7],points[8],sdf[xi+1,yi+1,zi+1],sdf[xi,yi+1,zi+1], eps)
360-
end
361-
if (edge_table[cubeindex] & 128 != 0)
362-
vertlist[8] =
363-
vertex_interp(iso,points[8],points[5],sdf[xi,yi+1,zi+1],sdf[xi,yi,zi+1], eps)
364-
end
365-
if (edge_table[cubeindex] & 256 != 0)
366-
vertlist[9] =
367-
vertex_interp(iso,points[1],points[5],sdf[xi,yi,zi],sdf[xi,yi,zi+1], eps)
368-
end
369-
if (edge_table[cubeindex] & 512 != 0)
370-
vertlist[10] =
371-
vertex_interp(iso,points[2],points[6],sdf[xi+1,yi,zi],sdf[xi+1,yi,zi+1], eps)
372-
end
373-
if (edge_table[cubeindex] & 1024 != 0)
374-
vertlist[11] =
375-
vertex_interp(iso,points[3],points[7],sdf[xi+1,yi+1,zi],sdf[xi+1,yi+1,zi+1], eps)
341+
find_vertices_interp!(vertlist, points, iso_vals, cubeindex, iso, eps)
342+
343+
# Create the triangle
344+
for i = 1:3:13
345+
tri_table[cubeindex][i] == -1 && break
346+
push!(vts, vertlist[tri_table[cubeindex][i ]])
347+
push!(vts, vertlist[tri_table[cubeindex][i+1]])
348+
push!(vts, vertlist[tri_table[cubeindex][i+2]])
349+
fct = length(vts)
350+
push!(fcs, Face{3,Int}(fct, fct-1, fct-2))
376351
end
377-
if (edge_table[cubeindex] & 2048 != 0)
378-
vertlist[12] =
379-
vertex_interp(iso,points[4],points[8],sdf[xi,yi+1,zi],sdf[xi,yi+1,zi+1], eps)
352+
end
353+
MT(vts,fcs)
354+
end
355+
356+
357+
function marching_cubes(f::Function,
358+
bounds::HyperRectangle,
359+
samples::NTuple{3,Int}=(256,256,256),
360+
iso=0.0,
361+
MT::Type{M}=SimpleMesh{Point{3,Float64},Face{3,Int}},
362+
eps=0.00001) where {ST,FT,M<:AbstractMesh}
363+
nx, ny, nz = samples[1], samples[2], samples[3]
364+
w = widths(bounds)
365+
orig = origin(bounds)
366+
367+
# we subtract one from the length along each axis because
368+
# an NxNxN SDF has N-1 cells on each axis
369+
s = Point{3,Float64}(w[1]/(nx-1), w[2]/(ny-1), w[3]/(nz-1))
370+
371+
# arrays for vertices and faces
372+
vts = Point{3,Float64}[]
373+
fcs = Face{3,Int}[]
374+
mt = max(nx,ny,nz)
375+
sizehint!(vts, mt*mt*6)
376+
sizehint!(fcs, mt*mt*2)
377+
vertlist = Vector{Point{3,Float64}}(undef, 12)
378+
iso_vals = Vector{Float64}(undef,8)
379+
points = Vector{Point{3,Float64}}(undef,8)
380+
@inbounds for xi = 1:nx-1, yi = 1:ny-1, zi = 1:nz-1
381+
382+
if zi == 1
383+
points[1] = Point{3,Float64}(xi-1,yi-1,zi-1) .* s .+ orig
384+
points[2] = Point{3,Float64}(xi,yi-1,zi-1) .* s .+ orig
385+
points[3] = Point{3,Float64}(xi,yi,zi-1) .* s .+ orig
386+
points[4] = Point{3,Float64}(xi-1,yi,zi-1) .* s .+ orig
387+
points[5] = Point{3,Float64}(xi-1,yi-1,zi) .* s .+ orig
388+
points[6] = Point{3,Float64}(xi,yi-1,zi) .* s .+ orig
389+
points[7] = Point{3,Float64}(xi,yi,zi) .* s .+ orig
390+
points[8] = Point{3,Float64}(xi-1,yi,zi) .* s .+ orig
391+
for i = 1:8
392+
iso_vals[i] = f(points[i])
393+
end
394+
else
395+
points[1] = points[5]
396+
points[2] = points[6]
397+
points[3] = points[7]
398+
points[4] = points[8]
399+
points[5] = Point{3,Float64}(xi-1,yi-1,zi) .* s .+ orig
400+
points[6] = Point{3,Float64}(xi,yi-1,zi) .* s .+ orig
401+
points[7] = Point{3,Float64}(xi,yi,zi) .* s .+ orig
402+
points[8] = Point{3,Float64}(xi-1,yi,zi) .* s .+ orig
403+
iso_vals[1] = iso_vals[5]
404+
iso_vals[2] = iso_vals[6]
405+
iso_vals[3] = iso_vals[7]
406+
iso_vals[4] = iso_vals[8]
407+
iso_vals[5] = f(points[5])
408+
iso_vals[6] = f(points[6])
409+
iso_vals[7] = f(points[7])
410+
iso_vals[8] = f(points[8])
380411
end
381412

413+
#Determine the index into the edge table which
414+
#tells us which vertices are inside of the surface
415+
cubeindex = iso_vals[1] < iso ? 1 : 0
416+
iso_vals[2] < iso && (cubeindex |= 2)
417+
iso_vals[3] < iso && (cubeindex |= 4)
418+
iso_vals[4] < iso && (cubeindex |= 8)
419+
iso_vals[5] < iso && (cubeindex |= 16)
420+
iso_vals[6] < iso && (cubeindex |= 32)
421+
iso_vals[7] < iso && (cubeindex |= 64)
422+
iso_vals[8] < iso && (cubeindex |= 128)
423+
cubeindex += 1
424+
425+
# Cube is entirely in/out of the surface
426+
edge_table[cubeindex] == 0 && continue
427+
428+
# Find the vertices where the surface intersects the cube
429+
# TODO this can use the underlying function to find the zero.
430+
# The underlying space is non-linear so there will be error otherwise
431+
find_vertices_interp!(vertlist, points, iso_vals, cubeindex, iso, eps)
432+
382433
# Create the triangle
383434
for i = 1:3:13
384435
tri_table[cubeindex][i] == -1 && break
@@ -392,6 +443,57 @@ function marching_cubes(sdf::SignedDistanceField{3,ST,FT},
392443
MT(vts,fcs)
393444
end
394445

446+
@inline function find_vertices_interp!(vertlist, points, iso_vals, cubeindex, iso, eps)
447+
if (edge_table[cubeindex] & 1 != 0)
448+
vertlist[1] =
449+
vertex_interp(iso,points[1],points[2],iso_vals[1],iso_vals[2], eps)
450+
end
451+
if (edge_table[cubeindex] & 2 != 0)
452+
vertlist[2] =
453+
vertex_interp(iso,points[2],points[3],iso_vals[2],iso_vals[3], eps)
454+
end
455+
if (edge_table[cubeindex] & 4 != 0)
456+
vertlist[3] =
457+
vertex_interp(iso,points[3],points[4],iso_vals[3],iso_vals[4], eps)
458+
end
459+
if (edge_table[cubeindex] & 8 != 0)
460+
vertlist[4] =
461+
vertex_interp(iso,points[4],points[1],iso_vals[4],iso_vals[1], eps)
462+
end
463+
if (edge_table[cubeindex] & 16 != 0)
464+
vertlist[5] =
465+
vertex_interp(iso,points[5],points[6],iso_vals[5],iso_vals[6], eps)
466+
end
467+
if (edge_table[cubeindex] & 32 != 0)
468+
vertlist[6] =
469+
vertex_interp(iso,points[6],points[7],iso_vals[6],iso_vals[7], eps)
470+
end
471+
if (edge_table[cubeindex] & 64 != 0)
472+
vertlist[7] =
473+
vertex_interp(iso,points[7],points[8],iso_vals[7],iso_vals[8], eps)
474+
end
475+
if (edge_table[cubeindex] & 128 != 0)
476+
vertlist[8] =
477+
vertex_interp(iso,points[8],points[5],iso_vals[8],iso_vals[5], eps)
478+
end
479+
if (edge_table[cubeindex] & 256 != 0)
480+
vertlist[9] =
481+
vertex_interp(iso,points[1],points[5],iso_vals[1],iso_vals[5], eps)
482+
end
483+
if (edge_table[cubeindex] & 512 != 0)
484+
vertlist[10] =
485+
vertex_interp(iso,points[2],points[6],iso_vals[2],iso_vals[6], eps)
486+
end
487+
if (edge_table[cubeindex] & 1024 != 0)
488+
vertlist[11] =
489+
vertex_interp(iso,points[3],points[7],iso_vals[3],iso_vals[7], eps)
490+
end
491+
if (edge_table[cubeindex] & 2048 != 0)
492+
vertlist[12] =
493+
vertex_interp(iso,points[4],points[8],iso_vals[4],iso_vals[8], eps)
494+
end
495+
end
496+
395497
# Linearly interpolate the position where an isosurface cuts
396498
# an edge between two vertices, each with their own scalar value
397499
function vertex_interp(iso, p1, p2, valp1, valp2, eps = 0.00001)
@@ -415,3 +517,11 @@ MarchingCubes(iso::T1=0.0, eps::T2=1e-3) where {T1, T2} = MarchingCubes{promote_
415517
function (::Type{MT})(df::SignedDistanceField, method::MarchingCubes)::MT where {MT <: AbstractMesh}
416518
marching_cubes(df, method.iso, MT, method.eps)
417519
end
520+
521+
function (::Type{MT})(f::Function, h::HyperRectangle, size::NTuple{3,Int}, method::MarchingCubes)::MT where {MT <: AbstractMesh}
522+
marching_cubes(f, h, size, method.iso, MT, method.eps)
523+
end
524+
525+
function (::Type{MT})(f::Function, h::HyperRectangle, method::MarchingCubes; size::NTuple{3,Int}=(128,128,128))::MT where {MT <: AbstractMesh}
526+
marching_cubes(f, h, size, method.iso, MT, method.eps)
527+
end

0 commit comments

Comments
 (0)