This commit is contained in:
Gökhan Özdemir 2025-03-21 01:52:23 +03:00
commit 9a3bc05131
4 changed files with 338 additions and 0 deletions

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=

259
main.go Normal file
View File

@ -0,0 +1,259 @@
package main
import (
"fmt"
"math"
"github.com/veandco/go-sdl2/sdl"
worldMap "kisekinopureya.com.tr/go-raycasting/worldMap"
)
const (
screenWidth = 640
screenHeight = 480
fontPath = "assets/test.ttf"
fontSize = 16
debug = true
texWidth = 64
texHeight = 64
)
func verLine(x int32, drawStart int32, drawEnd int32, color sdl.Color, window *sdl.Window) {
surface, err := window.GetSurface()
if err != nil {
panic(err)
}
for y := drawStart; y < drawEnd; y++ {
pixel := sdl.MapRGBA(surface.Format, color.R, color.G, color.B, color.A)
surface.FillRect(&sdl.Rect{X: x, Y: y, W: 1, H: 1}, pixel)
}
window.UpdateSurface()
}
func main() {
level := worldMap.GetMap()
var buffer [screenHeight][screenWidth]uint32
texture := worldMap.GetTextures()
var renderer *sdl.Renderer
if err := sdl.Init(sdl.INIT_EVERYTHING); err != nil {
panic(err)
}
defer sdl.Quit()
window, err := sdl.CreateWindow("go raycasting", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, screenWidth, screenHeight, sdl.WINDOW_SHOWN)
if err != nil {
panic(err)
}
defer window.Destroy()
renderer, err = sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED)
if err != nil {
panic(err)
}
var posX float64 = 3 //22 //x and y start position
var posY float64 = 4 //12
var dirX float64 = -1
var dirY float64 = 0 //initial direction vector
var planeX float64 = 0
var planeY float64 = 0.66 //the 2d raycaster version of camera plane
var time float64 = 0 //time of current frame
var oldTime float64 = 0 //time of previous frame
running := true
for running {
for x := range screenWidth {
var cameraX float64 = (float64)(2*x/screenWidth - 1)
var rayDirX = dirX + planeX*cameraX
var rayDirY = dirY + planeY*cameraX
var mapX int = int(posX)
var mapY int = int(posY)
var sideDistX float64
var sideDistY float64
var deltaDistX float64
if rayDirX == 0 {
deltaDistX = 1e30
} else {
deltaDistX = math.Abs(1 / rayDirX)
}
var deltaDistY float64
if rayDirY == 0 {
deltaDistY = 1e30
} else {
deltaDistY = math.Abs(1 / rayDirY)
}
var perpWallDist float64
var stepX int
var stepY int
var hit int = 0 //was there a wall hit?
var side int //was a NS or a EW wall hit?
//calculate step and initial sideDist
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
}
//perform DDA
for hit == 0 {
//jump to next map square, either in x-direction, or in y-direction
if sideDistX < sideDistY {
sideDistX += deltaDistX
mapX += stepX
side = 0
} else {
sideDistY += deltaDistY
mapY += stepY
side = 1
}
//Check if ray has hit a wall
if level[mapX][mapY] > 0 {
hit = 1
}
}
if side == 0 {
perpWallDist = (sideDistX - deltaDistX)
} else {
perpWallDist = (sideDistY - deltaDistY)
}
var lineHeight int = (int)(screenHeight / perpWallDist)
var pitch int = 100
//calculate lowest and highest pixel to fill in current stripe
var drawStart int = -lineHeight/2 + screenHeight/2 + pitch
if drawStart < 0 {
drawStart = 0
}
var drawEnd int = lineHeight/2 + screenHeight/2 + pitch
if drawEnd >= screenHeight {
drawEnd = screenHeight - 1
}
var texNum int = 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))
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
}
step := 1.0 * float64(texHeight) / float64(lineHeight)
// Starting texture coordinate
texPos := (float64(drawStart) - float64(pitch) - float64(screenHeight)/2 + float64(lineHeight)/2) * step
for y := drawStart; y < drawEnd; y++ {
// Cast the texture coordinate to integer, and mask with (texHeight - 1) in case of overflow
texY := int(texPos) & (texHeight - 1)
texPos += step
color := texture[texNum][texHeight*texY+texX]
//make color darker for y-sides: R, G and B byte each divided through two with a "shift" and an "and"
if side == 1 {
color = (color >> 1) & 8355711
}
buffer[y][x] = color
}
//draw the pixels of the stripe as a vertical line
for y := range screenHeight {
for x := range screenWidth {
renderer.SetDrawColor((uint8(buffer[y][x]>>16) & 255), uint8((buffer[y][x]>>8)&255), uint8(buffer[y][x]&255), uint8((buffer[y][x]>>24)&255))
renderer.DrawPoint(int32(x), int32(y))
}
}
renderer.Present()
oldTime = time
time = float64(sdl.GetTicks64())
var frameTime = (time - oldTime) / 1000.0
var moveSpeed float64 = frameTime * 5.0 //the constant value is in squares/second
var rotSpeed float64 = frameTime * 3.0
fmt.Println(1.0 / frameTime)
window.UpdateSurface()
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
switch e := event.(type) {
case *sdl.QuitEvent: // NOTE: Please use `*sdl.QuitEvent` for `v0.4.x` (current version).
println("Quit")
running = false
break
case *sdl.KeyboardEvent:
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
}
break
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
}
break
case sdl.K_d:
var oldDirX float64 = dirX
dirX = dirX*math.Cos(-rotSpeed) - dirY*math.Sin(-rotSpeed)
dirY = oldDirX*math.Sin(-rotSpeed) + dirY*math.Cos(-rotSpeed)
var oldPlaneX float64 = planeX
planeX = planeX*math.Cos(-rotSpeed) - planeY*math.Sin(-rotSpeed)
planeY = oldPlaneX*math.Sin(-rotSpeed) + planeY*math.Cos(-rotSpeed)
fmt.Println("rotating right")
break
case sdl.K_a:
var oldDirX float64 = dirX
dirX = dirX*math.Cos(rotSpeed) - dirY*math.Sin(rotSpeed)
dirY = oldDirX*math.Sin(rotSpeed) + dirY*math.Cos(rotSpeed)
var oldPlaneX float64 = planeX
planeX = planeX*math.Cos(rotSpeed) - planeY*math.Sin(rotSpeed)
planeY = oldPlaneX*math.Sin(rotSpeed) + planeY*math.Cos(rotSpeed)
fmt.Println("rotating left")
break
case sdl.K_ESCAPE:
running = false
break
default:
break
}
}
}
}
}
}

72
worldMap/worldMap.go Normal file
View File

@ -0,0 +1,72 @@
package worldMap
const (
mapWidth = 24
mapHeight = 24
texWidth = 64
texHeight = 64
)
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 GetMap() [24][24]int {
return level
}
var texture = make([][]uint32, 8)
func GetTextures() [][]uint32 {
for i := range texture {
texture[i] = make([]uint32, texWidth*texHeight)
}
var tex0, tex5 int
for x := range texWidth {
for y := range texHeight {
xorcolor := (x * 256 / texWidth) ^ (y * 256 / texHeight)
ycolor := y * 256 / texHeight
xycolor := y*128/texHeight + x*128/texWidth
if x != y && x != texWidth-y {
tex0 = 1
} else {
tex0 = 0
}
texture[0][texWidth*y+x] = uint32(65536 * 254 * tex0) //flat red texture with black cross
texture[1][texWidth*y+x] = uint32(xycolor + 256*xycolor + 65536*xycolor) //sloped greyscale
texture[2][texWidth*y+x] = uint32(256*xycolor + 65536*xycolor) //sloped yellow gradient
texture[3][texWidth*y+x] = uint32(xorcolor + 256*xorcolor + 65536*xorcolor) //xor greyscale
texture[4][texWidth*y+x] = uint32(256 * xorcolor)
if x%16 != 0 && y%16 != 0 {
tex5 = 1
} else {
tex5 = 0
} //xor green
texture[5][texWidth*y+x] = uint32(65536 * 192 * tex5) //red bricks
texture[6][texWidth*y+x] = uint32(65536 * ycolor) //red gradient
texture[7][texWidth*y+x] = uint32(128 + 256*128 + 65536*128) //flat grey texture
}
}
return texture
}