From e966771f5bec35b9de9887b4120d95b4689a7172 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Tue, 21 Feb 2023 13:30:32 -0500 Subject: [PATCH] The raycaster is faster but more bg=uggyh agghgfghgfhgfgh --- examples/raycaster/ray.go | 161 +++++++++++++++++++++++++------- examples/raycaster/raycaster.go | 67 +++++++++++-- 2 files changed, 185 insertions(+), 43 deletions(-) diff --git a/examples/raycaster/ray.go b/examples/raycaster/ray.go index d9ff36f..0b4d6ae 100644 --- a/examples/raycaster/ray.go +++ b/examples/raycaster/ray.go @@ -21,14 +21,43 @@ func (world DefaultWorld) At (position image.Point) int { return world.Data[index] } -type Camera struct { - X, Y float64 - Angle float64 - Fov float64 +type Vector struct { + X, Y float64 } -func (camera *Camera) Point () (image.Point) { - return image.Pt(int(camera.X), int(camera.Y)) +func (vector Vector) Point () (image.Point) { + return image.Pt(int(vector.X), int(vector.Y)) +} + +func (vector Vector) Add (other Vector) Vector { + return Vector { + vector.X + other.X, + vector.Y + other.Y, + } +} + +func (vector Vector) Sub (other Vector) Vector { + return Vector { + vector.X - other.X, + vector.Y - other.Y, + } +} + +func (vector Vector) Mul (by float64) Vector { + return Vector { + vector.X * by, + vector.Y * by, + } +} + +func (vector Vector) Hypot () float64 { + return math.Hypot(vector.X, vector.Y) +} + +type Camera struct { + Vector + Angle float64 + Fov float64 } func (camera *Camera) Rotate (by float64) { @@ -38,53 +67,115 @@ func (camera *Camera) Rotate (by float64) { } func (camera *Camera) Walk (by float64) { - dx, dy := camera.Delta() - camera.X += dx * by - camera.Y += dy * by + delta := camera.Delta() + camera.X += delta.X * by + camera.Y += delta.Y * by } func (camera *Camera) Strafe (by float64) { - dx, dy := camera.OffsetDelta() - camera.X += dx * by - camera.Y += dy * by + delta := camera.OffsetDelta() + camera.X += delta.X * by + camera.Y += delta.Y * by } -func (camera *Camera) Delta () (x float64, y float64) { - return math.Cos(camera.Angle), math.Sin(camera.Angle) +func (camera *Camera) Delta () Vector { + return Vector { + math.Cos(camera.Angle), + math.Sin(camera.Angle), + } } -func (camera *Camera) OffsetDelta () (x float64, y float64) { +func (camera *Camera) OffsetDelta () Vector { offset := math.Pi / 2 - return math.Cos(camera.Angle + offset), math.Sin(camera.Angle + offset) + return Vector { + math.Cos(camera.Angle + offset), + math.Sin(camera.Angle + offset), + } } type Ray struct { - X, Y float64 + Vector Angle float64 - Precision int } -func (ray *Ray) Cast (world World, max int) (distance float64) { - precision := 64 +func (ray *Ray) Cast (world World, max int) (distance float64, hit Vector) { + // return ray.castV(world, max) + if world.At(ray.Point()) > 0 { + return 0, Vector { } + } + hDistance, hPos := ray.castH(world, max) + vDistance, vPos := ray.castV(world, max) + if hDistance < vDistance { + return hDistance, hPos + } else { + return vDistance, vPos + } +} - dX := math.Cos(ray.Angle) / float64(precision) - dY := math.Sin(ray.Angle) / float64(precision) - origX, origY := ray.X, ray.Y +func (ray *Ray) castH (world World, max int) (distance float64, hit Vector) { + var position Vector + var delta Vector + ray.Angle = math.Mod(ray.Angle, math.Pi * 2) + if ray.Angle < 0 { + ray.Angle += math.Pi * 2 + } + tan := math.Tan(math.Pi - ray.Angle) + if ray.Angle > math.Pi { + // facing up + position.Y = math.Floor(ray.Y) - (1.0 / 64) + delta.Y = -1 + } else if ray.Angle < math.Pi { + // facing down + position.Y = math.Floor(ray.Y) + 1 + delta.Y = 1 + } else { + // facing straight left or right + return float64(max), Vector { } + } + position.X = ray.X + (ray.Y - position.Y) / tan + delta.X = -delta.Y / tan - wall := 0 - depth := 0 - for wall == 0 && depth < max * precision { - ray.X += dX - ray.Y += dY - wall = world.At(ray.Point()) - depth ++ + // cast da ray + steps := 0 + for { + cell := world.At(position.Point()) + if cell > 0 || steps > max { break } + position = position.Add(delta) + steps ++ } - distanceX := origX - ray.X - distanceY := origY - ray.Y - return math.Sqrt(distanceX * distanceX + distanceY * distanceY) + return position.Sub(ray.Vector).Hypot(), position } -func (ray *Ray) Point () (image.Point) { - return image.Pt(int(ray.X), int(ray.Y)) +func (ray *Ray) castV (world World, max int) (distance float64, hit Vector) { + var position Vector + var delta Vector + tan := math.Tan(math.Pi - ray.Angle) + offsetAngle := math.Mod(ray.Angle + math.Pi / 2, math.Pi * 2) + if offsetAngle > math.Pi { + // facing left + position.X = math.Floor(ray.X) - (1.0 / 64) + delta.X = -1 + } else if offsetAngle < math.Pi { + // facing right + position.X = math.Floor(ray.X) + 1 + delta.X = 1 + } else { + // facing straight left or right + return float64(max), Vector { } + } + position.Y = ray.Y + (ray.X - position.X) * tan + delta.Y = -delta.X * tan + + // cast da ray + steps := 0 + for { + cell := world.At(position.Point()) + if cell > 0 || steps > max { break } + position = position.Add(delta) + steps ++ + } + + return position.Sub(ray.Vector).Hypot(), position + return } diff --git a/examples/raycaster/raycaster.go b/examples/raycaster/raycaster.go index 789f767..b72af53 100644 --- a/examples/raycaster/raycaster.go +++ b/examples/raycaster/raycaster.go @@ -1,6 +1,8 @@ package main +// import "fmt" import "math" +import "image" import "image/color" import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/config" @@ -32,9 +34,11 @@ type Raycaster struct { func NewRaycaster (world World) (element *Raycaster) { element = &Raycaster { Camera: Camera { - X: 2, - Y: 2, - Angle: 1, + Vector: Vector { + X: 1.5, + Y: 1.5, + }, + Angle: math.Pi / 3, Fov: 1, }, world: world, @@ -103,16 +107,13 @@ func (element *Raycaster) drawAll () { width := bounds.Dx() height := bounds.Dy() - ray := Ray { - Angle: element.Camera.Angle - element.Camera.Fov / 2, - Precision: 64, - } + ray := Ray { Angle: element.Camera.Angle - element.Camera.Fov / 2 } for x := 0; x < width; x ++ { ray.X = element.Camera.X ray.Y = element.Camera.Y - distance := ray.Cast(element.world, 8) + distance, _ := ray.Cast(element.world, 8) distanceFac := float64(distance) / 8 distance *= math.Cos(ray.Angle - element.Camera.Angle) @@ -150,4 +151,54 @@ func (element *Raycaster) drawAll () { // increment angle ray.Angle += element.Camera.Fov / float64(width) } + + // element.drawMinimap() +} + +func (element *Raycaster) drawMinimap () { + bounds := element.Bounds() + scale := 16 + for y := 0; y < 10; y ++ { + for x := 0; x < 10; x ++ { + cellPt := image.Pt(x, y) + cell := element.world.At(cellPt) + cellBounds := + image.Rectangle { + cellPt.Mul(scale), + cellPt.Add(image.Pt(1, 1)).Mul(scale), + }.Add(bounds.Min) + cellColor := color.RGBA { 0x22, 0x22, 0x22, 0xFF } + if cell == 1 { + cellColor = color.RGBA { 0xFF, 0xFF, 0xFF, 0xFF } + } + artist.FillRectangle ( + element.core, + artist.NewUniform(cellColor), + cellBounds.Inset(1)) + }} + + playerPt := element.Camera.Mul(float64(scale)).Point().Add(bounds.Min) + playerAnglePt := + element.Camera.Add(element.Camera.Delta()). + Mul(float64(scale)).Point().Add(bounds.Min) + ray := Ray { Vector: element.Camera.Vector, Angle: element.Camera.Angle } + _, hit := ray.Cast(element.world, 8) + hitPt := hit.Mul(float64(scale)).Point().Add(bounds.Min) + // fmt.Println(rayDistance) + + playerBounds := image.Rectangle { playerPt, playerPt }.Inset(scale / -8) + artist.FillEllipse ( + element.core, + artist.Uhex(0xFFFFFFFF), + playerBounds) + artist.Line ( + element.core, + artist.Uhex(0xFFFFFFFF), 1, + playerPt, + playerAnglePt) + artist.Line ( + element.core, + artist.Uhex(0x00FF00FF), 1, + playerPt, + hitPt) }