package render import ( stdmath "math" "github.com/pnx/go-raytracer/math" "github.com/pnx/go-raytracer/world" ) type RayHit struct { Cell math.Vec2u8 Side int Distance float64 Pos math.Vec2f } func CastRay(camera math.Transform, level *world.Level, x int, max_rays int) RayHit { angle := camera.Direction.Get() cameraX := 2*float64(x)/float64(max_rays) - 1 rayDirX := stdmath.Cos(angle) + cameraX*stdmath.Cos(angle+stdmath.Pi/2) rayDirY := stdmath.Sin(angle) + cameraX*stdmath.Sin(angle+stdmath.Pi/2) mapX := int(camera.X) / world.TileSize mapY := int(camera.Y) / world.TileSize deltaDistX := stdmath.Abs(1 / rayDirX) deltaDistY := stdmath.Abs(1 / rayDirY) var stepX, stepY int var sideDistX, sideDistY float64 if rayDirX < 0 { stepX = -1 sideDistX = (float64(camera.X)/world.TileSize - float64(mapX)) * deltaDistX } else { stepX = 1 sideDistX = (float64(mapX+1) - float64(camera.X)/world.TileSize) * deltaDistX } if rayDirY < 0 { stepY = -1 sideDistY = (float64(camera.Y)/world.TileSize - float64(mapY)) * deltaDistY } else { stepY = 1 sideDistY = (float64(mapY+1) - float64(camera.Y)/world.TileSize) * deltaDistY } // DDA loop var side int // 0 = x, 1 = y for { if sideDistX < sideDistY { sideDistX += deltaDistX mapX += stepX side = 0 } else { sideDistY += deltaDistY mapY += stepY side = 1 } if mapX < 0 || mapX >= level.W || mapY < 0 || mapY >= level.H { break // out of bounds } if level.Wall(mapX, mapY) { break } } // Calculate distance to wall var perpWallDist float64 if side == 0 { perpWallDist = (float64(mapX) - float64(camera.X)/world.TileSize + float64(1-stepX)/2) / rayDirX } else { perpWallDist = (float64(mapY) - float64(camera.Y)/world.TileSize + float64(1-stepY)/2) / rayDirY } return RayHit{ Cell: math.Vec2u8{X: uint8(mapX), Y: uint8(mapY)}, Side: side, Distance: perpWallDist, Pos: math.Vec2f{ X: rayDirX * perpWallDist, Y: rayDirY * perpWallDist, }, } }