Skip to content

Instantly share code, notes, and snippets.

@s-l-teichmann
Created November 5, 2012 01:18
Show Gist options
  • Save s-l-teichmann/4014677 to your computer and use it in GitHub Desktop.
Save s-l-teichmann/4014677 to your computer and use it in GitHub Desktop.
Fills a LevelDB with generated terrain chunks.
//
// terrainstore.go
// ---------------
//
// Fills a LevelDB with generated terrain chunks.
//
// (c) 2012 by Sascha L. Teichmann
//
package main
import (
"bitbucket.org/s_l_teichmann/simplexnoise"
"bytes"
"encoding/binary"
"flag"
leveldb "github.com/jmhodges/levigo"
//leveldb "github.com/jmhodges/levigo_leveldb_1.4"
"log"
"runtime"
)
const (
BITS = uint32(20)
ONE32 = uint32(1)
ZERO32 = uint32(0)
HI_MASK = ONE32 << (BITS + ONE32)
ZERO64 = uint64(0)
ONE64 = uint64(1)
WORLD_MID = ^(^0 << 19)
)
type PUGeneratorOptions struct {
Seed int64
Step float64
PUSize int
Thresh float64
PT float64
PB float64
}
type PUGenerator struct {
noise *simplexnoise.SimplexNoise
Options *PUGeneratorOptions
}
type Pos struct {
X, Y, Z int
}
type PersistenceUnit struct {
Pos
data []byte
}
func (p *Pos) ZCode() uint64 {
return ZEncode(
uint32(p.X+WORLD_MID),
uint32(p.Y+WORLD_MID),
uint32(p.Z+WORLD_MID))
}
func NewPersistenceUnit(x, y, z int, data []byte) *PersistenceUnit {
return &PersistenceUnit{Pos{x, y, z}, data}
}
func (pu *PersistenceUnit) Data() []byte {
return pu.data
}
type RLEncoder struct {
buf *bytes.Buffer
last byte
count int
}
func NewRLEncoder() *RLEncoder {
return &RLEncoder{new(bytes.Buffer), 0, 0}
}
func (rle *RLEncoder) Reset() {
rle.buf.Reset()
rle.last = 0
rle.count = 0
}
func (rle *RLEncoder) WriteByte(b byte) {
if rle.last == b {
rle.count++
} else {
rle.Flush()
rle.last = b
rle.count = 1
}
}
func (rle *RLEncoder) Flush() {
if rle.count > 0 {
for ; rle.count > 127; rle.count -= 127 {
rle.buf.WriteByte(0xff)
rle.buf.WriteByte(rle.last)
}
if rle.count == 1 {
if (rle.last & (1 << 7)) != 0 {
rle.buf.WriteByte((1 << 7) | 1)
}
rle.buf.WriteByte(rle.last)
} else {
rle.buf.WriteByte((1 << 7) | byte(rle.count))
rle.buf.WriteByte(rle.last)
}
rle.count = 0
}
}
func (rle *RLEncoder) Bytes() []byte {
return rle.buf.Bytes()
}
type Backend struct {
db *leveldb.DB
PUChannel chan *PersistenceUnit
}
func NewBackend(path string) (*Backend, error) {
opts := leveldb.NewOptions()
opts.SetCreateIfMissing(true)
db, err := leveldb.Open(path, opts)
if err != nil {
return nil, err
}
pu_chan := make(chan *PersistenceUnit, 32)
return &Backend{db, pu_chan}, nil
}
func (p *Backend) Close() {
log.Println("closing persistence layer")
p.db.Close()
close(p.PUChannel)
}
func (b *Backend) Store(done chan bool, worker int) {
wo := leveldb.NewWriteOptions()
for {
unit := <-b.PUChannel
if unit == nil {
worker--
if worker <= 0 {
break
}
continue
}
data := unit.Data()
l := len(data)
zcode := unit.ZCode()
log.Printf("Store (%d, %d, %d) size = %d, code = %x\n",
unit.X, unit.Y, unit.Z, l, zcode)
key := ZCodeAsKey(zcode)
b.db.Put(wo, key, data)
}
done <- true
}
func NewPUGenerator(options *PUGeneratorOptions) *PUGenerator {
noise := simplexnoise.NewSimplexNoise(options.Seed)
return &PUGenerator{noise, options}
}
func (tg *PUGenerator) generatePU(p *Pos, pt, pb float64, rle *RLEncoder) {
size := tg.Options.PUSize
step := tg.Options.Step
x_start := float64(p.X*size) * step
y_start := float64(p.Y*size) * step
z_start := float64(p.Z*size) * step
// pt = 0*m + b <=> b = pt
// pb = (size-1)*m + pt
// m = (pb-pt)/(size-1)
b := float64(pt)
var m float64
if size == 1 {
m = 0
} else {
m = float64(pb-pt) / float64(size-1)
}
noise := tg.noise
thresh := tg.Options.Thresh
y_pos := y_start
for y := 0; y < size; y++ {
prob := m*float64(y) + b
z_pos := z_start
for z := 0; z < size; z++ {
x_pos := x_start
for x := 0; x < size; x++ {
n := (noise.Noise3D(x_pos, y_pos, z_pos) + 1.0) * 0.5 * prob
var v byte
if n < thresh {
v = 0
} else {
v = 1
}
rle.WriteByte(v)
x_pos += step
}
z_pos += step
}
y_pos += step
}
rle.Flush()
}
func (tg *PUGenerator) Generate(jobs chan *Pos, units chan *PersistenceUnit) {
for {
p := <-jobs
if p == nil {
break
}
log.Printf("Generate (%d, %d, %d)\n", p.X, p.Y, p.Z)
if p.Y <= 0 { // Do not generate anything when above the ground.
var pt, pb float64
if p.Y == 0 {
// generate surface
pt, pb = tg.Options.PT, tg.Options.PB
} else {
// generate undergroud
pt, pb = 1.0, 1.0
}
rle := NewRLEncoder()
tg.generatePU(p, pt, pb, rle)
units <- NewPersistenceUnit(p.X, p.Y, p.Z, rle.Bytes())
}
}
units <- nil
}
func ZEncode(x, y, z uint32) uint64 {
code, p := ZERO64, ONE64
for mask := ONE32; mask != HI_MASK; mask <<= 1 {
if (x & mask) != 0 {
code |= p
}
p <<= 1
if (y & mask) != 0 {
code |= p
}
p <<= 1
if (z & mask) != 0 {
code |= p
}
p <<= 1
}
return code
}
func ZDecode(code uint64) (x, y, z uint32) {
p := ONE64
x, y, z = ZERO32, ZERO32, ZERO32
for mask := ONE32; mask != HI_MASK; mask <<= 1 {
if (code & p) != 0 {
x |= mask
}
p <<= 1
if (code & p) != 0 {
y |= mask
}
p <<= 1
if (code & p) != 0 {
z |= mask
}
p <<= 1
}
return
}
func ZCodeAsKey(code uint64) []byte {
var b bytes.Buffer // XXX: Is there a better way todo this?
binary.Write(&b, binary.BigEndian, &code)
return b.Bytes()
}
func main() {
var (
tgo PUGeneratorOptions
db string
x_size int
y_size int
z_size int
x_pos int
y_pos int
z_pos int
worker int
)
flag.StringVar(&db, "db", "terrain.db", "World database path")
flag.IntVar(&x_size, "xsize", 512, "number of units in x direction")
flag.IntVar(&y_size, "ysize", 512, "number of units in y direction")
flag.IntVar(&z_size, "zsize", 3, "number of units in z direction")
flag.IntVar(&x_pos, "xpos", 0, "position in x direction")
flag.IntVar(&y_pos, "ypos", 0, "position in y direction")
flag.IntVar(&z_pos, "zpos", 0, "position in z direction")
flag.Int64Var(&tgo.Seed, "seed", 1, "random seed")
flag.Float64Var(&tgo.Step, "step", 1.0/128.0, "step width in noise range")
flag.Float64Var(&tgo.Thresh, "thresh", 0.3, "threshold to generate holes")
flag.IntVar(&tgo.PUSize, "pusize", 64, "size of PUs")
flag.IntVar(&worker, "worker", runtime.NumCPU(), "number of workers")
flag.Float64Var(&tgo.PT, "pt", 0.0, "probability top")
flag.Float64Var(&tgo.PB, "pb", 1.0, "probability bottom")
flag.Parse()
backend, err := NewBackend(db)
if err != nil {
log.Fatal("Cannot open world database.", err)
}
defer backend.Close()
tg := NewPUGenerator(&tgo)
done_chan := make(chan bool)
go backend.Store(done_chan, worker)
work_chan := make(chan *Pos)
for w := 0; w < worker; w++ {
go tg.Generate(work_chan, backend.PUChannel)
}
for z := 0; z < z_size; z++ {
zp := z + z_pos
for y := 0; y < y_size; y++ {
yp := y + y_pos
for x := 0; x < x_size; x++ {
xp := x + x_pos
work_chan <- &Pos{xp, yp, zp}
}
}
}
for w := 0; w < worker; w++ {
work_chan <- nil
}
<-done_chan
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment