added ray casting and block destruction
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 22 Dec 2021 18:52:08 +0000 (12:52 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 22 Dec 2021 18:52:08 +0000 (12:52 -0600)
.gitignore
run_tree/lib/onyx_opengles.so
run_tree/run.sh
src/chunk.onyx
src/main.onyx
src/mesh.onyx
src/vecmath.onyx
src/world.onyx

index 6cbd667378e00473e291fdd80c4898564d963205..d875df8a6858363b1fd143a829a5abf182334966 100644 (file)
@@ -2,4 +2,5 @@ src/config.onyx
 *.code-workspace
 *.sublime-project
 *.sublime-workspace
-.vscode/c_cpp_properties.json
\ No newline at end of file
+.vscode/c_cpp_properties.json
+*.wasm
index 393e39ffa4675376eb0aa7d2a7f7d30540f470de..cc89c8ceae7de89bc5d293eed274a2f643a69003 100755 (executable)
Binary files a/run_tree/lib/onyx_opengles.so and b/run_tree/lib/onyx_opengles.so differ
index c58eb2e551e4b44afef66cb9f80825df8b1a60f7..b4ccbc534fd00a6d403bd8f1b178d031d25f8a8c 100755 (executable)
@@ -1,2 +1,2 @@
 
-onyx run build -I ../src
\ No newline at end of file
+onyx build -I ../src -o game.wasm && onyxrun game.wasm
index 9bd74437ce2c7508e8eeb1cbb8a80cc179f48383..12395d5c689ce1c7072c3f6f71188e3c5e36f6a5 100644 (file)
@@ -1,4 +1,5 @@
 use package core
+use package opengles
 
 Block :: #distinct u32;
 #operator == macro (b1, b2: Block) => cast(u32) b1 == cast(u32) b2;
@@ -145,9 +146,59 @@ chunk_draw :: (chunk: ^Chunk) {
 }
 
 
+// Provides x, y, z, and block in the body
 chunk_foreach :: macro (chunk: ^Chunk, body: Code) {
     for x: Chunk_Size do for y: Chunk_Size do for z: Chunk_Size {
         block := chunk.blocks[x * Chunk_Size * Chunk_Size + y * Chunk_Size + z];
         #insert body;
     }
 }
+
+block_highlight: ^Mesh(Chunk_Vertex);
+chunk_highlight_block :: (x, y, z: f32) {
+    data := 0xf000;
+
+    vertex_data := cast(^Chunk_Vertex) alloca(sizeof Chunk_Vertex * 8);
+    vertex_data[0] = .{.{x-0.001,y-0.001,z-0.001},data};
+    vertex_data[1] = .{.{x-0.001,y+1.001,z-0.001},data};
+    vertex_data[2] = .{.{x+1.001,y+1.001,z-0.001},data};
+    vertex_data[3] = .{.{x+1.001,y-0.001,z-0.001},data};
+    vertex_data[4] = .{.{x-0.001,y-0.001,z+1.001},data};
+    vertex_data[5] = .{.{x-0.001,y+1.001,z+1.001},data};
+    vertex_data[6] = .{.{x+1.001,y+1.001,z+1.001},data};
+    vertex_data[7] = .{.{x+1.001,y-0.001,z+1.001},data};
+
+    if block_highlight == null {
+        #persist index_data := u32.[
+            2, 1, 1, 0,  // back
+            0, 3, 3, 2,
+
+            6, 2, 2, 3, // right side
+            3, 7, 7, 6,
+
+            0, 1, 1, 5, // left side
+            5, 4, 4, 0,
+
+            4, 5, 5, 6, // front
+            6, 7, 7, 4,
+
+            5, 1, 1, 2, // top
+            2, 6, 6, 5,
+
+            0, 4, 4, 7, // bottom
+            7, 3, 3, 0,
+        ];
+
+        mesh := mesh_make(vertex_data[0..8], index_data);
+        mesh.primitive = GL_LINES;
+        block_highlight = mesh;
+
+    } else {
+        mesh_update_verticies(block_highlight, vertex_data[0 .. 8]);
+    }
+
+    update_model_matrix(.{0,0,0});
+    mesh_draw(block_highlight);
+}
+
+
index 9502bca96838889f2fe1fcb4f3b809219d7b45c8..86ccd1d11c65a261d4c6a31df278d941ab55aadf 100644 (file)
@@ -62,6 +62,7 @@ toggle_cursor_grabbed :: () {
 world: ^World;
 world_shader: Shader;
 font: Font;
+selected_block: Vector3i;
 
 setup_opengl :: () {
     glInit(glfwGetLoadProcAddress());
@@ -144,7 +145,17 @@ update :: (dt: f32) {
         camera.position -= Vector3.norm(Vector3.cross(facing, .{0,1,0})) * speed;
     }
 
+    dir: Vector3i;
+    if !world_ray_cast(.{ camera.position, facing }, 40, null, (_, p) => {
+        return world_get_block(world, p.x, p.y, p.z) != Block_Empty;
+    }, ^selected_block, ^dir) {
+        selected_block = .{0,0,0};
+    }
 
+    if glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS {
+        world_set_block(world, selected_block.x, selected_block.y, selected_block.z, Block_Empty);
+    }
+    
 
     {
         #persist last_debug_key := 0;
@@ -166,12 +177,19 @@ draw :: () {
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
     shader_use(world_shader);
+    glLineWidth(2);
     world_draw(world);
+    chunk_highlight_block(~~selected_block.x, ~~selected_block.y, ~~selected_block.z);
 
     if debug_screen {
+        ww, wh: i32;
+        glfwGetWindowSize(window, ^ww, ^wh);
+        font_print(font, ~~(ww / 2), ~~(wh / 2), "X");
+
         font_print(font, 0, 32, "FPS: {}", game_fps);
         font_print(font, 0, 64, "Position: {}", camera.position);
         font_print(font, 0, 96, "Facing: {}", camera.y_rot * 180 / math.PI);
+        font_print(font, 0, 128, "Looking at: {}", selected_block);
     }
 
     glfwSwapBuffers(window);
index 42859a99ba6cae61a47b2f0bf1eb2990046f7896..3c969c7bfc1bc4c565402accd5e369472e64a8e6 100644 (file)
@@ -8,6 +8,7 @@ Mesh :: struct (T: type_expr) {
 
     vertex_count: u32;
     index_count: u32;
+    primitive: GLuint = GL_TRIANGLES;
 }
 
 mesh_make :: (verticies: [] $T, indicies: [] u32) -> ^Mesh(T) {
@@ -52,12 +53,18 @@ mesh_make :: (verticies: [] $T, indicies: [] u32) -> ^Mesh(T) {
     return mesh;
 }
 
+mesh_update_verticies :: (use mesh: ^Mesh($T), verticies: [] T) {
+    glBindBuffer(GL_ARRAY_BUFFER, vertex_handle);
+    glBufferSubData(GL_ARRAY_BUFFER, 0, verticies.count * sizeof T, verticies.data);
+    glBindBuffer(GL_ARRAY_BUFFER, -1);
+}
+
 mesh_draw :: (use mesh: ^Mesh($T)) {
     glBindVertexArray(handle);
     if index_count > 0 {
-        glDrawElements(GL_TRIANGLES, index_count, GL_UNSIGNED_INT, ~~0);
+        glDrawElements(primitive, index_count, GL_UNSIGNED_INT, ~~0);
     } else {
-        glDrawArrays(GL_TRIANGLES, 0, vertex_count);
+        glDrawArrays(primitive, 0, vertex_count);
     }
     glBindVertexArray(-1);
 }
@@ -71,41 +78,3 @@ mesh_free :: (mesh: ^Mesh($T)) {
 
 
 
-#if false {
-// Old code for making a single cube
-mesh_make_cube :: (x, y, z: f32) -> ^Mesh {
-    data := random.between(0x0000, 0xffff);
-
-    vertex_data := memory.make_slice(Vertex, 8);
-    vertex_data[0] = .{x+0,y+0,z+0,data};
-    vertex_data[1] = .{x+0,y+1,z+0,data};
-    vertex_data[2] = .{x+1,y+1,z+0,data};
-    vertex_data[3] = .{x+1,y+0,z+0,data};
-    vertex_data[4] = .{x+0,y+0,z+1,data};
-    vertex_data[5] = .{x+0,y+1,z+1,data};
-    vertex_data[6] = .{x+1,y+1,z+1,data};
-    vertex_data[7] = .{x+1,y+0,z+1,data};
-
-    #persist index_data := u32.[
-        0, 2, 1,  // back
-        0, 3, 2,
-
-        3, 6, 2, // right side
-        3, 7, 6,
-
-        5, 0, 1, // left side
-        5, 4, 0,
-
-        4, 5, 6, // front
-        4, 6, 7,
-
-        5, 1, 2, // top
-        5, 2, 6,
-
-        0, 4, 7, // bottom
-        0, 7, 3,
-    ];
-
-    return mesh_make(vertex_data, index_data);
-}
-}
\ No newline at end of file
index e6bf75d1dd8b41b018a0c227c3d504a5dd905841..bf6b7d7c2aac58029999474b430091b66c73a53d 100644 (file)
@@ -9,6 +9,8 @@ Vector3i :: struct {
 Vector3 :: struct {
     x, y, z: f32;
 
+    mag :: macro (v: Vector3) => math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
+
     neg :: macro (v: Vector3) => Vector3.{ -v.x, -v.y, -v.z };
 
     dot :: macro (v1, v2: Vector3) -> f32 {
index c740e6d4fc5aa3d25b95e5c4854f3cf625b8a987..43f2c53c606a8bbc0233e9b018845ba9e8b1296a 100644 (file)
@@ -28,9 +28,103 @@ world_make :: (allocator := context.allocator) -> ^World {
     return world;
 }
 
+world_get_block :: (world: ^World, x, y, z: i32) -> Block {
+    @WorldCenter
+    cx := x / Chunk_Size;
+    cy := y / Chunk_Size;
+    cz := z / Chunk_Size;
+    if cx < 0 || cx >= world.chunk_dist do return Block_Empty;
+    if cy < 0 || cy >= world.chunk_dist do return Block_Empty;
+    if cz < 0 || cz >= world.chunk_dist do return Block_Empty;
+    chunk := world.chunks[cx * world.chunk_dist * world.chunk_dist + cy * world.chunk_dist + cz];
+    return chunk_get(chunk, x % Chunk_Size, y % Chunk_Size, z % Chunk_Size);
+}
+
+world_set_block :: (world: ^World, x, y, z: i32, block: Block) {
+    @WorldCenter
+    cx := x / Chunk_Size;
+    cy := y / Chunk_Size;
+    cz := z / Chunk_Size;
+    if cx < 0 || cx >= world.chunk_dist do return;
+    if cy < 0 || cy >= world.chunk_dist do return;
+    if cz < 0 || cz >= world.chunk_dist do return;
+    chunk := world.chunks[cx * world.chunk_dist * world.chunk_dist + cy * world.chunk_dist + cz];
+    chunk_set(chunk, x % Chunk_Size, y % Chunk_Size, z % Chunk_Size, block);
+}
+
+Ray :: struct {
+    origin, direction: Vector3;
+}
+
+world_ray_cast :: (ray: Ray, max_distance: f32, ctx: $T, is_solid: (T, Vector3i) -> bool, out_block: ^Vector3i, out_dir: ^Vector3i) -> bool {
+    pos := Vector3i.{ ~~math.floor(ray.origin.x), ~~math.floor(ray.origin.y), ~~math.floor(ray.origin.z) };
+
+    dir := ray.direction;
+    step := Vector3i.{ ~~math.sign(dir.x), ~~math.sign(dir.y), ~~math.sign(dir.z) };
+
+    tmax: Vector3;
+    tmax.x = ((math.ceil(ray.origin.x) - ray.origin.x) if dir.x > 0 else (ray.origin.x - math.floor(ray.origin.x))) / math.abs(dir.x);
+    tmax.y = ((math.ceil(ray.origin.y) - ray.origin.y) if dir.y > 0 else (ray.origin.y - math.floor(ray.origin.y))) / math.abs(dir.y);
+    tmax.z = ((math.ceil(ray.origin.z) - ray.origin.z) if dir.z > 0 else (ray.origin.z - math.floor(ray.origin.z))) / math.abs(dir.z);
+
+    tdelta := Vector3.{ ~~step.x / dir.x, ~~step.y / dir.y, ~~step.z / dir.z };
+    radius := max_distance / Vector3.mag(dir);
+
+    while true {
+        if is_solid(ctx, pos) {
+            *out_block = pos;
+            return true;
+        }
+
+        if tmax.x < tmax.y {
+            if tmax.x < tmax.z {
+                if tmax.x > radius {
+                    break;
+                }
+
+                pos.x += step.x;
+                tmax.x += tdelta.x;
+                *out_dir = Vector3i.{ -step.x, 0, 0 };
+
+            } else {
+                if tmax.z > radius {
+                    break;
+                }
+
+                pos.z += step.z;
+                tmax.z += tdelta.z;
+                *out_dir = Vector3i.{ 0, 0, -step.z };
+            }
+
+        } else {
+            if tmax.y < tmax.z {
+                if tmax.y > radius {
+                    break;
+                }
+
+                pos.y += step.y;
+                tmax.y += tdelta.y;
+                *out_dir = Vector3i.{ 0, -step.y, 0 };
+
+            } else {
+                if tmax.z > radius {
+                    break;
+                }
+
+                pos.z += step.z;
+                tmax.z += tdelta.z;
+                *out_dir = Vector3i.{ 0, 0, -step.z };
+            }
+        }
+    }
+
+    return false;
+}
+
+
 world_draw :: (use world: ^World) {
     for chunks {
         chunk_build_mesh(it);
         chunk_draw(it);
     }
-}
\ No newline at end of file
+}