first commit
This commit is contained in:
commit
c450ae3cff
39
README.md
Normal file
39
README.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# spritesheet_generator
|
||||||
|
|
||||||
|
This is a simple program to generate a template for isometric sprite
|
||||||
|
tilesheets, in png format. For use with software such as [Tiled](https://www.mapeditor.org).
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
`go install github.com/tardisx/spritesheet_generator@latest`
|
||||||
|
|
||||||
|
If you really want binaries, pester me :-)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
`Usage of ./spritesheet_generator:
|
||||||
|
-height int
|
||||||
|
base tile height in pixels (default 128)
|
||||||
|
-multiplier int
|
||||||
|
tile height multiplier (default 2)
|
||||||
|
-output string
|
||||||
|
output filename
|
||||||
|
-width int
|
||||||
|
tile width in pixels (default 128)
|
||||||
|
-x int
|
||||||
|
number of tiles across (default 8)
|
||||||
|
-y int
|
||||||
|
number of tiles down (default 8)`
|
||||||
|
|
||||||
|
Hopefully these options are mostly self-explanatory.
|
||||||
|
|
||||||
|
The `multiplier` option describes how 'tall' the tiles are. Normally you
|
||||||
|
want some height to tiles to give them the illusion of depth and the ability
|
||||||
|
to hide things behind them. If you are unsure, start with the default of 2.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
![Screenshot][screenshot]
|
||||||
|
|
||||||
|
|
||||||
|
[screenshot]: https://raw.githubusercontent.com/tardisx/spritesheet_generator/main/example.png
|
BIN
example.png
Normal file
BIN
example.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module github.com/tardisx/spritesheet_generator
|
||||||
|
|
||||||
|
go 1.19
|
||||||
|
|
||||||
|
require golang.org/x/image v0.2.0
|
26
go.sum
Normal file
26
go.sum
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/image v0.2.0 h1:/DcQ0w3VHKCC5p0/P2B0JpAZ9Z++V2KOo2fyU89CXBQ=
|
||||||
|
golang.org/x/image v0.2.0/go.mod h1:la7oBXb9w3YFjBqaAwtynVioc1ZvOnNteUNrifGNmAI=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
248
main.go
Normal file
248
main.go
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/png"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/image/font"
|
||||||
|
"golang.org/x/image/font/basicfont"
|
||||||
|
"golang.org/x/image/math/fixed"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tile struct {
|
||||||
|
X int
|
||||||
|
Y int
|
||||||
|
}
|
||||||
|
|
||||||
|
type pt struct {
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
var tileWidth, tileHeight, multiplier int
|
||||||
|
var xTiles, yTiles int
|
||||||
|
var output string
|
||||||
|
|
||||||
|
flag.IntVar(&tileWidth, "width", 128, "tile width in pixels")
|
||||||
|
flag.IntVar(&tileHeight, "height", 128, "base tile height in pixels")
|
||||||
|
flag.IntVar(&multiplier, "multiplier", 2, "tile height multiplier")
|
||||||
|
flag.IntVar(&xTiles, "x", 8, "number of tiles across")
|
||||||
|
flag.IntVar(&yTiles, "y", 8, "number of tiles down")
|
||||||
|
flag.StringVar(&output, "output", "", "output filename")
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if output == "" {
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
t := tile{tileWidth, tileHeight}
|
||||||
|
|
||||||
|
width := xTiles * t.X
|
||||||
|
height := yTiles * t.Y
|
||||||
|
|
||||||
|
upLeft := image.Point{0, 0}
|
||||||
|
lowRight := image.Point{width, height}
|
||||||
|
|
||||||
|
img := image.NewRGBA(image.Rectangle{upLeft, lowRight})
|
||||||
|
|
||||||
|
for x := 0; x < xTiles; x++ {
|
||||||
|
for y := 0; y < yTiles; y++ {
|
||||||
|
drawDiamond(img, t, x, y, multiplier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode as PNG.
|
||||||
|
f, err := os.Create(output)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("could not output to %s - %s", output, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
png.Encode(f, img)
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func drawDiamond(img *image.RGBA, t tile, x int, y int, multiplier int) {
|
||||||
|
|
||||||
|
tileX := x * t.X
|
||||||
|
tileY := y * t.Y
|
||||||
|
|
||||||
|
yTop := t.Y - (t.Y / multiplier)
|
||||||
|
yMid := t.Y - (t.Y / (multiplier * 2))
|
||||||
|
|
||||||
|
pt1 := pt{tileX, tileY + yMid} // left middle
|
||||||
|
pt2 := pt{tileX + t.X/2, tileY + t.Y} // bottom
|
||||||
|
pt3 := pt{tileX + t.X - 1, tileY + yMid} // right
|
||||||
|
pt4 := pt{tileX + t.X/2, tileY + yTop} // top
|
||||||
|
|
||||||
|
drawLine(img, pt1.x, pt1.y, pt2.x, pt2.y, color.Black)
|
||||||
|
drawLine(img, pt2.x, pt2.y, pt3.x, pt3.y, color.Black)
|
||||||
|
drawLine(img, pt3.x, pt3.y, pt4.x, pt4.y, color.Black)
|
||||||
|
drawLine(img, pt4.x, pt4.y, pt1.x, pt1.y, color.Black)
|
||||||
|
addLabel(img, tileX+t.X/2-10, tileY+30, fmt.Sprintf("%d,%d", x, y))
|
||||||
|
|
||||||
|
// tl => bl
|
||||||
|
drawLine(img, tileX, tileY, tileX, tileY+t.Y-1, color.White)
|
||||||
|
// bl => br
|
||||||
|
drawLine(img, tileX, tileY+t.Y-1, tileX+t.X-1, tileY+t.Y-1, color.White)
|
||||||
|
// br => tr
|
||||||
|
drawLine(img, tileX+t.X-1, tileY+t.Y-1, tileX+t.X-1, tileY, color.White)
|
||||||
|
// tr => tl
|
||||||
|
drawLine(img, tileX+t.X-1, tileY, tileX, tileY, color.White)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// thanks to https://github.com/StephaneBunel/bresenham
|
||||||
|
func drawLine(img *image.RGBA, x1, y1, x2, y2 int, col color.Color) {
|
||||||
|
var dx, dy, e, slope int
|
||||||
|
|
||||||
|
// Because drawing p1 -> p2 is equivalent to draw p2 -> p1,
|
||||||
|
// I sort points in x-axis order to handle only half of possible cases.
|
||||||
|
if x1 > x2 {
|
||||||
|
x1, y1, x2, y2 = x2, y2, x1, y1
|
||||||
|
}
|
||||||
|
|
||||||
|
dx, dy = x2-x1, y2-y1
|
||||||
|
// Because point is x-axis ordered, dx cannot be negative
|
||||||
|
if dy < 0 {
|
||||||
|
dy = -dy
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
|
||||||
|
// Is line a point ?
|
||||||
|
case x1 == x2 && y1 == y2:
|
||||||
|
img.Set(x1, y1, col)
|
||||||
|
|
||||||
|
// Is line an horizontal ?
|
||||||
|
case y1 == y2:
|
||||||
|
for ; dx != 0; dx-- {
|
||||||
|
img.Set(x1, y1, col)
|
||||||
|
x1++
|
||||||
|
}
|
||||||
|
img.Set(x1, y1, col)
|
||||||
|
|
||||||
|
// Is line a vertical ?
|
||||||
|
case x1 == x2:
|
||||||
|
if y1 > y2 {
|
||||||
|
y1, y2 = y2, y1
|
||||||
|
}
|
||||||
|
for ; dy != 0; dy-- {
|
||||||
|
img.Set(x1, y1, col)
|
||||||
|
y1++
|
||||||
|
}
|
||||||
|
img.Set(x1, y1, col)
|
||||||
|
|
||||||
|
// Is line a diagonal ?
|
||||||
|
case dx == dy:
|
||||||
|
if y1 < y2 {
|
||||||
|
for ; dx != 0; dx-- {
|
||||||
|
img.Set(x1, y1, col)
|
||||||
|
x1++
|
||||||
|
y1++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for ; dx != 0; dx-- {
|
||||||
|
img.Set(x1, y1, col)
|
||||||
|
x1++
|
||||||
|
y1--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
img.Set(x1, y1, col)
|
||||||
|
|
||||||
|
// wider than high ?
|
||||||
|
case dx > dy:
|
||||||
|
if y1 < y2 {
|
||||||
|
// BresenhamDxXRYD(img, x1, y1, x2, y2, col)
|
||||||
|
dy, e, slope = 2*dy, dx, 2*dx
|
||||||
|
for ; dx != 0; dx-- {
|
||||||
|
img.Set(x1, y1, col)
|
||||||
|
x1++
|
||||||
|
e -= dy
|
||||||
|
if e < 0 {
|
||||||
|
y1++
|
||||||
|
e += slope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// BresenhamDxXRYU(img, x1, y1, x2, y2, col)
|
||||||
|
dy, e, slope = 2*dy, dx, 2*dx
|
||||||
|
for ; dx != 0; dx-- {
|
||||||
|
img.Set(x1, y1, col)
|
||||||
|
x1++
|
||||||
|
e -= dy
|
||||||
|
if e < 0 {
|
||||||
|
y1--
|
||||||
|
e += slope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
img.Set(x2, y2, col)
|
||||||
|
|
||||||
|
// higher than wide.
|
||||||
|
default:
|
||||||
|
if y1 < y2 {
|
||||||
|
// BresenhamDyXRYD(img, x1, y1, x2, y2, col)
|
||||||
|
dx, e, slope = 2*dx, dy, 2*dy
|
||||||
|
for ; dy != 0; dy-- {
|
||||||
|
img.Set(x1, y1, col)
|
||||||
|
y1++
|
||||||
|
e -= dx
|
||||||
|
if e < 0 {
|
||||||
|
x1++
|
||||||
|
e += slope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// BresenhamDyXRYU(img, x1, y1, x2, y2, col)
|
||||||
|
dx, e, slope = 2*dx, dy, 2*dy
|
||||||
|
for ; dy != 0; dy-- {
|
||||||
|
img.Set(x1, y1, col)
|
||||||
|
y1--
|
||||||
|
e -= dx
|
||||||
|
if e < 0 {
|
||||||
|
x1++
|
||||||
|
e += slope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
img.Set(x2, y2, col)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addLabel(img *image.RGBA, x, y int, label string) {
|
||||||
|
col := color.RGBA{20, 20, 240, 255}
|
||||||
|
point := fixed.Point26_6{X: fixed.I(x), Y: fixed.I(y)}
|
||||||
|
|
||||||
|
d := &font.Drawer{
|
||||||
|
Dst: img,
|
||||||
|
Src: image.NewUniform(col),
|
||||||
|
Face: basicfont.Face7x13,
|
||||||
|
Dot: point,
|
||||||
|
}
|
||||||
|
d.DrawString(label)
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Colors are defined by Red, Green, Blue, Alpha uint8 values.
|
||||||
|
// cyan := color.RGBA{100, 200, 200, 0xff}
|
||||||
|
|
||||||
|
// // Set color for each pixel.
|
||||||
|
// for x := 0; x < width; x++ {
|
||||||
|
// for y := 0; y < height; y++ {
|
||||||
|
// switch {
|
||||||
|
// case x < width/2 && y < height/2: // upper left quadrant
|
||||||
|
// img.Set(x, y, cyan)
|
||||||
|
// case x >= width/2 && y >= height/2: // lower right quadrant
|
||||||
|
// img.Set(x, y, color.White)
|
||||||
|
// default:
|
||||||
|
// // Use zero value.
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
Loading…
x
Reference in New Issue
Block a user