1
0
forked from MTSR/mapserver
mapserver/mapblockrenderer/renderer.go

251 lines
5.2 KiB
Go
Raw Permalink Normal View History

2019-01-10 17:21:34 +03:00
package mapblockrenderer
import (
2019-01-13 18:37:03 +03:00
"errors"
"image"
2019-01-17 15:13:18 +03:00
"image/color"
2019-01-11 12:24:10 +03:00
"mapserver/mapblockaccessor"
2023-12-29 18:00:11 +03:00
"mapserver/types"
2019-01-11 16:17:16 +03:00
"time"
2019-01-20 23:03:58 +03:00
2023-01-20 19:44:59 +03:00
"github.com/minetest-go/colormapping"
2019-02-15 10:36:48 +03:00
"github.com/prometheus/client_golang/prometheus"
2019-02-15 11:08:22 +03:00
"github.com/sirupsen/logrus"
2019-01-10 17:21:34 +03:00
)
type MapBlockRenderer struct {
2019-02-27 19:36:30 +03:00
accessor *mapblockaccessor.MapBlockAccessor
colors *colormapping.ColorMapping
enableShadow bool
enableTransparency bool
2019-01-10 17:21:34 +03:00
}
2019-06-20 08:14:14 +03:00
func NewMapBlockRenderer(accessor *mapblockaccessor.MapBlockAccessor, colors *colormapping.ColorMapping) *MapBlockRenderer {
2019-01-28 15:31:48 +03:00
return &MapBlockRenderer{
2019-02-27 19:36:30 +03:00
accessor: accessor,
colors: colors,
enableShadow: true,
enableTransparency: false,
2019-01-28 15:31:48 +03:00
}
2019-01-10 17:21:34 +03:00
}
2019-01-11 13:44:17 +03:00
const (
2019-01-13 18:37:03 +03:00
IMG_SCALE = 16
IMG_SIZE = IMG_SCALE * 16
EXPECTED_BLOCKS_PER_FLAT_MAPBLOCK = 16 * 16
2019-01-11 13:44:17 +03:00
)
2019-01-17 15:13:18 +03:00
func IsViewBlocking(nodeName string) bool {
2019-01-20 23:26:25 +03:00
if nodeName == "" {
return false
}
if nodeName == "vacuum:vacuum" {
return false
}
if nodeName == "air" {
return false
}
return true
2019-01-17 15:13:18 +03:00
}
func clamp(num int) uint8 {
2019-01-18 11:13:37 +03:00
if num < 0 {
return 0
}
2019-01-17 15:13:18 +03:00
2019-01-18 11:13:37 +03:00
if num > 255 {
return 255
}
2019-01-17 15:13:18 +03:00
2019-01-18 11:13:37 +03:00
return uint8(num)
2019-01-17 15:13:18 +03:00
}
func addColorComponent(c *color.RGBA, value int) *color.RGBA {
return &color.RGBA{
2019-01-18 11:13:37 +03:00
R: clamp(int(c.R) + value),
G: clamp(int(c.G) + value),
B: clamp(int(c.B) + value),
A: c.A,
2019-01-17 15:13:18 +03:00
}
}
2023-12-29 18:00:11 +03:00
func (r *MapBlockRenderer) Render(pos1, pos2 *types.MapBlockCoords) (*image.NRGBA, error) {
2019-01-11 15:30:51 +03:00
if pos1.X != pos2.X {
2023-10-11 18:35:39 +03:00
return nil, errors.New("x does not line up")
2019-01-11 15:30:51 +03:00
}
if pos1.Z != pos2.Z {
2023-10-11 18:35:39 +03:00
return nil, errors.New("z does not line up")
2019-01-11 15:30:51 +03:00
}
2019-01-13 18:37:03 +03:00
2019-02-15 10:36:48 +03:00
renderedMapblocks.Inc()
timer := prometheus.NewTimer(renderDuration)
2019-02-15 11:08:22 +03:00
defer timer.ObserveDuration()
2019-02-15 10:36:48 +03:00
2019-01-11 16:17:16 +03:00
start := time.Now()
2019-01-13 18:37:03 +03:00
defer func() {
2019-01-11 16:17:16 +03:00
t := time.Now()
elapsed := t.Sub(start)
2019-01-13 18:37:03 +03:00
log.WithFields(logrus.Fields{"elapsed": elapsed}).Debug("Rendering completed")
2019-01-11 16:17:16 +03:00
}()
2019-01-11 15:30:51 +03:00
2019-01-11 13:44:17 +03:00
upLeft := image.Point{0, 0}
lowRight := image.Point{IMG_SIZE, IMG_SIZE}
2019-01-11 15:30:51 +03:00
img := image.NewNRGBA(image.Rectangle{upLeft, lowRight})
maxY := pos1.Y
minY := pos2.Y
if minY > maxY {
maxY, minY = minY, maxY
}
foundBlocks := 0
xzOccupationMap := make([][]bool, 16)
for x := range xzOccupationMap {
xzOccupationMap[x] = make([]bool, 16)
}
for mapBlockY := maxY; mapBlockY >= minY; mapBlockY-- {
2023-12-29 18:00:11 +03:00
currentPos := types.NewMapBlockCoords(pos1.X, mapBlockY, pos1.Z)
2019-01-11 15:30:51 +03:00
mb, err := r.accessor.GetMapBlock(currentPos)
if err != nil {
return nil, err
}
2019-04-17 09:25:01 +03:00
if mb == nil || mb.IsEmpty() {
2019-01-11 15:30:51 +03:00
continue
}
for x := 0; x < 16; x++ {
for z := 0; z < 16; z++ {
for y := 15; y >= 0; y-- {
if xzOccupationMap[x][z] {
break
2019-01-11 15:30:51 +03:00
}
2019-01-13 18:37:03 +03:00
nodeName := mb.GetNodeName(x, y, z)
2019-06-20 08:14:14 +03:00
param2 := mb.GetParam2(x, y, z)
2019-01-11 15:30:51 +03:00
if nodeName == "" {
continue
}
2019-06-20 08:14:14 +03:00
c := r.colors.GetColor(nodeName, param2)
2019-01-11 15:30:51 +03:00
if c == nil {
continue
}
2024-06-28 17:57:05 +03:00
// clamp alpha channel to max
c.A = 255
2019-01-17 15:13:18 +03:00
if r.enableShadow {
var left, leftAbove, top, topAbove string
if x > 0 {
//same mapblock
left = mb.GetNodeName(x-1, y, z)
2019-01-28 13:45:23 +03:00
if y < 15 {
leftAbove = mb.GetNodeName(x-1, y+1, z)
}
2019-01-17 15:13:18 +03:00
} else {
//neighbouring mapblock
2023-12-29 18:00:11 +03:00
neighbourPos := types.NewMapBlockCoords(currentPos.X-1, currentPos.Y, currentPos.Z)
2019-01-17 15:13:18 +03:00
neighbourMapblock, err := r.accessor.GetMapBlock(neighbourPos)
if neighbourMapblock != nil && err == nil {
2019-01-20 23:26:25 +03:00
left = neighbourMapblock.GetNodeName(15, y, z)
2019-01-28 13:45:23 +03:00
if y < 15 {
leftAbove = neighbourMapblock.GetNodeName(15, y+1, z)
}
2019-01-17 15:13:18 +03:00
}
}
if z < 14 {
//same mapblock
top = mb.GetNodeName(x, y, z+1)
2019-01-28 13:45:23 +03:00
if y < 15 {
topAbove = mb.GetNodeName(x, y+1, z+1)
}
2019-01-17 15:13:18 +03:00
} else {
//neighbouring mapblock
2023-12-29 18:00:11 +03:00
neighbourPos := types.NewMapBlockCoords(currentPos.X, currentPos.Y, currentPos.Z+1)
2019-01-17 15:13:18 +03:00
neighbourMapblock, err := r.accessor.GetMapBlock(neighbourPos)
if neighbourMapblock != nil && err == nil {
2019-01-20 23:26:25 +03:00
top = neighbourMapblock.GetNodeName(x, y, 0)
2019-01-28 13:45:23 +03:00
if y < 15 {
topAbove = neighbourMapblock.GetNodeName(x, y+1, 0)
2019-01-28 13:45:23 +03:00
}
2019-01-17 15:13:18 +03:00
}
}
if IsViewBlocking(leftAbove) {
//add shadow
c = addColorComponent(c, -10)
}
if IsViewBlocking(topAbove) {
//add shadow
c = addColorComponent(c, -10)
}
if !IsViewBlocking(left) {
//add light
c = addColorComponent(c, 10)
}
if !IsViewBlocking(top) {
//add light
c = addColorComponent(c, 10)
}
}
2019-01-20 23:03:58 +03:00
imgX := x * IMG_SCALE
imgY := (15 - z) * IMG_SCALE
r32, g32, b32, a32 := c.RGBA()
r8, g8, b8, a8 := uint8(r32), uint8(g32), uint8(b32), uint8(a32)
2019-10-01 09:14:06 +03:00
for Y := imgY; Y < imgY+IMG_SCALE; Y++ {
ix := (Y*IMG_SIZE + imgX) << 2
for X := 0; X < IMG_SCALE; X++ {
img.Pix[ix] = r8
ix++
img.Pix[ix] = g8
ix++
img.Pix[ix] = b8
ix++
img.Pix[ix] = a8
ix++
}
}
2019-01-11 15:30:51 +03:00
2019-02-27 19:36:30 +03:00
if c.A != 0xFF || !r.enableTransparency {
2019-02-27 18:33:37 +03:00
//not transparent, mark as rendered
foundBlocks++
xzOccupationMap[x][z] = true
}
2019-02-27 19:36:30 +03:00
2019-01-11 15:30:51 +03:00
if foundBlocks == EXPECTED_BLOCKS_PER_FLAT_MAPBLOCK {
2019-01-11 15:53:41 +03:00
return img, nil
2019-01-11 15:30:51 +03:00
}
}
}
}
}
2019-01-11 13:44:17 +03:00
2019-01-11 16:17:16 +03:00
if foundBlocks == 0 {
return nil, nil
}
2019-01-11 15:30:51 +03:00
return img, nil
2019-01-10 17:21:34 +03:00
}