From 623d2248cc82811106edd6583303e4f90b62cc1c Mon Sep 17 00:00:00 2001 From: Andre Renaud Date: Mon, 5 Jun 2023 14:10:52 +1200 Subject: [PATCH 1/3] Added rudimentary hexagonal renderer --- render/hexagonal.go | 75 +++++++++++++++++++++++++++++++++++++++++++++ render/renderer.go | 2 ++ 2 files changed, 77 insertions(+) create mode 100644 render/hexagonal.go diff --git a/render/hexagonal.go b/render/hexagonal.go new file mode 100644 index 0000000..edbd845 --- /dev/null +++ b/render/hexagonal.go @@ -0,0 +1,75 @@ +/* +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 { + return image.Rect(0, 0, e.m.Width*e.m.TileWidth, e.m.Height*e.m.TileHeight) +} + +// 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 { + 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) +} diff --git a/render/renderer.go b/render/renderer.go index 17f3a99..35ea9e6 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 } From 0af35cbe476abc0e046e6b72f28e7e3129773913 Mon Sep 17 00:00:00 2001 From: Andre Renaud Date: Mon, 5 Jun 2023 14:59:28 +1200 Subject: [PATCH 2/3] Added hexagonal rendering order --- render/renderer.go | 66 +++++++++++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/render/renderer.go b/render/renderer.go index 35ea9e6..52a3d53 100644 --- a/render/renderer.go +++ b/render/renderer.go @@ -139,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 @@ -153,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++ } } From 51b1d72bcdfa9233aec3f7d28f42be371b503b32 Mon Sep 17 00:00:00 2001 From: Andre Renaud Date: Tue, 6 Jun 2023 07:52:26 +1200 Subject: [PATCH 3/3] Fixed up Y-axis rendering & added exampe map --- assets/hex.tmx | 18 +++++++++++++++ assets/tilesets/hex-tiles.png | Bin 0 -> 620 bytes assets/tilesets/hex-tiles.tsx | 4 ++++ render/hexagonal.go | 41 +++++++++++++++++++++++++--------- 4 files changed, 53 insertions(+), 10 deletions(-) create mode 100644 assets/hex.tmx create mode 100644 assets/tilesets/hex-tiles.png create mode 100644 assets/tilesets/hex-tiles.tsx 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 @@ + + + + + +1,2,4,6,6,6,2,6,6,6, +4,5,4,6,6,2,6,6,6,6, +5,5,4,6,6,2,6,6,6,6, +4,4,6,6,2,6,6,6,6,6, +6,6,6,6,2,6,6,6,6,6, +6,6,6,2,6,6,6,6,6,6, +6,6,6,2,6,6,6,6,6,6, +6,6,2,6,6,6,6,6,6,6, +6,6,2,6,6,6,6,6,6,6, +6,2,6,6,6,6,6,6,6,6 + + + diff --git a/assets/tilesets/hex-tiles.png b/assets/tilesets/hex-tiles.png new file mode 100644 index 0000000000000000000000000000000000000000..92d97012ac93543f3a162eea0e9d2760a7c089e2 GIT binary patch literal 620 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy!Sl0AZa85pY67#JE_7#My5g&JNk zFq9fFFuY1&V6d9Oz#v{QXIG#NP{KICC&U#<13}?u$Nz`3{{R2)xk}*w7MItrUt8od zT%KaKY15`=H-^MT|0^9C920+AC~+z)^^rvA)u}cK7ddwYm(R833B7(aX=8-W zvgw!H?+5RS*<+v%C9;^(dlm7Xv5sFcJBSe$1$XWaPNXU0#j2oRcl!r^_+i34|U&S-4ktl7Z8 z#PJ}yA+g|>q=ftO`Hh)Hzb=0~xPjx;<=w6U^X};?J%4S^E2tWOPcZP)Q(3|FHfk3A zoad6Ao&fEbHKEdX1;`E|D}Bxx=}gaO9;j41Q=|GvetX$Ip|kg|-*vyA4D^PW^|z1J z7j5yuorr(FU(x-*z|cu#+~N6r;0#>fsB7oL>GRZ z1Y|gXyx{U#js@%mlXs43|5S`NSlpiVUrBZO{&m$1j4T2Nwlmy*_GSAsTaCB7FW>Ly zZ*vXUSIfVtbI$tvCN5KID!;#XoKf-A5Ew`&X1A!h#0eX%=dArSq2-jf({t5EeJ!5@ tHo_L?TWU9Gw#;0A#QM8}(!bwvUziWscUSV=-WLx_2%fHfF6*2UngFZG5e5JN literal 0 HcmV?d00001 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 index edbd845..c1e4cd8 100644 --- a/render/hexagonal.go +++ b/render/hexagonal.go @@ -41,7 +41,13 @@ func (e *HexagonalRendererEngine) Init(m *tiled.Map) { // GetFinalImageSize returns final image size based on map data. func (e *HexagonalRendererEngine) GetFinalImageSize() image.Rectangle { - return image.Rect(0, 0, e.m.Width*e.m.TileWidth, e.m.Height*e.m.TileHeight) + 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. @@ -62,14 +68,29 @@ func (e *HexagonalRendererEngine) RotateTileImage(tile *tiled.LayerTile, img ima // GetTilePosition returns tile position in image. func (e *HexagonalRendererEngine) GetTilePosition(x, y int) image.Rectangle { - oddColumn := (x % 2) == 1 - offsetWidth := e.m.TileWidth * 3 / 4 - yBump := 0 - if oddColumn { - yBump = e.m.TileHeight / 2 + 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.Rect(x*offsetWidth, - y*e.m.TileHeight+yBump, - x*offsetWidth+e.m.TileWidth, - (y+2)*e.m.TileHeight+yBump) + return image.Rectangle{} }