Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ debug
/tmx2img
vendor/
map.png
assets/test_output/
76 changes: 76 additions & 0 deletions assets/test_isometric.tmx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.11.2" orientation="isometric" renderorder="right-down" width="30" height="20" tilewidth="32" tileheight="16" infinite="0" nextlayerid="4" nextobjectid="1">
<tileset firstgid="1" source="tilesets/isometric.tsx"/>
<layer id="1" name="Tile Layer 1" width="30" height="20">
<data encoding="csv">
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,2,2,2,2,9,9,9,9,9,9,9,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,2,2,2,2,9,9,9,9,9,9,9,9,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,2,2,2,2,2,9,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,2,2,2,12,12,12,12,12,12,12,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,2,2,2,12,12,12,12,12,12,12,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2
</data>
</layer>
<layer id="2" name="Tile Layer 2" width="30" height="20">
<data encoding="csv">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1073741835,0,2147483659,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1610612751,0,2684354572,3221225486,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,9,11,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,9,11,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,9,11,0,0,0,0,0,0,0,
0,0,0,12,12,0,12,12,12,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</data>
</layer>
<layer id="3" name="Tile Layer 3" width="30" height="20">
<data encoding="csv">
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,13,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
</data>
</layer>
</map>
4 changes: 4 additions & 0 deletions assets/tilesets/isometric.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.10" tiledversion="1.11.2" name="isotiles" tilewidth="32" tileheight="32" tilecount="64" columns="8">
<image source="isometric_tiles.png" width="256" height="256"/>
</tileset>
Binary file added assets/tilesets/isometric_tiles.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
58 changes: 58 additions & 0 deletions render/isometric.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package render

import (
"image"

"github.com/disintegration/imaging"
"github.com/lafriks/go-tiled"
)

type IsometricRendererEngine struct {
m *tiled.Map
}

func (e *IsometricRendererEngine) Init(m *tiled.Map) {
e.m = m
}

func (e *IsometricRendererEngine) GetFinalImageSize() image.Rectangle {
side := e.m.Height + e.m.Width
hx := side * e.m.TileWidth/2
hy := side * e.m.TileHeight/2

return image.Rect(0, 0, hx, hy)
}

func (e *IsometricRendererEngine) RotateTileImage(tile *tiled.LayerTile, img image.Image) image.Image {
timg := img
if tile.DiagonalFlip {
timg = imaging.FlipH(imaging.Rotate270(timg))
}
if tile.HorizontalFlip {
timg = imaging.FlipH(timg)
}
if tile.VerticalFlip {
timg = imaging.FlipV(timg)
}

return timg
}

func (e *IsometricRendererEngine) GetTilePosition(x, y int) image.Point {
tw, th := e.m.TileWidth, e.m.TileHeight

stepX := tw / 2
stepY := th / 2

offsetX := e.m.Height * e.m.TileWidth/2

offsetY := 0
if tw > th {
offsetY = tw - th
}

sx := (x - y) * stepX + offsetX - stepX
sy := (x + y) * stepY - offsetY

return image.Pt(sx, sy)
}
8 changes: 3 additions & 5 deletions render/orthogonal.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,7 @@ func (e *OrthogonalRendererEngine) RotateTileImage(tile *tiled.LayerTile, img im
}

// GetTilePosition returns tile position in image.
func (e *OrthogonalRendererEngine) GetTilePosition(x, y int) image.Rectangle {
return image.Rect(x*e.m.TileWidth,
y*e.m.TileHeight,
(x+1)*e.m.TileWidth,
(y+1)*e.m.TileHeight)
func (e *OrthogonalRendererEngine) GetTilePosition(x, y int) image.Point {
return image.Pt(x*e.m.TileWidth,
y*e.m.TileHeight)
}
2 changes: 1 addition & 1 deletion render/render_objects_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func TestRenderer_RenderObjectGroup(t *testing.T) {

renderer.RenderObjectGroup(0)

w, _ := os.Create("../assets/test_render_objects.png")
w, _ := os.Create("../assets/test_output/test_render_objects.png")
defer w.Close()

if err = renderer.SaveAsPng(w); err != nil {
Expand Down
15 changes: 10 additions & 5 deletions render/renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ type RendererEngine interface {
Init(m *tiled.Map)
GetFinalImageSize() image.Rectangle
RotateTileImage(tile *tiled.LayerTile, img image.Image) image.Image
GetTilePosition(x, y int) image.Rectangle
GetTilePosition(x, y int) image.Point
}

// Renderer represents an rendering engine.
Expand All @@ -74,10 +74,14 @@ func NewRenderer(m *tiled.Map) (*Renderer, error) {
// NewRendererWithFileSystem creates new rendering engine instance with a custom file system.
func NewRendererWithFileSystem(m *tiled.Map, fs fs.FS) (*Renderer, error) {
r := &Renderer{m: m, tileCache: make(map[uint32]image.Image), fs: fs}
if r.m.Orientation == "orthogonal" {
switch r.m.Orientation {
case "orthogonal":
r.engine = &OrthogonalRendererEngine{}
} else {
case "isometric":
r.engine = &IsometricRendererEngine{}
default:
return nil, ErrUnsupportedOrientation

}

r.engine.Init(r.m)
Expand Down Expand Up @@ -165,13 +169,14 @@ func (r *Renderer) _renderLayer(layer *tiled.Layer) error {
}

pos := r.engine.GetTilePosition(x, y)
renderRect := image.Rect(pos.X, pos.Y, pos.X + img.Bounds().Dx(), pos.Y + img.Bounds().Dy())

if layer.Opacity < 1 {
mask := image.NewUniform(color.Alpha{uint8(layer.Opacity * 255)})

draw.DrawMask(r.Result, pos, img, img.Bounds().Min, mask, mask.Bounds().Min, draw.Over)
draw.DrawMask(r.Result, renderRect, img, img.Bounds().Min, mask, mask.Bounds().Min, draw.Over)
} else {
draw.Draw(r.Result, pos, img, img.Bounds().Min, draw.Over)
draw.Draw(r.Result, renderRect, img, img.Bounds().Min, draw.Over)
}

i++
Expand Down
81 changes: 81 additions & 0 deletions render/renderer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package render

import (
"image"
"os"
"testing"
"path/filepath"

"github.com/lafriks/go-tiled"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestMain(m *testing.M) {
dir := filepath.Join("..", "assets", "test_output")
if err := os.MkdirAll(dir, 0755); err != nil {
os.Exit(1)
}

exitCode := m.Run()
os.Exit(exitCode)
}

func TestRenderer_RenderOrthogonalMap(t *testing.T) {
tiledMap, err := tiled.LoadFile("../assets/test_wangsets_map.tmx")

if err != nil {
t.Error(err)
return
}

renderer, err := NewRenderer(tiledMap)
if err != nil {
t.Error(err)
return
}

renderer.RenderVisibleLayers()

w, _ := os.Create("../assets/test_output/test_render_orthogonal.png")
defer w.Close()

if err = renderer.SaveAsPng(w); err != nil {
t.Error(err)
}
}

func TestRenderer_RenderIsometricMap(t *testing.T) {
tiledMap, err := tiled.LoadFile("../assets/test_isometric.tmx")
if err != nil {
t.Error(err)
return
}

renderer, err := NewRenderer(tiledMap)
if err != nil {
t.Error(err)
return
}

renderer.RenderVisibleLayers()

outputPath := "../assets/test_output/test_render_isomap.png"

w, _ := os.Create(outputPath)
defer w.Close()

if err = renderer.SaveAsPng(w); err != nil {
t.Error(err)
}

file, err := os.Open(outputPath)
require.NoError(t, err)
defer file.Close()

img, _, err := image.Decode(file)
require.NoError(t, err)

assert.Equal(t, 800, img.Bounds().Dx(), "image width should be 800 pixels")
assert.Equal(t, 400, img.Bounds().Dy(), "image height should be 400 pixels")
}