initial commit

This commit is contained in:
Gökhan Özdemir 2025-03-21 01:52:23 +03:00
commit 40342a6d35
15 changed files with 330 additions and 0 deletions

BIN
assets/barrel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets/bluestone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
assets/colorstone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
assets/eagle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
assets/greenlight.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

BIN
assets/greystone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
assets/mossy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
assets/pillar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
assets/purplestone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
assets/redbrick.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
assets/wood.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module kisekinopureya.com.tr/go-raycasting
go 1.24.1
require github.com/veandco/go-sdl2 v0.4.40

2
go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/veandco/go-sdl2 v0.4.40 h1:fZv6wC3zz1Xt167P09gazawnpa0KY5LM7JAvKpX9d/U=
github.com/veandco/go-sdl2 v0.4.40/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY=

210
main.go Normal file
View File

@ -0,0 +1,210 @@
package main
import (
"log"
"math"
"unsafe"
"github.com/veandco/go-sdl2/sdl"
"kisekinopureya.com.tr/go-raycasting/worldMap"
)
const (
screenWidth = 640
screenHeight = 480
screenScale = 1
texWidth = 64
texHeight = 64
mapWidth = 24
mapHeight = 24
)
var posX, posY, dirX, dirY, planeX, planeY float64 = 2, 2, -1, 0, 0, 0.66
func render(renderer *sdl.Renderer, texture *sdl.Texture, pixels []uint32) {
err := texture.Update(nil, unsafe.Pointer(&pixels[0]), screenWidth*4)
if err != nil {
log.Printf("Texture update failed: %v", err)
}
// Clear renderer, copy texture, present frame
renderer.Clear()
renderer.Copy(texture, nil, nil)
renderer.Present()
}
func main() {
level := worldMap.GetMap()
if err := sdl.Init(sdl.INIT_VIDEO); err != nil {
log.Fatalf("SDL Initialization failed: %v", err)
}
defer sdl.Quit()
window, err := sdl.CreateWindow("Raycaster", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, screenWidth, screenHeight, sdl.WINDOW_SHOWN)
if err != nil {
log.Fatalf("Window creation failed: %v", err)
}
defer window.Destroy()
renderer, err := sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED)
if err != nil {
log.Fatalf("Renderer creation failed: %v", err)
}
defer renderer.Destroy()
texture, err := renderer.CreateTexture(sdl.PIXELFORMAT_ARGB8888, sdl.TEXTUREACCESS_STREAMING, screenWidth, screenHeight)
if err != nil {
log.Fatalf("Texture creation failed: %v", err)
}
defer texture.Destroy()
textures, err := worldMap.LoadTextures(renderer)
if err != nil {
log.Fatalf("Texture creation failed: %v", err)
}
pixels := make([]uint32, screenWidth*screenHeight)
running := true
for running {
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
switch e := event.(type) {
case *sdl.QuitEvent:
running = false
case *sdl.KeyboardEvent:
if e.Type == sdl.KEYDOWN {
moveSpeed := 0.1
rotSpeed := 0.05
switch e.Keysym.Sym {
case sdl.K_w:
if level[int(posX+dirX*moveSpeed)][int(posY)] == 0 {
posX += dirX * moveSpeed
}
if level[int(posX)][int(posY+dirY*moveSpeed)] == 0 {
posY += dirY * moveSpeed
}
case sdl.K_s:
if level[int(posX-dirX*moveSpeed)][int(posY)] == 0 {
posX -= dirX * moveSpeed
}
if level[int(posX)][int(posY-dirY*moveSpeed)] == 0 {
posY -= dirY * moveSpeed
}
case sdl.K_d:
oldDirX := dirX
dirX = dirX*math.Cos(-rotSpeed) - dirY*math.Sin(-rotSpeed)
dirY = oldDirX*math.Sin(-rotSpeed) + dirY*math.Cos(-rotSpeed)
oldPlaneX := planeX
planeX = planeX*math.Cos(-rotSpeed) - planeY*math.Sin(-rotSpeed)
planeY = oldPlaneX*math.Sin(-rotSpeed) + planeY*math.Cos(-rotSpeed)
case sdl.K_a:
oldDirX := dirX
dirX = dirX*math.Cos(rotSpeed) - dirY*math.Sin(rotSpeed)
dirY = oldDirX*math.Sin(rotSpeed) + dirY*math.Cos(rotSpeed)
oldPlaneX := planeX
planeX = planeX*math.Cos(rotSpeed) - planeY*math.Sin(rotSpeed)
planeY = oldPlaneX*math.Sin(rotSpeed) + planeY*math.Cos(rotSpeed)
}
}
}
}
for i := range pixels {
pixels[i] = 0x00000000 // Set background to black
}
// Raycasting logic
for x := 0; x < screenWidth; x += screenScale {
rayDirX := dirX + planeX*(2*float64(x)/float64(screenWidth)-1)
rayDirY := dirY + planeY*(2*float64(x)/float64(screenWidth)-1)
mapX, mapY := int(posX), int(posY)
deltaDistX := math.Abs(1 / rayDirX)
deltaDistY := math.Abs(1 / rayDirY)
var sideDistX, sideDistY float64
var stepX, stepY int
if rayDirX < 0 {
stepX = -1
sideDistX = (posX - float64(mapX)) * deltaDistX
} else {
stepX = 1
sideDistX = (float64(mapX) + 1.0 - posX) * deltaDistX
}
if rayDirY < 0 {
stepY = -1
sideDistY = (posY - float64(mapY)) * deltaDistY
} else {
stepY = 1
sideDistY = (float64(mapY) + 1.0 - posY) * deltaDistY
}
var perpWallDist float64
hit := false
var side int
for !hit {
if sideDistX < sideDistY {
mapX += stepX
perpWallDist = sideDistX
sideDistX += deltaDistX
side = 0
} else {
mapY += stepY
perpWallDist = sideDistY
sideDistY += deltaDistY
side = 1
}
if level[mapX][mapY] > 0 {
hit = true
}
}
if hit {
lineHeight := int(screenHeight / perpWallDist)
drawStart := -lineHeight/2 + screenHeight/2
if drawStart < 0 {
drawStart = 0
}
drawEnd := lineHeight/2 + screenHeight/2
if drawEnd >= screenHeight {
drawEnd = screenHeight - 1
}
wallType := level[mapX][mapY] - 1
var wallX float64 //where exactly the wall was hit
if side == 0 {
wallX = posY + perpWallDist*rayDirY
} else {
wallX = posX + perpWallDist*rayDirX
}
wallX -= math.Floor(wallX)
//x coordinate on the texture
var texX int = int(wallX * float64(texWidth))
if side == 0 && rayDirX > 0 {
texX = texWidth - texX - 1
}
if side == 1 && rayDirY < 0 {
texX = texWidth - texX - 1
}
var step float64 = 1.0 * float64(texHeight) / float64(lineHeight)
// Starting texture coordinate
var texPos float64 = float64(drawStart-screenHeight/2+lineHeight/2) * step
// Draw vertical line
for y := drawStart; y < drawEnd; y++ {
var texY int = int(texPos) & (texHeight - 1)
texPos += step
var color uint32 = textures[wallType][texHeight*texY+texX]
if side == 1 {
color = (color >> 1) & 8355711
}
pixels[y*screenWidth+x] = color
}
}
}
// Render updated scene
render(renderer, texture, pixels)
}
}

113
worldMap/worldMap.go Normal file
View File

@ -0,0 +1,113 @@
package worldMap
import (
"bytes"
"fmt"
"image"
"image/png"
"os"
"github.com/veandco/go-sdl2/sdl"
)
const (
mapWidth = 24
mapHeight = 24
texWidth = 64
texHeight = 64
)
var textures [8][]uint32
var level = [mapWidth][mapHeight]int{
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 3, 0, 3, 0, 3, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 3, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 2, 2, 0, 2, 2, 0, 0, 0, 0, 3, 0, 3, 0, 3, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 4, 0, 0, 0, 0, 5, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 4, 0, 4, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 4, 0, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
}
func loadImage(out *[]uint32, w uint32, h uint32, filename string) error {
// Read the file content
file, err := os.ReadFile(filename)
if err != nil {
fmt.Println("Error reading file:", err)
return err
}
// Decode the PNG file
img, err := png.Decode(bytes.NewReader(file))
fmt.Println(filename)
if err != nil {
fmt.Println("Error decoding PNG:", err)
return err
}
// Get the width and height
w = uint32(img.Bounds().Dx())
h = uint32(img.Bounds().Dy())
// Resize the output slice to hold the pixel data
*out = make([]uint32, w*h)
// Convert the image to RGBA and extract pixel data
rgbaImg, ok := img.(*image.RGBA)
if !ok {
fmt.Println("Image is not in RGBA format")
return err
}
// Process each pixel and store it in the output slice
for i := range *out {
r := rgbaImg.Pix[i*4+0]
g := rgbaImg.Pix[i*4+1]
b := rgbaImg.Pix[i*4+2]
a := rgbaImg.Pix[i*4+3]
// Pack the ARGB values into a uint32
(*out)[i] = uint32(a)<<24 | uint32(r)<<16 | uint32(g)<<8 | uint32(b)
}
return nil
}
func GetMap() [24][24]int {
return level
}
func LoadTextures(renderer *sdl.Renderer) ([][]uint32, error) {
textureFiles := []string{
"assets/eagle.png", "assets/redbrick.png", "assets/purplestone.png", "assets/greystone.png",
"assets/bluestone.png", "assets/mossy.png", "assets/wood.png", "assets/colorstone.png",
}
textures := make([][]uint32, 8)
for i, file := range textureFiles {
var out []uint32
if err := loadImage(&out, uint32(64), 64, file); err != nil {
panic(err)
}
textures[i] = out
}
return textures, nil
}