// 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 ( "flag" "fmt" "image/color" "log" "os" "path/filepath" "sync" "bitbucket.org/s_l_teichmann/mtredisalize/common" ) const ( width = 18 height = 18 baseLevelDir = "8" yOrderCapacity = 512 ) 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 main() { var ( port int host string xMin, zMin int xMax, zMax int colorsfile string outDir string numWorkers int ) 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", -1936, "x min of the area to tile") flag.IntVar(&xMax, "xmax", 1920, "x max of the area to tile") flag.IntVar(&zMin, "zmin", -1936, "z min of the area to tile") flag.IntVar(&zMax, "zmax", 1920, "z max of the area to tile") flag.StringVar(&colorsfile, "colors", "colors.txt", "definition of colors") 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, "worker", 1, "number of workers") flag.IntVar(&numWorkers, "w", 1, "number of workers (shorthand)") flag.Parse() var err error var colors *common.Colors if colors, err = common.ParseColors(colorsfile); err != nil { log.Fatalf("Cannot open color file: %s", err) } baseDir := filepath.Join(outDir, baseLevelDir) if err = os.MkdirAll(baseDir, os.ModePerm); err != nil { log.Fatalf("Cannot create base dir '%s': %s", baseDir, err) } jobs := make(chan blockPos) var done sync.WaitGroup address := fmt.Sprintf("%s:%d", host, port) for i := 0; i < numWorkers; i++ { var client *common.RedisClient if client, err = common.NewRedisClient("tcp", address); err != nil { log.Fatalf("Cannot connect to '%s': %s", address, err) } 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() }