Skip to content

Commit 8cb23f3

Browse files
authored
use geometry package octree; improve gsdfaux.RenderShader3D logging (#13)
1 parent d1f796c commit 8cb23f3

File tree

8 files changed

+206
-512
lines changed

8 files changed

+206
-512
lines changed

glrender/dual_contour.go

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@ import (
44
"errors"
55

66
"github.com/chewxy/math32"
7+
"github.com/soypat/geometry/i3"
78
"github.com/soypat/geometry/ms3"
89
"github.com/soypat/gsdf/gleval"
910
)
1011

1112
type DualContourRenderer struct {
1213
sdf gleval.SDF3
14+
oct ms3.Octree
1315
contourer DualContourer
14-
cubeMap map[ivec]int
15-
cubebuf []icube
16+
cubeMap map[i3.Vec]int
17+
cubebuf []i3.Cube
1618
posbuf []ms3.Vec
1719
distbuf []float32
1820
// cubeinfo stores both cube and edge information for dual contouring algorithm.
@@ -33,15 +35,16 @@ func (dcr *DualContourRenderer) Reset(sdf gleval.SDF3, res float32, vertexPlacer
3335
if err != nil {
3436
return err
3537
}
36-
nCubes := int(topCube.decomposesTo(1))
38+
nCubes := int(topCube.DecomposesTo(1))
3739
if cap(dcr.cubebuf) < nCubes*2 {
38-
dcr.cubebuf = make([]icube, 0, nCubes*2) // Want to fully decompose cube.
40+
dcr.cubebuf = make([]i3.Cube, 0, nCubes*2) // Want to fully decompose cube.
3941
}
4042
var ok bool
41-
dcr.cubebuf, ok = octreeDecomposeBFS(dcr.cubebuf[:0], topCube, 1)
43+
dcr.oct = ms3.Octree{Resolution: res, Origin: origin}
44+
dcr.cubebuf, ok = dcr.oct.DecomposeBFS(dcr.cubebuf[:0], topCube, 1)
4245
if !ok {
4346
return errors.New("unable to decompose top level cube")
44-
} else if dcr.cubebuf[0].lvl != 1 {
47+
} else if dcr.cubebuf[0].Level != 1 {
4548
return errors.New("short buffer decomposing all cubes")
4649
} else if len(dcr.cubebuf) != nCubes {
4750
panic("failed to decompose all edges?")
@@ -54,7 +57,7 @@ func (dcr *DualContourRenderer) Reset(sdf gleval.SDF3, res float32, vertexPlacer
5457
posbuf := dcr.posbuf[:cap(dcr.posbuf)]
5558

5659
// Keep cubes that contain a surface and their neighbors.
57-
dcr.cubebuf, _, err = octreePrune(sdf, dcr.cubebuf, origin, res, posbuf, dcr.distbuf[:len(posbuf)], userData, 2, true)
60+
dcr.cubebuf, _, err = octreePrunea(sdf, dcr.cubebuf, origin, res, posbuf, dcr.distbuf[:len(posbuf)], userData, 2, true)
5861
if err != nil {
5962
return err
6063
}
@@ -64,7 +67,7 @@ func (dcr *DualContourRenderer) Reset(sdf gleval.SDF3, res float32, vertexPlacer
6467
dcr.cubeinfo = make([]DualCube, nCubes)
6568
}
6669
if dcr.cubeMap == nil {
67-
dcr.cubeMap = make(map[ivec]int)
70+
dcr.cubeMap = make(map[i3.Vec]int)
6871
}
6972
clear(dcr.cubeMap)
7073
dcr.res = res
@@ -84,8 +87,9 @@ func (dcr *DualContourRenderer) RenderAll(dst []ms3.Triangle, userData any) ([]m
8487
cubes := dcr.cubeinfo[:len(edges)]
8588

8689
for e, edge := range edges {
87-
sz := edge.size(res)
88-
edgeOrig := edge.origin(origin, sz)
90+
91+
sz := dcr.oct.CubeSize(edge)
92+
edgeOrig := dcr.oct.CubeOrigin(edge, sz)
8993
// posbuf has edge origin, edge extremes in x,y,z and the center of the voxel.
9094
posbuf = append(posbuf,
9195
edgeOrig,
@@ -94,7 +98,7 @@ func (dcr *DualContourRenderer) RenderAll(dst []ms3.Triangle, userData any) ([]m
9498
ms3.Add(edgeOrig, ms3.Vec{Z: sz}),
9599
)
96100
cubes[e].FinalVertex = edgeOrig // By default set to center.
97-
cubeMap[edge.ivec] = e
101+
cubeMap[edge.Vec] = e
98102
}
99103

100104
lenPos := len(posbuf)
@@ -108,7 +112,7 @@ func (dcr *DualContourRenderer) RenderAll(dst []ms3.Triangle, userData any) ([]m
108112
posbuf = posbuf[:0]
109113
for e, edge := range edges {
110114
// First for loop accumulates edge biases into voxels/cubes.
111-
cube := makeDualCube(edge.ivec, distbuf[e*4:])
115+
cube := makeDualCube(edge.Vec, distbuf[e*4:])
112116
cubes[e] = cube
113117
if !cube.IsActive() {
114118
continue
@@ -180,7 +184,7 @@ func (dcr *DualContourRenderer) RenderAll(dst []ms3.Triangle, userData any) ([]m
180184
return dst, nil
181185
}
182186

183-
func makeDualCube(ivec ivec, data []float32) DualCube {
187+
func makeDualCube(ivec i3.Vec, data []float32) DualCube {
184188
if len(data) < 4 {
185189
panic("short dual cube info buffer")
186190
}
@@ -196,7 +200,7 @@ func makeDualCube(ivec ivec, data []float32) DualCube {
196200
// DualCube corresponds to a voxel anmd contains both cube and edge data.
197201
type DualCube struct {
198202
// ivec stores the octree index of the cube, used to find neighboring cube ivec indices and the absolute position of the cube.
199-
ivec ivec
203+
ivec i3.Vec
200204
// Neighbors contains neighboring index into dualCube buffer and contributing edge intersect axis.
201205
// - Neighbors[0]: Index into dualCube buffer to cube neighbor with edge.
202206
// - Neighbors[1]: Intersecting axis. 0 is x; 1 is y; 2 is z.
@@ -212,7 +216,9 @@ type DualCube struct {
212216
}
213217

214218
func (dc *DualCube) SizeAndOrigin(res float32, octreeOrigin ms3.Vec) (float32, ms3.Vec) {
215-
return res, icube{ivec: dc.ivec, lvl: 1}.origin(octreeOrigin, res)
219+
cb := i3.Cube{Vec: dc.ivec, Level: 1}
220+
oct := ms3.Octree{Resolution: res, Origin: octreeOrigin}
221+
return res, oct.CubeOrigin(cb, oct.CubeSize(cb))
216222
}
217223

218224
func (dc *DualCube) IsActive() bool {
@@ -235,22 +241,22 @@ func (dc *DualCube) FlipX() bool { return dc.XDist-dc.OrigDist < 0 }
235241
func (dc *DualCube) FlipY() bool { return dc.YDist-dc.OrigDist < 0 }
236242
func (dc *DualCube) FlipZ() bool { return dc.ZDist-dc.OrigDist < 0 }
237243

238-
func (dc *DualCube) EdgeNeighborsX() [4]ivec {
244+
func (dc *DualCube) EdgeNeighborsX() [4]i3.Vec {
239245
const sub = -(1 << minIcubeLvl)
240246
v := dc.ivec
241-
return [4]ivec{v.Add(ivec{y: sub, z: sub}), v.Add(ivec{z: sub}), v, v.Add(ivec{y: sub})}
247+
return [4]i3.Vec{v.Add(i3.Vec{Y: sub, Z: sub}), v.Add(i3.Vec{Z: sub}), v, v.Add(i3.Vec{Y: sub})}
242248
}
243249

244-
func (dc *DualCube) EdgeNeighborsY() [4]ivec {
250+
func (dc *DualCube) EdgeNeighborsY() [4]i3.Vec {
245251
const sub = -(1 << minIcubeLvl)
246252
v := dc.ivec
247-
return [4]ivec{v.Add(ivec{x: sub, z: sub}), v.Add(ivec{x: sub}), v, v.Add(ivec{z: sub})}
253+
return [4]i3.Vec{v.Add(i3.Vec{X: sub, Z: sub}), v.Add(i3.Vec{X: sub}), v, v.Add(i3.Vec{Z: sub})}
248254
}
249255

250-
func (dc *DualCube) EdgeNeighborsZ() [4]ivec {
256+
func (dc *DualCube) EdgeNeighborsZ() [4]i3.Vec {
251257
const sub = -(1 << minIcubeLvl)
252258
v := dc.ivec
253-
return [4]ivec{v.Add(ivec{x: sub, y: sub}), v.Add(ivec{y: sub}), v, v.Add(ivec{x: sub})}
259+
return [4]i3.Vec{v.Add(i3.Vec{X: sub, Y: sub}), v.Add(i3.Vec{Y: sub}), v, v.Add(i3.Vec{X: sub})}
254260
}
255261

256262
// minecraftRender performs a minecraft-like render of the SDF using a dual contour method.
@@ -261,21 +267,27 @@ func minecraftRender(dst []ms3.Triangle, sdf gleval.SDF3, res float32) ([]ms3.Tr
261267
if err != nil {
262268
return dst, err
263269
}
264-
decomp := topCube.decomposesTo(1)
265-
cubes := make([]icube, 0, decomp*2)
266-
cubes, ok := octreeDecomposeBFS(cubes, topCube, 1)
270+
oct := ms3.Octree{
271+
Resolution: res,
272+
Origin: origin,
273+
}
274+
275+
decomp := topCube.DecomposesTo(1)
276+
cubes := make([]i3.Cube, 0, decomp*2)
277+
278+
cubes, ok := oct.DecomposeBFS(cubes, topCube, 1)
267279
if !ok {
268280
return dst, errors.New("unable to decompose top level cube")
269-
} else if cubes[0].lvl != 1 {
281+
} else if cubes[0].Level != 1 {
270282
return dst, errors.New("short buffer decomposing all cubes")
271283
}
272284

273285
var posbuf []ms3.Vec
274286
iCubes := 0
275287
for ; iCubes < len(cubes); iCubes++ {
276288
cube := cubes[iCubes]
277-
sz := cube.size(res)
278-
cubeOrig := cube.origin(origin, sz)
289+
sz := oct.CubeSize(cube)
290+
cubeOrig := oct.CubeOrigin(cube, sz)
279291
// Append origin and edge-end vertices.
280292
posbuf = append(posbuf,
281293
cubeOrig,
@@ -292,9 +304,9 @@ func minecraftRender(dst []ms3.Triangle, sdf gleval.SDF3, res float32) ([]ms3.Tr
292304
}
293305
for j := 0; j < iCubes; j++ {
294306
cube := cubes[j]
295-
sz := cube.size(res)
296-
srcOrig := cube.origin(origin, sz)
297-
dci := makeDualCube(cube.ivec, distbuf[j*4:])
307+
sz := oct.CubeSize(cube)
308+
srcOrig := oct.CubeOrigin(cube, sz)
309+
dci := makeDualCube(cube.Vec, distbuf[j*4:])
298310
origOff := sz
299311
if dci.ActiveX() {
300312
xOrig := ms3.Add(srcOrig, ms3.Vec{X: origOff})

glrender/glrender.go

Lines changed: 0 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -35,110 +35,8 @@ func RenderAll(r Renderer, userData any) ([]ms3.Triangle, error) {
3535
return result, err
3636
}
3737

38-
type ivec struct {
39-
x int
40-
y int
41-
z int
42-
}
43-
44-
func (a ivec) Add(b ivec) ivec { return ivec{x: a.x + b.x, y: a.y + b.y, z: a.z + b.z} }
45-
func (a ivec) AddScalar(f int) ivec { return ivec{x: a.x + f, y: a.y + f, z: a.z + f} }
46-
func (a ivec) MulScalar(f int) ivec { return ivec{x: a.x * f, y: a.y * f, z: a.z * f} }
47-
func (a ivec) DivScalar(f int) ivec { return ivec{x: a.x / f, y: a.y / f, z: a.z / f} }
48-
func (a ivec) ShiftRight(lo int) ivec { return ivec{x: a.x >> lo, y: a.y >> lo, z: a.z >> lo} }
49-
func (a ivec) ShiftLeft(hi int) ivec { return ivec{x: a.x << hi, y: a.y << hi, z: a.z << hi} }
50-
func (a ivec) Sub(b ivec) ivec { return ivec{x: a.x - b.x, y: a.y - b.y, z: a.z - b.z} }
51-
func (a ivec) Vec() ms3.Vec { return ms3.Vec{X: float32(a.x), Y: float32(a.y), Z: float32(a.z)} }
52-
func (a ivec) AndScalar(f int) ivec { return ivec{x: a.x & f, y: a.y & f, z: a.z & f} }
53-
func (a ivec) OrScalar(f int) ivec { return ivec{x: a.x | f, y: a.y | f, z: a.z | f} }
54-
func (a ivec) XorScalar(f int) ivec { return ivec{x: a.x ^ f, y: a.y ^ f, z: a.z ^ f} }
55-
func (a ivec) AndnotScalar(f int) ivec { return ivec{x: a.x &^ f, y: a.y &^ f, z: a.z &^ f} }
56-
5738
const minIcubeLvl = 1
5839

59-
type icube struct {
60-
ivec
61-
lvl int
62-
}
63-
64-
func (c icube) isSmallest() bool { return c.lvl == 1 }
65-
func (c icube) isSecondSmallest() bool { return c.lvl == 2 }
66-
67-
// decomposesTo returns the amount of cubes generated from decomposing the cube down to cubes of the argument target level.
68-
func (c icube) decomposesTo(targetLvl int) uint64 {
69-
if targetLvl > c.lvl {
70-
panic("invalid targetLvl to icube.decomposesTo")
71-
}
72-
return pow8(c.lvl - targetLvl)
73-
}
74-
75-
func (c icube) size(baseRes float32) float32 {
76-
dim := 1 << (c.lvl - 1)
77-
return float32(dim) * baseRes
78-
}
79-
80-
// supercube returns the icube's parent octree icube.
81-
func (c icube) supercube() icube {
82-
upLvl := c.lvl + 1
83-
bitmask := (1 << upLvl) - 1
84-
return icube{
85-
ivec: c.ivec.AndnotScalar(bitmask),
86-
lvl: upLvl,
87-
}
88-
}
89-
90-
func (c icube) box(origin ms3.Vec, size float32) ms3.Box {
91-
origin = c.origin(origin, size) // Replace origin with icube origin.
92-
return ms3.Box{
93-
Min: origin,
94-
Max: ms3.AddScalar(size, origin),
95-
}
96-
}
97-
98-
func (c icube) origin(origin ms3.Vec, size float32) ms3.Vec {
99-
idx := c.lvlIdx()
100-
return ms3.Add(origin, ms3.Scale(size, idx.Vec()))
101-
}
102-
103-
func (c icube) lvlIdx() ivec {
104-
return c.ivec.ShiftRight(c.lvl) // icube indices per level in the octree.
105-
}
106-
107-
func (c icube) center(origin ms3.Vec, size float32) ms3.Vec {
108-
return c.box(origin, size).Center() // TODO(soypat): this can probably be optimized.
109-
}
110-
111-
// corners returns the cube corners. Be aware size is NOT the minimum cube resolution but
112-
// can be calculated with the [icube.size] method using resolution. If [icube.lvl]==1 then size is resolution.
113-
func (c icube) corners(origin ms3.Vec, size float32) [8]ms3.Vec {
114-
origin = c.origin(origin, size)
115-
return [8]ms3.Vec{
116-
ms3.Add(origin, ms3.Vec{X: 0, Y: 0, Z: 0}),
117-
ms3.Add(origin, ms3.Vec{X: size, Y: 0, Z: 0}),
118-
ms3.Add(origin, ms3.Vec{X: size, Y: size, Z: 0}),
119-
ms3.Add(origin, ms3.Vec{X: 0, Y: size, Z: 0}),
120-
ms3.Add(origin, ms3.Vec{X: 0, Y: 0, Z: size}),
121-
ms3.Add(origin, ms3.Vec{X: size, Y: 0, Z: size}),
122-
ms3.Add(origin, ms3.Vec{X: size, Y: size, Z: size}),
123-
ms3.Add(origin, ms3.Vec{X: 0, Y: size, Z: size}),
124-
}
125-
}
126-
127-
func (c icube) octree() [8]icube {
128-
lvl := c.lvl - 1
129-
s := 1 << lvl
130-
return [8]icube{
131-
{ivec: c.Add(ivec{0, 0, 0}), lvl: lvl},
132-
{ivec: c.Add(ivec{s, 0, 0}), lvl: lvl},
133-
{ivec: c.Add(ivec{s, s, 0}), lvl: lvl},
134-
{ivec: c.Add(ivec{0, s, 0}), lvl: lvl},
135-
{ivec: c.Add(ivec{0, 0, s}), lvl: lvl},
136-
{ivec: c.Add(ivec{s, 0, s}), lvl: lvl},
137-
{ivec: c.Add(ivec{s, s, s}), lvl: lvl},
138-
{ivec: c.Add(ivec{0, s, s}), lvl: lvl},
139-
}
140-
}
141-
14240
func min(a, b int) int {
14341
if a < b {
14442
return a

0 commit comments

Comments
 (0)