Skip to content

Instantly share code, notes, and snippets.

@Zyko0
Last active June 21, 2023 07:49
Show Gist options
  • Save Zyko0/185f7b625a46cfc4c1df34efad29a6fa to your computer and use it in GitHub Desktop.
Save Zyko0/185f7b625a46cfc4c1df34efad29a6fa to your computer and use it in GitHub Desktop.
Ebiten: apparent seams when drawing tiles with DrawTriangles
package main
import (
"errors"
"fmt"
"image"
"image/color"
"log"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
)
const (
MouseSensitivity = 0.32109
ScreenWidth, ScreenHeight = 1920, 1080
SrcTileSize = 512
SrcSheetCols = 5
SrcSheetRows = 2
DstTileCols = 17
DstTileRows = 17
DstTileSize = ScreenWidth / DstTileCols
DstTileScale = float64(DstTileSize) / SrcTileSize
)
var (
sheetImage = ebiten.NewImage(SrcSheetCols*SrcTileSize, SrcSheetCols*SrcTileSize)
quadIndices = [6]uint16{0, 1, 2, 1, 2, 3}
)
func init() {
// Drawing sheet
sheetImage.Fill(color.RGBA{128, 0, 128, 255})
// Y = 0
sheetImage.SubImage(image.Rect(0, 0, SrcTileSize, SrcTileSize)).(*ebiten.Image).Fill(color.RGBA{196, 0, 0, 255})
sheetImage.SubImage(image.Rect(SrcTileSize*1, 0, SrcTileSize*2, SrcTileSize)).(*ebiten.Image).Fill(color.RGBA{0, 196, 0, 255})
sheetImage.SubImage(image.Rect(SrcTileSize*2, 0, SrcTileSize*3, SrcTileSize)).(*ebiten.Image).Fill(color.RGBA{0, 0, 196, 255})
sheetImage.SubImage(image.Rect(SrcTileSize*3, 0, SrcTileSize*4, SrcTileSize)).(*ebiten.Image).Fill(color.RGBA{196, 64, 0, 255})
sheetImage.SubImage(image.Rect(SrcTileSize*4, 0, SrcTileSize*5, SrcTileSize)).(*ebiten.Image).Fill(color.RGBA{196, 0, 64, 255})
// Y = 1
sheetImage.SubImage(image.Rect(0, SrcTileSize, SrcTileSize, SrcTileSize*2)).(*ebiten.Image).Fill(color.RGBA{64, 196, 0, 255})
sheetImage.SubImage(image.Rect(SrcTileSize*1, SrcTileSize, SrcTileSize*2, SrcTileSize*2)).(*ebiten.Image).Fill(color.RGBA{0, 196, 64, 255})
sheetImage.SubImage(image.Rect(SrcTileSize*2, SrcTileSize, SrcTileSize*3, SrcTileSize*2)).(*ebiten.Image).Fill(color.RGBA{64, 0, 196, 255})
sheetImage.SubImage(image.Rect(SrcTileSize*3, SrcTileSize, SrcTileSize*4, SrcTileSize*2)).(*ebiten.Image).Fill(color.RGBA{0, 64, 196, 255})
sheetImage.SubImage(image.Rect(SrcTileSize*4, SrcTileSize, SrcTileSize*5, SrcTileSize*2)).(*ebiten.Image).Fill(color.RGBA{64, 64, 255, 255})
}
const (
ModeDrawTriangles = false
ModeDrawImage = true
)
type Game struct {
mouseX int
mouseY int
offsetX float64
offsetY float64
mode bool
}
func (g *Game) Update() error {
if ebiten.IsKeyPressed(ebiten.KeyEscape) {
return errors.New("quit")
}
// Reset to problematic offset (debug purposes)
// Mine for example: (-68.71326, 712.49871)
// Another one (-818.45, -406.499)
// It seems Y needs to end with .499 for black bars to show
if ebiten.IsKeyPressed(ebiten.KeyR) {
g.offsetX = -68.71326
g.offsetY = 712.499
}
x, y := ebiten.CursorPosition()
dx, dy := x-g.mouseX, y-g.mouseY
g.mouseX, g.mouseY = x, y
g.offsetX += float64(dx) * MouseSensitivity
g.offsetY += float64(dy) * MouseSensitivity
g.mode = ebiten.IsKeyPressed(ebiten.KeySpace)
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {
index := 0
for x := 0; x < DstTileCols; x++ {
for y := 0; y < DstTileRows; y++ {
srcx := (x % SrcSheetCols) * SrcTileSize
srcy := (y % SrcSheetRows) * SrcTileSize
dstx := float64(x*DstTileSize) - g.offsetX
dsty := float64(y*DstTileSize) - g.offsetY
switch g.mode {
case ModeDrawTriangles:
vertices := []ebiten.Vertex{}
indices := []uint16{}
vertices = append(vertices, []ebiten.Vertex{
{
DstX: float32(dstx),
DstY: float32(dsty),
SrcX: float32(srcx),
SrcY: float32(srcy),
ColorR: 1,
ColorG: 1,
ColorB: 1,
ColorA: 1,
},
{
DstX: float32(dstx + DstTileSize),
DstY: float32(dsty),
SrcX: float32(srcx + SrcTileSize),
SrcY: float32(srcy),
ColorR: 1,
ColorG: 1,
ColorB: 1,
ColorA: 1,
},
{
DstX: float32(dstx),
DstY: float32(dsty + DstTileSize),
SrcX: float32(srcx),
SrcY: float32(srcy + SrcTileSize),
ColorR: 1,
ColorG: 1,
ColorB: 1,
ColorA: 1,
},
{
DstX: float32(dstx + DstTileSize),
DstY: float32(dsty + DstTileSize),
SrcX: float32(srcx + SrcTileSize),
SrcY: float32(srcy + SrcTileSize),
ColorR: 1,
ColorG: 1,
ColorB: 1,
ColorA: 1,
},
}...)
indiceCursor := uint16(index * 4)
indices = append(indices, []uint16{
quadIndices[0] + indiceCursor,
quadIndices[1] + indiceCursor,
quadIndices[2] + indiceCursor,
quadIndices[3] + indiceCursor,
quadIndices[4] + indiceCursor,
quadIndices[5] + indiceCursor,
}...)
screen.DrawTriangles(vertices, indices, sheetImage, &ebiten.DrawTrianglesOptions{})
case ModeDrawImage:
opts := &ebiten.DrawImageOptions{}
opts.GeoM.Scale(DstTileScale, DstTileScale)
opts.GeoM.Translate(dstx, dsty)
screen.DrawImage(
sheetImage.SubImage(image.Rect(srcx, srcy, srcx+SrcTileSize, srcy+SrcTileSize)).(*ebiten.Image), opts,
)
}
}
}
debug := "Mode (Press 'space' to switch): DrawTriangles"
if g.mode == ModeDrawImage {
debug = "Move: DrawImage"
}
debug += fmt.Sprintf(" Offset (%.5f, %.5f) - Reset to the fixed one (Press 'R')", g.offsetX, g.offsetY)
ebitenutil.DebugPrint(screen, debug)
}
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
return ScreenWidth, ScreenHeight
}
func main() {
ebiten.SetFullscreen(true)
ebiten.SetVsyncEnabled(true)
ebiten.SetCursorMode(ebiten.CursorModeCaptured)
// Below is black borders test
// broken: 1c09ec5e44727a0c38b605552d93e4d470a128ab
// perfect: 7c0fbce0cfd887a81c4f5a57757d6b804da803b5
if err := ebiten.RunGame(&Game{
mouseX: ScreenWidth / 2,
mouseY: ScreenHeight / 2,
}); err != nil {
log.Fatal(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment