diff --git a/.gitignore b/.gitignore
index 4502932..f3f73ff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,3 +27,4 @@ debug
/tmx2img
vendor/
map.png
+assets/test_output/
diff --git a/assets/test_isometric.tmx b/assets/test_isometric.tmx
new file mode 100644
index 0000000..80e2f6c
--- /dev/null
+++ b/assets/test_isometric.tmx
@@ -0,0 +1,76 @@
+
+
diff --git a/assets/tilesets/isometric.tsx b/assets/tilesets/isometric.tsx
new file mode 100755
index 0000000..19ab86a
--- /dev/null
+++ b/assets/tilesets/isometric.tsx
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/assets/tilesets/isometric_tiles.png b/assets/tilesets/isometric_tiles.png
new file mode 100755
index 0000000..40f5e47
Binary files /dev/null and b/assets/tilesets/isometric_tiles.png differ
diff --git a/render/isometric.go b/render/isometric.go
new file mode 100644
index 0000000..96153f4
--- /dev/null
+++ b/render/isometric.go
@@ -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)
+}
\ No newline at end of file
diff --git a/render/orthogonal.go b/render/orthogonal.go
index cfe762d..0f2de33 100644
--- a/render/orthogonal.go
+++ b/render/orthogonal.go
@@ -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)
}
diff --git a/render/render_objects_test.go b/render/render_objects_test.go
index 20820cb..71ad447 100644
--- a/render/render_objects_test.go
+++ b/render/render_objects_test.go
@@ -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 {
diff --git a/render/renderer.go b/render/renderer.go
index 56d4c48..e0eebff 100644
--- a/render/renderer.go
+++ b/render/renderer.go
@@ -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.
@@ -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)
@@ -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++
diff --git a/render/renderer_test.go b/render/renderer_test.go
new file mode 100644
index 0000000..7a841b3
--- /dev/null
+++ b/render/renderer_test.go
@@ -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")
+}
\ No newline at end of file