Initial commit
This commit is contained in:
commit
f26c478727
18 changed files with 621 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/game
|
||||||
5
go.mod
Normal file
5
go.mod
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
module github.com/pnx/go-raytracer
|
||||||
|
|
||||||
|
go 1.24.3
|
||||||
|
|
||||||
|
require github.com/veandco/go-sdl2 v0.4.40
|
||||||
2
go.sum
Normal file
2
go.sum
Normal 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=
|
||||||
65
graphics/sdl.go
Normal file
65
graphics/sdl.go
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
package graphics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pnx/go-raytracer/math"
|
||||||
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Context struct {
|
||||||
|
w, h int32
|
||||||
|
window *sdl.Window
|
||||||
|
renderer *sdl.Renderer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Context) Width() int32 {
|
||||||
|
return c.w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Context) Height() int32 {
|
||||||
|
return c.h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Context) DrawLine(x1 int32, y1 int32, x2 int32, y2 int32, color math.Color) {
|
||||||
|
c.renderer.SetDrawColor(color.R, color.G, color.B, color.A)
|
||||||
|
c.renderer.DrawLine(x1, y1, x2, y2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Context) DrawRect(x, y, w, h int32, color math.Color) {
|
||||||
|
c.renderer.SetDrawColor(color.R, color.G, color.B, color.A)
|
||||||
|
c.renderer.FillRect(&sdl.Rect{
|
||||||
|
X: x,
|
||||||
|
Y: y,
|
||||||
|
W: w,
|
||||||
|
H: h,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Context) Sync() {
|
||||||
|
c.renderer.Present()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Context) Exit() {
|
||||||
|
c.window.Destroy()
|
||||||
|
c.renderer.Destroy()
|
||||||
|
sdl.Quit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Init(title string, w, h int32) (*Context, error) {
|
||||||
|
if err := sdl.Init(sdl.INIT_VIDEO); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
window, err := sdl.CreateWindow(title, sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, w, h, sdl.WINDOW_SHOWN)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
renderer, err := sdl.CreateRenderer(window, -1, sdl.RENDERER_SOFTWARE)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Context{
|
||||||
|
w: w,
|
||||||
|
h: h,
|
||||||
|
window: window,
|
||||||
|
renderer: renderer,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
97
main.go
Normal file
97
main.go
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
stdmath "math"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pnx/go-raytracer/graphics"
|
||||||
|
"github.com/pnx/go-raytracer/math"
|
||||||
|
"github.com/pnx/go-raytracer/render"
|
||||||
|
"github.com/pnx/go-raytracer/world"
|
||||||
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
gfxContext *graphics.Context
|
||||||
|
player Player
|
||||||
|
level *world.Level
|
||||||
|
startOfFrame time.Time
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadLevel(newLevel *world.Level) {
|
||||||
|
level = newLevel
|
||||||
|
|
||||||
|
// Set player start position
|
||||||
|
start := level.PlayerStart()
|
||||||
|
|
||||||
|
player.Position = math.Position{
|
||||||
|
X: (float64(start.X) + 0.5) * tileSize,
|
||||||
|
Y: (float64(start.Y) + 0.5) * tileSize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func update() bool {
|
||||||
|
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
|
||||||
|
switch ev := event.(type) {
|
||||||
|
case *sdl.QuitEvent:
|
||||||
|
return true
|
||||||
|
case *sdl.KeyboardEvent:
|
||||||
|
if ev.Type == sdl.KEYDOWN {
|
||||||
|
switch ev.Keysym.Sym {
|
||||||
|
case sdl.K_ESCAPE:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := sdl.GetKeyboardState()
|
||||||
|
|
||||||
|
if keys[sdl.SCANCODE_UP] != 0 {
|
||||||
|
player.MoveForward(level)
|
||||||
|
}
|
||||||
|
if keys[sdl.SCANCODE_DOWN] != 0 {
|
||||||
|
player.MoveBackward(level)
|
||||||
|
}
|
||||||
|
if keys[sdl.SCANCODE_LEFT] != 0 {
|
||||||
|
player.RotateLeft()
|
||||||
|
}
|
||||||
|
if keys[sdl.SCANCODE_RIGHT] != 0 {
|
||||||
|
player.RotateRight()
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
gfxContext, err = graphics.Init("Go Raycaster", 1024, 768)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
player = Player{
|
||||||
|
MoveSpd: 2,
|
||||||
|
RotSpd: 0.05,
|
||||||
|
}
|
||||||
|
|
||||||
|
loadLevel(&world.Level1)
|
||||||
|
|
||||||
|
for {
|
||||||
|
startOfFrame := time.Now()
|
||||||
|
if update() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
render.DrawScene(gfxContext, player.Transform, level, colorMap)
|
||||||
|
render.DrawMiniMap(gfxContext, player.Transform, level)
|
||||||
|
gfxContext.Sync()
|
||||||
|
|
||||||
|
elapsed := time.Since(startOfFrame)
|
||||||
|
sdl.Delay(uint32(stdmath.Max(16-float64(elapsed.Milliseconds()), 1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
gfxContext.Exit()
|
||||||
|
}
|
||||||
6
makefile
Normal file
6
makefile
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
GO=go
|
||||||
|
|
||||||
|
.PHONY: game
|
||||||
|
|
||||||
|
game:
|
||||||
|
$(GO) build -o $@ .
|
||||||
23
math/color.go
Normal file
23
math/color.go
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
package math
|
||||||
|
|
||||||
|
type Color struct {
|
||||||
|
R, G, B, A uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Color) Shade(value uint8) Color {
|
||||||
|
return Color{
|
||||||
|
R: c.R / value,
|
||||||
|
G: c.G / value,
|
||||||
|
B: c.B / value,
|
||||||
|
A: c.A,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Color) Sub(value uint8) Color {
|
||||||
|
return Color{
|
||||||
|
R: c.R - value,
|
||||||
|
G: c.G - value,
|
||||||
|
B: c.B - value,
|
||||||
|
A: c.A,
|
||||||
|
}
|
||||||
|
}
|
||||||
7
math/degree.go
Normal file
7
math/degree.go
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
package math
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
func DecToRad(deg float64) float64 {
|
||||||
|
return deg * (math.Pi / 180)
|
||||||
|
}
|
||||||
26
math/direction.go
Normal file
26
math/direction.go
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
package math
|
||||||
|
|
||||||
|
import (
|
||||||
|
stdmath "math"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Direction float64
|
||||||
|
|
||||||
|
func (d *Direction) Set(v float64) {
|
||||||
|
*d = Direction(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Direction) Get() float64 {
|
||||||
|
return float64(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Direction) ForwardVector() Vec2f {
|
||||||
|
return Vec2f{
|
||||||
|
X: stdmath.Cos(float64(d)),
|
||||||
|
Y: stdmath.Sin(float64(d)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Direction) Rotate(delta float64) {
|
||||||
|
*d += Direction(delta)
|
||||||
|
}
|
||||||
11
math/position.go
Normal file
11
math/position.go
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
package math
|
||||||
|
|
||||||
|
type Position Vec2f
|
||||||
|
|
||||||
|
func (p *Position) Set(v Vec2f) {
|
||||||
|
*p = Position(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Position) Get() Vec2f {
|
||||||
|
return Vec2f(p)
|
||||||
|
}
|
||||||
6
math/transform.go
Normal file
6
math/transform.go
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
package math
|
||||||
|
|
||||||
|
type Transform struct {
|
||||||
|
Position
|
||||||
|
Direction
|
||||||
|
}
|
||||||
82
math/vec2.go
Normal file
82
math/vec2.go
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
package math
|
||||||
|
|
||||||
|
import stdmath "math"
|
||||||
|
|
||||||
|
type UnsignedInteger interface {
|
||||||
|
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type SignedInteger interface {
|
||||||
|
~int | ~int8 | ~int16 | ~int32 | ~int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Integer interface {
|
||||||
|
UnsignedInteger | SignedInteger
|
||||||
|
}
|
||||||
|
|
||||||
|
type Float interface {
|
||||||
|
~float32 | ~float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Number interface {
|
||||||
|
Integer | Float
|
||||||
|
}
|
||||||
|
|
||||||
|
type Vec2[T Number] struct {
|
||||||
|
X, Y T
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
Vec2i = Vec2[int]
|
||||||
|
Vec2i32 = Vec2[int32]
|
||||||
|
Vec2f = Vec2[float64]
|
||||||
|
Vec2u8 = Vec2[uint8]
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v Vec2[T]) Add(x, y T) Vec2[T] {
|
||||||
|
return Vec2[T]{
|
||||||
|
X: v.X + x,
|
||||||
|
Y: v.Y + y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vec2[T]) AddS(s T) Vec2[T] {
|
||||||
|
return v.Add(s, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vec2[T]) AddVec(other Vec2[T]) Vec2[T] {
|
||||||
|
return Vec2[T]{
|
||||||
|
X: v.X + other.X,
|
||||||
|
Y: v.Y + other.Y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vec2[T]) Mul(x, y T) Vec2[T] {
|
||||||
|
return Vec2[T]{
|
||||||
|
X: v.X * x,
|
||||||
|
Y: v.Y * y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vec2[T]) MulVec(other Vec2[T]) Vec2[T] {
|
||||||
|
return v.Mul(other.X, other.Y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vec2[T]) Scale(f T) Vec2[T] {
|
||||||
|
return v.Mul(f, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vec2[T]) Length() float64 {
|
||||||
|
return stdmath.Sqrt(float64(v.X*v.X) + float64(v.Y*v.Y))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vec2[T]) Normalize() Vec2[T] {
|
||||||
|
l := v.Length()
|
||||||
|
if l <= 0.0 {
|
||||||
|
return Vec2[T]{}
|
||||||
|
}
|
||||||
|
return Vec2[T]{
|
||||||
|
X: T(float64(v.X) / l),
|
||||||
|
Y: T(float64(v.Y) / l),
|
||||||
|
}
|
||||||
|
}
|
||||||
38
player.go
Normal file
38
player.go
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pnx/go-raytracer/math"
|
||||||
|
"github.com/pnx/go-raytracer/world"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Player struct {
|
||||||
|
math.Transform
|
||||||
|
MoveSpd float64
|
||||||
|
RotSpd float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) Move(delta float64, level *world.Level) {
|
||||||
|
deltaVec := p.ForwardVector().Scale(delta)
|
||||||
|
newPos := p.Position.Get().AddVec(deltaVec)
|
||||||
|
|
||||||
|
// make sure we don't move past walls.
|
||||||
|
if !level.Wall(int(newPos.X)/world.TileSize, int(newPos.Y)/world.TileSize) {
|
||||||
|
p.Position.Set(newPos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) MoveForward(level *world.Level) {
|
||||||
|
p.Move(p.MoveSpd, level)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) MoveBackward(level *world.Level) {
|
||||||
|
p.Move(-p.MoveSpd, level)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) RotateLeft() {
|
||||||
|
p.Rotate(-p.RotSpd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) RotateRight() {
|
||||||
|
p.Rotate(p.RotSpd)
|
||||||
|
}
|
||||||
47
render/engine.go
Normal file
47
render/engine.go
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
package render
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pnx/go-raytracer/graphics"
|
||||||
|
"github.com/pnx/go-raytracer/math"
|
||||||
|
"github.com/pnx/go-raytracer/world"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DrawColumn(ctx *graphics.Context, x int32, wall_h int32, color math.Color) {
|
||||||
|
if wall_h < ctx.Height() {
|
||||||
|
y1 := (ctx.Height() - wall_h) / 2
|
||||||
|
y2 := (ctx.Height() + wall_h) / 2
|
||||||
|
|
||||||
|
// Top
|
||||||
|
// renderer.SetDrawColor(90, 90, 0, 255)
|
||||||
|
ctx.DrawLine(x, 0, int32(x), y1, math.Color{R: 90, G: 90, B: 0, A: 255})
|
||||||
|
|
||||||
|
// // Middle
|
||||||
|
// renderer.SetDrawColor(color.R, color.G, color.B, color.A)
|
||||||
|
ctx.DrawLine(x, y1, int32(x), y2, color)
|
||||||
|
|
||||||
|
// Bottom
|
||||||
|
if y2 < ctx.Height() {
|
||||||
|
// renderer.SetDrawColor(50, 50, 50, 255)
|
||||||
|
ctx.DrawLine(x, y2, int32(x), ctx.Height(), math.Color{R: 50, G: 50, B: 50, A: 255})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ctx.SetDrawColor(color.R, color.G, color.B, color.A)
|
||||||
|
ctx.DrawLine(x, 0, int32(x), ctx.Height(), color)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DrawScene(ctx *graphics.Context, camera math.Transform, level *world.Level, colorMap []math.Color) {
|
||||||
|
for x := range ctx.Width() {
|
||||||
|
result := CastRay(camera, level, int(x), int(ctx.Width()))
|
||||||
|
lineHeight := int(float64(ctx.Height()) / result.Distance)
|
||||||
|
|
||||||
|
color := colorMap[level.Cell(int(result.Cell.X), int(result.Cell.Y))-1]
|
||||||
|
|
||||||
|
if result.Side > 0 {
|
||||||
|
color = color.Shade(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawColumn(ctx, int32(x), int32(lineHeight), color)
|
||||||
|
}
|
||||||
|
}
|
||||||
36
render/minimap.go
Normal file
36
render/minimap.go
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
package render
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pnx/go-raytracer/graphics"
|
||||||
|
"github.com/pnx/go-raytracer/math"
|
||||||
|
"github.com/pnx/go-raytracer/world"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DrawMiniMap(ctx *graphics.Context, camera math.Transform, level *world.Level) {
|
||||||
|
const tileSize = 32
|
||||||
|
const scale = float64(tileSize) / float64(world.TileSize)
|
||||||
|
// offset := math.Vec2i32{X: 25, Y: 25}
|
||||||
|
// size := math.Vec2i32{X: 200, Y: 100}
|
||||||
|
|
||||||
|
ctx.DrawRect(0, 0, int32(level.W*tileSize)+3, int32(level.H*tileSize)+3, math.Color{})
|
||||||
|
|
||||||
|
for y := range level.H {
|
||||||
|
for x := range level.W {
|
||||||
|
if level.Wall(x, y) {
|
||||||
|
ctx.DrawRect(int32(x*tileSize), int32(y*tileSize), tileSize, tileSize, math.Color{R: 255, G: 255, B: 255, A: 255})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Camera Position
|
||||||
|
cPos := camera.Position.Get().Scale(scale)
|
||||||
|
ctx.DrawRect(int32(cPos.X-2), int32(cPos.Y-2), 4, 4, math.Color{R: 255, G: 0, B: 255, A: 255})
|
||||||
|
|
||||||
|
// Rays
|
||||||
|
for x := range ctx.Width() {
|
||||||
|
hit := CastRay(camera, level, int(x), int(ctx.Width()))
|
||||||
|
end := camera.Position.Get().AddVec(hit.Pos.Scale(world.TileSize)).Scale(scale)
|
||||||
|
|
||||||
|
ctx.DrawLine(int32(cPos.X), int32(cPos.Y), int32(end.X), int32(end.Y), math.Color{R: 150, G: 150, B: 0, A: 255})
|
||||||
|
}
|
||||||
|
}
|
||||||
85
render/raycaster.go
Normal file
85
render/raycaster.go
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
42
world/level.go
Normal file
42
world/level.go
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
package world
|
||||||
|
|
||||||
|
import "github.com/pnx/go-raytracer/math"
|
||||||
|
|
||||||
|
const TileSize = 64
|
||||||
|
|
||||||
|
type Special byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
Empty Special = 0
|
||||||
|
PlayerStart Special = 'S'
|
||||||
|
)
|
||||||
|
|
||||||
|
type Level struct {
|
||||||
|
W, H int
|
||||||
|
Grid []byte
|
||||||
|
Specials []Special
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Level) Wall(x, y int) bool {
|
||||||
|
return m.Grid[(y*m.W)+x] > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Level) Cell(x, y int) byte {
|
||||||
|
return m.Grid[(y*m.W)+x]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Level) PosToCell(x, y float64) (int, int) {
|
||||||
|
return int(x) / TileSize, int(y) / TileSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m Level) PlayerStart() math.Vec2[int] {
|
||||||
|
for k, v := range m.Specials {
|
||||||
|
if v == PlayerStart {
|
||||||
|
return math.Vec2[int]{
|
||||||
|
X: k % m.W,
|
||||||
|
Y: k / m.W,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return math.Vec2[int]{X: -1, Y: -1}
|
||||||
|
}
|
||||||
42
world/level1.go
Normal file
42
world/level1.go
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
package world
|
||||||
|
|
||||||
|
var Level1 = Level{
|
||||||
|
W: 16,
|
||||||
|
H: 16,
|
||||||
|
Grid: []uint8{
|
||||||
|
2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
|
||||||
|
2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 2, 0, 1,
|
||||||
|
2, 2, 2, 0, 2, 2, 2, 2, 0, 4, 4, 0, 0, 0, 0, 1,
|
||||||
|
3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1,
|
||||||
|
3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1,
|
||||||
|
3, 0, 0, 0, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
|
||||||
|
3, 0, 0, 0, 3, 0, 0, 0, 0, 0, 4, 0, 3, 0, 0, 1,
|
||||||
|
3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
|
||||||
|
3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1,
|
||||||
|
1, 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, 1,
|
||||||
|
1, 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, 1,
|
||||||
|
1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
},
|
||||||
|
Specials: []Special{
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 'S', 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue