initial commit

This commit is contained in:
Squishy Bloob 2024-03-23 04:15:23 +00:00
parent 4b293cd424
commit 1e2c2fe662
16 changed files with 697 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.fleet
.idea

5
build.bat Normal file
View File

@ -0,0 +1,5 @@
set CGO_ENABLED=1
set CC=x86_64-w64-mingw32-gcc
set GOOS=windows
set GOARCH=amd64
go build -tags static -ldflags "-s -w"

4
color/color.go Normal file
View File

@ -0,0 +1,4 @@
package color
const White = 0xffffffff
const Black = 0xff000000

BIN
font-thin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

32
font.go Normal file
View File

@ -0,0 +1,32 @@
package bloob
import _ "embed"
type ch struct {
xForward int
image *Image
}
type Font struct {
chars []ch
lineHeight int
}
//go:embed "font.png"
var fontdata []byte
var DefaultFont = LoadTilesetFontBytes(fontdata, Vec2i{X: 8, Y: 14})
//go:embed "font-thin.png"
var fontdataThin []byte
var DefaultFontThin = LoadTilesetFontBytes(fontdataThin, Vec2i{X: 6, Y: 8})
func LoadTilesetFontBytes(data []byte, tilesize Vec2i) *Font {
tiles := LoadImageBytes(data).MakeTileset(tilesize)
var font Font
for i := 0; i < len(tiles); i += 1 {
c := ch{xForward: tilesize.X, image: tiles[i]}
font.chars = append(font.chars, c)
}
font.lineHeight = tilesize.Y + 2
return &font
}

BIN
font.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
go.aseprite Normal file

Binary file not shown.

9
go.mod Normal file
View File

@ -0,0 +1,9 @@
module git.danitheskunk.com/squishy/blooblib
go 1.22.1
require (
github.com/askeladdk/aseprite v0.0.4
github.com/veandco/go-sdl2 v0.4.38
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81
)

BIN
go.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 986 B

9
go.sum Normal file
View File

@ -0,0 +1,9 @@
github.com/askeladdk/aseprite v0.0.4 h1:/k1VTiDkPORnrzonUUV5oXWwdHBoYjIIYJ1K/PupNMU=
github.com/askeladdk/aseprite v0.0.4/go.mod h1:lVW4EwZ7lgQjeHp7MhYj1NII5a/yLYyvAo7COPIn3WY=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/veandco/go-sdl2 v0.4.38 h1:lx8syOA2ccXlgViYkQe2Kn/4xt+p9mdd1Qc/yYMrmSo=
github.com/veandco/go-sdl2 v0.4.38/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY=
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 h1:6R2FC06FonbXQ8pK11/PDFY6N6LWlf9KlzibaCapmqc=
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=

356
image.go Normal file
View File

@ -0,0 +1,356 @@
package bloob
import (
"bytes"
_ "github.com/askeladdk/aseprite"
"image"
_ "image/png"
"math"
"os"
"runtime/pprof"
"sync"
"unsafe"
)
type Image struct {
Data []uint32
Size Vec2i
Alpha bool
}
var wg sync.WaitGroup
var threadProfile = pprof.Lookup("threadcreate")
func NewImage(size Vec2i) *Image {
return &Image{Data: make([]uint32, size.Size()), Size: Vec2i{X: size.X, Y: size.Y}, Alpha: true}
}
func NewImageFromPointer(pointer unsafe.Pointer, size Vec2i) *Image {
return &Image{Data: unsafe.Slice((*uint32)(pointer), size.Size()), Size: Vec2i{X: size.X, Y: size.Y}, Alpha: true}
}
func LoadImage(path string) *Image {
data, err := os.ReadFile(path)
if err != nil {
panic(err)
}
return LoadImageBytes(data)
}
func LoadImageBytes(data []byte) *Image {
file, _, _ := image.Decode(bytes.NewReader(data))
width := file.Bounds().Max.X
height := file.Bounds().Max.Y
img := NewImage(Vec2i{X: width, Y: height})
for y := 0; y < height; y += 1 {
for x := 0; x < width; x += 1 {
r, g, b, a := file.At(x, y).RGBA()
r = r >> 8
g = g >> 8
b = b >> 8
a = a >> 8
img.Data[x+y*width] = r<<16 | g<<8 | b | a<<24
}
}
return img
}
func (image *Image) Clear(color uint32) {
for i := 0; i < image.Size.X*image.Size.Y; i += 1 {
image.Data[i] = color
}
}
func CropToArea(sizeDst Vec2i, rectSrc Recti, posDst Vec2i) (start, end, dst Vec2i) {
if posDst.X+rectSrc.Pos.X < 0 {
start.X = -posDst.X
} else {
start.X = rectSrc.Pos.X
}
if posDst.X+rectSrc.Size.X-rectSrc.Pos.X >= sizeDst.X {
end.X = sizeDst.X - posDst.X
} else {
end.X = rectSrc.Pos.X + rectSrc.Size.X
}
if posDst.Y+rectSrc.Pos.Y < 0 {
start.Y = -posDst.Y
} else {
start.Y = rectSrc.Pos.Y
}
if posDst.Y+rectSrc.Size.Y-rectSrc.Pos.Y >= sizeDst.Y {
end.Y = sizeDst.Y - posDst.Y
} else {
end.Y = rectSrc.Pos.Y + rectSrc.Size.Y
}
dst = Sub(posDst, rectSrc.Pos)
//println(dst.X, posDst.X, rectSrc.Pos.X)
return
}
func (image *Image) Draw(other *Image, dst Vec2i) {
image.DrawSub(other, dst, Recti{Size: other.Size})
}
func (image *Image) DrawSub(other *Image, pos Vec2i, rect Recti) {
start, end, dst := CropToArea(image.Size, rect, pos)
//println(start.X, "|", end.X, "|", dst.X, "|", pos.X)
//println(end.Y)
if other.Alpha {
for y := start.Y; y < end.Y; y += 1 {
for x := start.X; x < end.X; x += 1 {
//println(y)
val := other.Data[x+y*other.Size.X]
if val&0xff000000 > 0 {
image.Data[(x+dst.X)+(y+dst.Y)*image.Size.X] = val
}
}
}
} else {
for y := start.Y; y < end.Y; y += 1 {
for x := start.X; x < end.X; x += 1 {
image.Data[(x+dst.X)+(y+dst.Y)*image.Size.X] = other.Data[x+y*other.Size.X]
}
}
}
}
func (image *Image) DrawColor(other *Image, dst Vec2i, color uint32) {
image.DrawSubColor(other, dst, Recti{Size: other.Size}, color)
}
func (image *Image) DrawSubColor(other *Image, pos Vec2i, rect Recti, color uint32) {
start, end, dst := CropToArea(image.Size, rect, pos)
for y := start.Y; y < end.Y; y += 1 {
for x := start.X; x < end.X; x += 1 {
//println(y)
val := other.Data[x+y*other.Size.X]
if val&0xff000000 > 0 {
image.Data[(x+dst.X)+(y+dst.Y)*image.Size.X] = color
}
}
}
}
func (image *Image) DrawUpscale(other *Image) {
var scale int
if image.Size.X/other.Size.X < image.Size.Y/other.Size.Y {
scale = image.Size.X / other.Size.X
} else {
scale = image.Size.Y / other.Size.Y
}
switch scale {
case 1:
wg.Add(other.Size.Y)
for y := 0; y < other.Size.Y; y += 1 {
go func(image, other *Image, y int) {
defer wg.Done()
for x := 0; x < other.Size.X; x += 1 {
val := other.Data[x+y*other.Size.X]
image.Data[(x*scale)+(y*scale)*image.Size.X] = val
}
}(image, other, y)
}
wg.Wait()
break
case 2:
wg.Add(other.Size.Y)
for y := 0; y < other.Size.Y; y += 1 {
go func(image, other *Image, y int) {
defer wg.Done()
for x := 0; x < other.Size.X; x += 1 {
val := other.Data[x+y*other.Size.X]
val2 := (val & 0xfefefe) >> 1
dx := x * 2
dy := y * 2
image.Data[(dx+0)+(dy+0)*image.Size.X] = val
image.Data[(dx+1)+(dy+0)*image.Size.X] = val
image.Data[(dx+0)+(dy+1)*image.Size.X] = val2
image.Data[(dx+1)+(dy+1)*image.Size.X] = val2
}
}(image, other, y)
}
wg.Wait()
break
case 3:
wg.Add(other.Size.Y)
//println("Before thread count : ", threadProfile.Count())
for y := 0; y < other.Size.Y; y += 1 {
go func(image, other *Image, y int) {
defer wg.Done()
for x := 0; x < other.Size.X; x += 1 {
val := other.Data[x+y*other.Size.X]
val2 := (val & 0xfefefe) >> 1
dx := x * 3
dy := y * 3
image.Data[(dx+0)+(dy+0)*image.Size.X] = val
image.Data[(dx+1)+(dy+0)*image.Size.X] = val
image.Data[(dx+2)+(dy+0)*image.Size.X] = val2
image.Data[(dx+0)+(dy+1)*image.Size.X] = val
image.Data[(dx+1)+(dy+1)*image.Size.X] = val
image.Data[(dx+2)+(dy+1)*image.Size.X] = val2
image.Data[(dx+0)+(dy+2)*image.Size.X] = val2
image.Data[(dx+1)+(dy+2)*image.Size.X] = val2
image.Data[(dx+2)+(dy+2)*image.Size.X] = val2
}
}(image, other, y)
}
//println("After thread count : ", threadProfile.Count())
wg.Wait()
break
case 4:
wg.Add(other.Size.Y)
for y := 0; y < other.Size.Y; y += 1 {
go func(image, other *Image, y int) {
defer wg.Done()
for x := 0; x < other.Size.X; x += 1 {
val := other.Data[x+y*other.Size.X]
dx := x * 4
dy := y * 4
image.Data[(dx+0)+(dy+0)*image.Size.X] = val
image.Data[(dx+1)+(dy+0)*image.Size.X] = val
image.Data[(dx+2)+(dy+0)*image.Size.X] = val
image.Data[(dx+3)+(dy+0)*image.Size.X] = val
image.Data[(dx+0)+(dy+1)*image.Size.X] = val
image.Data[(dx+1)+(dy+1)*image.Size.X] = val
image.Data[(dx+2)+(dy+1)*image.Size.X] = val
image.Data[(dx+3)+(dy+1)*image.Size.X] = val
image.Data[(dx+0)+(dy+2)*image.Size.X] = val
image.Data[(dx+1)+(dy+2)*image.Size.X] = val
image.Data[(dx+2)+(dy+2)*image.Size.X] = val
image.Data[(dx+3)+(dy+2)*image.Size.X] = val
image.Data[(dx+0)+(dy+3)*image.Size.X] = val
image.Data[(dx+1)+(dy+3)*image.Size.X] = val
image.Data[(dx+2)+(dy+3)*image.Size.X] = val
image.Data[(dx+3)+(dy+3)*image.Size.X] = val
}
}(image, other, y)
}
wg.Wait()
break
default:
wg.Add(other.Size.Y)
for y := 0; y < other.Size.Y; y += 1 {
go func(image, other *Image, y int) {
defer wg.Done()
for x := 0; x < other.Size.X; x += 1 {
val := other.Data[x+y*other.Size.X]
for sy := 0; sy < scale; sy += 1 {
for sx := 0; sx < scale; sx += 1 {
image.Data[(x*scale+sx)+(y*scale+sy)*image.Size.X] = val
}
}
}
}(image, other, y)
}
wg.Wait()
break
}
}
func (image *Image) DrawText(str string, font *Font, pos Vec2i, color uint32) {
origX := pos.X
for _, ch := range str {
c := font.chars[ch]
if ch == 10 {
pos.X = origX
pos.Y += font.lineHeight
continue
}
image.DrawColor(c.image, pos, color)
pos.X += c.xForward
}
}
func (image *Image) DrawTextSin(str string, font *Font, pos Vec2i, color uint32, freq, amplitude, phase float64) {
origX := pos.X
for _, ch := range str {
c := font.chars[ch]
if ch == 10 {
pos.X = origX
pos.Y += font.lineHeight
continue
}
image.DrawColor(c.image, Vec2i{X: pos.X, Y: int(math.Sin(float64(pos.X)*math.Pi/4/freq+phase*math.Pi*2)*amplitude) + pos.Y}, color)
pos.X += c.xForward
}
}
func (image *Image) DrawTextOutline(str string, font *Font, pos Vec2i, color, outlineColor uint32) {
origX := pos.X
for _, ch := range str {
c := font.chars[ch]
if ch == 10 {
pos.X = origX
pos.Y += font.lineHeight
continue
}
x := pos.X
y := pos.Y
image.DrawColor(c.image, Vec2i{X: x - 1, Y: y}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x + 1, Y: y}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x, Y: y - 1}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x, Y: y + 1}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x - 1, Y: y - 1}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x + 1, Y: y + 1}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x + 1, Y: y - 1}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x - 1, Y: y + 1}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x, Y: y + 2}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x - 1, Y: y + 2}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x + 1, Y: y + 2}, outlineColor)
image.DrawColor(c.image, pos, color)
pos.X += c.xForward
}
}
func (image *Image) DrawTextOutlineSin(str string, font *Font, pos Vec2i, color, outlineColor uint32, freq, amplitude, phase float64) {
origX := pos.X
for _, ch := range str {
c := font.chars[ch]
if ch == 10 {
pos.X = origX
pos.Y += font.lineHeight
continue
}
x := pos.X
y := int(math.Sin(float64(pos.X)*math.Pi/4/freq+phase*math.Pi*2)*amplitude) + pos.Y
image.DrawColor(c.image, Vec2i{X: x - 1, Y: y}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x + 1, Y: y}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x, Y: y - 1}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x, Y: y + 1}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x - 1, Y: y - 1}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x + 1, Y: y + 1}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x + 1, Y: y - 1}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x - 1, Y: y + 1}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x, Y: y + 2}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x - 1, Y: y + 2}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x + 1, Y: y + 2}, outlineColor)
image.DrawColor(c.image, Vec2i{X: x, Y: y}, color)
pos.X += c.xForward
}
}
func (image *Image) DrawTilemap(tilemap *Tilemap, pos Vec2i) {
tilesize := tilemap.Tileset[0].Size
for y := 0; y < tilemap.Size.Y; y += 1 {
for x := 0; x < tilemap.Size.X; x += 1 {
tpos := Vec2i{X: x, Y: y}
image.Draw(tilemap.Tileset[tilemap.Get(tpos)], Add(Mul(tpos, tilesize), pos))
}
}
}
func (image *Image) MakeTileset(tileSize Vec2i) []*Image {
tileCount := Div(image.Size, tileSize)
var tiles []*Image
for y := 0; y < tileCount.Y; y += 1 {
for x := 0; x < tileCount.X; x += 1 {
tile := NewImage(tileSize)
tile.DrawSub(image, Vec2i{}, Recti{Pos: Vec2i{X: x * tileSize.X, Y: y * tileSize.Y}, Size: tileSize})
tiles = append(tiles, tile)
}
}
return tiles
}

156
main.go Normal file
View File

@ -0,0 +1,156 @@
package main
import (
_ "embed"
"fmt"
"git.danitheskunk.com/squishy/blooblib/color"
. "git.danitheskunk.com/squishy/blooblib/src"
"github.com/veandco/go-sdl2/sdl"
"time"
)
const Width = 640
const Height = 360
//test
func main() {
_ = sdl.Init(sdl.INIT_EVERYTHING)
defer sdl.Quit()
scale := 3
window, _ := sdl.CreateWindow(
"test",
sdl.WINDOWPOS_CENTERED,
sdl.WINDOWPOS_CENTERED,
int32(Width*scale),
int32(Height*scale),
sdl.WINDOW_SHOWN,
)
surface, _ := window.GetSurface()
//data := unsafe.Slice((*uint32)(surface.Data()), Width*Height)
windowImage := NewImageFromPointer(surface.Data(), Vec2i{X: Width * scale, Y: Height * scale})
screen := NewImage(Vec2i{X: Width, Y: Height})
screen.Alpha = false
//sprite := NewImage(Vec2i{X: 32, Y: 32})
//sprite.Clear(0xabcdef)
fmt.Printf("%d\n", surface.Format.BitsPerPixel)
println("\n"[0])
tiles := LoadImage("go.png").MakeTileset(Vec2i{X: 16, Y: 16})
tilemap := NewTilemap(Vec2i{X: 40, Y: 23}, tiles)
tilemap.Clear(16)
for y := 0; y < 19; y += 1 {
for x := 0; x < 19; x += 1 {
tilemap.Set(Vec2i{X: 10 + x, Y: 1 + y}, 3)
}
//tilemap.Set(Vec2i{X: 9, Y: 1 + y}, 17)
tilemap.Set(Vec2i{X: 10, Y: 1 + y}, 11)
tilemap.Set(Vec2i{X: 28, Y: 1 + y}, 12)
}
for x := 0; x < 19; x += 1 {
//tilemap.Set(Vec2i{X: 10 + x, Y: 0}, 18)
tilemap.Set(Vec2i{X: 10 + x, Y: 1}, 9)
tilemap.Set(Vec2i{X: 10 + x, Y: 19}, 10)
tilemap.Set(Vec2i{X: 10 + x, Y: 20}, 17)
tilemap.Set(Vec2i{X: 10 + x, Y: 21}, 18)
}
for y := 0; y < 21; y += 1 {
tilemap.Set(Vec2i{X: 29, Y: 1 + y}, 19)
}
tilemap.Set(Vec2i{X: 10, Y: 21}, 20)
tilemap.Set(Vec2i{X: 29, Y: 1}, 21)
tilemap.Set(Vec2i{X: 10, Y: 1}, 5)
tilemap.Set(Vec2i{X: 28, Y: 1}, 6)
tilemap.Set(Vec2i{X: 10, Y: 19}, 7)
tilemap.Set(Vec2i{X: 28, Y: 19}, 8)
tilemap.Set(Vec2i{X: 13, Y: 4}, 4)
tilemap.Set(Vec2i{X: 25, Y: 4}, 4)
tilemap.Set(Vec2i{X: 13, Y: 16}, 4)
tilemap.Set(Vec2i{X: 25, Y: 16}, 4)
tilemap.Set(Vec2i{X: 13, Y: 10}, 4)
tilemap.Set(Vec2i{X: 13, Y: 16}, 4)
tilemap.Set(Vec2i{X: 25, Y: 10}, 4)
tilemap.Set(Vec2i{X: 25, Y: 16}, 4)
tilemap.Set(Vec2i{X: 19, Y: 4}, 4)
tilemap.Set(Vec2i{X: 19, Y: 10}, 4)
tilemap.Set(Vec2i{X: 19, Y: 16}, 4)
_ = window.UpdateSurface()
lastSecond := time.Now()
framesSinceLastSecond := 0
targetFPS := 60
frameDuration := time.Second / time.Duration(targetFPS)
lastFrame := lastSecond
running := true
tick := 0
for running {
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
switch event.(type) {
case *sdl.QuitEvent:
running = false
break
}
}
now := time.Now()
delta := now.Sub(lastSecond)
//println(delta.Seconds())
//println(now.Second())
if delta.Seconds() >= 1.0 {
fmt.Printf("FPS: %5d | mspf: %f\n", framesSinceLastSecond, 1000.0/float32(framesSinceLastSecond))
framesSinceLastSecond = 0
lastSecond = lastSecond.Add(time.Second)
}
delta2 := now.Sub(lastFrame)
if delta2 >= frameDuration {
//t := (float64(tick)) / 1000
screen.Clear(0x123456)
screen.DrawTextOutline(
// "Hello World, this is a test,\nand now for another line :)",
// "Hello World, this is a test",
// "Vigilance, deathtouch, haste\n\nQuesting Beast can't be blocked by creatures\nwith power 2 or less.\n\nCombat damage that would be dealt by\ncreatures you control can't be prevented.\n\nWhenever Questing Beast deals combat damage\nto an opponent, it deals that much damage to\ntarget planeswalker that player controls.",
"Lorem ipsum dolor sit amet,\n"+
"consectetur adipiscing elit,\n"+
"sed do eiusmod tempor incididunt\n"+
"ut labore et dolore magna aliqua.\n"+
"Ut enim ad minim veniam, quis\n"+
"nostrud exercitation ullamco\n"+
"laboris nisi ut aliquip ex ea\n"+
"commodo consequat. Duis aute\n"+
"irure dolor in reprehenderit in\n"+
"voluptate velit esse cillum dolore\n"+
"eu fugiat nulla pariatur.\n"+
"Excepteur sint occaecat cupidatat\n"+
"non proident, sunt in culpa qui\n"+
"officia deserunt mollit anim id\n"+
"est laborum.",
DefaultFont,
Vec2i{X: 10, Y: 10},
0xffdddd,
color.Black,
// 50,
// 3.0,
// float64(tick) / 60.0,
)
screen.DrawTilemap(tilemap, Vec2i{})
screen.Draw(tilemap.Tileset[2], Vec2i{X: 16 * 25, Y: 16 * 4})
screen.Draw(tilemap.Tileset[1], Vec2i{X: 16 * 13, Y: 16 * 16})
windowImage.DrawUpscale(screen)
//windowImage.Draw(screen, Vec2i{})
tick += 1
framesSinceLastSecond += 1
lastFrame = lastFrame.Add(frameDuration)
}
_ = window.UpdateSurface()
}
}

24
math.go Normal file
View File

@ -0,0 +1,24 @@
package bloob
import "math"
func FastSin(x float64) float64 {
const PI = 3.14159265358979323846264338327950288
const INVPI = 0.31830988618379067153776752674502872
const A = 0.00735246819687011731341356165096815
const B = -0.16528911397014738207016302002888890
const C = 0.99969198629596757779830113868360584
k := int32(math.Round(INVPI * x))
x -= float64(k) * PI
x2 := x * x
x = x * (C + x2*(B+A*x2))
if k%2 != 0 {
x = -x
}
return x
}
func FastCos(x float64) float64 {
return FastSin(x + math.Pi/2)
}

7
rectangle.go Normal file
View File

@ -0,0 +1,7 @@
package bloob
type Rect[T Number] struct {
Pos, Size Vec2[T]
}
type Recti = Rect[int]

31
tilemap.go Normal file
View File

@ -0,0 +1,31 @@
package bloob
type Tilemap struct {
tiles []int
Size Vec2i
Tileset []*Image
}
func NewTilemap(size Vec2i, tileset []*Image) *Tilemap {
return &Tilemap{tiles: make([]int, size.Size()), Size: size, Tileset: tileset}
}
func (tilemap *Tilemap) Set(pos Vec2i, val int) {
if pos.X >= 0 && pos.Y >= 0 && pos.X < tilemap.Size.X && pos.Y < tilemap.Size.Y {
tilemap.tiles[pos.X+pos.Y*tilemap.Size.X] = val
}
}
func (tilemap *Tilemap) Get(pos Vec2i) int {
if pos.X >= 0 && pos.Y >= 0 && pos.X < tilemap.Size.X && pos.Y < tilemap.Size.Y {
return tilemap.tiles[pos.X+pos.Y*tilemap.Size.X]
} else {
return 0
}
}
func (tilemap *Tilemap) Clear(val int) {
for i := 0; i < len(tilemap.tiles); i += 1 {
tilemap.tiles[i] = val
}
}

62
vec2.go Normal file
View File

@ -0,0 +1,62 @@
package bloob
import (
"golang.org/x/exp/constraints"
"math"
)
type Number interface {
constraints.Integer | constraints.Float
}
type Vec2[T Number] struct {
X, Y T
}
type Vec2i = Vec2[int]
func Add[T Number](a, b Vec2[T]) Vec2[T] {
return Vec2[T]{X: a.X + b.X, Y: a.Y + b.Y}
}
func AddScalar[T Number](a Vec2[T], b T) Vec2[T] {
return Vec2[T]{X: a.X + b, Y: a.Y + b}
}
func Sub[T Number](a, b Vec2[T]) Vec2[T] {
return Vec2[T]{X: a.X - b.X, Y: a.Y - b.Y}
}
func SubScalar[T Number](a Vec2[T], b T) Vec2[T] {
return Vec2[T]{X: a.X - b, Y: a.Y - b}
}
func Mul[T Number](a, b Vec2[T]) Vec2[T] {
return Vec2[T]{X: a.X * b.X, Y: a.Y * b.Y}
}
func MulScalar[T Number](a Vec2[T], b T) Vec2[T] {
return Vec2[T]{X: a.X * b, Y: a.Y * b}
}
func Div[T Number](a, b Vec2[T]) Vec2[T] {
return Vec2[T]{X: a.X / b.X, Y: a.Y / b.Y}
}
func DivScalar[T Number](a Vec2[T], b T) Vec2[T] {
return Vec2[T]{X: a.X / b, Y: a.Y / b}
}
func (a Vec2[T]) Size() T {
return a.X * a.Y
}
func Dot[T Number](a, b Vec2[T]) T {
return a.X*b.X + a.Y*b.Y
}
func (a Vec2[T]) Mag() float64 {
return math.Sqrt(float64(a.MagSqr()))
}
func (a Vec2[T]) MagSqr() T {
return a.X*a.X + a.Y*a.Y
}