Skip to content

Commit

Permalink
dual contouring: add minecraft-like renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
soypat committed Oct 12, 2024
1 parent 698ef41 commit 3dfe8f4
Show file tree
Hide file tree
Showing 11 changed files with 439 additions and 99 deletions.
61 changes: 13 additions & 48 deletions examples/test/glsdf3test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func test_sdf_gpu_cpu() error {
for _, primitive := range PremadePrimitives2D {
log.Printf("evaluate 2D %s\n", getBaseTypename(primitive))
bounds := primitive.Bounds()
pos := appendMeshgrid2D(scratchPos2[:0], bounds, nx, ny)
pos := ms2.AppendGrid(scratchPos2[:0], bounds, nx, ny)
distCPU := scratchDistCPU[:len(pos)]
distGPU := scratchDistGPU[:len(pos)]
sdfcpu, err := gleval.AssertSDF2(primitive)
Expand Down Expand Up @@ -215,7 +215,7 @@ func test_sdf_gpu_cpu() error {
for _, primitive := range PremadePrimitives {
log.Printf("begin evaluating %s", getBaseTypename(primitive))
bounds := primitive.Bounds()
pos := appendMeshgrid(scratchPos[:0], bounds, nx, ny, nz)
pos := ms3.AppendGrid(scratchPos[:0], bounds, nx, ny, nz)
distCPU := scratchDistCPU[:len(pos)]
distGPU := scratchDistGPU[:len(pos)]
sdfcpu, err := gleval.AssertSDF3(primitive)
Expand Down Expand Up @@ -249,7 +249,7 @@ func test_sdf_gpu_cpu() error {
p2 := PremadePrimitives[1]
obj := op(p1, p2)
bounds := obj.Bounds()
pos := appendMeshgrid(scratchPos[:0], bounds, nx, ny, nz)
pos := ms3.AppendGrid(scratchPos[:0], bounds, nx, ny, nz)
distCPU := scratchDistCPU[:len(pos)]
distGPU := scratchDistGPU[:len(pos)]
sdfcpu, err := gleval.AssertSDF3(obj)
Expand Down Expand Up @@ -283,7 +283,7 @@ func test_sdf_gpu_cpu() error {
p2 := PremadePrimitives[1]
obj := op(.1, p1, p2)
bounds := obj.Bounds()
pos := appendMeshgrid(scratchPos[:0], bounds, nx, ny, nz)
pos := ms3.AppendGrid(scratchPos[:0], bounds, nx, ny, nz)
distCPU := scratchDistCPU[:len(pos)]
distGPU := scratchDistGPU[:len(pos)]
sdfcpu, err := gleval.AssertSDF3(obj)
Expand Down Expand Up @@ -317,7 +317,7 @@ func test_sdf_gpu_cpu() error {
primitive := PremadePrimitives[rng.Intn(len(PremadePrimitives))]
obj := op(primitive, rng)
bounds := obj.Bounds()
pos := appendMeshgrid(scratchPos[:0], bounds, nx, ny, nz)
pos := ms3.AppendGrid(scratchPos[:0], bounds, nx, ny, nz)
distCPU := scratchDistCPU[:len(pos)]
distGPU := scratchDistGPU[:len(pos)]
sdfcpu, err := gleval.AssertSDF3(obj)
Expand Down Expand Up @@ -357,7 +357,7 @@ func test_sdf_gpu_cpu() error {
primitive := PremadePrimitives2D[rng.Intn(len(PremadePrimitives2D))]
obj := op(primitive, rng)
bounds := obj.Bounds()
pos := appendMeshgrid2D(scratchPos2[:0], bounds, nx, ny)
pos := ms2.AppendGrid(scratchPos2[:0], bounds, nx, ny)
distCPU := scratchDistCPU[:len(pos)]
distGPU := scratchDistGPU[:len(pos)]
sdfcpu, err := gleval.AssertSDF2(obj)
Expand Down Expand Up @@ -399,7 +399,7 @@ func test_sdf_gpu_cpu() error {
primitive := PremadePrimitives2D[rng.Intn(len(PremadePrimitives2D))]
obj := op(primitive, rng)
bounds := obj.Bounds()
pos := appendMeshgrid(scratchPos[:0], bounds, nx, ny, nz)
pos := ms3.AppendGrid(scratchPos[:0], bounds, nx, ny, nz)
distCPU := scratchDistCPU[:len(pos)]
distGPU := scratchDistGPU[:len(pos)]
sdfcpu, err := gleval.AssertSDF3(obj)
Expand Down Expand Up @@ -525,7 +525,7 @@ func test_union2D() error {
return err
}
bb := union.Bounds()
pos := appendMeshgrid2D(nil, bb, 32, 32)
pos := ms2.AppendGrid(nil, bb, 32, 32)
distCPU := make([]float32, len(pos))
distGPU := make([]float32, len(pos))
err = sdfCPU.Evaluate(pos, distCPU, nil)
Expand Down Expand Up @@ -558,7 +558,7 @@ func test_union3D() error {
sdfGPU := makeGPUSDF3(union)

bb := union.Bounds()
pos := appendMeshgrid(nil, bb, 32, 32, 32)
pos := ms3.AppendGrid(nil, bb, 32, 32, 32)
distCPU := make([]float32, len(pos))
distGPU := make([]float32, len(pos))
err = sdfCPU.Evaluate(pos, distCPU, nil)
Expand Down Expand Up @@ -685,7 +685,7 @@ func testsdf2(name string, sdfcpu, sdfgpu gleval.SDF2) (err error) {
var pos []ms2.Vec
for _, sz := range []int{32, 256, 512} {
now := time.Now()
pos = appendMeshgrid2D(pos[:0], bbGPU, sz, sz)
pos = ms2.AppendGrid(pos[:0], bbGPU, sz, sz)
distCPU := make([]float32, len(pos))
distGPU := make([]float32, len(pos))
err = sdfgpu.Evaluate(pos, distGPU, nil)
Expand Down Expand Up @@ -793,13 +793,13 @@ func test_bounds(sdf gleval.SDF3, scratchDist []float32, userData any) (err erro
// - Negative distance, which implies interior of SDF outside the intended bounding box.
// - Normals which point towards the original bounding box, which imply a SDF surface outside the bounding box.
var offs = [3]float32{-1, 0, 1}
originalPos := meshgrid(bb, nxbb, nybb, nzbb)
originalPos := ms3.AppendGrid(nil, bb, nxbb, nybb, nzbb)
newPos := make([]ms3.Vec, len(originalPos))
normals := make([]ms3.Vec, len(originalPos))

// Calculate approximate expected normal directions.
wantNormals := make([]ms3.Vec, len(originalPos))
wantNormals = appendMeshgrid(wantNormals[:0], bb.Add(ms3.Scale(-1, bb.Center())), nxbb, nybb, nzbb)
wantNormals = ms3.AppendGrid(wantNormals[:0], bb.Add(ms3.Scale(-1, bb.Center())), nxbb, nybb, nzbb)
var normOmitLog sync.Once
var offsize ms3.Vec
for _, xo := range offs {
Expand All @@ -813,7 +813,7 @@ func test_bounds(sdf gleval.SDF3, scratchDist []float32, userData any) (err erro
}
newBB := bb.Add(offsize)
// New mesh lies outside of bounding box.
newPos = appendMeshgrid(newPos[:0], newBB, nxbb, nybb, nzbb)
newPos = ms3.AppendGrid(newPos[:0], newBB, nxbb, nybb, nzbb)
// Calculate expected normal directions.

err = sdf.Evaluate(newPos, dist, userData)
Expand Down Expand Up @@ -857,41 +857,6 @@ func test_bounds(sdf gleval.SDF3, scratchDist []float32, userData any) (err erro
return nil
}

func meshgrid(bounds ms3.Box, nx, ny, nz int) []ms3.Vec {
return appendMeshgrid(make([]ms3.Vec, 0, nx*ny*nz), bounds, nx, ny, nz)
}

func appendMeshgrid(dst []ms3.Vec, bounds ms3.Box, nx, ny, nz int) []ms3.Vec {
nxyz := ms3.Vec{X: float32(nx), Y: float32(ny), Z: float32(nz)}
dxyz := ms3.DivElem(bounds.Size(), nxyz)
var xyz ms3.Vec
for k := 0; k < nz; k++ {
xyz.Z = bounds.Min.Z + dxyz.Z*float32(k)
for j := 0; j < ny; j++ {
xyz.Y = bounds.Min.Y + dxyz.Y*float32(j)
for i := 0; i < nx; i++ {
xyz.X = bounds.Min.X + dxyz.X*float32(i)
dst = append(dst, xyz)
}
}
}
return dst
}

func appendMeshgrid2D(dst []ms2.Vec, bounds ms2.Box, nx, ny int) []ms2.Vec {
nxyz := ms2.Vec{X: float32(nx), Y: float32(ny)}
dxyz := ms2.DivElem(bounds.Size(), nxyz)
var xy ms2.Vec
for j := 0; j < ny; j++ {
xy.Y = bounds.Min.Y + dxyz.Y*float32(j)
for i := 0; i < nx; i++ {
xy.X = bounds.Min.X + dxyz.X*float32(i)
dst = append(dst, xy)
}
}
return dst
}

func makeGPUSDF3(s glbuild.Shader3D) *gleval.SDF3Compute {
if s == nil {
panic("nil Shader3D")
Expand Down
49 changes: 49 additions & 0 deletions glbuild/glbuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,54 @@ func b2i(b bool) int {
return 0
}

// OverloadShader3DBounds overloads a [Shader3D] Bounds method with the argument bounding box.
func OverloadShader3DBounds(s Shader3D, bb ms3.Box) Shader3D {
return &overloadBounds3{
Shader3D: s,
bb: bb,
}
}

type overloadBounds3 struct {
Shader3D
bb ms3.Box
}

func (ob3 *overloadBounds3) Bounds() ms3.Box { return ob3.bb }

// Evaluate implements the gleval.SDF3 interface.
func (ob3 *overloadBounds3) Evaluate(pos []ms3.Vec, dist []float32, userData any) error {
sdf, ok := ob3.Shader3D.(sdf3)
if !ok {
return fmt.Errorf("%T does not implement gleval.SDF3", ob3.Shader3D)
}
return sdf.Evaluate(pos, dist, userData)
}

// OverloadShader2DBounds overloads a [Shader2D] Bounds method with the argument bounding box.
func OverloadShader2DBounds(s Shader2D, bb ms2.Box) Shader2D {
return &overloadBounds2{
Shader2D: s,
bb: bb,
}
}

type overloadBounds2 struct {
Shader2D
bb ms2.Box
}

func (ob2 *overloadBounds2) Bounds() ms2.Box { return ob2.bb }

// Evaluate implements the gleval.SDF2 interface.
func (ob3 *overloadBounds2) Evaluate(pos []ms2.Vec, dist []float32, userData any) error {
sdf, ok := ob3.Shader2D.(sdf2)
if !ok {
return fmt.Errorf("%T does not implement gleval.SDF3", ob3.Shader2D)
}
return sdf.Evaluate(pos, dist, userData)
}

var _ Shader3D = (*CachedShader3D)(nil) // Interface implementation compile-time check.

// CachedShader3D implements the Shader3D interface with results it caches for another Shader3D on a call to RefreshCache.
Expand Down Expand Up @@ -1080,6 +1128,7 @@ func (nos3 *nameOverloadShader3D) AppendShaderObjects(objs []ShaderObject) []Sha
return nos3.Shader.AppendShaderObjects(objs)
}

// mirrors of gleval.SDF3 and gleval.SDF2 interfaces to avoid cyclic dependencies.
type (
sdf3 interface {
Evaluate(pos []ms3.Vec, dist []float32, userData any) error
Expand Down
50 changes: 33 additions & 17 deletions gleval/cpu.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func (sdf *SDF3CPU) Evaluate(pos []ms3.Vec, dist []float32, userData any) error
err2 := sdf.vp.AssertAllReleased()
if err != nil {
if err2 != nil {
return fmt.Errorf("VecPool leak:(%s) SDF3 error:(%s)", err2, err)
return fmt.Errorf("VecPool leak: %s\nSDF3 error: %s", err2, err)
}
return err
}
Expand Down Expand Up @@ -129,7 +129,7 @@ func (sdf *SDF2CPU) Evaluate(pos []ms2.Vec, dist []float32, userData any) error
err2 := sdf.vp.AssertAllReleased()
if err != nil {
if err2 != nil {
return fmt.Errorf("VecPool leak:(%s) SDF2 error:(%s)", err2, err)
return fmt.Errorf("VecPool leak: %s\nSDF2 error: %s", err2, err)
}
return err
}
Expand Down Expand Up @@ -195,16 +195,28 @@ func (vp *VecPool) AssertAllReleased() error {
return nil
}

// TotalAlloc returns the number of bytes allocated by all underlying buffers.
func (vp *VecPool) TotalAlloc() uint64 {
return vp.Float.TotalAlloc() + vp.V2.TotalAlloc() + vp.V3.TotalAlloc()
// SetMinAllocationLen sets the minimum length allocated when creating a new buffer for all buffer pools.
func (vp *VecPool) SetMinAllocationLen(minumumAlloca int) {
if minumumAlloca < 0 {
panic("invalid minimum allocation size")
}
vp.Float.minAllocation = minumumAlloca
vp.V2.minAllocation = minumumAlloca
vp.V3.minAllocation = minumumAlloca
}

// TotalSize returns the number of bytes allocated by all underlying buffers.
func (vp *VecPool) TotalSize() uint64 {
return vp.Float.TotalSize() + vp.V2.TotalSize() + vp.V3.TotalSize()
}

type bufPool[T any] struct {
_ins [][]T
_acquired []bool
// releaseErr stores error on Release call since Release is usually used in concert with defer, thus losing the error.
releaseErr error
// minAllocation sets the minimum size of a buffer allocation.
minAllocation int
}

// Acquire gets a buffer from the pool of the desired length and marks it as used.
Expand All @@ -216,7 +228,11 @@ func (bp *bufPool[T]) Acquire(length int) []T {
return bp._ins[i][:length]
}
}
newSlice := make([]T, length)
allocLen := length
if bp.minAllocation > allocLen {
allocLen = bp.minAllocation
}
newSlice := make([]T, allocLen)
newSlice = newSlice[:cap(newSlice)]
bp._ins = append(bp._ins, newSlice)
bp._acquired = append(bp._acquired, true)
Expand Down Expand Up @@ -258,13 +274,8 @@ func (bp *bufPool[T]) assertAllReleased() error {
return nil
}

// NumBuffers returns quantity of buffers allocated by the pool.
func (bp *bufPool[T]) NumBuffers() int {
return len(bp._ins)
}

// TotalAlloc returns total amount of memory used by buffer in bytes.
func (bp *bufPool[T]) TotalAlloc() uint64 {
// TotalSize returns total amount of memory used by buffer in bytes.
func (bp *bufPool[T]) TotalSize() uint64 {
var t T
size := uint64(reflect.TypeOf(t).Size())
var n uint64
Expand All @@ -274,8 +285,13 @@ func (bp *bufPool[T]) TotalAlloc() uint64 {
return size * n
}

// Free returns total number of free buffers. To calculate number of used buffers do [bufPool.NumBuffers]() - [bufPool.Free]().
func (bp *bufPool[T]) Free() (free int) {
// NumBuffers returns quantity of buffers allocated by the pool.
func (bp *bufPool[T]) NumBuffers() int {
return len(bp._ins)
}

// NumFree returns total number of free buffers. To calculate number of used buffers do [bufPool.NumBuffers]() - [bufPool.NumFree]().
func (bp *bufPool[T]) NumFree() (free int) {
for _, b := range bp._acquired {
if !b {
free++
Expand All @@ -285,7 +301,7 @@ func (bp *bufPool[T]) Free() (free int) {
}

func (bp *bufPool[T]) String() string {
alloc := bp.TotalAlloc()
alloc := bp.TotalSize()
const (
_ = 1 << (10 * iota)
kB
Expand All @@ -300,5 +316,5 @@ func (bp *bufPool[T]) String() string {
alloc /= kB
units = "kB"
}
return fmt.Sprintf("bufPool{free:%d/%d mem:%d%s}", bp.Free(), bp.NumBuffers(), alloc, units)
return fmt.Sprintf("bufPool{free:%d/%d mem:%d%s}", bp.NumFree(), bp.NumBuffers(), alloc, units)
}
13 changes: 7 additions & 6 deletions gleval/gleval.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,18 +81,19 @@ func NormalsCentralDiff(s SDF3, pos []ms3.Vec, normals []ms3.Vec, step float32,
if err != nil {
return err
}

switch dim {
case 0:
for i := range normals {
normals[i].X = d1[i] - d2[i]
for i, d := range d1 {
normals[i].X = d - d2[i]
}
case 1:
for i := range normals {
normals[i].Y = d1[i] - d2[i]
for i, d := range d1 {
normals[i].Y = d - d2[i]
}
case 2:
for i := range normals {
normals[i].Z = d1[i] - d2[i]
for i, d := range d1 {
normals[i].Z = d - d2[i]
}
}
}
Expand Down
Loading

0 comments on commit 3dfe8f4

Please sign in to comment.