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

@ -17,14 +17,15 @@ import (
func main() { func main() {
var ( var (
webPort int webPort int
webHost string webHost string
webDir string webDir string
mapDir string mapDir string
redisPort int redisPort int
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,37 +210,64 @@ 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)
for z := 0; z < 16; z++ {
for x := 0; x < 16; x++ { if db.Transparent && r.tBuffer != nil {
currentY := yB[ofs] r.TransparentBlocks++
if currentY < blockY {
for y := 15; y >= 0; y-- { for z := 0; z < 16; z++ {
if c, ok := db.Content(x, y, z); ok { for x := 0; x < 16; x++ {
r.cBuffer[ofs] = c currentY := yB[ofs]
currentY = blockY + int32(y) if currentY < blockY {
yB[ofs] = currentY for y := 15; y >= 0; y-- {
break 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++
} }
if currentY < yMin { ofs += w - 16
yMin = currentY }
}
ofs++ } else {
r.SolidBlocks++
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 {
r.cBuffer[ofs] = c
currentY = blockY + int32(y)
yB[ofs] = currentY
break
}
}
}
if currentY < yMin {
yMin = currentY
}
ofs++
}
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,42 +415,91 @@ func (r *Renderer) CreateShadedImage(
pix := image.Pix pix := image.Pix
for z := height - 1; z >= 0; z-- { if r.tBuffer != nil { // Fast path for transparent images.
for x := 0; x < width; x++ { for z := height - 1; z >= 0; z-- {
colIdx := r.cBuffer[ofs] for x := 0; x < width; x++ {
if colIdx < 0 || colIdx >= numCols { colIdx := r.cBuffer[ofs]
pix[iofs] = background.R if colIdx < 0 || colIdx >= numCols {
pix[iofs+1] = background.G pix[iofs] = background.R
pix[iofs+2] = background.B pix[iofs+1] = background.G
pix[iofs+3] = 0xff pix[iofs+2] = background.B
} else { pix[iofs+3] = 0xff
var y, y1, y2 int32
y = r.yBuffer[ofs]
if x == 0 {
y1 = y
} else { } else {
y1 = r.yBuffer[ofs-1] 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]
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
} }
if z == 0 { iofs += 4
y2 = y ofs++
} else {
y2 = r.yBuffer[ofs+pw]
}
d := ((y - y1) + (y - y2)) * 12
if d > 36 {
d = 36
}
col := colors[colIdx]
pix[iofs] = safeColor(int32(col.R) + d)
pix[iofs+1] = safeColor(int32(col.G) + d)
pix[iofs+2] = safeColor(int32(col.B) + d)
pix[iofs+3] = 0xff
} }
iofs += 4 ofs += stride
ofs++ 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+1] = safeColor(int32(col.G) + d)
pix[iofs+2] = safeColor(int32(col.B) + d)
pix[iofs+3] = 0xff
}
iofs += 4
ofs++
}
ofs += stride
iofs -= istride
} }
ofs += stride
iofs -= istride
} }
return image return image
} }