diff --git a/common/coords.go b/common/coords.go index ae6e92b..4c32122 100644 --- a/common/coords.go +++ b/common/coords.go @@ -145,6 +145,10 @@ func CoordToInterleaved(c Coord) (result int64) { return } +func invert16(x int16) int16 { + return -x - 1 +} + func InterleavedToCoord(pos int64) Coord { const end = 1 << (numBitsPerComponent + 1) var x, y, z int16 diff --git a/common/coords_test.go b/common/coords_test.go index 519ac4a..011b05f 100644 --- a/common/coords_test.go +++ b/common/coords_test.go @@ -5,6 +5,7 @@ package common import ( + "math" "math/rand" "testing" ) @@ -180,6 +181,15 @@ func outsiders(zmin, zmax int64, fn func(int64)) { } } +func TestInvert16(t *testing.T) { + if invert16(math.MaxInt16) != math.MinInt16 { + t.Errorf("invert16(max) != min\n") + } + if invert16(math.MinInt16) != math.MaxInt16 { + t.Errorf("invert16(max) != min\n") + } +} + func TestBigMin(t *testing.T) { const tries = 20 for i := 0; i < tries; i++ { diff --git a/tilemapper/main.go b/tilemapper/main.go index f52170b..4abbeca 100644 --- a/tilemapper/main.go +++ b/tilemapper/main.go @@ -64,10 +64,11 @@ func main() { q2x, q2y, q2z := q1x+int16(width)-1, q1y+int16(depth)-1, q1z+int16(height)-1 renderer := NewRenderer(q1x, q1z, width, height) + yOrder := NewYOrder(renderer, 512) numBlocks := 0 drawBlock := func(block *common.Block) { - if err := renderer.RenderBlock(block, colors.NameIndex); err != nil { + if err := yOrder.RenderBlock(block, colors.NameIndex); err != nil { log.Printf("WARN: rendering block failed: %s", err) } numBlocks++ @@ -84,6 +85,9 @@ func main() { if err = client.QueryCuboid(cuboid, drawBlock); err != nil { log.Fatalf("query failed: %s", err) } + if err = yOrder.Drain(colors.NameIndex); err != nil { + log.Printf("WARN: rendering block failed: %s", err) + } if renderer.IsFilled() { break } diff --git a/tilemapper/renderer.go b/tilemapper/renderer.go index 22bac7a..80e63fe 100644 --- a/tilemapper/renderer.go +++ b/tilemapper/renderer.go @@ -5,6 +5,7 @@ package main import ( + "container/heap" "image" "image/color" "math" @@ -24,6 +25,95 @@ type Renderer struct { Rejected int } +type YOrder struct { + Renderer *Renderer + blocks []*common.Block + capacity int +} + +func NewYOrder(renderer *Renderer, capacity int) *YOrder { + return &YOrder{ + Renderer: renderer, + blocks: make([]*common.Block, 0, capacity), + capacity: capacity} +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func copyData(data []byte) []byte { + l := len(data) + ndata := make([]byte, l, max(l, 8*1024)) + copy(ndata, data) + return ndata +} + +func (yo *YOrder) RenderBlock(block *common.Block, nameIndex map[string]int32) (err error) { + var nblock *common.Block + if len(yo.blocks) == yo.capacity { + oblock := yo.blocks[0] + if oblock.Coord.Y < block.Coord.Y { + // New one is above highest old. Directly render new. + err = yo.Renderer.RenderBlock(block, nameIndex) + return + } + // Render old one. Store copy of new in heap. + heap.Pop(yo) + err = yo.Renderer.RenderBlock(oblock, nameIndex) + l := len(block.Data) + if cap(oblock.Data) < l { + oblock.Data = make([]byte, l, max(l, 8*1024)) + } else { + oblock.Data = oblock.Data[0:l] + } + copy(oblock.Data, block.Data) + oblock.Coord = block.Coord + nblock = oblock + } else { + nblock = &common.Block{Coord: block.Coord, Data: copyData(block.Data)} + } + + heap.Push(yo, nblock) + return +} + +func (yo *YOrder) Drain(nameIndex map[string]int32) (err error) { + for len(yo.blocks) > 0 { + if err = yo.Renderer.RenderBlock(heap.Pop(yo).(*common.Block), nameIndex); err != nil { + return + } + } + return +} + +func (yo *YOrder) Len() int { + return len(yo.blocks) +} + +func (yo *YOrder) Swap(i, j int) { + yo.blocks[i], yo.blocks[j] = yo.blocks[j], yo.blocks[i] +} + +func (yo *YOrder) Less(i, j int) bool { + // Reverse order intented. + return yo.blocks[i].Coord.Y > yo.blocks[j].Coord.Y +} + +func (yo *YOrder) Push(x interface{}) { + yo.blocks = append(yo.blocks, x.(*common.Block)) +} + +func (yo *YOrder) Pop() (x interface{}) { + l := len(yo.blocks) + x = yo.blocks[l-1] + yo.blocks = yo.blocks[0 : l-1] + return x +} + func NewRenderer(xOfs, zOfs int16, width, height int) (renderer *Renderer) { dim := width * height pixSize := dim * 16 * 16