diff --git a/assets/DejaVuSans.ttf b/assets/DejaVuSans.ttf new file mode 100644 index 0000000..e5f7eec Binary files /dev/null and b/assets/DejaVuSans.ttf differ diff --git a/main.go b/main.go index 0e3434d..6ab1d0a 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "unsafe" "github.com/veandco/go-sdl2/sdl" + "kisekinopureya.com.tr/go-raycasting/tools" "kisekinopureya.com.tr/go-raycasting/worldMap" ) @@ -21,6 +22,8 @@ const ( var posX, posY, dirX, dirY, planeX, planeY float64 = 2, 2, -1, 0, 0, 0.66 +var renderFps = false + func render(renderer *sdl.Renderer, texture *sdl.Texture, pixels []uint32) { err := texture.Update(nil, unsafe.Pointer(&pixels[0]), screenWidth*4) if err != nil { @@ -47,7 +50,7 @@ func main() { } defer window.Destroy() - renderer, err := sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED) + renderer, err := sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED|sdl.RENDERER_PRESENTVSYNC) if err != nil { log.Fatalf("Renderer creation failed: %v", err) } @@ -64,6 +67,8 @@ func main() { log.Fatalf("Texture creation failed: %v", err) } + tools.InitFont("assets/DejaVuSans.ttf", 24) + pixels := make([]uint32, screenWidth*screenHeight) running := true @@ -105,6 +110,12 @@ func main() { oldPlaneX := planeX planeX = planeX*math.Cos(rotSpeed) - planeY*math.Sin(rotSpeed) planeY = oldPlaneX*math.Sin(rotSpeed) + planeY*math.Cos(rotSpeed) + case sdl.K_F1: + if renderFps { + renderFps = false + } else { + renderFps = true + } } } } @@ -244,6 +255,10 @@ func main() { } } } + + if renderFps { + tools.FpsCounter(pixels, screenWidth, screenHeight) + } // Render updated scene render(renderer, texture, pixels) } diff --git a/tools/fps.go b/tools/fps.go new file mode 100644 index 0000000..4fcedb8 --- /dev/null +++ b/tools/fps.go @@ -0,0 +1,99 @@ +package tools + +import ( + "fmt" + "log" + "time" + + "github.com/veandco/go-sdl2/sdl" + "github.com/veandco/go-sdl2/ttf" +) + +var ( + oldTime = time.Now() + font *ttf.Font +) + +// Initialize font once (call this in setup) +func InitFont(fontPath string, fontSize int) { + var err error + if err := ttf.Init(); err != nil { + log.Fatal(err) + } + font, err = ttf.OpenFont(fontPath, fontSize) + if err != nil { + log.Fatalf("Failed to load font: %v", err) + } +} + +// Converts text to raw []uint32 pixels +func textToPixels(text string, color sdl.Color) ([]uint32, int, int) { + surface, err := font.RenderUTF8Blended(text, color) + if err != nil { + log.Fatalf("Render failed: %v", err) + } + defer surface.Free() + + w := int(surface.W) + h := int(surface.H) + + if err := surface.Lock(); err != nil { + log.Fatalf("Failed to lock surface: %v", err) + } + defer surface.Unlock() + + converted, err := surface.ConvertFormat(sdl.PIXELFORMAT_ARGB8888, 0) + if err != nil { + log.Fatal(err) + } + defer converted.Free() + + pixelBytes := converted.Pixels() + pixels := make([]uint32, w*h) + + for i := 0; i < w*h; i++ { + b := pixelBytes[i*4 : i*4+4] + pixels[i] = uint32(b[3])<<24 | uint32(b[2])<<16 | uint32(b[1])<<8 | uint32(b[0]) + } + dst := make([]uint32, w*h) + copy(dst, pixels) + return dst, w, h +} + +// Draws text pixels onto your screen framebuffer +func blitTextToBuffer(dst []uint32, dstW, dstH, dstPitch int, src []uint32, srcW, srcH, posX, posY int) { + for y := 0; y < srcH; y++ { + for x := 0; x < srcW; x++ { + dx := posX + x + dy := posY + y + if dx < 0 || dx >= dstW || dy < 0 || dy >= dstH { + continue + } + srcPixel := src[y*srcW+x] + + // Only draw non-transparent pixels + if (srcPixel>>24)&0xFF > 0 { + dst[dy*dstPitch+dx] = srcPixel + } + } + } +} + +func FpsCounter(pixels []uint32, screenWidth, screenHeight int) { + now := time.Now() + frameTime := now.Sub(oldTime).Seconds() + oldTime = now + + if frameTime == 0 { + return + } + + fps := 1.0 / frameTime + fpsText := fmt.Sprintf("FPS: %.0f", fps) + + color := sdl.Color{R: 255, G: 255, B: 255, A: 255} + textPixels, textW, textH := textToPixels(fpsText, color) + + // Draw text at top-left (10,10) + blitTextToBuffer(pixels, screenWidth, screenHeight, screenWidth, textPixels, textW, textH, 10, 10) +}