Skip to content

Commit 39c7d23

Browse files
committed
[SN] non-sdf SN implementation
1 parent 7322fc2 commit 39c7d23

File tree

2 files changed

+216
-21
lines changed

2 files changed

+216
-21
lines changed

src/surface_nets.jl

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,175 @@ function surface_nets(data::Vector{T}, dims,eps,scale,origin) where {T}
205205
vertices, faces # faces are quads, indexed to vertices
206206
end
207207

208+
"""
209+
Generate a mesh using naive surface nets.
210+
This takes the center of mass of the voxel as the vertex for each cube.
211+
"""
212+
function surface_nets(f::Function, dims::NTuple{3,Int},eps,scale,origin)
213+
214+
# TODO
215+
T = Float64
216+
217+
vertices = Point{3,T}[]
218+
faces = Face{4,Int}[]
219+
220+
sizehint!(vertices,ceil(Int,maximum(dims)^2/2))
221+
sizehint!(faces,ceil(Int,maximum(dims)^2/2))
222+
223+
n = 0
224+
x = [0,0,0]
225+
R = Array{Int}([1, (dims[1]+1), (dims[1]+1)*(dims[2]+1)])
226+
buf_no = 1
227+
228+
buffer = fill(zero(Int),R[3]*2)
229+
230+
v = Vector{T}([0.0,0.0,0.0])
231+
232+
#March over the voxel grid
233+
x[3] = 0
234+
@inbounds while x[3]<dims[3]-1
235+
236+
# m is the pointer into the buffer we are going to use.
237+
# This is slightly obtuse because javascript does not have good support for packed data structures, so we must use typed arrays :(
238+
# The contents of the buffer will be the indices of the vertices on the previous x/y slice of the volume
239+
m = 1 + (dims[1]+1) * (1 + buf_no * (dims[2]+1))
240+
241+
x[2]=0
242+
@inbounds while x[2]<dims[2]-1
243+
244+
x[1]=0
245+
@inbounds while x[1] < dims[1]-1
246+
247+
# Read in 8 field values around this vertex and store them in an array
248+
points = (Point{3,Float64}(x[1],x[2],x[3]).* scale + origin,
249+
Point{3,Float64}(x[1]+1,x[2],x[3]).* scale + origin,
250+
Point{3,Float64}(x[1],x[2]+1,x[3]).* scale + origin,
251+
Point{3,Float64}(x[1]+1,x[2]+1,x[3]).* scale + origin,
252+
Point{3,Float64}(x[1],x[2],x[3]+1).* scale + origin,
253+
Point{3,Float64}(x[1]+1,x[2],x[3]+1).* scale + origin,
254+
Point{3,Float64}(x[1],x[2]+1,x[3]+1).* scale + origin,
255+
Point{3,Float64}(x[1]+1,x[2]+1,x[3]+1).* scale + origin)
256+
257+
@inbounds grid = map(f, points)
258+
259+
# Also calculate 8-bit mask, like in marching cubes, so we can speed up sign checks later
260+
mask = 0x00
261+
signbit(grid[1]) && (mask |= 0x01)
262+
signbit(grid[2]) && (mask |= 0x02)
263+
signbit(grid[3]) && (mask |= 0x04)
264+
signbit(grid[4]) && (mask |= 0x08)
265+
signbit(grid[5]) && (mask |= 0x10)
266+
signbit(grid[6]) && (mask |= 0x20)
267+
signbit(grid[7]) && (mask |= 0x40)
268+
signbit(grid[8]) && (mask |= 0x80)
269+
270+
# Check for early termination if cell does not intersect boundary
271+
if mask == 0x00 || mask == 0xff
272+
x[1] += 1
273+
n += 1
274+
m += 1
275+
continue
276+
end
277+
278+
#Sum up edge intersections
279+
edge_mask = sn_edge_table[mask+1]
280+
v[1] = 0.0
281+
v[2] = 0.0
282+
v[3] = 0.0
283+
e_count = 0
284+
285+
#For every edge of the cube...
286+
@inbounds for i=0:11
287+
288+
#Use edge mask to check if it is crossed
289+
if (edge_mask & (1<<i)) == 0
290+
continue
291+
end
292+
293+
#If it did, increment number of edge crossings
294+
e_count += 1
295+
296+
#Now find the point of intersection
297+
e0 = cube_edges[(i<<1)+1] #Unpack vertices
298+
e1 = cube_edges[(i<<1)+2]
299+
g0 = grid[e0+1] #Unpack grid values
300+
g1 = grid[e1+1]
301+
t = g0 - g1 #Compute point of intersection
302+
if abs(t) > eps
303+
t = g0 / t
304+
else
305+
continue
306+
end
307+
308+
#Interpolate vertices and add up intersections (this can be done without multiplying)
309+
k = 1
310+
for j = 1:3
311+
a = e0 & k
312+
b = e1 & k
313+
(a != 0) && (v[j] += 1.0)
314+
if a != b
315+
v[j] += (a != 0 ? - t : t)
316+
end
317+
k<<=1
318+
end
319+
end # edge check
320+
321+
#Now we just average the edge intersections and add them to coordinate
322+
s = 1.0 / e_count
323+
for i=1:3
324+
@inbounds v[i] = (x[i] + s * v[i])# * scale[i] + origin[i]
325+
end
326+
327+
#Add vertex to buffer, store pointer to vertex index in buffer
328+
buffer[m+1] = length(vertices)
329+
push!(vertices, Point{3,T}(v[1],v[2],v[3]))
330+
331+
#Now we need to add faces together, to do this we just loop over 3 basis components
332+
for i=0:2
333+
#The first three entries of the edge_mask count the crossings along the edge
334+
if (edge_mask & (1<<i)) == 0
335+
continue
336+
end
337+
338+
# i = axes we are point along. iu, iv = orthogonal axes
339+
iu = (i+1)%3
340+
iv = (i+2)%3
341+
342+
#If we are on a boundary, skip it
343+
if (x[iu+1] == 0 || x[iv+1] == 0)
344+
continue
345+
end
346+
347+
#Otherwise, look up adjacent edges in buffer
348+
du = R[iu+1]
349+
dv = R[iv+1]
350+
351+
#Remember to flip orientation depending on the sign of the corner.
352+
if (mask & 0x01) != 0x00
353+
push!(faces,Face{4,Int}(buffer[m+1]+1, buffer[m-du+1]+1, buffer[m-du-dv+1]+1, buffer[m-dv+1]+1));
354+
else
355+
push!(faces,Face{4,Int}(buffer[m+1]+1, buffer[m-dv+1]+1, buffer[m-du-dv+1]+1, buffer[m-du+1]+1));
356+
end
357+
end
358+
x[1] += 1
359+
n += 1
360+
m += 1
361+
end
362+
x[2] += 1
363+
n += 1
364+
m += 2
365+
end
366+
x[3] += 1
367+
n+=dims[1]
368+
buf_no = xor(buf_no,1)
369+
R[3]=-R[3]
370+
end
371+
#All done! Return the result
372+
373+
vertices, faces # faces are quads, indexed to vertices
374+
end
375+
376+
208377
struct NaiveSurfaceNets{T} <: AbstractMeshingAlgorithm
209378
iso::T
210379
eps::T
@@ -235,3 +404,18 @@ function (::Type{MT})(sdf::SignedDistanceField, method::NaiveSurfaceNets) where
235404
orig)
236405
MT(vts, fcs)::MT
237406
end
407+
408+
function (::Type{MT})(f::Function, bounds::HyperRectangle, size::NTuple{3,Int}, method::NaiveSurfaceNets) where {MT <: AbstractMesh}
409+
orig = origin(bounds)
410+
w = widths(bounds)
411+
scale = w ./ Point(size .- 1) # subtract 1 because an SDF with N points per side has N-1 cells
412+
413+
# TODO ISO val
414+
415+
vts, fcs = surface_nets(f,
416+
size,
417+
method.eps,
418+
scale,
419+
orig)
420+
MT(vts, fcs)::MT
421+
end

test/runtests.jl

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,32 @@ using LinearAlgebra: dot, norm
99

1010
@testset "meshing" begin
1111
@testset "surface nets" begin
12-
sdf_sphere = SignedDistanceField(HyperRectangle(Vec(-1,-1,-1.),Vec(2,2,2.))) do v
13-
sqrt(sum(dot(v,v))) - 1 # sphere
14-
end
15-
sdf_torus = SignedDistanceField(HyperRectangle(Vec(-2,-2,-2.),Vec(4,4,4.)), 0.05) do v
16-
(sqrt(v[1]^2+v[2]^2)-0.5)^2 + v[3]^2 - 0.25
17-
end
18-
sphere = HomogenousMesh(sdf_sphere, NaiveSurfaceNets())
19-
torus = HomogenousMesh(sdf_torus, NaiveSurfaceNets())
20-
@test length(vertices(sphere)) == 1832
21-
@test length(vertices(torus)) == 5532
22-
@test length(faces(sphere)) == 1830
23-
@test length(faces(torus)) == 5532
12+
sdf_sphere = SignedDistanceField(HyperRectangle(Vec(-1,-1,-1.),Vec(2,2,2.))) do v
13+
sqrt(sum(dot(v,v))) - 1 # sphere
14+
end
15+
sdf_torus = SignedDistanceField(HyperRectangle(Vec(-2,-2,-2.),Vec(4,4,4.)), 0.05) do v
16+
(sqrt(v[1]^2+v[2]^2)-0.5)^2 + v[3]^2 - 0.25
17+
end
18+
19+
snf_torus = HomogenousMesh(HyperRectangle(Vec(-2,-2,-2.),Vec(4,4,4.)), (81,81,81), NaiveSurfaceNets()) do v
20+
(sqrt(v[1]^2+v[2]^2)-0.5)^2 + v[3]^2 - 0.25
21+
end
22+
23+
snf_sphere = HomogenousMesh(HyperRectangle(Vec(-1,-1,-1.),Vec(2,2,2.)), (21,21,21), NaiveSurfaceNets()) do v
24+
sqrt(sum(dot(v,v))) - 1 # sphere
25+
end
26+
27+
sphere = HomogenousMesh(sdf_sphere, NaiveSurfaceNets())
28+
torus = HomogenousMesh(sdf_torus, NaiveSurfaceNets())
29+
@test length(vertices(sphere)) == 1832
30+
@test length(vertices(torus)) == 5532
31+
@test length(faces(sphere)) == 1830
32+
@test length(faces(torus)) == 5532
33+
34+
@test length(vertices(sphere)) == length(vertices(snf_sphere))
35+
@test length(vertices(torus)) == length(vertices(snf_torus))
36+
@test length(faces(sphere)) == length(faces(snf_sphere))
37+
@test length(faces(torus)) == length(faces(snf_torus))
2438
end
2539

2640

@@ -69,20 +83,17 @@ using LinearAlgebra: dot, norm
6983
sqrt(sum(dot(v,v))) - 1 # sphere
7084
end
7185

86+
mf = marching_cubes(HyperRectangle(Vec(-1,-1,-1.),Vec(2,2,2.)),(21,21,21)) do v
87+
sqrt(sum(dot(v,v))) - 1 # sphere
88+
end
89+
7290
m = marching_cubes(sdf,0)
7391
m2 = marching_cubes(sdf)
7492
@test length(vertices(m)) == 10968
7593
@test length(faces(m)) == 3656
7694
@test m == m2
77-
78-
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
95+
@test length(vertices(m)) == length(vertices(mf))
96+
@test length(faces(m)) == length(faces(mf))
8697
end
8798

8899
@testset "respect origin" begin

0 commit comments

Comments
 (0)