From 9a3bc0513155107ffdce035b1a8e3831332b8afb Mon Sep 17 00:00:00 2001 From: kisekinopureya Date: Fri, 21 Mar 2025 01:52:23 +0300 Subject: [PATCH] a --- go.mod | 5 + go.sum | 2 + main.go | 259 +++++++++++++++++++++++++++++++++++++++++++ worldMap/worldMap.go | 72 ++++++++++++ 4 files changed, 338 insertions(+) create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 worldMap/worldMap.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b948362 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module kisekinopureya.com.tr/go-raycasting + +go 1.24.1 + +require github.com/veandco/go-sdl2 v0.4.40 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..bc27727 --- /dev/null +++ b/go.sum @@ -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= diff --git a/main.go b/main.go new file mode 100644 index 0000000..e5ef857 --- /dev/null +++ b/main.go @@ -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 + } + } + } + } + } +} diff --git a/worldMap/worldMap.go b/worldMap/worldMap.go new file mode 100644 index 0000000..0ac9c21 --- /dev/null +++ b/worldMap/worldMap.go @@ -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 +}