Remplissage du dépôt
This commit is contained in:
122
cmd/mtseeder/baselevel.go
Normal file
122
cmd/mtseeder/baselevel.go
Normal file
@ -0,0 +1,122 @@
|
||||
// Copyright 2014, 2015 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 (
|
||||
"image/color"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"bitbucket.org/s_l_teichmann/mtsatellite/common"
|
||||
)
|
||||
|
||||
const (
|
||||
baseLevelDir = "8"
|
||||
)
|
||||
|
||||
type blockPos struct {
|
||||
x, z int16
|
||||
j, i int
|
||||
}
|
||||
|
||||
func createTiles(
|
||||
btc *common.BaseTileCreator,
|
||||
jobs chan blockPos,
|
||||
done *sync.WaitGroup) {
|
||||
|
||||
wFns := make(chan func() (bool, error))
|
||||
|
||||
// Writing already rendered tiles to disk can be done in background.
|
||||
go func() {
|
||||
for wfn := range wFns {
|
||||
if _, err := wfn(); err != nil {
|
||||
log.Printf("WARN: writing file failed: %v.\n", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
defer func() {
|
||||
close(wFns)
|
||||
btc.Close()
|
||||
done.Done()
|
||||
}()
|
||||
|
||||
for job := range jobs {
|
||||
if err := btc.RenderArea(job.x-1, job.z-1); err != nil {
|
||||
log.Printf("WARN: rendering failed: %v.\n", err)
|
||||
continue
|
||||
}
|
||||
wFns <- btc.WriteFunc(job.i, job.j, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func createBaseLevel(
|
||||
address string,
|
||||
xMin, yMin, zMin, xMax, yMax, zMax int,
|
||||
transparent bool, transparentDim float32,
|
||||
colorsFile string, bg color.RGBA, outDir string,
|
||||
numWorkers int) (err error) {
|
||||
|
||||
var colors *common.Colors
|
||||
|
||||
if colors, err = common.ParseColors(colorsFile); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
colors.TransparentDim = transparentDim
|
||||
|
||||
baseDir := filepath.Join(outDir, baseLevelDir)
|
||||
if err = os.MkdirAll(baseDir, os.ModePerm); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
jobs := make(chan blockPos)
|
||||
var done sync.WaitGroup
|
||||
|
||||
var proto string
|
||||
if strings.ContainsRune(address, '/') {
|
||||
proto = "unix"
|
||||
} else {
|
||||
proto = "tcp"
|
||||
}
|
||||
|
||||
for i := 0; i < numWorkers; i++ {
|
||||
var client *common.RedisClient
|
||||
|
||||
if client, err = common.NewRedisClient(proto, address); err != nil {
|
||||
return
|
||||
}
|
||||
done.Add(1)
|
||||
btc := common.NewBaseTileCreator(
|
||||
client, colors, bg,
|
||||
int16(yMin), int16(yMax),
|
||||
transparent, baseDir)
|
||||
go createTiles(btc, jobs, &done)
|
||||
}
|
||||
|
||||
zMin, zMax = common.Order(zMin, zMax)
|
||||
|
||||
for x, i := int16(xMin), 0; x <= int16(xMax); x += 16 {
|
||||
xDir := filepath.Join(baseDir, strconv.Itoa(i))
|
||||
log.Printf("creating dir: %s\n", xDir)
|
||||
if err = os.MkdirAll(xDir, os.ModePerm); err != nil {
|
||||
log.Fatalf("Cannot create directory '%s': %s\n", 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
|
||||
}
|
97
cmd/mtseeder/main.go
Normal file
97
cmd/mtseeder/main.go
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright 2014, 2015 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 (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"bitbucket.org/s_l_teichmann/mtsatellite/common"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
port int
|
||||
host string
|
||||
xMin, yMin, zMin int
|
||||
xMax, yMax, zMax int
|
||||
colorsFile string
|
||||
bgColor string
|
||||
outDir string
|
||||
numWorkers int
|
||||
skipBaseLevel bool
|
||||
skipPyramid bool
|
||||
transparent bool
|
||||
transparentDim float64
|
||||
version bool
|
||||
)
|
||||
|
||||
defaultBgColor := common.ColorToHex(common.BackgroundColor)
|
||||
|
||||
flag.IntVar(&port, "port", 6379, "port to of mtredisalize server")
|
||||
flag.IntVar(&port, "p", 6379, "port to of mtredisalize server (shorthand)")
|
||||
flag.StringVar(&host, "host", "localhost", "host to mtredisalize server")
|
||||
flag.IntVar(&xMin, "xmin", -1933, "x min of the area to tile")
|
||||
flag.IntVar(&xMax, "xmax", 1932, "x max of the area to tile")
|
||||
flag.IntVar(&yMin, "ymin", common.MinHeight, "Minimum y in blocks.")
|
||||
flag.IntVar(&yMax, "ymax", common.MaxHeight, "Maximum y in blocks.")
|
||||
flag.IntVar(&zMin, "zmin", -1933, "z min of the area to tile")
|
||||
flag.IntVar(&zMax, "zmax", 1932, "z max of the area to tile")
|
||||
flag.StringVar(&colorsFile, "colors", "colors.txt", "definition of colors")
|
||||
flag.StringVar(&bgColor, "background", defaultBgColor, "background color")
|
||||
flag.StringVar(&bgColor, "bg", defaultBgColor, "background color (shorthand)")
|
||||
flag.StringVar(&outDir, "output-dir", "map", "directory with the resulting image tree")
|
||||
flag.StringVar(&outDir, "o", "map", "directory with the resulting image tree")
|
||||
flag.IntVar(&numWorkers, "workers", 1, "number of workers")
|
||||
flag.IntVar(&numWorkers, "w", 1, "number of workers (shorthand)")
|
||||
flag.BoolVar(&skipBaseLevel, "skip-base-level", false, "Do not generate base level tiles")
|
||||
flag.BoolVar(&skipBaseLevel, "sb", false, "Do not generate base level tiles (shorthand)")
|
||||
flag.BoolVar(&skipPyramid, "skip-pyramid", false, "Do not generate pyramid tiles")
|
||||
flag.BoolVar(&skipPyramid, "sp", false, "Do not generate pyramid tiles (shorthand)")
|
||||
flag.BoolVar(&transparent, "transparent", false, "Render transparent blocks.")
|
||||
flag.BoolVar(&transparent, "t", false, "Render transparent blocks (shorthand).")
|
||||
flag.Float64Var(&transparentDim,
|
||||
"transparent-dim", common.DefaultTransparentDim*100.0,
|
||||
"Extra dimming of transparent nodes each depth meter in percent.")
|
||||
flag.Float64Var(&transparentDim,
|
||||
"td", common.DefaultTransparentDim*100.0,
|
||||
"Extra fimming of transparent nodes each depth meter in percent. (shorthand)")
|
||||
flag.BoolVar(&version, "version", false, "Print version and exit.")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if version {
|
||||
common.PrintVersionAndExit()
|
||||
}
|
||||
|
||||
bg := common.ParseColorDefault(bgColor, common.BackgroundColor)
|
||||
|
||||
if !skipBaseLevel {
|
||||
td := common.Clamp32f(float32(transparentDim/100.0), 0.0, 1.0)
|
||||
var address string
|
||||
if strings.ContainsRune(host, '/') {
|
||||
address = host
|
||||
} else {
|
||||
address = fmt.Sprintf("%s:%d", host, port)
|
||||
}
|
||||
if err := createBaseLevel(
|
||||
address,
|
||||
xMin, yMin, zMin, xMax, yMax, zMax,
|
||||
transparent, td,
|
||||
colorsFile, bg,
|
||||
outDir,
|
||||
numWorkers); err != nil {
|
||||
log.Fatalf("Creating base level tiles failed: %s", err)
|
||||
}
|
||||
}
|
||||
if !skipPyramid {
|
||||
pc := pyramidCreator{numWorkers: numWorkers, outDir: outDir, bg: bg}
|
||||
if err := pc.create(); err != nil {
|
||||
log.Fatalf("Creating pyramid tiles failed: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
247
cmd/mtseeder/pyramid.go
Normal file
247
cmd/mtseeder/pyramid.go
Normal file
@ -0,0 +1,247 @@
|
||||
// Copyright 2014, 2015 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 (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"bitbucket.org/s_l_teichmann/mtsatellite/common"
|
||||
|
||||
"github.com/bamiaux/rez"
|
||||
)
|
||||
|
||||
type pyramidCreator struct {
|
||||
numWorkers int
|
||||
outDir string
|
||||
bg color.RGBA
|
||||
}
|
||||
|
||||
func findMaxDir(files []os.FileInfo) (min, max int) {
|
||||
min, max = math.MaxInt32, math.MinInt32
|
||||
for _, file := range files {
|
||||
if !file.Mode().IsDir() {
|
||||
continue
|
||||
}
|
||||
if x, err := strconv.Atoi(file.Name()); err == nil {
|
||||
if x > max {
|
||||
max = x
|
||||
}
|
||||
if x < min {
|
||||
min = x
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func findMaxFile(files []os.FileInfo) (min, max int) {
|
||||
min, max = math.MaxInt32, math.MinInt32
|
||||
for _, file := range files {
|
||||
if !file.Mode().IsRegular() {
|
||||
continue
|
||||
}
|
||||
name := file.Name()
|
||||
name = strings.TrimSuffix(name, filepath.Ext(name))
|
||||
if x, err := strconv.Atoi(name); err == nil {
|
||||
if x > max {
|
||||
max = x
|
||||
}
|
||||
if x < min {
|
||||
min = x
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type pyramidJob struct {
|
||||
src [4]string
|
||||
dst string
|
||||
}
|
||||
|
||||
func (pc *pyramidCreator) createParentLevel(
|
||||
oldDir string,
|
||||
jobs chan pyramidJob) (newDir string, err error) {
|
||||
|
||||
oldName := filepath.Base(oldDir)
|
||||
|
||||
var oldLevel int
|
||||
if oldLevel, err = strconv.Atoi(oldName); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if oldLevel <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var files []os.FileInfo
|
||||
if files, err = ioutil.ReadDir(oldDir); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
xMin, xMax := findMaxDir(files)
|
||||
if xMax == math.MinInt32 {
|
||||
return
|
||||
}
|
||||
|
||||
newLevel := oldLevel - 1
|
||||
|
||||
log.Printf("Generating tiles of level %d\n", newLevel)
|
||||
|
||||
parentDir := filepath.Dir(oldDir)
|
||||
newDir = filepath.Join(parentDir, strconv.Itoa(newLevel))
|
||||
|
||||
if err = os.MkdirAll(newDir, os.ModePerm); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for ox, nx := xMin, xMin; ox <= xMax; ox += 2 {
|
||||
|
||||
ox1Dir := filepath.Join(oldDir, strconv.Itoa(ox))
|
||||
ox2Dir := filepath.Join(oldDir, strconv.Itoa(ox+1))
|
||||
|
||||
if files, err = ioutil.ReadDir(ox1Dir); err != nil {
|
||||
return
|
||||
}
|
||||
zMin, zMax := findMaxFile(files)
|
||||
if zMax == math.MinInt32 {
|
||||
nx++
|
||||
continue
|
||||
}
|
||||
|
||||
nxDir := filepath.Join(newDir, strconv.Itoa(nx))
|
||||
if err = os.MkdirAll(nxDir, os.ModePerm); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for oz, nz := zMin, zMin; oz <= zMax; oz += 2 {
|
||||
oz1 := strconv.Itoa(oz) + ".png"
|
||||
oz2 := strconv.Itoa(oz+1) + ".png"
|
||||
s1 := filepath.Join(ox1Dir, oz1)
|
||||
s2 := filepath.Join(ox1Dir, oz2)
|
||||
s3 := filepath.Join(ox2Dir, oz1)
|
||||
s4 := filepath.Join(ox2Dir, oz2)
|
||||
d := filepath.Join(nxDir, strconv.Itoa(nz)+".png")
|
||||
jobs <- pyramidJob{src: [4]string{s1, s2, s3, s4}, dst: d}
|
||||
nz++
|
||||
}
|
||||
|
||||
nx++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func clip8(x int) int {
|
||||
switch {
|
||||
case x < 0:
|
||||
return 0
|
||||
case x > 256:
|
||||
return 256
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func clipRect(r image.Rectangle) image.Rectangle {
|
||||
return image.Rectangle{
|
||||
Min: image.Point{X: clip8(r.Min.X), Y: clip8(r.Min.Y)},
|
||||
Max: image.Point{X: clip8(r.Max.X), Y: clip8(r.Max.Y)}}
|
||||
}
|
||||
|
||||
var dps = [4]image.Point{
|
||||
image.Pt(0, 256),
|
||||
image.Pt(0, 0),
|
||||
image.Pt(256, 256),
|
||||
image.Pt(256, 0)}
|
||||
|
||||
func (pc *pyramidCreator) fuseTile(
|
||||
scratch, resized *image.RGBA,
|
||||
conv rez.Converter,
|
||||
job *pyramidJob) error {
|
||||
|
||||
for i, path := range job.src {
|
||||
|
||||
img := common.LoadPNG(path, pc.bg)
|
||||
|
||||
sr := clipRect(img.Bounds())
|
||||
r := sr.Sub(sr.Min).Add(dps[i])
|
||||
|
||||
draw.Draw(scratch, r, img, sr.Min, draw.Src)
|
||||
}
|
||||
|
||||
if err := conv.Convert(resized, scratch); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Writing pyramid tile '%s'.\n", job.dst)
|
||||
|
||||
return common.SaveAsPNG(job.dst, resized)
|
||||
}
|
||||
|
||||
func (pc *pyramidCreator) fuseTiles(jobs chan pyramidJob, done *sync.WaitGroup) {
|
||||
defer done.Done()
|
||||
scratch := image.NewRGBA(image.Rect(0, 0, 512, 512))
|
||||
resized := image.NewRGBA(image.Rect(0, 0, 256, 256))
|
||||
|
||||
cfg, err := rez.PrepareConversion(resized, scratch)
|
||||
if err != nil {
|
||||
log.Printf("WARN: cannot prepare rescaling: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
conv, err := rez.NewConverter(cfg, common.ResizeFilter)
|
||||
if err != nil {
|
||||
log.Printf("WARN: Cannot create image converter: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
for job := range jobs {
|
||||
if err := pc.fuseTile(scratch, resized, conv, &job); err != nil {
|
||||
log.Printf("WARN: Writing image failed: %s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *pyramidCreator) create() (err error) {
|
||||
|
||||
for oldDir := filepath.Join(pc.outDir, baseLevelDir); oldDir != ""; {
|
||||
if oldDir, err = pc.createLevel(oldDir); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (pc *pyramidCreator) createLevel(oldDir string) (string, error) {
|
||||
|
||||
jobs := make(chan pyramidJob)
|
||||
|
||||
var done sync.WaitGroup
|
||||
|
||||
for i := 0; i < pc.numWorkers; i++ {
|
||||
done.Add(1)
|
||||
go pc.fuseTiles(jobs, &done)
|
||||
}
|
||||
|
||||
newDir, err := pc.createParentLevel(oldDir, jobs)
|
||||
close(jobs)
|
||||
|
||||
if err != nil {
|
||||
return newDir, err
|
||||
}
|
||||
|
||||
done.Wait()
|
||||
|
||||
return newDir, err
|
||||
}
|
Reference in New Issue
Block a user