2014-09-20 15:21:01 +02:00
|
|
|
// Copyright 2014 by Sascha L. Teichmann
|
|
|
|
// Use of this source code is governed by the MIT license
|
|
|
|
// that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package common
|
|
|
|
|
|
|
|
import (
|
|
|
|
"image/color"
|
2015-07-22 01:11:14 +02:00
|
|
|
"io/ioutil"
|
2014-09-20 15:21:01 +02:00
|
|
|
"log"
|
|
|
|
"path/filepath"
|
|
|
|
"strconv"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
tileWidth = 18
|
|
|
|
tileHeight = 18
|
|
|
|
yOrderCapacity = 512
|
|
|
|
)
|
|
|
|
|
2015-07-27 19:03:47 +02:00
|
|
|
const (
|
|
|
|
MaxHeight = 1934
|
|
|
|
MinHeight = -1934
|
|
|
|
)
|
|
|
|
|
2014-09-20 15:21:01 +02:00
|
|
|
// To scan the whole height in terms of the y coordinate
|
|
|
|
// the database is queried in height units defined in the tileDepths table.
|
|
|
|
var tileDepths = [...][2]int16{
|
2015-07-27 19:03:47 +02:00
|
|
|
{1024, MaxHeight},
|
2014-09-20 15:21:01 +02:00
|
|
|
{256, 1023},
|
|
|
|
{128, 255},
|
|
|
|
{64, 127},
|
|
|
|
{32, 63},
|
|
|
|
{16, 31},
|
|
|
|
{8, 15},
|
|
|
|
{4, 7},
|
|
|
|
{2, 3},
|
|
|
|
{0, 1},
|
|
|
|
{-1, 0},
|
|
|
|
{-4, -2},
|
|
|
|
{-8, -5},
|
|
|
|
{-16, -9},
|
|
|
|
{-32, -17},
|
|
|
|
{-64, -33},
|
|
|
|
{-128, -65},
|
|
|
|
{-256, -129},
|
|
|
|
{-1024, -257},
|
2015-07-27 19:03:47 +02:00
|
|
|
{MinHeight, -1025}}
|
2014-09-20 15:21:01 +02:00
|
|
|
|
2015-07-22 01:11:14 +02:00
|
|
|
var BackgroundColor = color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
|
|
|
|
|
2016-05-08 17:33:51 +02:00
|
|
|
type BaseTileUpdateFunc func(x, y int, hash []byte) bool
|
2016-05-08 15:38:50 +02:00
|
|
|
|
2014-09-20 15:21:01 +02:00
|
|
|
type BaseTileCreator struct {
|
2015-07-22 01:11:14 +02:00
|
|
|
client *RedisClient
|
|
|
|
colors *Colors
|
|
|
|
renderer *Renderer
|
|
|
|
yOrder *YOrder
|
2015-07-27 19:03:47 +02:00
|
|
|
yMin int16
|
|
|
|
yMax int16
|
2015-07-22 01:11:14 +02:00
|
|
|
baseDir string
|
2016-05-08 15:38:50 +02:00
|
|
|
update BaseTileUpdateFunc
|
2015-07-22 01:11:14 +02:00
|
|
|
emptyImage []byte
|
2016-04-23 16:45:33 +02:00
|
|
|
bg color.RGBA
|
2014-09-20 15:21:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewBaseTileCreator(
|
|
|
|
client *RedisClient,
|
|
|
|
colors *Colors,
|
2016-04-23 16:45:33 +02:00
|
|
|
bg color.RGBA,
|
2015-07-27 19:03:47 +02:00
|
|
|
yMin, yMax int16,
|
2014-10-26 18:36:47 +01:00
|
|
|
transparent bool,
|
2014-09-20 15:21:01 +02:00
|
|
|
baseDir string,
|
2016-05-08 15:38:50 +02:00
|
|
|
update BaseTileUpdateFunc) *BaseTileCreator {
|
2014-10-26 18:36:47 +01:00
|
|
|
renderer := NewRenderer(tileWidth, tileHeight, transparent)
|
2015-07-27 19:03:47 +02:00
|
|
|
yMin, yMax = Order16(yMin, yMax)
|
2014-09-20 15:21:01 +02:00
|
|
|
return &BaseTileCreator{
|
|
|
|
client: client,
|
|
|
|
colors: colors,
|
2016-04-23 16:45:33 +02:00
|
|
|
bg: bg,
|
2014-09-20 15:21:01 +02:00
|
|
|
renderer: renderer,
|
|
|
|
yOrder: NewYOrder(renderer, yOrderCapacity),
|
2015-07-27 19:03:47 +02:00
|
|
|
yMin: yMin,
|
|
|
|
yMax: yMax,
|
2014-09-20 15:21:01 +02:00
|
|
|
baseDir: baseDir,
|
|
|
|
update: update}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (btc *BaseTileCreator) Close() error {
|
|
|
|
return btc.client.Close()
|
|
|
|
}
|
|
|
|
|
2016-05-08 15:38:50 +02:00
|
|
|
func (btc *BaseTileCreator) CreateTile(x, z int16, i, j int) (bool, error) {
|
2014-09-20 15:21:01 +02:00
|
|
|
btc.renderer.Reset()
|
|
|
|
btc.renderer.SetPos(x, z)
|
|
|
|
btc.yOrder.Reset()
|
|
|
|
|
|
|
|
drawBlock := func(block *Block) {
|
2014-10-19 21:05:38 +02:00
|
|
|
if err := btc.yOrder.RenderBlock(block, btc.colors); err != nil {
|
2015-07-20 14:19:41 +02:00
|
|
|
log.Printf("WARN: rendering block failed: %s\n", err)
|
2014-09-20 15:21:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var c1, c2 Coord
|
|
|
|
|
|
|
|
nareas := make([]Area, 0, tileWidth*tileHeight/2)
|
|
|
|
oareas := make([]Area, 1, tileWidth*tileHeight/2)
|
|
|
|
|
|
|
|
oareas[0] = Area{
|
|
|
|
X1: 0, Z1: 0,
|
|
|
|
X2: int16(tileWidth) - 1, Z2: int16(tileHeight) - 1}
|
|
|
|
|
|
|
|
for _, yRange := range tileDepths {
|
2015-07-27 19:03:47 +02:00
|
|
|
if yRange[0] > btc.yMax || yRange[1] < btc.yMin {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
c1.Y = max16(yRange[0], btc.yMin)
|
|
|
|
c2.Y = min16(yRange[1], btc.yMax)
|
2014-09-20 15:21:01 +02:00
|
|
|
|
|
|
|
nareas = btc.renderer.UncoveredAreas(nareas, oareas)
|
|
|
|
|
|
|
|
if len(nareas) == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, area := range nareas {
|
|
|
|
c1.X = area.X1 + x
|
|
|
|
c1.Z = area.Z1 + z
|
|
|
|
c2.X = area.X2 + x
|
|
|
|
c2.Z = area.Z2 + z
|
|
|
|
query := Cuboid{P1: c1, P2: c2}
|
|
|
|
if err := btc.client.QueryCuboid(query, drawBlock); err != nil {
|
2016-05-08 15:38:50 +02:00
|
|
|
return false, err
|
2014-09-20 15:21:01 +02:00
|
|
|
}
|
2014-10-19 21:05:38 +02:00
|
|
|
if err := btc.yOrder.Drain(btc.colors); err != nil {
|
2015-07-20 14:19:41 +02:00
|
|
|
log.Printf("WARN: rendering block failed: %s\n", err)
|
2014-09-20 15:21:01 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
oareas, nareas = nareas, oareas[0:0]
|
|
|
|
}
|
|
|
|
|
2015-07-22 01:11:14 +02:00
|
|
|
path := filepath.Join(btc.baseDir, strconv.Itoa(i), strconv.Itoa(j)+".png")
|
|
|
|
|
|
|
|
// Empty images are likely to be produced during seeding.
|
2016-05-08 15:38:50 +02:00
|
|
|
if btc.update == nil && btc.renderer.IsEmpty() {
|
2015-07-22 01:11:14 +02:00
|
|
|
// To avoid redundant encoding cache the resulting empty image.
|
|
|
|
if btc.emptyImage == nil {
|
|
|
|
var err error
|
2016-04-23 16:45:33 +02:00
|
|
|
m := BackgroundImage((tileWidth-2)*16, (tileHeight-2)*16, btc.bg)
|
2015-07-22 01:11:14 +02:00
|
|
|
if btc.emptyImage, err = EncodeToMem(m); err != nil {
|
2016-05-08 15:38:50 +02:00
|
|
|
return false, err
|
2015-07-22 01:11:14 +02:00
|
|
|
}
|
|
|
|
}
|
2015-07-23 16:20:42 +02:00
|
|
|
//log.Printf("Writing empty (%d, %d) to file %s\n", x, z, path)
|
2016-05-08 15:38:50 +02:00
|
|
|
return true, ioutil.WriteFile(path, btc.emptyImage, 0666)
|
2015-07-22 01:11:14 +02:00
|
|
|
}
|
|
|
|
|
2014-09-20 15:21:01 +02:00
|
|
|
image := btc.renderer.CreateShadedImage(
|
|
|
|
16, 16, (tileWidth-2)*16, (tileHeight-2)*16,
|
2016-04-23 16:45:33 +02:00
|
|
|
btc.colors, btc.bg)
|
2014-09-20 15:21:01 +02:00
|
|
|
|
2016-05-08 15:38:50 +02:00
|
|
|
if btc.update == nil {
|
|
|
|
log.Printf("Writing (%d, %d) to file %s.\n", x, z, path)
|
|
|
|
return true, SaveAsPNG(path, image)
|
|
|
|
}
|
2014-09-20 15:21:01 +02:00
|
|
|
|
2016-05-08 17:33:51 +02:00
|
|
|
if btc.update(i, j, SHA1Image(image)) {
|
2016-05-08 15:38:50 +02:00
|
|
|
log.Printf("Writing (%d, %d) to file %s.\n", x, z, path)
|
|
|
|
return true, SaveAsPNGAtomic(path, image)
|
2014-09-20 15:21:01 +02:00
|
|
|
}
|
|
|
|
|
2016-05-08 17:43:48 +02:00
|
|
|
log.Printf("(%d, %d) is unchanged.\n", x, z)
|
2016-05-08 15:38:50 +02:00
|
|
|
|
|
|
|
return false, nil
|
2014-09-20 15:21:01 +02:00
|
|
|
}
|