From: Brendan Hansen Date: Wed, 22 Dec 2021 18:52:08 +0000 (-0600) Subject: added ray casting and block destruction X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=b524cbc068183960c4949bfab98e8462f6c78665;p=voxel-shooter.git added ray casting and block destruction --- diff --git a/.gitignore b/.gitignore index 6cbd667..d875df8 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/run_tree/lib/onyx_opengles.so b/run_tree/lib/onyx_opengles.so index 393e39f..cc89c8c 100755 Binary files a/run_tree/lib/onyx_opengles.so and b/run_tree/lib/onyx_opengles.so differ diff --git a/run_tree/run.sh b/run_tree/run.sh index c58eb2e..b4ccbc5 100755 --- a/run_tree/run.sh +++ b/run_tree/run.sh @@ -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 diff --git a/src/chunk.onyx b/src/chunk.onyx index 9bd7443..12395d5 100644 --- a/src/chunk.onyx +++ b/src/chunk.onyx @@ -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); +} + + diff --git a/src/main.onyx b/src/main.onyx index 9502bca..86ccd1d 100644 --- a/src/main.onyx +++ b/src/main.onyx @@ -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); diff --git a/src/mesh.onyx b/src/mesh.onyx index 42859a9..3c969c7 100644 --- a/src/mesh.onyx +++ b/src/mesh.onyx @@ -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 diff --git a/src/vecmath.onyx b/src/vecmath.onyx index e6bf75d..bf6b7d7 100644 --- a/src/vecmath.onyx +++ b/src/vecmath.onyx @@ -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 { diff --git a/src/world.onyx b/src/world.onyx index c740e6d..43f2c53 100644 --- a/src/world.onyx +++ b/src/world.onyx @@ -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 +}