mtsatellite/cmd/mtseeder/baselevel.go

204 lines
4.3 KiB
Go

// 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 main
import (
"fmt"
"image/color"
"log"
"os"
"path/filepath"
"sync"
"bitbucket.org/s_l_teichmann/mtredisalize/common"
)
const (
width = 18
height = 18
baseLevelDir = "8"
yOrderCapacity = 512
)
// To scan the whole height in terms of the y coordinate
// the database is queried in height units defined in the yRanges table.
var yRanges = [][]int16{
{1024, 1934},
{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},
{-1936, -1025}}
type blockPos struct {
x, z int16
j, i int
}
type baseLevelWorker struct {
client *common.RedisClient
colors *common.Colors
renderer *common.Renderer
yOrder *common.YOrder
baseDir string
}
func newBaseLevelWorker(client *common.RedisClient,
colors *common.Colors, baseDir string) *baseLevelWorker {
renderer := common.NewRenderer(width, height)
return &baseLevelWorker{
client: client,
colors: colors,
baseDir: baseDir,
renderer: renderer,
yOrder: common.NewYOrder(renderer, yOrderCapacity)}
}
func (blw *baseLevelWorker) close() error {
return blw.client.Close()
}
func (blw *baseLevelWorker) createTile(x, z int16, i, j int) {
blw.renderer.Reset()
blw.renderer.SetPos(x, z)
blw.yOrder.Reset()
drawBlock := func(block *common.Block) {
if err := blw.yOrder.RenderBlock(block, blw.colors.NameIndex); err != nil {
log.Printf("WARN: rendering block failed: %s", err)
}
}
var c1, c2 common.Coord
nareas := make([]common.Area, 0, width*height/2)
oareas := make([]common.Area, 1, width*height/2)
oareas[0] = common.Area{
X1: 0, Z1: 0,
X2: int16(width) - 1, Z2: int16(height) - 1}
var err error
for _, yRange := range yRanges {
c1.Y = yRange[0]
c2.Y = yRange[1]
nareas = blw.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 := common.Cuboid{P1: c1, P2: c2}
if err = blw.client.QueryCuboid(query, drawBlock); err != nil {
log.Printf("WARN: query failed: %s", err)
return
}
if err = blw.yOrder.Drain(blw.colors.NameIndex); err != nil {
log.Printf("WARN: rendering block failed: %s", err)
}
}
oareas, nareas = nareas, oareas[0:0]
}
image := blw.renderer.CreateShadedImage(
16, 16, (width-2)*16, (height-2)*16,
blw.colors.Colors, color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff})
path := filepath.Join(blw.baseDir, fmt.Sprintf("%d", i), fmt.Sprintf("%d.png", j))
log.Printf("Writing (%d, %d) (%d, %d)", i, j, x, z)
if err = common.SaveAsPNG(path, image); err != nil {
log.Fatalf("writing image failed: %s", err)
}
}
func (blw *baseLevelWorker) run(jobs chan blockPos, done *sync.WaitGroup) {
defer done.Done()
for job := range jobs {
blw.createTile(job.x-1, job.z-1, job.i, job.j)
}
}
func order(a, b int) (int, int) {
if a < b {
return a, b
}
return b, a
}
func createBaseLevel(
address string,
xMin, zMin, xMax, zMax int,
colorsFile, outDir string,
numWorkers int) (err error) {
var colors *common.Colors
if colors, err = common.ParseColors(colorsFile); err != nil {
return
}
baseDir := filepath.Join(outDir, baseLevelDir)
if err = os.MkdirAll(baseDir, os.ModePerm); err != nil {
return
}
jobs := make(chan blockPos)
var done sync.WaitGroup
for i := 0; i < numWorkers; i++ {
var client *common.RedisClient
if client, err = common.NewRedisClient("tcp", address); err != nil {
return
}
done.Add(1)
blw := newBaseLevelWorker(client, colors, baseDir)
defer blw.close()
go blw.run(jobs, &done)
}
zMin, zMax = order(zMin, zMax)
for x, i := int16(xMin), 0; x <= int16(xMax); x += 16 {
xDir := filepath.Join(baseDir, fmt.Sprintf("%d", i))
log.Printf("creating dir: %s", xDir)
if err = os.MkdirAll(xDir, os.ModePerm); err != nil {
log.Fatalf("Cannot create directory '%s': %s", xDir, err)
}
for z, j := int16(zMin), 0; z <= int16(zMax); z += 16 {
jobs <- blockPos{x: x, z: z, i: i, j: j}
j++
}
i++
}
close(jobs)
done.Wait()
return
}