diff --git a/assets/hex.tmx b/assets/hex.tmx
new file mode 100644
index 0000000..d474e03
--- /dev/null
+++ b/assets/hex.tmx
@@ -0,0 +1,18 @@
+
+
diff --git a/assets/tilesets/hex-tiles.png b/assets/tilesets/hex-tiles.png
new file mode 100644
index 0000000..92d9701
Binary files /dev/null and b/assets/tilesets/hex-tiles.png differ
diff --git a/assets/tilesets/hex-tiles.tsx b/assets/tilesets/hex-tiles.tsx
new file mode 100644
index 0000000..02d5f06
--- /dev/null
+++ b/assets/tilesets/hex-tiles.tsx
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/render/hexagonal.go b/render/hexagonal.go
new file mode 100644
index 0000000..c1e4cd8
--- /dev/null
+++ b/render/hexagonal.go
@@ -0,0 +1,96 @@
+/*
+Copyright (c) 2022 Andre Renaud
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+package render
+
+import (
+ "image"
+
+ "github.com/disintegration/imaging"
+ tiled "github.com/lafriks/go-tiled"
+)
+
+// HexagonalRendererEngine represents hexangonal rendering engine.
+type HexagonalRendererEngine struct {
+ m *tiled.Map
+}
+
+// Init initializes rendering engine with provided map options.
+func (e *HexagonalRendererEngine) Init(m *tiled.Map) {
+ e.m = m
+}
+
+// GetFinalImageSize returns final image size based on map data.
+func (e *HexagonalRendererEngine) GetFinalImageSize() image.Rectangle {
+ switch e.m.StaggerAxis {
+ case tiled.AxisX:
+ return image.Rect(0, 0, e.m.Width*e.m.TileWidth, e.m.Height*e.m.TileHeight+e.m.TileHeight/2)
+ case tiled.AxisY:
+ return image.Rect(0, 0, e.m.Width*e.m.TileWidth+e.m.TileWidth/2, (e.m.Height+1)*e.m.TileHeight*3/4)
+ }
+ return image.Rectangle{}
+}
+
+// RotateTileImage rotates provided tile layer.
+func (e *HexagonalRendererEngine) RotateTileImage(tile *tiled.LayerTile, img image.Image) image.Image {
+ timg := img
+ if tile.HorizontalFlip {
+ timg = imaging.FlipH(timg)
+ }
+ if tile.VerticalFlip {
+ timg = imaging.FlipV(timg)
+ }
+ if tile.DiagonalFlip {
+ timg = imaging.FlipH(imaging.Rotate90(timg))
+ }
+
+ return timg
+}
+
+// GetTilePosition returns tile position in image.
+func (e *HexagonalRendererEngine) GetTilePosition(x, y int) image.Rectangle {
+ switch e.m.StaggerAxis {
+ case tiled.AxisX:
+ oddColumn := (x % 2) == 1
+ offsetWidth := e.m.TileWidth * 3 / 4
+ yBump := 0
+ if oddColumn {
+ yBump = e.m.TileHeight / 2
+ }
+ return image.Rect(x*offsetWidth,
+ y*e.m.TileHeight+yBump,
+ x*offsetWidth+e.m.TileWidth,
+ (y+2)*e.m.TileHeight+yBump)
+ case tiled.AxisY:
+ oddRow := (y % 2) == 1
+ offsetHeight := e.m.TileHeight * 3 / 4
+ xBump := 0
+ if oddRow {
+ xBump = e.m.TileWidth / 2
+ }
+ return image.Rect(x*e.m.TileHeight+xBump,
+ y*offsetHeight,
+ (x+2)*e.m.TileWidth+xBump,
+ (y+1)*offsetHeight+e.m.TileHeight)
+ }
+ return image.Rectangle{}
+}
diff --git a/render/renderer.go b/render/renderer.go
index 17f3a99..52a3d53 100644
--- a/render/renderer.go
+++ b/render/renderer.go
@@ -75,6 +75,8 @@ 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" {
r.engine = &OrthogonalRendererEngine{}
+ } else if r.m.Orientation == "hexagonal" {
+ r.engine = &HexagonalRendererEngine{}
} else {
return nil, ErrUnsupportedOrientation
}
@@ -137,10 +139,42 @@ func (r *Renderer) getTileImage(tile *tiled.LayerTile) (image.Image, error) {
return r.engine.RotateTileImage(tile, timg), nil
}
+func (r *Renderer) _renderTile(layer *tiled.Layer, i int, x int, y int) error {
+ if layer.Tiles[i].IsNil() {
+ return nil
+ }
+
+ img, err := r.getTileImage(layer.Tiles[i])
+ if err != nil {
+ return err
+ }
+
+ pos := r.engine.GetTilePosition(x, y)
+
+ 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)
+ } else {
+ draw.Draw(r.Result, pos, img, img.Bounds().Min, draw.Over)
+ }
+
+ return nil
+}
+
func (r *Renderer) _renderLayer(layer *tiled.Layer) error {
var xs, xe, xi, ys, ye, yi int
- if r.m.RenderOrder == "" || r.m.RenderOrder == "right-down" {
+ var odd bool
+ if r.m.Orientation == "hexagonal" {
+ xs = 0
+ xe = r.m.Width
+ xi = 2
+ ys = 0
+ ye = r.m.Height
+ yi = 1
+ odd = true
+ } else if r.m.RenderOrder == "" || r.m.RenderOrder == "right-down" {
xs = 0
xe = r.m.Width
xi = 1
@@ -151,30 +185,22 @@ func (r *Renderer) _renderLayer(layer *tiled.Layer) error {
return ErrUnsupportedRenderOrder
}
- i := 0
for y := ys; y*yi < ye; y = y + yi {
- for x := xs; x*xi < xe; x = x + xi {
- if layer.Tiles[i].IsNil() {
- i++
- continue
- }
-
- img, err := r.getTileImage(layer.Tiles[i])
- if err != nil {
+ i := (y - ys) * xe
+ for x := xs; x < xe; x = x + xi {
+ if err := r._renderTile(layer, i, x, y); err != nil {
return err
}
-
- pos := r.engine.GetTilePosition(x, y)
-
- 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)
- } else {
- draw.Draw(r.Result, pos, img, img.Bounds().Min, draw.Over)
+ i += xi
+ }
+ if odd {
+ i = (y-ys)*xe + 1
+ for x := xs + 1; x < xe; x = x + xi {
+ if err := r._renderTile(layer, i, x, y); err != nil {
+ return err
+ }
+ i += xi
}
-
- i++
}
}