All checks were successful
commit-lint / commit-lint (push) Successful in 33s
266 lines
7.2 KiB
Go
266 lines
7.2 KiB
Go
package main
|
|
|
|
import (
|
|
"log"
|
|
"math"
|
|
"unsafe"
|
|
|
|
"github.com/veandco/go-sdl2/sdl"
|
|
"kisekinopureya.com.tr/go-raycasting/tools"
|
|
"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
|
|
|
|
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 {
|
|
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|sdl.RENDERER_PRESENTVSYNC)
|
|
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)
|
|
}
|
|
|
|
tools.InitFont("assets/DejaVuSans.ttf", 24)
|
|
|
|
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)
|
|
case sdl.K_F1:
|
|
if renderFps {
|
|
renderFps = false
|
|
} else {
|
|
renderFps = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Floorcasting logic
|
|
for y := 0; y < screenHeight; y += screenScale {
|
|
rayDirX0 := dirX - planeX
|
|
rayDirY0 := dirY - planeY
|
|
rayDirX1 := dirX + planeX
|
|
rayDirY1 := dirY + planeY
|
|
|
|
p := y - screenHeight/2
|
|
if p == 0 {
|
|
continue // avoid division by zero
|
|
}
|
|
|
|
posZ := 0.5 * float64(screenHeight)
|
|
rowDistance := posZ / float64(p)
|
|
|
|
floorStepX := rowDistance * (rayDirX1 - rayDirX0) / float64(screenWidth)
|
|
floorStepY := rowDistance * (rayDirY1 - rayDirY0) / float64(screenWidth)
|
|
|
|
floorX := posX + rowDistance*rayDirX0
|
|
floorY := posY + rowDistance*rayDirY0
|
|
|
|
for x := 0; x < screenWidth; x++ {
|
|
cellX := int(floorX)
|
|
cellY := int(floorY)
|
|
|
|
tx := int(float64(texWidth)*(floorX-float64(cellX))) & (texWidth - 1)
|
|
ty := int(float64(texHeight)*(floorY-float64(cellY))) & (texHeight - 1)
|
|
|
|
floorX += floorStepX
|
|
floorY += floorStepY
|
|
|
|
floorTexture := 3
|
|
ceilingTexture := 6
|
|
|
|
var color uint32 = textures[floorTexture][texWidth*ty+tx]
|
|
color = (color >> 1) & 0x7F7F7F // darken
|
|
pixels[y*screenWidth+x] = color
|
|
|
|
color = textures[ceilingTexture][texWidth*ty+tx]
|
|
color = (color >> 1) & 0x7F7F7F
|
|
pixels[(screenHeight-y-1)*screenWidth+x] = color
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
}
|
|
}
|
|
|
|
if renderFps {
|
|
tools.FpsCounter(pixels, screenWidth, screenHeight)
|
|
}
|
|
// Render updated scene
|
|
render(renderer, texture, pixels)
|
|
}
|
|
}
|