aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorJames Barnett <noreply@jamesbarnett.xyz>2020-12-28 15:49:23 +0000
committerJames Barnett <noreply@jamesbarnett.xyz>2020-12-28 15:49:23 +0000
commitf28500d963d6546feda522d7748a0462a568ba28 (patch)
tree0240d3528277930b28790c336ed10d77ea8928e1 /src/main
parent01727649bb1fbe3e0c721a850bd21973d7e760f2 (diff)
downloadkotlin-raycaster-f28500d963d6546feda522d7748a0462a568ba28.tar.xz
kotlin-raycaster-f28500d963d6546feda522d7748a0462a568ba28.zip
Refactor components
Diffstat (limited to 'src/main')
-rw-r--r--src/main/kotlin/Map.kt17
-rw-r--r--src/main/kotlin/Minimap.kt17
-rw-r--r--src/main/kotlin/Raycaster.kt42
-rw-r--r--src/main/kotlin/Renderer.kt8
-rw-r--r--src/main/kotlin/main.kt68
-rw-r--r--src/main/kotlin/raycaster.kt117
6 files changed, 138 insertions, 131 deletions
diff --git a/src/main/kotlin/Map.kt b/src/main/kotlin/Map.kt
new file mode 100644
index 0000000..d458a67
--- /dev/null
+++ b/src/main/kotlin/Map.kt
@@ -0,0 +1,17 @@
+class Map {
+ val data = listOf(
+ listOf(1,1,1,1,1,1,1,1,1,1),
+ listOf(1,0,0,0,0,0,0,0,0,1),
+ listOf(1,0,0,0,0,0,0,0,0,1),
+ listOf(1,0,0,1,1,0,1,0,0,1),
+ listOf(1,0,0,1,0,0,1,0,0,1),
+ listOf(1,0,0,1,0,0,1,0,0,1),
+ listOf(1,0,0,1,0,1,1,0,0,1),
+ listOf(1,0,0,0,0,0,0,0,0,1),
+ listOf(1,0,0,0,0,0,0,0,0,1),
+ listOf(1,1,1,1,1,1,1,1,1,1)
+ )
+
+ val width = data[0].size
+ val height = data.size
+} \ No newline at end of file
diff --git a/src/main/kotlin/Minimap.kt b/src/main/kotlin/Minimap.kt
index dc7cc91..eb762ab 100644
--- a/src/main/kotlin/Minimap.kt
+++ b/src/main/kotlin/Minimap.kt
@@ -2,16 +2,13 @@ import kotlinx.browser.document
import org.w3c.dom.CanvasRenderingContext2D
import org.w3c.dom.HTMLCanvasElement
-class Minimap(private val map: List<List<Int>>) {
-
+class Minimap(private val map: Map) {
private val scale = 30
- private val mapWidth = map[0].size
- private val mapHeight = map.size
private val canvas = (document.createElement("canvas") as HTMLCanvasElement)
.apply {
- width = mapWidth * scale
- height = mapHeight * scale
+ width = map.width * scale
+ height = map.height * scale
id = "minimap"
style.width = "${width}px"
style.height = "${height}px"
@@ -23,9 +20,9 @@ class Minimap(private val map: List<List<Int>>) {
}
private fun drawMap() {
- for (y in 0 until mapHeight) {
- for (x in 0 until mapWidth) {
- val wall = map[y][x]
+ for (y in 0 until map.height) {
+ for (x in 0 until map.height) {
+ val wall = map.data[y][x]
if (wall > 0) {
context.fillStyle = "#000000"
context.fillRect((x * scale).toDouble(), (y * scale).toDouble(), scale.toDouble(), scale.toDouble())
@@ -35,7 +32,7 @@ class Minimap(private val map: List<List<Int>>) {
}
fun update(camera: Camera) {
- context.clearRect(0.0, 0.0, (mapWidth * scale).toDouble(), (mapHeight * scale).toDouble())
+ context.clearRect(0.0, 0.0, (map.width * scale).toDouble(), (map.height * scale).toDouble())
drawMap()
context.fillStyle = "#FF0000"
context.fillRect(camera.xPos * scale, camera.yPos * scale, scale.toDouble(), scale.toDouble())
diff --git a/src/main/kotlin/Raycaster.kt b/src/main/kotlin/Raycaster.kt
new file mode 100644
index 0000000..d3fa694
--- /dev/null
+++ b/src/main/kotlin/Raycaster.kt
@@ -0,0 +1,42 @@
+import kotlin.math.pow
+
+class Raycaster(private val stepPrecision: Int = 32) {
+
+ fun raycast(raycastContext: RaycastContext) {
+ val (renderer, camera, map, _) = raycastContext
+
+ val viewportWidth = renderer.viewportWidth
+ val viewportHeight = renderer.viewportHeight
+ val viewportHeightHalf = viewportHeight / 2
+
+ var raySweepAngle = camera.rotation - camera.halfFov
+
+ for (rayIndex in 0 until viewportWidth) {
+
+ var rayX = camera.xPos
+ var rayY = camera.yPos
+
+ do {
+ rayX += kotlin.math.cos(toRadians(raySweepAngle)) / stepPrecision
+ rayY += kotlin.math.sin(toRadians(raySweepAngle)) / stepPrecision
+ // TODO bounds checking
+ val wallHit = map.data[kotlin.math.floor(rayY).toInt()][kotlin.math.floor(rayX).toInt()] > 0
+ } while (!wallHit)
+
+ val distanceToWall = kotlin.math.sqrt((camera.xPos - rayX).pow(2) + (camera.yPos - rayY).pow(2))
+ val wallHeight = kotlin.math.floor(viewportHeightHalf / distanceToWall).toInt()
+
+ // Sky
+ renderer.drawLine(rayIndex, 0, rayIndex, viewportHeightHalf - wallHeight, "cyan")
+ // Wall
+ renderer.drawLine(rayIndex, viewportHeightHalf - wallHeight, rayIndex, viewportHeightHalf + wallHeight, "red")
+ // Floor
+ renderer.drawLine(rayIndex, viewportHeightHalf + wallHeight, rayIndex, viewportHeight, "green")
+
+ raySweepAngle += (camera.fov / viewportWidth.toDouble())
+ }
+ }
+
+
+
+} \ No newline at end of file
diff --git a/src/main/kotlin/Renderer.kt b/src/main/kotlin/Renderer.kt
index b266e61..0bc8988 100644
--- a/src/main/kotlin/Renderer.kt
+++ b/src/main/kotlin/Renderer.kt
@@ -2,12 +2,12 @@ import kotlinx.browser.document
import org.w3c.dom.CanvasRenderingContext2D
import org.w3c.dom.HTMLCanvasElement
-class Renderer(val w: Int, val h: Int) {
+class Renderer(val viewportWidth: Int, val viewportHeight: Int) {
private val canvas = (document.createElement("canvas") as HTMLCanvasElement)
.apply {
- width = w
- height = h
+ width = viewportWidth
+ height = viewportHeight
}
private val context = canvas.getContext("2d") as CanvasRenderingContext2D
@@ -25,7 +25,7 @@ class Renderer(val w: Int, val h: Int) {
}
fun clear() {
- context.clearRect(0.0, 0.0, w.toDouble(), h.toDouble())
+ context.clearRect(0.0, 0.0, viewportWidth.toDouble(), viewportHeight.toDouble())
}
} \ No newline at end of file
diff --git a/src/main/kotlin/main.kt b/src/main/kotlin/main.kt
new file mode 100644
index 0000000..c07a661
--- /dev/null
+++ b/src/main/kotlin/main.kt
@@ -0,0 +1,68 @@
+import kotlinx.browser.document
+
+private const val MOVE_SPEED = 0.5
+private const val ROTATE_SPEED = 5
+
+data class RaycastContext(
+ val renderer: Renderer,
+ val camera: Camera,
+ val map: Map,
+ val minimap: Minimap
+)
+
+fun main() {
+ val renderer = Renderer(640, 480)
+ val camera = Camera(
+ fov = 60,
+ xPos = 2.0,
+ yPos = 2.0,
+ rotation = 90.0
+ )
+ val map = Map()
+ val minimap = Minimap(map)
+
+ val context = RaycastContext(renderer, camera, map, minimap)
+
+ val raycaster = Raycaster()
+
+ document.onkeydown = {
+ when (it.code) {
+ "KeyW" -> {
+ console.log("key w")
+ val cameraCos = kotlin.math.cos(toRadians(camera.rotation)) * MOVE_SPEED
+ val cameraSin = kotlin.math.sin(toRadians(camera.rotation)) * MOVE_SPEED
+ camera.xPos += cameraCos
+ camera.yPos += cameraSin
+ }
+ "KeyS" -> {
+ console.log("key s")
+ val cameraCos = kotlin.math.cos(toRadians(camera.rotation)) * MOVE_SPEED
+ val cameraSin = kotlin.math.sin(toRadians(camera.rotation)) * MOVE_SPEED
+ camera.xPos -= cameraCos
+ camera.yPos -= cameraSin
+ }
+ "KeyA" -> {
+ console.log("key a")
+ camera.rotation -= ROTATE_SPEED
+ }
+ "KeyD" -> {
+ console.log("key d")
+ camera.rotation += ROTATE_SPEED
+ }
+ }
+ paint(raycaster, context)
+ console.log("camera x:${camera.xPos} y:${camera.yPos} r: ${camera.rotation}")
+ }
+
+ paint(raycaster, context)
+}
+
+fun paint(raycaster: Raycaster, raycastContext: RaycastContext) {
+ raycastContext.renderer.clear()
+ raycaster.raycast(raycastContext)
+ raycastContext.minimap.update(raycastContext.camera)
+}
+
+fun toRadians(degrees: Double): Double {
+ return degrees * kotlin.math.PI / 180
+}
diff --git a/src/main/kotlin/raycaster.kt b/src/main/kotlin/raycaster.kt
deleted file mode 100644
index 5b717bc..0000000
--- a/src/main/kotlin/raycaster.kt
+++ /dev/null
@@ -1,117 +0,0 @@
-import kotlinx.browser.document
-import kotlin.math.pow
-
-private const val SCREEN_WIDTH = 640
-private const val SCREEN_HEIGHT = 480
-private const val MOVE_SPEED = 0.5
-private const val ROTATE_SPEED = 5
-
-private val map = listOf(
- listOf(1,1,1,1,1,1,1,1,1,1),
- listOf(1,0,0,0,0,0,0,0,0,1),
- listOf(1,0,0,0,0,0,0,0,0,1),
- listOf(1,0,0,1,1,0,1,0,0,1),
- listOf(1,0,0,1,0,0,1,0,0,1),
- listOf(1,0,0,1,0,0,1,0,0,1),
- listOf(1,0,0,1,0,1,1,0,0,1),
- listOf(1,0,0,0,0,0,0,0,0,1),
- listOf(1,0,0,0,0,0,0,0,0,1),
- listOf(1,1,1,1,1,1,1,1,1,1)
-)
-
-data class RaycastContext(
- val renderer: Renderer,
- val camera: Camera,
- val minimap: Minimap
-)
-
-fun main() {
- val renderer = Renderer(SCREEN_WIDTH, SCREEN_HEIGHT)
- val camera = Camera(
- fov = 60,
- xPos = 2.0,
- yPos = 2.0,
- rotation = 90.0
- )
- val minimap = Minimap(map)
-
- val context = RaycastContext(renderer, camera, minimap)
-
- document.onkeydown = {
- when (it.code) {
- "KeyW" -> {
- console.log("key w")
- val cameraCos = kotlin.math.cos(toRadians(camera.rotation)) * MOVE_SPEED
- val cameraSin = kotlin.math.sin(toRadians(camera.rotation)) * MOVE_SPEED
- camera.xPos += cameraCos
- camera.yPos += cameraSin
- }
- "KeyS" -> {
- console.log("key s")
- val cameraCos = kotlin.math.cos(toRadians(camera.rotation)) * MOVE_SPEED
- val cameraSin = kotlin.math.sin(toRadians(camera.rotation)) * MOVE_SPEED
- camera.xPos -= cameraCos
- camera.yPos -= cameraSin
- }
- "KeyA" -> {
- console.log("key a")
- camera.rotation -= ROTATE_SPEED
- }
- "KeyD" -> {
- console.log("key d")
- camera.rotation += ROTATE_SPEED
- }
- }
- paint(context)
- console.log("camera x:${camera.xPos} y:${camera.yPos} r: ${camera.rotation}")
- }
-
- paint(context)
-}
-
-fun paint(raycastContext: RaycastContext) {
- raycastContext.renderer.clear()
- raycast(raycastContext)
- raycastContext.minimap.update(raycastContext.camera)
-}
-
-fun raycast(raycastContext: RaycastContext) {
- val incrementAngle : Double = raycastContext.camera.fov / SCREEN_WIDTH.toDouble()
- val rayCastPrecision = 32
-
- var rayAngle = raycastContext.camera.rotation - raycastContext.camera.halfFov
-
- for (rayIndex in 0 until SCREEN_WIDTH) {
-
- var rayX = raycastContext.camera.xPos
- var rayY = raycastContext.camera.yPos
-
- val rayCos = kotlin.math.cos(toRadians(rayAngle)) / rayCastPrecision
- val raySin = kotlin.math.sin(toRadians(rayAngle)) / rayCastPrecision
-
- var isWall = false
- while(!isWall) {
- rayX += rayCos
- rayY += raySin
-
- // TODO bounds checking
- val foo = map[kotlin.math.floor(rayY).toInt()][kotlin.math.floor(rayX).toInt()]
- if (foo == 1) {
- isWall = true
- }
- }
-
- val distance = kotlin.math.sqrt((raycastContext.camera.xPos - rayX).pow(2) + (raycastContext.camera.yPos - rayY).pow(2))
- val wallHeight = kotlin.math.floor((SCREEN_HEIGHT/2) / distance).toInt()
-
- raycastContext.renderer.drawLine(rayIndex, 0, rayIndex, (SCREEN_HEIGHT/2)-wallHeight, "cyan")
- raycastContext.renderer.drawLine(rayIndex, (SCREEN_HEIGHT/2)-wallHeight, rayIndex, (SCREEN_HEIGHT/2)+wallHeight, "red")
- raycastContext.renderer.drawLine(rayIndex, (SCREEN_HEIGHT/2)+wallHeight, rayIndex, SCREEN_HEIGHT, "green")
-
- rayAngle += incrementAngle
- }
-}
-
-fun toRadians(degrees: Double): Double {
- return degrees * kotlin.math.PI / 180
-}