diff --git a/common/area.go b/common/area.go index c26fb1d..9fb8f4e 100644 --- a/common/area.go +++ b/common/area.go @@ -4,7 +4,9 @@ package common -import "math" +import ( + "math" +) type Area struct { X1, Z1 int16 @@ -28,79 +30,73 @@ func areasContain(areas []Area, x, z int16) bool { return false } -// UncoveredAreas implements a greedy algorithm to figure out +// recalculate implements a greedy algorithm to figure out // a list of disjunct areas of free regions in the domain // to the (x, z) block plane. // oldAreas are searched and found free areas are appended // to newAreas which ist return. // This is useful to spatial query only blocks from db // that are not below already rendered blocks. -func UncoveredAreas(r *Renderer, newAreas, oldAreas []Area) []Area { +func (area Area) recalculate(r *Renderer, nareas []Area) []Area { yM := r.yMin - // Scan old areas. - for _, oldArea := range oldAreas { - for z := oldArea.Z1; z <= oldArea.Z2; z++ { - row := z * int16(r.width) - for x := oldArea.X1; x <= oldArea.X2; x++ { - // Uncovered and not in list of new areas? - if yM[row+x] > math.MinInt32 || areasContain(newAreas, x, z) { - continue - } - area := Area{X1: x, Z1: z, X2: x, Z2: z} - // Try to extend the area in x and/or z till no further extension is possible. - for extendDirs := 1 | 2; extendDirs != 0; { - var xFirst bool - // Try to extend in the direction with most gain - // of blocks. - if area.higher() { // Higher means to win more blocks in x direction. - xFirst = true - } - dirs: - for i := 0; i < 2; i++ { - if xFirst { - // Extension in x possible? - if extendDirs&1 == 1 { - nx := area.X2 + 1 - if nx > oldArea.X2 { - extendDirs &= ^1 - continue - } - // Scan line below current area if its fully free. - for nz := area.Z1; nz <= area.Z2; nz++ { - if yM[nz*int16(r.width)+nx] > math.MinInt32 || areasContain(newAreas, nx, nz) { - extendDirs &= ^1 - continue dirs - } - } - // free -> extend - area.X2 = nx - } - } else if extendDirs&2 == 2 { - // Symmetric case in z direction - nz := area.Z2 + 1 - if nz > oldArea.Z2 { - extendDirs &= ^2 - continue - } - // Scan line right beside the area if its free. - row2 := nz * int16(r.width) - for nx := area.X1; nx <= area.X2; nx++ { - if yM[row2+nx] > math.MinInt32 || areasContain(newAreas, nx, nz) { - extendDirs &= ^2 - continue dirs - } - } - area.Z2 = nz - } - // Switch to other search direction (x -> z or z -> x) - xFirst = !xFirst - } - } - // At this point the area is extended to max. - newAreas = append(newAreas, area) + const ex = 1 + const ez = 2 + + nas := len(nareas) + + for z := area.Z1; z <= area.Z2; z++ { + row := z * int16(r.width) + for x := area.X1; x <= area.X2; x++ { + // Uncovered and not in list of new areas? + if yM[row+x] > math.MinInt32 || areasContain(nareas[nas:], x, z) { + continue } + a := Area{X1: x, Z1: z, X2: x, Z2: z} + // Try to extend the area in x and/or z till no further extension is possible. + ext: + for extend := ex | ez; extend != 0; { + // If we extending in both directions a the current area + // is higher than wide we gain more block if extend + // in the x direction first. + if (extend == ex|ez && a.higher()) || extend&ex == ex { // check x + nx := a.X2 + 1 + if nx > area.X2 { // reached border of area + extend &= ^ex + continue + } + // Check column right of the current area if its fully free. + for nz := a.Z1; nz <= a.Z2; nz++ { + if yM[nz*int16(r.width)+nx] > math.MinInt32 || + areasContain(nareas[nas:], nx, nz) { + extend &= ^ex + continue ext + } + } + // free -> extend + a.X2 = nx + } else if extend&ez == ez { // check z + nz := a.Z2 + 1 + if nz > area.Z2 { + extend &= ^ez + continue + } + // Check line right below the current area if its free. + row2 := nz * int16(r.width) + for nx := a.X1; nx <= a.X2; nx++ { + if yM[row2+nx] > math.MinInt32 || + areasContain(nareas[nas:], nx, nz) { + extend &= ^ez + continue ext + } + } + // free -> extend + a.Z2 = nz + } + } + // At this point the area is extended to max. + nareas = append(nareas, a) } } - return newAreas + return nareas } diff --git a/common/basetilecreator.go b/common/basetilecreator.go index 12b8b2e..9e02df5 100644 --- a/common/basetilecreator.go +++ b/common/basetilecreator.go @@ -103,10 +103,10 @@ func (btc *BaseTileCreator) CreateTile(x, z int16, i, j int) (bool, error) { var c1, c2 Coord - oareas := make([]Area, 0, tileWidth*tileHeight/2) - nareas := make([]Area, 1, tileWidth*tileHeight/2) + nareas := make([]Area, 0, tileWidth*tileHeight/2) + areas := make([]Area, 1, tileWidth*tileHeight/2) - nareas[0] = Area{ + areas[0] = Area{ X1: 0, Z1: 0, X2: int16(tileWidth) - 1, Z2: int16(tileHeight) - 1} @@ -118,9 +118,7 @@ func (btc *BaseTileCreator) CreateTile(x, z int16, i, j int) (bool, error) { c1.Y = max16(yRange[0], btc.yMin) c2.Y = min16(yRange[1], btc.yMax) - var allCount int - - for _, area := range nareas { + for _, area := range areas { c1.X = area.X1 + x c1.Z = area.Z1 + z c2.X = area.X2 + x @@ -131,18 +129,22 @@ func (btc *BaseTileCreator) CreateTile(x, z int16, i, j int) (bool, error) { if count, err = btc.client.QueryCuboid(query, btc.drawBlock); err != nil { return false, err } - allCount += count if err = btc.yOrder.Drain(btc.colors); err != nil { log.Printf("WARN: rendering block failed: %s\n", err) } - } - if allCount > 0 { - xareas := UncoveredAreas(btc.renderer, oareas, nareas) - if len(xareas) == 0 { - break + + // If there where loaded blocks in this area recalculate coverage. + if count > 0 { + nareas = area.recalculate(btc.renderer, nareas) + } else { + nareas = append(nareas, area) } - nareas, oareas = xareas, nareas[:0] } + + if len(nareas) == 0 { + break + } + areas, nareas = nareas, areas[:0] } path := filepath.Join(btc.baseDir, strconv.Itoa(i), strconv.Itoa(j)+".png")