Render transparent tiles if command line flag is set.

This commit is contained in:
Sascha L. Teichmann 2014-10-26 18:36:47 +01:00
parent 2aa9ee0e24
commit 8452a26fcd
8 changed files with 198 additions and 77 deletions

View File

@ -48,6 +48,7 @@ func order(a, b int) (int, int) {
func createBaseLevel( func createBaseLevel(
address string, address string,
xMin, zMin, xMax, zMax int, xMin, zMin, xMax, zMax int,
transparent bool,
colorsFile, outDir string, colorsFile, outDir string,
numWorkers int) (err error) { numWorkers int) (err error) {
@ -71,7 +72,7 @@ func createBaseLevel(
return return
} }
done.Add(1) done.Add(1)
btc := common.NewBaseTileCreator(client, colors, baseDir, false) btc := common.NewBaseTileCreator(client, colors, transparent, baseDir, false)
go createTiles(btc, jobs, &done) go createTiles(btc, jobs, &done)
} }

View File

@ -21,6 +21,7 @@ func main() {
numWorkers int numWorkers int
skipBaseLevel bool skipBaseLevel bool
skipPyramid bool skipPyramid bool
transparent bool
) )
flag.IntVar(&port, "port", 6379, "port to of mtredisalize server") flag.IntVar(&port, "port", 6379, "port to of mtredisalize server")
@ -39,6 +40,8 @@ func main() {
flag.BoolVar(&skipBaseLevel, "sb", false, "Do not generate base level tiles (shorthand)") 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, "skip-pyramid", false, "Do not generate pyramid tiles")
flag.BoolVar(&skipPyramid, "sp", false, "Do not generate pyramid tiles (shorthand)") 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.Parse() flag.Parse()
@ -46,7 +49,12 @@ func main() {
if !skipBaseLevel { if !skipBaseLevel {
address := fmt.Sprintf("%s:%d", host, port) address := fmt.Sprintf("%s:%d", host, port)
if err = createBaseLevel( if err = createBaseLevel(
address, xMin, zMin, xMax, zMax, colorsFile, outDir, numWorkers); err != nil { address,
xMin, zMin, xMax, zMax,
transparent,
colorsFile,
outDir,
numWorkers); err != nil {
log.Fatalf("Creating base level tiles failed: %s", err) log.Fatalf("Creating base level tiles failed: %s", err)
} }
} }

View File

@ -23,6 +23,7 @@ func main() {
colorsfile string colorsfile string
outfile string outfile string
shaded bool shaded bool
transparent bool
) )
flag.IntVar(&port, "port", 6379, "port to of mtredisalize server") flag.IntVar(&port, "port", 6379, "port to of mtredisalize server")
@ -41,6 +42,7 @@ func main() {
flag.StringVar(&outfile, "output", "out.png", "image file of result") flag.StringVar(&outfile, "output", "out.png", "image file of result")
flag.StringVar(&outfile, "o", "out.png", "image file of result (shorthand)") flag.StringVar(&outfile, "o", "out.png", "image file of result (shorthand)")
flag.BoolVar(&shaded, "shaded", true, "draw relief") flag.BoolVar(&shaded, "shaded", true, "draw relief")
flag.BoolVar(&transparent, "transparent", false, "render transparent blocks")
flag.Parse() flag.Parse()
@ -70,7 +72,7 @@ func main() {
q1x, q1y, q1z := int16(x), int16(y), int16(z) q1x, q1y, q1z := int16(x), int16(y), int16(z)
q2x, q2y, q2z := q1x+int16(width)-1, q1y+int16(depth)-1, q1z+int16(height)-1 q2x, q2y, q2z := q1x+int16(width)-1, q1y+int16(depth)-1, q1z+int16(height)-1
renderer := common.NewRenderer(width, height) renderer := common.NewRenderer(width, height, transparent)
renderer.SetPos(q1x, q1z) renderer.SetPos(q1x, q1z)
yOrder := common.NewYOrder(renderer, 512) yOrder := common.NewYOrder(renderer, 512)
@ -106,7 +108,7 @@ func main() {
if shaded { if shaded {
image = renderer.CreateShadedImage( image = renderer.CreateShadedImage(
16, 16, (width-2)*16, (height-2)*16, 16, 16, (width-2)*16, (height-2)*16,
colors.Colors, color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}) colors, color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff})
} else { } else {
image = renderer.CreateImage( image = renderer.CreateImage(
colors.Colors, color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}) colors.Colors, color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff})

View File

@ -25,6 +25,7 @@ func main() {
redisHost string redisHost string
colorsFile string colorsFile string
workers int workers int
transparent bool
) )
flag.IntVar(&webPort, "web-port", 8808, "port of the web server") flag.IntVar(&webPort, "web-port", 8808, "port of the web server")
flag.IntVar(&webPort, "p", 8808, "port of the web server (shorthand)") flag.IntVar(&webPort, "p", 8808, "port of the web server (shorthand)")
@ -41,6 +42,8 @@ func main() {
flag.IntVar(&workers, "workers", 1, "number of workers to render tiles") flag.IntVar(&workers, "workers", 1, "number of workers to render tiles")
flag.StringVar(&colorsFile, "colors", "colors.txt", "colors used to render map tiles.") flag.StringVar(&colorsFile, "colors", "colors.txt", "colors used to render map tiles.")
flag.StringVar(&colorsFile, "c", "colors.txt", "colors used to render map tiles (shorthand).") flag.StringVar(&colorsFile, "c", "colors.txt", "colors used to render map tiles (shorthand).")
flag.BoolVar(&transparent, "transparent", false, "Render transparent blocks.")
flag.BoolVar(&transparent, "t", false, "Render transparent blocks (shorthand).")
flag.Parse() flag.Parse()
@ -56,7 +59,7 @@ func main() {
log.Fatalf("ERROR: problem loading colors: %s", err) log.Fatalf("ERROR: problem loading colors: %s", err)
} }
redisAddress := fmt.Sprintf("%s:%d", redisHost, redisPort) redisAddress := fmt.Sprintf("%s:%d", redisHost, redisPort)
tu := newTileUpdater(mapDir, redisAddress, colors, workers) tu := newTileUpdater(mapDir, redisAddress, colors, transparent, workers)
go tu.doUpdates() go tu.doUpdates()
router.Path("/update").Methods("POST").Handler(tu) router.Path("/update").Methods("POST").Handler(tu)
} }

View File

@ -26,6 +26,7 @@ type tileUpdater struct {
redisAddress string redisAddress string
colors *common.Colors colors *common.Colors
workers int workers int
transparent bool
cond *sync.Cond cond *sync.Cond
mu sync.Mutex mu sync.Mutex
} }
@ -56,12 +57,18 @@ func (c xz) parent() xzm {
Mask: 1 << (zr<<1 | xr)} Mask: 1 << (zr<<1 | xr)}
} }
func newTileUpdater(mapDir, redisAddress string, colors *common.Colors, workers int) *tileUpdater { func newTileUpdater(
mapDir, redisAddress string,
colors *common.Colors,
transparent bool,
workers int) *tileUpdater {
tu := tileUpdater{ tu := tileUpdater{
mapDir: mapDir, mapDir: mapDir,
redisAddress: redisAddress, redisAddress: redisAddress,
changes: map[xz]bool{}, changes: map[xz]bool{},
colors: colors, colors: colors,
transparent: transparent,
workers: workers} workers: workers}
tu.cond = sync.NewCond(&tu.mu) tu.cond = sync.NewCond(&tu.mu)
return &tu return &tu
@ -113,7 +120,7 @@ func (tu *tileUpdater) doUpdates() {
log.Printf("WARN: Cannot connect to redis server: %s", err) log.Printf("WARN: Cannot connect to redis server: %s", err)
continue continue
} }
btc := common.NewBaseTileCreator(client, tu.colors, baseDir, true) btc := common.NewBaseTileCreator(client, tu.colors, tu.transparent, baseDir, true)
done.Add(1) done.Add(1)
go updateBaseTiles(jobs, btc, &done) go updateBaseTiles(jobs, btc, &done)
} }

View File

@ -54,9 +54,10 @@ type BaseTileCreator struct {
func NewBaseTileCreator( func NewBaseTileCreator(
client *RedisClient, client *RedisClient,
colors *Colors, colors *Colors,
transparent bool,
baseDir string, baseDir string,
update bool) *BaseTileCreator { update bool) *BaseTileCreator {
renderer := NewRenderer(tileWidth, tileHeight) renderer := NewRenderer(tileWidth, tileHeight, transparent)
return &BaseTileCreator{ return &BaseTileCreator{
client: client, client: client,
colors: colors, colors: colors,
@ -118,7 +119,7 @@ func (btc *BaseTileCreator) CreateTile(x, z int16, i, j int) error {
image := btc.renderer.CreateShadedImage( image := btc.renderer.CreateShadedImage(
16, 16, (tileWidth-2)*16, (tileHeight-2)*16, 16, 16, (tileWidth-2)*16, (tileHeight-2)*16,
btc.colors.Colors, color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}) btc.colors, color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff})
path := filepath.Join(btc.baseDir, strconv.Itoa(i), fmt.Sprintf("%d.png", j)) path := filepath.Join(btc.baseDir, strconv.Itoa(i), fmt.Sprintf("%d.png", j))

View File

@ -115,8 +115,8 @@ func (colors *Colors) BlendColors(span *Span, col color.RGBA, pos int32) color.R
} }
const scale = float32(1) / float32(100) const scale = float32(1) / float32(100)
for ; curr != nil; curr = curr.Next { for ; curr != nil; curr = curr.Next {
// At least 20% attenuation + 5% extra for each depth meter. // At least 35% attenuation + 5% extra for each depth meter.
factor := float32(min(100, 20+(curr.To-curr.From)*5)) * scale factor := float32(min(100, 35+(curr.To-curr.From)*5)) * scale
col = BlendColor(colors.Colors[curr.Value], col, factor) col = BlendColor(colors.Colors[curr.Value], col, factor)
} }
return col return col

View File

@ -27,6 +27,8 @@ type Renderer struct {
RejectedBlocks int RejectedBlocks int
SolidBlocks int SolidBlocks int
TransparentBlocks int TransparentBlocks int
spans *SpanPool
tBuffer []*Span
} }
type YOrder struct { type YOrder struct {
@ -122,19 +124,29 @@ func (yo *YOrder) Pop() (x interface{}) {
return x return x
} }
func NewRenderer(width, height int) (renderer *Renderer) { func NewRenderer(width, height int, transparent bool) (renderer *Renderer) {
dim := width * height dim := width * height
pixSize := dim * 16 * 16 pixSize := dim * 16 * 16
yBuffer := make([]int32, pixSize) yBuffer := make([]int32, pixSize)
cBuffer := make([]int32, pixSize) cBuffer := make([]int32, pixSize)
yMin := make([]int32, dim) yMin := make([]int32, dim)
var tBuffer []*Span
var spans *SpanPool
if transparent {
tBuffer = make([]*Span, pixSize)
spans = NewSpanPool()
}
renderer = &Renderer{ renderer = &Renderer{
width: width, width: width,
height: height, height: height,
yBuffer: yBuffer, yBuffer: yBuffer,
cBuffer: cBuffer, cBuffer: cBuffer,
yMin: yMin} yMin: yMin,
tBuffer: tBuffer,
spans: spans}
renderer.Reset() renderer.Reset()
return return
@ -153,6 +165,15 @@ func (r *Renderer) Reset() {
for i, n := 0, len(r.yMin); i < n; i++ { for i, n := 0, len(r.yMin); i < n; i++ {
r.yMin[i] = math.MinInt32 r.yMin[i] = math.MinInt32
} }
if r.tBuffer != nil {
for i, t := range r.tBuffer {
if t != nil {
r.spans.FreeAll(t)
r.tBuffer[i] = nil
}
}
}
} }
func (r *Renderer) IsFilled() bool { func (r *Renderer) IsFilled() bool {
@ -189,17 +210,42 @@ func (r *Renderer) RenderBlock(block *Block, colors *Colors) (err error) {
return return
} }
if db.Transparent {
r.TransparentBlocks++
} else {
r.SolidBlocks++
}
w := r.width << 4 w := r.width << 4
ofs := int(bz)*w<<4 + int(bx)<<4 ofs := int(bz)*w<<4 + int(bx)<<4
yB := r.yBuffer yB := r.yBuffer
yMin := int32(math.MaxInt32) yMin := int32(math.MaxInt32)
if db.Transparent && r.tBuffer != nil {
r.TransparentBlocks++
for z := 0; z < 16; z++ {
for x := 0; x < 16; x++ {
currentY := yB[ofs]
if currentY < blockY {
for y := 15; y >= 0; y-- {
if c, ok := db.Content(x, y, z); ok {
currentY = blockY + int32(y)
if colors.IsTransparent(c) {
r.tBuffer[ofs] = r.spans.Insert(r.tBuffer[ofs], currentY, c)
} else {
r.cBuffer[ofs] = c
yB[ofs] = currentY
break
}
}
}
}
if currentY < yMin {
yMin = currentY
}
ofs++
}
ofs += w - 16
}
} else {
r.SolidBlocks++
for z := 0; z < 16; z++ { for z := 0; z < 16; z++ {
for x := 0; x < 16; x++ { for x := 0; x < 16; x++ {
currentY := yB[ofs] currentY := yB[ofs]
@ -220,6 +266,8 @@ func (r *Renderer) RenderBlock(block *Block, colors *Colors) (err error) {
} }
ofs += w - 16 ofs += w - 16
} }
}
r.yMin[pos] = yMin r.yMin[pos] = yMin
return return
@ -349,13 +397,15 @@ func safeColor(x int32) uint8 {
func (r *Renderer) CreateShadedImage( func (r *Renderer) CreateShadedImage(
xOfs, zOfs, width, height int, xOfs, zOfs, width, height int,
colors []color.RGBA, background color.RGBA) *image.RGBA { cols *Colors, background color.RGBA) *image.RGBA {
image := image.NewRGBA(image.Rect(0, 0, width, height)) image := image.NewRGBA(image.Rect(0, 0, width, height))
pw := r.width << 4 pw := r.width << 4
ofs, numCols := zOfs*pw+xOfs, int32(len(colors)) cs := cols.Colors
ofs, numCols := zOfs*pw+xOfs, int32(len(cs))
stride := pw - width stride := pw - width
@ -365,6 +415,7 @@ func (r *Renderer) CreateShadedImage(
pix := image.Pix pix := image.Pix
if r.tBuffer != nil { // Fast path for transparent images.
for z := height - 1; z >= 0; z-- { for z := height - 1; z >= 0; z-- {
for x := 0; x < width; x++ { for x := 0; x < width; x++ {
colIdx := r.cBuffer[ofs] colIdx := r.cBuffer[ofs]
@ -390,7 +441,54 @@ func (r *Renderer) CreateShadedImage(
if d > 36 { if d > 36 {
d = 36 d = 36
} }
col := colors[colIdx] col := cs[colIdx]
col = color.RGBA{
R: safeColor(int32(col.R) + d),
G: safeColor(int32(col.G) + d),
B: safeColor(int32(col.B) + d),
A: 0xff}
if r.tBuffer[ofs] != nil {
col = cols.BlendColors(r.tBuffer[ofs], col, y)
}
pix[iofs] = col.R
pix[iofs+1] = col.G
pix[iofs+2] = col.B
pix[iofs+3] = col.A
}
iofs += 4
ofs++
}
ofs += stride
iofs -= istride
}
} else { // Solid images.
for z := height - 1; z >= 0; z-- {
for x := 0; x < width; x++ {
colIdx := r.cBuffer[ofs]
if colIdx < 0 || colIdx >= numCols {
pix[iofs] = background.R
pix[iofs+1] = background.G
pix[iofs+2] = background.B
pix[iofs+3] = 0xff
} else {
var y, y1, y2 int32
y = r.yBuffer[ofs]
if x == 0 {
y1 = y
} else {
y1 = r.yBuffer[ofs-1]
}
if z == 0 {
y2 = y
} else {
y2 = r.yBuffer[ofs+pw]
}
d := ((y - y1) + (y - y2)) * 12
if d > 36 {
d = 36
}
col := cs[colIdx]
pix[iofs] = safeColor(int32(col.R) + d) pix[iofs] = safeColor(int32(col.R) + d)
pix[iofs+1] = safeColor(int32(col.G) + d) pix[iofs+1] = safeColor(int32(col.G) + d)
pix[iofs+2] = safeColor(int32(col.B) + d) pix[iofs+2] = safeColor(int32(col.B) + d)
@ -402,5 +500,6 @@ func (r *Renderer) CreateShadedImage(
ofs += stride ofs += stride
iofs -= istride iofs -= istride
} }
}
return image return image
} }