From: Brendan Hansen Date: Tue, 22 Feb 2022 22:54:09 +0000 (-0600) Subject: organized project X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=f813fd6b7fd73a0ba82b888d68e54889db9df3fc;p=voxel-shooter.git organized project --- diff --git a/src/build.onyx b/src/build.onyx index 2549a3e..137d631 100644 --- a/src/build.onyx +++ b/src/build.onyx @@ -7,18 +7,8 @@ #library_path "./lib" // Primary source files -#load "camera" -#load "chunk" #load "config" -#load "input" -#load "logger" #load "main" -#load "physics" -#load "player" -#load "utils" -#load "vecmath" -#load "world" -#load "worldgen" #load "gfx/canvas" #load "gfx/font" @@ -28,6 +18,18 @@ #load "gfx/texture" #load "gfx/ui" +#load "utils/camera" +#load "utils/input" +#load "utils/logger" +#load "utils/utils" +#load "utils/vecmath" + +#load "world/chunk" +#load "world/physics" +#load "world/player" +#load "world/world" +#load "world/worldgen" + // Onyx library code #load "stb_truetype" #load "stb_image" diff --git a/src/camera.onyx b/src/camera.onyx deleted file mode 100644 index c8bc72d..0000000 --- a/src/camera.onyx +++ /dev/null @@ -1,91 +0,0 @@ -use package core - -@GlobalVariable -camera: Camera; - -Camera :: struct { - fov: f32; // Radians - window_width, window_height: f32; - - z_near := 0.01f; - z_far := 300.0f; - - x_rot, y_rot: f32; - position: Vector3; -} - -camera_set_fov :: (cam: ^Camera, fov: f32) { - cam.fov = fov * math.PI / 180; - cam.fov = math.clamp(cam.fov, 0, math.PI - 0.001f); -} - -camera_set_window_size :: (use cam: ^Camera, ww, wh: f32) { - window_width = ww; - window_height = wh; -} - -camera_get_view_matrix :: (use cam: ^Camera, out: [16] f32) { - ar := window_width / window_height; - z_range := z_far - z_near; - tan_half_fov := math.sin(fov / 2) / math.cos(fov / 2); - - out[0] = 1 / (tan_half_fov * ar); - out[5] = 1 / tan_half_fov; - out[10] = -z_far / z_range; - out[11] = -1; - out[14] = -z_far*z_near/z_range; -} - -camera_get_forward :: (use cam: ^Camera) -> Vector3 { - return Vector3.{ - math.cos(x_rot) * math.sin(y_rot), - -math.sin(x_rot), - math.cos(y_rot) * math.cos(x_rot) - } |> Vector3.norm(); -} - -camera_get_world_matrix :: (use cam: ^Camera, out: [16] f32) { - forward := Vector3.{ - math.cos(x_rot) * math.sin(y_rot), - -math.sin(x_rot), - math.cos(y_rot) * math.cos(x_rot) - } |> Vector3.norm(); - - side := Vector3.cross(forward, .{0,1,0}) |> Vector3.norm(); - new_up := Vector3.cross(side, forward); - - neg_pos := Vector3.neg(position); - neg_forward := Vector3.neg(forward); - - out[0] = side.x; - out[1] = new_up.x; - out[2] = neg_forward.x; - out[4] = side.y; - out[5] = new_up.y; - out[6] = neg_forward.y; - out[8] = side.z; - out[9] = new_up.z; - out[10] = neg_forward.z; - out[12] = Vector3.dot(side, neg_pos); - out[13] = Vector3.dot(new_up, neg_pos); - out[14] = Vector3.dot(neg_forward, neg_pos); - out[15] = 1; -} - -camera_get_window_matrix :: (use cam: ^Camera, out: [16] f32) { - top := 0.0f; - left := 0.0f; - right := cast(f32) window_width; - bottom := cast(f32) window_height; - far := 10.0f; - near := 0f; - - out[0] = 2 / (right - left); - out[5] = 2 / (top - bottom); - out[10] = -2 / (far - near); - out[12] = -(right + left) / (right - left); - out[13] = -(top + bottom) / (top - bottom); - out[14] = -(far + near) / (far - near); - out[15] = 1; -} - diff --git a/src/chunk.onyx b/src/chunk.onyx deleted file mode 100644 index c076957..0000000 --- a/src/chunk.onyx +++ /dev/null @@ -1,254 +0,0 @@ -use package core -use package opengles - -Block :: #distinct u32; -#operator == macro (b1, b2: Block) => cast(u32) b1 == cast(u32) b2; -#operator != macro (b1, b2: Block) => cast(u32) b1 != cast(u32) b2; - -Block_Empty :: cast(Block) 0; - -Block_Options :: struct { - texture_enabled := true; -} - -block_make :: (red, green, blue: f32, brightness: f32, options := Block_Options.{}) -> Block { - r := cast(u32) (red * 15.0f); - g := cast(u32) (green * 15.0f); - b := cast(u32) (blue * 15.0f); - i := cast(u32) (brightness * 15.0f); - - tex := 1 if options.texture_enabled else 0; - - return ~~((tex << 16) | (i << 12) | (b << 8) | (g << 4) | r); -} - -Chunk_Vertex :: struct { - position : Vector3; - texture : Vector2; - - // This field is broken up by bit, but Onyx does not (nor ever will) support bit-fields. - // - // 4-bits (0..3) - red color - // 4-bits (4..7) - green color - // 4-bits (8..11) - blue color - // 4-bits (12..15) - intensity - data: u32; -} - -Chunk_Size :: 32 - -Chunk :: struct { - coord: Vector3i; - position: Vector3; - blocks: [] Block; - - mesh: ^Mesh(Chunk_Vertex) = null; - mesh_dirty := false; -} - -chunk_make :: (x, y, z: i32, allocator := context.allocator) -> ^Chunk { - chunk := new(Chunk, allocator); - chunk.coord = .{ x, y, z }; - chunk.position = .{ - ~~(x * Chunk_Size), - ~~(y * Chunk_Size), - ~~(z * Chunk_Size), - }; - memory.alloc_slice(^chunk.blocks, Chunk_Size * Chunk_Size * Chunk_Size); - memory.fill_slice(chunk.blocks, Block_Empty); - return chunk; -} - -chunk_free :: (chunk: ^Chunk) { - chunk_destroy_mesh(chunk); - memory.free_slice(^chunk.blocks); - cfree(chunk); -} - -#local in_chunk_bounds :: macro (x, y, z: i32) -> bool { - if x < 0 do return false; - if x >= Chunk_Size do return false; - if y < 0 do return false; - if y >= Chunk_Size do return false; - if z < 0 do return false; - if z >= Chunk_Size do return false; - return true; -} - -chunk_coords_to_world :: (use chunk: ^Chunk, x, y, z: i32) -> Vector3i { - return coord * Chunk_Size + .{x,y,z}; -} - -chunk_set :: (use chunk: ^Chunk, x, y, z: i32, block: Block) { - if !in_chunk_bounds(x, y, z) do return; - blocks[x * Chunk_Size * Chunk_Size + y * Chunk_Size + z] = block; - mesh_dirty = true; -} - -chunk_get :: (use chunk: ^Chunk, x, y, z: i32) -> Block { - if !in_chunk_bounds(x, y, z) do return Block_Empty; - return blocks[x * Chunk_Size * Chunk_Size + y * Chunk_Size + z]; -} - -#local block_neighbors := Vector3i.[ - Vector3i.{ 0, 0, -1 }, - Vector3i.{ 1, 0, 0 }, - Vector3i.{ -1, 0, 0 }, - Vector3i.{ 0, 0, 1 }, - Vector3i.{ 0, 1, 0 }, - Vector3i.{ 0, -1, 0 }, -]; - -#local block_verticies := Vector3.[ - Vector3.{ 0, 0, 0 }, - Vector3.{ 0, 1, 0 }, - Vector3.{ 1, 1, 0 }, - Vector3.{ 1, 0, 0 }, - Vector3.{ 0, 0, 1 }, - Vector3.{ 0, 1, 1 }, - Vector3.{ 1, 1, 1 }, - Vector3.{ 1, 0, 1 }, -]; - -#local block_textures := Vector2.[ - Vector2.{ 0, 0 }, - Vector2.{ 0, 1 }, - Vector2.{ 1, 1 }, - Vector2.{ 1, 0 }, -]; - -#local block_texture_indicies := ([6] u32).[ - u32.[ 0, 2, 1, 0, 3, 2 ], - u32.[ 0, 2, 1, 0, 3, 2 ], - u32.[ 0, 2, 1, 0, 3, 2 ], - u32.[ 0, 1, 2, 0, 2, 3 ], - u32.[ 0, 1, 2, 0, 2, 3 ], - u32.[ 0, 1, 2, 0, 2, 3 ], -]; - -#local block_indicies := ([6] u32).[ - u32.[ 0, 2, 1, 0, 3, 2 ], - u32.[ 3, 6, 2, 3, 7, 6 ], - u32.[ 5, 0, 1, 5, 4, 0 ], - u32.[ 4, 5, 6, 4, 6, 7 ], - u32.[ 5, 1, 2, 5, 2, 6 ], - u32.[ 0, 4, 7, 0, 7, 3 ] -]; - -chunk_build_mesh :: (use chunk: ^Chunk) { - if mesh != null && mesh_dirty == false do return; - if mesh != null { - mesh_free(mesh); - mesh = null; - } - - verticies := array.make(Chunk_Vertex, capacity=512); - defer array.free(^verticies); - - chunk_foreach(chunk) { - if block == Block_Empty do continue; - - for i: 6 { - n := ^block_neighbors[i]; - nx := x + n.x; - ny := y + n.y; - nz := z + n.z; - - if chunk_get(chunk, nx, ny, nz) != Block_Empty do continue; - color := block; - l := ~~((cast(u32) color & 0xf000) >> 12) / 16.0f; - if i != 4 do l *= 0.75; - if i == 5 do l *= 0.75; - color = cast(Block) (((cast(u32) (l * 16.0f)) << 12) | (0xffff0fff & cast(u32) color)); - - indicies := cast([] u32) block_indicies[i]; - tex_indicies := cast([] u32) block_texture_indicies[i]; - for j: 6 { - v := block_verticies[indicies[j]]; - t := block_textures[tex_indicies[j]]; - verticies << .{ .{ ~~x+v.x, ~~y+v.y, ~~z+v.z }, t, ~~color }; - } - } - } - - mesh = mesh_make(verticies, .[]); - mesh_dirty = false; -} - -chunk_destroy_mesh :: (use chunk: ^Chunk) { - if mesh == null do return; - - mesh_free(mesh); - mesh = null; - - mesh_dirty = true; // Just in case this chunk will be reused later. -} - -#local block_texture: Texture; -chunk_draw :: (chunk: ^Chunk) { - if block_texture.filename.data == null { - block_texture', _ := texture_lookup("assets/textures/block.png"); - } - - texture_use(^block_texture); - update_model_matrix(chunk.position); - mesh_draw(chunk.mesh); -} - - -// 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; - } -} - -#local block_highlight: ^Mesh(Chunk_Vertex); -chunk_highlight_block :: (x, y, z: f32) { - data := 0xf000; - - vertex_data := cast(^Chunk_Vertex) alloc.from_stack(sizeof Chunk_Vertex * 8); - vertex_data[0] = .{.{x-0.001,y-0.001,z-0.001},.{0,0},data}; - vertex_data[1] = .{.{x-0.001,y+1.001,z-0.001},.{0,0},data}; - vertex_data[2] = .{.{x+1.001,y+1.001,z-0.001},.{0,0},data}; - vertex_data[3] = .{.{x+1.001,y-0.001,z-0.001},.{0,0},data}; - vertex_data[4] = .{.{x-0.001,y-0.001,z+1.001},.{0,0},data}; - vertex_data[5] = .{.{x-0.001,y+1.001,z+1.001},.{0,0},data}; - vertex_data[6] = .{.{x+1.001,y+1.001,z+1.001},.{0,0},data}; - vertex_data[7] = .{.{x+1.001,y-0.001,z+1.001},.{0,0},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/input.onyx b/src/input.onyx deleted file mode 100644 index 2a5f217..0000000 --- a/src/input.onyx +++ /dev/null @@ -1,101 +0,0 @@ -use package core -use package glfw3 - -#local { - keys_this_frame : [..] u32 // Keys currently being pressed this frame - keys_pulse_frame : [..] u32 // Keys pressed during this frame, only set once per keypress - keys_last_frame : [..] u32 // Keys being pressed in the last frame - - buttons_this_frame: [8] bool // Mouse buttons being pressed this frame - buttons_last_frame: [8] bool // Mouse buttons being pressed last frame -} - -input_update :: () { - glfwGetCursorPos(window, ^mouse_x, ^mouse_y); - - array.clear(^keys_pulse_frame); - for keys_this_frame { - if !array.contains(keys_last_frame, it) { - keys_pulse_frame << it; - } - } -} - -input_post_update :: () { - array.clear(^keys_pulse_frame); - array.clear(^keys_last_frame); - for keys_this_frame do keys_last_frame << it; - - for 8 do buttons_last_frame[it] = buttons_this_frame[it]; - - last_mouse_x = mouse_x; - last_mouse_y = mouse_y; -} - -input_get_keys_this_frame :: () -> [] u32 { - return keys_pulse_frame; -} - -is_key_down :: (key) => array.contains(keys_this_frame, key); -is_key_just_down :: (key) => array.contains(keys_this_frame, key) && !array.contains(keys_last_frame, key); -is_key_just_up :: (key) => !array.contains(keys_this_frame, key) && array.contains(keys_last_frame, key); - -is_button_down :: (button) => buttons_this_frame[button]; -is_button_just_down :: (button) => buttons_this_frame[button] && !buttons_last_frame[button]; -is_button_just_up :: (button) => !buttons_this_frame[button] && buttons_last_frame[button]; - -#local { - last_mouse_x: f64; - last_mouse_y: f64; - - mouse_x: f64; - mouse_y: f64; -} - -mouse_get_delta :: () -> (f64, f64) { - return mouse_x - last_mouse_x, mouse_y - last_mouse_y; -} - -mouse_get_delta_vector :: () -> Vector2 { - dmx, dmy := mouse_get_delta(); - return .{ ~~dmx, ~~dmy }; -} - -mouse_get_position :: () -> (f64, f64) { - return mouse_x, mouse_y; -} - -mouse_get_position_vector :: () -> Vector2 { - return .{ ~~mouse_x, ~~mouse_y }; -} - - -input_bind_glfw_events :: (window: GLFWwindow_p) { - glfwSetKeyCallback(window, INPUT_KEY_EVENT); - glfwSetMouseButtonCallback(window, INPUT_BUTTON_EVENT); -} - -#local { - INPUT_BUTTON_EVENT :: "__input_button_event" - INPUT_KEY_EVENT :: "__input_key_event" -} - -#export INPUT_BUTTON_EVENT (window: GLFWwindow_p, button, action, mod: u32) { - if action == GLFW_PRESS { - buttons_this_frame[button] = true; - } - - if action == GLFW_RELEASE { - buttons_this_frame[button] = false; - } -} - -#export INPUT_KEY_EVENT (window: GLFWwindow_p, key, scancode, action, mod: u32) { - if action == GLFW_PRESS { - keys_this_frame << key; - } - - if action == GLFW_RELEASE { - array.remove(^keys_this_frame, key); - } -} diff --git a/src/logger.onyx b/src/logger.onyx deleted file mode 100644 index 529ddee..0000000 --- a/src/logger.onyx +++ /dev/null @@ -1,35 +0,0 @@ -// -// This may become an in-game or external file logger in the future, -// but for now this is just for logging to the command line. - -use package core - -Log_Level :: enum { - Debug; - Info; - Warning; - Error; - Critical; -} - -debug_log :: (level: Log_Level, format: str, args: ..any) { - debug_log_va(level, format, ~~args); -} - -debug_log_va :: (level: Log_Level, format: str, args: [] any) { - buf: [2048] u8; - output := conv.format_va(buf, format, args); - printf("[{}] {}\n", level_string(level), output); -} - -#local level_string :: (level: Log_Level) => { - switch level { - case .Debug do return "DEBUG"; - case .Info do return "INFO "; - case .Warning do return "WARN "; - case .Error do return "ERROR"; - case .Critical do return "CRIT "; - } - - return " "; -} diff --git a/src/physics.onyx b/src/physics.onyx deleted file mode 100644 index 746e39d..0000000 --- a/src/physics.onyx +++ /dev/null @@ -1,65 +0,0 @@ -use package core - -AABB :: struct { - x0, y0, z0, x1, y1, z1: f32; - - intersects :: (a1, a2: AABB) -> bool { - return a1.x0 < a2.x1 && a1.x1 > a2.x0 - && a1.y0 < a2.y1 && a1.y1 > a2.y0 - && a1.z0 < a2.z1 && a1.z1 > a2.z0; - } - - contains :: (a: AABB, v: Vector3) -> bool { - return a.x0 < v.x && v.x < a.x1 - && a.y0 < v.y && v.y < a.y1 - && a.z0 < v.z && v.z < a.z1; - } -} - -PhysicsBody :: struct { - pos: Vector3; - vel: Vector3; - acc: Vector3; - - on_ground := false; - - get_collision_object: (pos: Vector3) -> AABB; // This will get expanded on when other collision objects are defined -} - -physics_apply_gravity :: (use body: ^PhysicsBody) { - acc.y = -30; -} - -physics_simulate :: (use body: ^PhysicsBody, dt: f32, world: ^World) { - aabb_buffer := (cast(^AABB) alloc.from_stack(sizeof [256] AABB))[0..256]; - - aabbs := world_get_aabbs(world, pos, 3, aabb_buffer); - try_move :: macro (delta: Vector3) -> f32 { - new_pos := body.pos + delta; - body_aabb := get_collision_object(new_pos); - - for^ aabbs { - if AABB.intersects(body_aabb, *it) { - if delta.y < 0 do on_ground = true; - return 0; - } - } - - return 1; - } - - on_ground = false; - move_x := try_move(.{acc.x*0.5*dt*dt + vel.x * dt, 0, 0}); - move_y := try_move(.{0, acc.y*0.5*dt*dt + vel.y * dt, 0}); - move_z := try_move(.{0, 0, acc.z*0.5*dt*dt + vel.z * dt}); - - pos.x += vel.x * dt * move_x; - pos.y += vel.y * dt * move_y; - pos.z += vel.z * dt * move_z; - - vel += acc * dt; - - if on_ground do vel.y = 0; -} - - diff --git a/src/player.onyx b/src/player.onyx deleted file mode 100644 index c10d6d1..0000000 --- a/src/player.onyx +++ /dev/null @@ -1,94 +0,0 @@ -use package core -use package glfw3 - -Player :: struct { - camera: ^Camera; // Should the camera exist on the player? Or should the player just control the camera? - body: PhysicsBody; -} - -player_make :: () -> Player { - player: Player; - - player.camera = null; - player.body = .{ - .{0,48,0}, - .{0,0,0}, - .{0,0,0}, - - get_collision_object = player_get_aabb - }; - - return player; -} - -player_update :: (use player: ^Player, dt: f32) { - speed := 6f; - if is_key_down(GLFW_KEY_LEFT_CONTROL) { - speed = 10; - } - if is_key_down(GLFW_KEY_LEFT_SHIFT) { - speed = 1; - } - - mdx, mdy := mouse_get_delta(); - camera.y_rot += ~~(-mdx / 400); - camera.x_rot += ~~( mdy / 400); - while camera.y_rot >= 2 * math.PI do camera.y_rot -= 2 * math.PI; - while camera.y_rot < 0 do camera.y_rot += 2 * math.PI; - camera.x_rot = math.clamp(camera.x_rot, -math.PI / 2 + 0.01, math.PI / 2 - 0.01); - - forward := camera_get_forward(camera); - facing := forward; - facing.y = 0; - facing = Vector3.norm(facing); - - xz_vel := Vector3.{0,0,0}; - if is_key_down(GLFW_KEY_W) do xz_vel += facing; - if is_key_down(GLFW_KEY_S) do xz_vel -= facing; - if is_key_down(GLFW_KEY_D) do xz_vel += Vector3.norm(Vector3.cross(facing, .{0,1,0})); - if is_key_down(GLFW_KEY_A) do xz_vel -= Vector3.norm(Vector3.cross(facing, .{0,1,0})); - - if Vector3.mag(xz_vel) > 0 do xz_vel = Vector3.norm(xz_vel) * speed; - body.vel.x = xz_vel.x; - body.vel.z = xz_vel.z; - - physics_apply_gravity(^body); - - if is_key_down(GLFW_KEY_SPACE) && body.on_ground { - body.acc.y = 500; - } - - physics_simulate(^body, dt, world); - - // Fix falling off the world - if body.pos.y < -100 do body.pos = .{0,48,0}; - - camera.position = body.pos; - - ray_cast_to_block :: (_, p) => world_get_block(world, p.x, p.y, p.z) != Block_Empty; - - dir: Vector3i; - if !ray_cast(.{ camera.position, forward }, 10, null, ray_cast_to_block, ^selected_block, ^dir) { - selected_block = .{0,0,0}; - } - - if is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) { - world_set_block(world, selected_block.x, selected_block.y, selected_block.z, Block_Empty); - } - - if is_button_just_down(GLFW_MOUSE_BUTTON_RIGHT) { - target := selected_block + dir; - world_set_block(world, target.x, target.y, target.z, block_make(1,0,0,1)); - } -} - -player_get_aabb :: (pos: Vector3) -> AABB { - return .{ - x0 = pos.x - 0.3f, - y0 = pos.y - 1.8f, - z0 = pos.z - 0.3f, - x1 = pos.x + 0.3f, - y1 = pos.y + 0.1f, - z1 = pos.z + 0.3f, - }; -} diff --git a/src/utils.onyx b/src/utils.onyx deleted file mode 100644 index 25a9873..0000000 --- a/src/utils.onyx +++ /dev/null @@ -1,72 +0,0 @@ -use package core - -Ray :: struct { - origin, direction: Vector3; -} - - -ray_cast :: (ray: Ray, max_distance: f32, ctx: rawptr, is_solid: (rawptr, 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 = .{ -step.x, 0, 0 }; - - } else { - if tmax.z > radius { - break; - } - - pos.z += step.z; - tmax.z += tdelta.z; - *out_dir = .{ 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 = .{ 0, -step.y, 0 }; - - } else { - if tmax.z > radius { - break; - } - - pos.z += step.z; - tmax.z += tdelta.z; - *out_dir = .{ 0, 0, -step.z }; - } - } - } - - return false; -} - diff --git a/src/utils/camera.onyx b/src/utils/camera.onyx new file mode 100644 index 0000000..c8bc72d --- /dev/null +++ b/src/utils/camera.onyx @@ -0,0 +1,91 @@ +use package core + +@GlobalVariable +camera: Camera; + +Camera :: struct { + fov: f32; // Radians + window_width, window_height: f32; + + z_near := 0.01f; + z_far := 300.0f; + + x_rot, y_rot: f32; + position: Vector3; +} + +camera_set_fov :: (cam: ^Camera, fov: f32) { + cam.fov = fov * math.PI / 180; + cam.fov = math.clamp(cam.fov, 0, math.PI - 0.001f); +} + +camera_set_window_size :: (use cam: ^Camera, ww, wh: f32) { + window_width = ww; + window_height = wh; +} + +camera_get_view_matrix :: (use cam: ^Camera, out: [16] f32) { + ar := window_width / window_height; + z_range := z_far - z_near; + tan_half_fov := math.sin(fov / 2) / math.cos(fov / 2); + + out[0] = 1 / (tan_half_fov * ar); + out[5] = 1 / tan_half_fov; + out[10] = -z_far / z_range; + out[11] = -1; + out[14] = -z_far*z_near/z_range; +} + +camera_get_forward :: (use cam: ^Camera) -> Vector3 { + return Vector3.{ + math.cos(x_rot) * math.sin(y_rot), + -math.sin(x_rot), + math.cos(y_rot) * math.cos(x_rot) + } |> Vector3.norm(); +} + +camera_get_world_matrix :: (use cam: ^Camera, out: [16] f32) { + forward := Vector3.{ + math.cos(x_rot) * math.sin(y_rot), + -math.sin(x_rot), + math.cos(y_rot) * math.cos(x_rot) + } |> Vector3.norm(); + + side := Vector3.cross(forward, .{0,1,0}) |> Vector3.norm(); + new_up := Vector3.cross(side, forward); + + neg_pos := Vector3.neg(position); + neg_forward := Vector3.neg(forward); + + out[0] = side.x; + out[1] = new_up.x; + out[2] = neg_forward.x; + out[4] = side.y; + out[5] = new_up.y; + out[6] = neg_forward.y; + out[8] = side.z; + out[9] = new_up.z; + out[10] = neg_forward.z; + out[12] = Vector3.dot(side, neg_pos); + out[13] = Vector3.dot(new_up, neg_pos); + out[14] = Vector3.dot(neg_forward, neg_pos); + out[15] = 1; +} + +camera_get_window_matrix :: (use cam: ^Camera, out: [16] f32) { + top := 0.0f; + left := 0.0f; + right := cast(f32) window_width; + bottom := cast(f32) window_height; + far := 10.0f; + near := 0f; + + out[0] = 2 / (right - left); + out[5] = 2 / (top - bottom); + out[10] = -2 / (far - near); + out[12] = -(right + left) / (right - left); + out[13] = -(top + bottom) / (top - bottom); + out[14] = -(far + near) / (far - near); + out[15] = 1; +} + diff --git a/src/utils/input.onyx b/src/utils/input.onyx new file mode 100644 index 0000000..2a5f217 --- /dev/null +++ b/src/utils/input.onyx @@ -0,0 +1,101 @@ +use package core +use package glfw3 + +#local { + keys_this_frame : [..] u32 // Keys currently being pressed this frame + keys_pulse_frame : [..] u32 // Keys pressed during this frame, only set once per keypress + keys_last_frame : [..] u32 // Keys being pressed in the last frame + + buttons_this_frame: [8] bool // Mouse buttons being pressed this frame + buttons_last_frame: [8] bool // Mouse buttons being pressed last frame +} + +input_update :: () { + glfwGetCursorPos(window, ^mouse_x, ^mouse_y); + + array.clear(^keys_pulse_frame); + for keys_this_frame { + if !array.contains(keys_last_frame, it) { + keys_pulse_frame << it; + } + } +} + +input_post_update :: () { + array.clear(^keys_pulse_frame); + array.clear(^keys_last_frame); + for keys_this_frame do keys_last_frame << it; + + for 8 do buttons_last_frame[it] = buttons_this_frame[it]; + + last_mouse_x = mouse_x; + last_mouse_y = mouse_y; +} + +input_get_keys_this_frame :: () -> [] u32 { + return keys_pulse_frame; +} + +is_key_down :: (key) => array.contains(keys_this_frame, key); +is_key_just_down :: (key) => array.contains(keys_this_frame, key) && !array.contains(keys_last_frame, key); +is_key_just_up :: (key) => !array.contains(keys_this_frame, key) && array.contains(keys_last_frame, key); + +is_button_down :: (button) => buttons_this_frame[button]; +is_button_just_down :: (button) => buttons_this_frame[button] && !buttons_last_frame[button]; +is_button_just_up :: (button) => !buttons_this_frame[button] && buttons_last_frame[button]; + +#local { + last_mouse_x: f64; + last_mouse_y: f64; + + mouse_x: f64; + mouse_y: f64; +} + +mouse_get_delta :: () -> (f64, f64) { + return mouse_x - last_mouse_x, mouse_y - last_mouse_y; +} + +mouse_get_delta_vector :: () -> Vector2 { + dmx, dmy := mouse_get_delta(); + return .{ ~~dmx, ~~dmy }; +} + +mouse_get_position :: () -> (f64, f64) { + return mouse_x, mouse_y; +} + +mouse_get_position_vector :: () -> Vector2 { + return .{ ~~mouse_x, ~~mouse_y }; +} + + +input_bind_glfw_events :: (window: GLFWwindow_p) { + glfwSetKeyCallback(window, INPUT_KEY_EVENT); + glfwSetMouseButtonCallback(window, INPUT_BUTTON_EVENT); +} + +#local { + INPUT_BUTTON_EVENT :: "__input_button_event" + INPUT_KEY_EVENT :: "__input_key_event" +} + +#export INPUT_BUTTON_EVENT (window: GLFWwindow_p, button, action, mod: u32) { + if action == GLFW_PRESS { + buttons_this_frame[button] = true; + } + + if action == GLFW_RELEASE { + buttons_this_frame[button] = false; + } +} + +#export INPUT_KEY_EVENT (window: GLFWwindow_p, key, scancode, action, mod: u32) { + if action == GLFW_PRESS { + keys_this_frame << key; + } + + if action == GLFW_RELEASE { + array.remove(^keys_this_frame, key); + } +} diff --git a/src/utils/logger.onyx b/src/utils/logger.onyx new file mode 100644 index 0000000..529ddee --- /dev/null +++ b/src/utils/logger.onyx @@ -0,0 +1,35 @@ +// +// This may become an in-game or external file logger in the future, +// but for now this is just for logging to the command line. + +use package core + +Log_Level :: enum { + Debug; + Info; + Warning; + Error; + Critical; +} + +debug_log :: (level: Log_Level, format: str, args: ..any) { + debug_log_va(level, format, ~~args); +} + +debug_log_va :: (level: Log_Level, format: str, args: [] any) { + buf: [2048] u8; + output := conv.format_va(buf, format, args); + printf("[{}] {}\n", level_string(level), output); +} + +#local level_string :: (level: Log_Level) => { + switch level { + case .Debug do return "DEBUG"; + case .Info do return "INFO "; + case .Warning do return "WARN "; + case .Error do return "ERROR"; + case .Critical do return "CRIT "; + } + + return " "; +} diff --git a/src/utils/utils.onyx b/src/utils/utils.onyx new file mode 100644 index 0000000..25a9873 --- /dev/null +++ b/src/utils/utils.onyx @@ -0,0 +1,72 @@ +use package core + +Ray :: struct { + origin, direction: Vector3; +} + + +ray_cast :: (ray: Ray, max_distance: f32, ctx: rawptr, is_solid: (rawptr, 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 = .{ -step.x, 0, 0 }; + + } else { + if tmax.z > radius { + break; + } + + pos.z += step.z; + tmax.z += tdelta.z; + *out_dir = .{ 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 = .{ 0, -step.y, 0 }; + + } else { + if tmax.z > radius { + break; + } + + pos.z += step.z; + tmax.z += tdelta.z; + *out_dir = .{ 0, 0, -step.z }; + } + } + } + + return false; +} + diff --git a/src/utils/vecmath.onyx b/src/utils/vecmath.onyx new file mode 100644 index 0000000..eb6f639 --- /dev/null +++ b/src/utils/vecmath.onyx @@ -0,0 +1,71 @@ +Vector2 :: struct [conv.Custom_Format.{format_vector2}] { + x, y: f32; +} + +Vector3i :: struct [conv.Custom_Format.{format_vector3i}] { + x, y, z: i32; +} + +Vector3 :: struct [conv.Custom_Format.{format_vector3}] { + 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 { + return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; + } + + norm :: macro (v: Vector3) -> Vector3 { + l := math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); + return .{ v.x / l, v.y / l, v.z / l }; + } + + cross :: macro (v1, v2: Vector3) -> Vector3 { + return .{ + v1.y * v2.z - v1.z * v2.y, + v1.z * v2.x - v1.x * v2.z, + v1.x * v2.y - v1.y * v2.x, + }; + } + + clamp :: macro (v: Vector3, min: Vector3, max: Vector3) -> Vector3 { + return .{ + math.clamp(v.x, min.x, max.x), + math.clamp(v.y, min.y, max.y), + math.clamp(v.z, min.z, max.z), + }; + } +} + +#operator + macro (v1, v2: Vector2) => (typeof v1).{ v1.x + v2.x, v1.y + v2.y }; +#operator - macro (v1, v2: Vector2) => Vector2.{ v1.x - v2.x, v1.y - v2.y }; +#operator * macro (v: Vector2, s: f32) => Vector2.{ v.x * s, v.y * s }; +#operator == macro (v1, v2: Vector2) => v1.x == v2.x && v1.y == v2.y; + +#operator + macro (v1, v2: Vector3) => Vector3.{ v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; +#operator - macro (v1, v2: Vector3) => Vector3.{ v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; +#operator * macro (v: Vector3, s: f32) => Vector3.{ v.x * s, v.y * s, v.z * s }; +#operator == macro (v1, v2: Vector3) => v1.x == v2.x && v1.y == v2.y && v1.z == v2.z; + +#operator + macro (v1, v2: Vector3i) => Vector3i.{ v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; +#operator - macro (v1, v2: Vector3i) => Vector3i.{ v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; +#operator * macro (v: Vector3i, s: i32) => Vector3i.{ v.x * s, v.y * s, v.z * s }; +#operator == macro (v1, v2: Vector3i) => v1.x == v2.x && v1.y == v2.y && v1.z == v2.z; + +#local { + conv :: package core.conv + + format_vector2 :: (output: ^conv.Format_Output, format: ^conv.Format, v: ^Vector2) { + conv.format(output, "({}, {})", v.x, v.y); + } + + format_vector3 :: (output: ^conv.Format_Output, format: ^conv.Format, v: ^Vector3) { + conv.format(output, "({}, {}, {})", v.x, v.y, v.z); + } + + format_vector3i :: (output: ^conv.Format_Output, format: ^conv.Format, v: ^Vector3i) { + conv.format(output, "({}, {}, {})", v.x, v.y, v.z); + } +} diff --git a/src/vecmath.onyx b/src/vecmath.onyx deleted file mode 100644 index eb6f639..0000000 --- a/src/vecmath.onyx +++ /dev/null @@ -1,71 +0,0 @@ -Vector2 :: struct [conv.Custom_Format.{format_vector2}] { - x, y: f32; -} - -Vector3i :: struct [conv.Custom_Format.{format_vector3i}] { - x, y, z: i32; -} - -Vector3 :: struct [conv.Custom_Format.{format_vector3}] { - 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 { - return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; - } - - norm :: macro (v: Vector3) -> Vector3 { - l := math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z); - return .{ v.x / l, v.y / l, v.z / l }; - } - - cross :: macro (v1, v2: Vector3) -> Vector3 { - return .{ - v1.y * v2.z - v1.z * v2.y, - v1.z * v2.x - v1.x * v2.z, - v1.x * v2.y - v1.y * v2.x, - }; - } - - clamp :: macro (v: Vector3, min: Vector3, max: Vector3) -> Vector3 { - return .{ - math.clamp(v.x, min.x, max.x), - math.clamp(v.y, min.y, max.y), - math.clamp(v.z, min.z, max.z), - }; - } -} - -#operator + macro (v1, v2: Vector2) => (typeof v1).{ v1.x + v2.x, v1.y + v2.y }; -#operator - macro (v1, v2: Vector2) => Vector2.{ v1.x - v2.x, v1.y - v2.y }; -#operator * macro (v: Vector2, s: f32) => Vector2.{ v.x * s, v.y * s }; -#operator == macro (v1, v2: Vector2) => v1.x == v2.x && v1.y == v2.y; - -#operator + macro (v1, v2: Vector3) => Vector3.{ v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; -#operator - macro (v1, v2: Vector3) => Vector3.{ v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; -#operator * macro (v: Vector3, s: f32) => Vector3.{ v.x * s, v.y * s, v.z * s }; -#operator == macro (v1, v2: Vector3) => v1.x == v2.x && v1.y == v2.y && v1.z == v2.z; - -#operator + macro (v1, v2: Vector3i) => Vector3i.{ v1.x + v2.x, v1.y + v2.y, v1.z + v2.z }; -#operator - macro (v1, v2: Vector3i) => Vector3i.{ v1.x - v2.x, v1.y - v2.y, v1.z - v2.z }; -#operator * macro (v: Vector3i, s: i32) => Vector3i.{ v.x * s, v.y * s, v.z * s }; -#operator == macro (v1, v2: Vector3i) => v1.x == v2.x && v1.y == v2.y && v1.z == v2.z; - -#local { - conv :: package core.conv - - format_vector2 :: (output: ^conv.Format_Output, format: ^conv.Format, v: ^Vector2) { - conv.format(output, "({}, {})", v.x, v.y); - } - - format_vector3 :: (output: ^conv.Format_Output, format: ^conv.Format, v: ^Vector3) { - conv.format(output, "({}, {}, {})", v.x, v.y, v.z); - } - - format_vector3i :: (output: ^conv.Format_Output, format: ^conv.Format, v: ^Vector3i) { - conv.format(output, "({}, {}, {})", v.x, v.y, v.z); - } -} diff --git a/src/world.onyx b/src/world.onyx deleted file mode 100644 index 397b380..0000000 --- a/src/world.onyx +++ /dev/null @@ -1,207 +0,0 @@ - -use package core - -World :: struct { - chunk_dist: i32; - chunks: [] ^Chunk; - - center_chunk: Vector3i; - - chunks_to_load: [..] Vector3i; -} - -world_make :: (allocator := context.allocator) -> ^World { - world := new(World, allocator); - world.chunk_dist = 2; - world.center_chunk = .{0,0,0}; - - sl := world.chunk_dist * 2 + 1; - memory.alloc_slice(^world.chunks, sl * sl * sl); - memory.fill_slice(world.chunks, null); - - return world; -} - -world_load_chunk :: (world: ^World, chunk_to_load: Vector3i) { - // - // For now, no persistence will happen to the chunk, so generate a completely new chunk. - // - chunk := chunk_make(chunk_to_load.x, chunk_to_load.y, chunk_to_load.z); - generate_chunk(world, chunk); - world_set_chunk(world, chunk_to_load, chunk); -} - -world_unload_chunk :: #match { - (world: ^World, chunk_to_unload: Vector3i) { - chunk := world_get_chunk(world, chunk_to_unload); - world_unload_chunk(world, chunk); - world_set_chunk(world, chunk_to_unload, null); - }, - - (world: ^World, chunk: ^Chunk) { - if chunk != null { - chunk_free(chunk); - } - } -} - -world_get_chunk :: #match { - @CompilerBug // If I write the following function in quick form: - // - // (world: ^World, v: Vector3i) => world_get_chunk(world, v.x, v.y, v.z), - // - // the ',' at the end appears to mean "compound expression", so the following - // function defintion gets lumped as the second return result. Maybe there - // should be a special case for parsing this? I've always hated how parsing - // inside of a #match block works. - // - - (world: ^World, x, y, z: i32) -> ^Chunk { - d := world.chunk_dist; - sl := d * 2 + 1; - - ix := x - world.center_chunk.x + d; - iy := y - world.center_chunk.y + d; - iz := z - world.center_chunk.z + d; - if ix < 0 || ix > 2 * world.chunk_dist do return null; - if iy < 0 || iy > 2 * world.chunk_dist do return null; - if iz < 0 || iz > 2 * world.chunk_dist do return null; - - return world.chunks[ix * sl * sl + iy * sl + iz]; - }, - - (world: ^World, v: Vector3i) => world_get_chunk(world, v.x, v.y, v.z) -} - -world_set_chunk :: #match { - (world: ^World, v: Vector3i, chunk: ^Chunk) -> #auto { return world_set_chunk(world, v.x, v.y, v.z, chunk); }, - - (world: ^World, x, y, z: i32, chunk: ^Chunk) -> bool { - d := world.chunk_dist; - sl := d * 2 + 1; - - ix := x - world.center_chunk.x + d; - iy := y - world.center_chunk.y + d; - iz := z - world.center_chunk.z + d; - if ix < 0 || ix > 2 * world.chunk_dist do return false; - if iy < 0 || iy > 2 * world.chunk_dist do return false; - if iz < 0 || iz > 2 * world.chunk_dist do return false; - - world.chunks[ix * sl * sl + iy * sl + iz] = chunk; - return true; - } -} - -// Getting around the stupidness that is integer arithmetic on modern processors -#local world_coord_to_chunk_and_block :: (world: ^World, x, y, z: i32) -> (chunk: Vector3i, block: Vector3i) { - chunk, block: Vector3i; - chunk.x = x / Chunk_Size; - chunk.y = y / Chunk_Size; - chunk.z = z / Chunk_Size; - block.x = x % Chunk_Size; - block.y = y % Chunk_Size; - block.z = z % Chunk_Size; - - if x < 0 && block.x != 0 { chunk.x -= 1; block.x += Chunk_Size; } - if y < 0 && block.y != 0 { chunk.y -= 1; block.y += Chunk_Size; } - if z < 0 && block.z != 0 { chunk.z -= 1; block.z += Chunk_Size; } - - return chunk, block; -} - -world_position_to_chunk :: (world: ^World, use pos: Vector3) -> Vector3i { - chunk: Vector3i; - chunk.x = ~~math.floor(x / ~~Chunk_Size); - chunk.y = ~~math.floor(y / ~~Chunk_Size); - chunk.z = ~~math.floor(z / ~~Chunk_Size); - return chunk; -} - -world_get_block :: (world: ^World, x, y, z: i32) -> Block { - chunk_pos, block_pos := world_coord_to_chunk_and_block(world, x, y, z); - chunk := world_get_chunk(world, chunk_pos); - if chunk == null do return Block_Empty; - return chunk_get(chunk, block_pos.x, block_pos.y, block_pos.z); -} - -world_set_block :: (world: ^World, x, y, z: i32, block: Block) { - chunk_pos, block_pos := world_coord_to_chunk_and_block(world, x, y, z); - chunk := world_get_chunk(world, chunk_pos); - if chunk == null do return; - chunk_set(chunk, block_pos.x, block_pos.y, block_pos.z, block); -} - -world_move_center :: (use world: ^World, new_center: Vector3i) { - if center_chunk == new_center do return; - - sl := world.chunk_dist * 2 + 1; - current_chunks := memory.copy_slice(world.chunks); - memory.fill_slice(world.chunks, null); - - center_chunk = new_center; - for current_chunks { - if it == null do continue; - if !world_set_chunk(world, it.coord, it) { - world_unload_chunk(world, it); - } - } - - array.clear(^chunks_to_load); - for x: center_chunk.x-world.chunk_dist .. center_chunk.x+world.chunk_dist+1 - do for y: center_chunk.y-world.chunk_dist .. center_chunk.y+world.chunk_dist+1 - do for z: center_chunk.z-world.chunk_dist .. center_chunk.z+world.chunk_dist+1 { - if world_get_chunk(world, x, y, z) == null do world.chunks_to_load << .{x, y, z}; - } - - memory.free_slice(^current_chunks); -} - -world_get_aabbs :: (use world: ^World, center: Vector3, radius: f32, buffer: [] AABB = .[]) -> [] AABB { - is_dynamic := buffer.count == 0; - aabbs: [..] AABB; - buffer_pos := 0; - - pos := Vector3i.{ ~~math.floor(center.x), ~~math.floor(center.y), ~~math.floor(center.z) }; - rad := cast(i32) math.ceil(radius); - - for x: pos.x-rad .. pos.x+rad+1 { - for y: pos.y-rad .. pos.y+rad+1 { - for z: pos.z-rad .. pos.z+rad+1 { - if world_get_block(world, x, y, z) != Block_Empty { - aabb := AABB.{ - ~~x, ~~y, ~~z, - ~~(x+1), ~~(y+1), ~~(z+1) - }; - - if is_dynamic { - aabbs << aabb; - } else { - buffer[buffer_pos] = aabb; - buffer_pos += 1; - if buffer_pos >= buffer.count do break break break; - } - } - } - } - } - - if is_dynamic do return aabbs; - return buffer[0 .. buffer_pos]; -} - -world_update :: (use world: ^World, dt: f32) { - if chunks_to_load.count > 0 { - chunk_to_load := chunks_to_load[0]; - array.delete(^chunks_to_load, 0); - - world_load_chunk(world, chunk_to_load); - } -} - -world_draw :: (use world: ^World) { - for chunks { - if it == null do continue; - chunk_build_mesh(it); - chunk_draw(it); - } -} diff --git a/src/world/chunk.onyx b/src/world/chunk.onyx new file mode 100644 index 0000000..c076957 --- /dev/null +++ b/src/world/chunk.onyx @@ -0,0 +1,254 @@ +use package core +use package opengles + +Block :: #distinct u32; +#operator == macro (b1, b2: Block) => cast(u32) b1 == cast(u32) b2; +#operator != macro (b1, b2: Block) => cast(u32) b1 != cast(u32) b2; + +Block_Empty :: cast(Block) 0; + +Block_Options :: struct { + texture_enabled := true; +} + +block_make :: (red, green, blue: f32, brightness: f32, options := Block_Options.{}) -> Block { + r := cast(u32) (red * 15.0f); + g := cast(u32) (green * 15.0f); + b := cast(u32) (blue * 15.0f); + i := cast(u32) (brightness * 15.0f); + + tex := 1 if options.texture_enabled else 0; + + return ~~((tex << 16) | (i << 12) | (b << 8) | (g << 4) | r); +} + +Chunk_Vertex :: struct { + position : Vector3; + texture : Vector2; + + // This field is broken up by bit, but Onyx does not (nor ever will) support bit-fields. + // + // 4-bits (0..3) - red color + // 4-bits (4..7) - green color + // 4-bits (8..11) - blue color + // 4-bits (12..15) - intensity + data: u32; +} + +Chunk_Size :: 32 + +Chunk :: struct { + coord: Vector3i; + position: Vector3; + blocks: [] Block; + + mesh: ^Mesh(Chunk_Vertex) = null; + mesh_dirty := false; +} + +chunk_make :: (x, y, z: i32, allocator := context.allocator) -> ^Chunk { + chunk := new(Chunk, allocator); + chunk.coord = .{ x, y, z }; + chunk.position = .{ + ~~(x * Chunk_Size), + ~~(y * Chunk_Size), + ~~(z * Chunk_Size), + }; + memory.alloc_slice(^chunk.blocks, Chunk_Size * Chunk_Size * Chunk_Size); + memory.fill_slice(chunk.blocks, Block_Empty); + return chunk; +} + +chunk_free :: (chunk: ^Chunk) { + chunk_destroy_mesh(chunk); + memory.free_slice(^chunk.blocks); + cfree(chunk); +} + +#local in_chunk_bounds :: macro (x, y, z: i32) -> bool { + if x < 0 do return false; + if x >= Chunk_Size do return false; + if y < 0 do return false; + if y >= Chunk_Size do return false; + if z < 0 do return false; + if z >= Chunk_Size do return false; + return true; +} + +chunk_coords_to_world :: (use chunk: ^Chunk, x, y, z: i32) -> Vector3i { + return coord * Chunk_Size + .{x,y,z}; +} + +chunk_set :: (use chunk: ^Chunk, x, y, z: i32, block: Block) { + if !in_chunk_bounds(x, y, z) do return; + blocks[x * Chunk_Size * Chunk_Size + y * Chunk_Size + z] = block; + mesh_dirty = true; +} + +chunk_get :: (use chunk: ^Chunk, x, y, z: i32) -> Block { + if !in_chunk_bounds(x, y, z) do return Block_Empty; + return blocks[x * Chunk_Size * Chunk_Size + y * Chunk_Size + z]; +} + +#local block_neighbors := Vector3i.[ + Vector3i.{ 0, 0, -1 }, + Vector3i.{ 1, 0, 0 }, + Vector3i.{ -1, 0, 0 }, + Vector3i.{ 0, 0, 1 }, + Vector3i.{ 0, 1, 0 }, + Vector3i.{ 0, -1, 0 }, +]; + +#local block_verticies := Vector3.[ + Vector3.{ 0, 0, 0 }, + Vector3.{ 0, 1, 0 }, + Vector3.{ 1, 1, 0 }, + Vector3.{ 1, 0, 0 }, + Vector3.{ 0, 0, 1 }, + Vector3.{ 0, 1, 1 }, + Vector3.{ 1, 1, 1 }, + Vector3.{ 1, 0, 1 }, +]; + +#local block_textures := Vector2.[ + Vector2.{ 0, 0 }, + Vector2.{ 0, 1 }, + Vector2.{ 1, 1 }, + Vector2.{ 1, 0 }, +]; + +#local block_texture_indicies := ([6] u32).[ + u32.[ 0, 2, 1, 0, 3, 2 ], + u32.[ 0, 2, 1, 0, 3, 2 ], + u32.[ 0, 2, 1, 0, 3, 2 ], + u32.[ 0, 1, 2, 0, 2, 3 ], + u32.[ 0, 1, 2, 0, 2, 3 ], + u32.[ 0, 1, 2, 0, 2, 3 ], +]; + +#local block_indicies := ([6] u32).[ + u32.[ 0, 2, 1, 0, 3, 2 ], + u32.[ 3, 6, 2, 3, 7, 6 ], + u32.[ 5, 0, 1, 5, 4, 0 ], + u32.[ 4, 5, 6, 4, 6, 7 ], + u32.[ 5, 1, 2, 5, 2, 6 ], + u32.[ 0, 4, 7, 0, 7, 3 ] +]; + +chunk_build_mesh :: (use chunk: ^Chunk) { + if mesh != null && mesh_dirty == false do return; + if mesh != null { + mesh_free(mesh); + mesh = null; + } + + verticies := array.make(Chunk_Vertex, capacity=512); + defer array.free(^verticies); + + chunk_foreach(chunk) { + if block == Block_Empty do continue; + + for i: 6 { + n := ^block_neighbors[i]; + nx := x + n.x; + ny := y + n.y; + nz := z + n.z; + + if chunk_get(chunk, nx, ny, nz) != Block_Empty do continue; + color := block; + l := ~~((cast(u32) color & 0xf000) >> 12) / 16.0f; + if i != 4 do l *= 0.75; + if i == 5 do l *= 0.75; + color = cast(Block) (((cast(u32) (l * 16.0f)) << 12) | (0xffff0fff & cast(u32) color)); + + indicies := cast([] u32) block_indicies[i]; + tex_indicies := cast([] u32) block_texture_indicies[i]; + for j: 6 { + v := block_verticies[indicies[j]]; + t := block_textures[tex_indicies[j]]; + verticies << .{ .{ ~~x+v.x, ~~y+v.y, ~~z+v.z }, t, ~~color }; + } + } + } + + mesh = mesh_make(verticies, .[]); + mesh_dirty = false; +} + +chunk_destroy_mesh :: (use chunk: ^Chunk) { + if mesh == null do return; + + mesh_free(mesh); + mesh = null; + + mesh_dirty = true; // Just in case this chunk will be reused later. +} + +#local block_texture: Texture; +chunk_draw :: (chunk: ^Chunk) { + if block_texture.filename.data == null { + block_texture', _ := texture_lookup("assets/textures/block.png"); + } + + texture_use(^block_texture); + update_model_matrix(chunk.position); + mesh_draw(chunk.mesh); +} + + +// 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; + } +} + +#local block_highlight: ^Mesh(Chunk_Vertex); +chunk_highlight_block :: (x, y, z: f32) { + data := 0xf000; + + vertex_data := cast(^Chunk_Vertex) alloc.from_stack(sizeof Chunk_Vertex * 8); + vertex_data[0] = .{.{x-0.001,y-0.001,z-0.001},.{0,0},data}; + vertex_data[1] = .{.{x-0.001,y+1.001,z-0.001},.{0,0},data}; + vertex_data[2] = .{.{x+1.001,y+1.001,z-0.001},.{0,0},data}; + vertex_data[3] = .{.{x+1.001,y-0.001,z-0.001},.{0,0},data}; + vertex_data[4] = .{.{x-0.001,y-0.001,z+1.001},.{0,0},data}; + vertex_data[5] = .{.{x-0.001,y+1.001,z+1.001},.{0,0},data}; + vertex_data[6] = .{.{x+1.001,y+1.001,z+1.001},.{0,0},data}; + vertex_data[7] = .{.{x+1.001,y-0.001,z+1.001},.{0,0},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/world/physics.onyx b/src/world/physics.onyx new file mode 100644 index 0000000..746e39d --- /dev/null +++ b/src/world/physics.onyx @@ -0,0 +1,65 @@ +use package core + +AABB :: struct { + x0, y0, z0, x1, y1, z1: f32; + + intersects :: (a1, a2: AABB) -> bool { + return a1.x0 < a2.x1 && a1.x1 > a2.x0 + && a1.y0 < a2.y1 && a1.y1 > a2.y0 + && a1.z0 < a2.z1 && a1.z1 > a2.z0; + } + + contains :: (a: AABB, v: Vector3) -> bool { + return a.x0 < v.x && v.x < a.x1 + && a.y0 < v.y && v.y < a.y1 + && a.z0 < v.z && v.z < a.z1; + } +} + +PhysicsBody :: struct { + pos: Vector3; + vel: Vector3; + acc: Vector3; + + on_ground := false; + + get_collision_object: (pos: Vector3) -> AABB; // This will get expanded on when other collision objects are defined +} + +physics_apply_gravity :: (use body: ^PhysicsBody) { + acc.y = -30; +} + +physics_simulate :: (use body: ^PhysicsBody, dt: f32, world: ^World) { + aabb_buffer := (cast(^AABB) alloc.from_stack(sizeof [256] AABB))[0..256]; + + aabbs := world_get_aabbs(world, pos, 3, aabb_buffer); + try_move :: macro (delta: Vector3) -> f32 { + new_pos := body.pos + delta; + body_aabb := get_collision_object(new_pos); + + for^ aabbs { + if AABB.intersects(body_aabb, *it) { + if delta.y < 0 do on_ground = true; + return 0; + } + } + + return 1; + } + + on_ground = false; + move_x := try_move(.{acc.x*0.5*dt*dt + vel.x * dt, 0, 0}); + move_y := try_move(.{0, acc.y*0.5*dt*dt + vel.y * dt, 0}); + move_z := try_move(.{0, 0, acc.z*0.5*dt*dt + vel.z * dt}); + + pos.x += vel.x * dt * move_x; + pos.y += vel.y * dt * move_y; + pos.z += vel.z * dt * move_z; + + vel += acc * dt; + + if on_ground do vel.y = 0; +} + + diff --git a/src/world/player.onyx b/src/world/player.onyx new file mode 100644 index 0000000..c10d6d1 --- /dev/null +++ b/src/world/player.onyx @@ -0,0 +1,94 @@ +use package core +use package glfw3 + +Player :: struct { + camera: ^Camera; // Should the camera exist on the player? Or should the player just control the camera? + body: PhysicsBody; +} + +player_make :: () -> Player { + player: Player; + + player.camera = null; + player.body = .{ + .{0,48,0}, + .{0,0,0}, + .{0,0,0}, + + get_collision_object = player_get_aabb + }; + + return player; +} + +player_update :: (use player: ^Player, dt: f32) { + speed := 6f; + if is_key_down(GLFW_KEY_LEFT_CONTROL) { + speed = 10; + } + if is_key_down(GLFW_KEY_LEFT_SHIFT) { + speed = 1; + } + + mdx, mdy := mouse_get_delta(); + camera.y_rot += ~~(-mdx / 400); + camera.x_rot += ~~( mdy / 400); + while camera.y_rot >= 2 * math.PI do camera.y_rot -= 2 * math.PI; + while camera.y_rot < 0 do camera.y_rot += 2 * math.PI; + camera.x_rot = math.clamp(camera.x_rot, -math.PI / 2 + 0.01, math.PI / 2 - 0.01); + + forward := camera_get_forward(camera); + facing := forward; + facing.y = 0; + facing = Vector3.norm(facing); + + xz_vel := Vector3.{0,0,0}; + if is_key_down(GLFW_KEY_W) do xz_vel += facing; + if is_key_down(GLFW_KEY_S) do xz_vel -= facing; + if is_key_down(GLFW_KEY_D) do xz_vel += Vector3.norm(Vector3.cross(facing, .{0,1,0})); + if is_key_down(GLFW_KEY_A) do xz_vel -= Vector3.norm(Vector3.cross(facing, .{0,1,0})); + + if Vector3.mag(xz_vel) > 0 do xz_vel = Vector3.norm(xz_vel) * speed; + body.vel.x = xz_vel.x; + body.vel.z = xz_vel.z; + + physics_apply_gravity(^body); + + if is_key_down(GLFW_KEY_SPACE) && body.on_ground { + body.acc.y = 500; + } + + physics_simulate(^body, dt, world); + + // Fix falling off the world + if body.pos.y < -100 do body.pos = .{0,48,0}; + + camera.position = body.pos; + + ray_cast_to_block :: (_, p) => world_get_block(world, p.x, p.y, p.z) != Block_Empty; + + dir: Vector3i; + if !ray_cast(.{ camera.position, forward }, 10, null, ray_cast_to_block, ^selected_block, ^dir) { + selected_block = .{0,0,0}; + } + + if is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) { + world_set_block(world, selected_block.x, selected_block.y, selected_block.z, Block_Empty); + } + + if is_button_just_down(GLFW_MOUSE_BUTTON_RIGHT) { + target := selected_block + dir; + world_set_block(world, target.x, target.y, target.z, block_make(1,0,0,1)); + } +} + +player_get_aabb :: (pos: Vector3) -> AABB { + return .{ + x0 = pos.x - 0.3f, + y0 = pos.y - 1.8f, + z0 = pos.z - 0.3f, + x1 = pos.x + 0.3f, + y1 = pos.y + 0.1f, + z1 = pos.z + 0.3f, + }; +} diff --git a/src/world/world.onyx b/src/world/world.onyx new file mode 100644 index 0000000..397b380 --- /dev/null +++ b/src/world/world.onyx @@ -0,0 +1,207 @@ + +use package core + +World :: struct { + chunk_dist: i32; + chunks: [] ^Chunk; + + center_chunk: Vector3i; + + chunks_to_load: [..] Vector3i; +} + +world_make :: (allocator := context.allocator) -> ^World { + world := new(World, allocator); + world.chunk_dist = 2; + world.center_chunk = .{0,0,0}; + + sl := world.chunk_dist * 2 + 1; + memory.alloc_slice(^world.chunks, sl * sl * sl); + memory.fill_slice(world.chunks, null); + + return world; +} + +world_load_chunk :: (world: ^World, chunk_to_load: Vector3i) { + // + // For now, no persistence will happen to the chunk, so generate a completely new chunk. + // + chunk := chunk_make(chunk_to_load.x, chunk_to_load.y, chunk_to_load.z); + generate_chunk(world, chunk); + world_set_chunk(world, chunk_to_load, chunk); +} + +world_unload_chunk :: #match { + (world: ^World, chunk_to_unload: Vector3i) { + chunk := world_get_chunk(world, chunk_to_unload); + world_unload_chunk(world, chunk); + world_set_chunk(world, chunk_to_unload, null); + }, + + (world: ^World, chunk: ^Chunk) { + if chunk != null { + chunk_free(chunk); + } + } +} + +world_get_chunk :: #match { + @CompilerBug // If I write the following function in quick form: + // + // (world: ^World, v: Vector3i) => world_get_chunk(world, v.x, v.y, v.z), + // + // the ',' at the end appears to mean "compound expression", so the following + // function defintion gets lumped as the second return result. Maybe there + // should be a special case for parsing this? I've always hated how parsing + // inside of a #match block works. + // + + (world: ^World, x, y, z: i32) -> ^Chunk { + d := world.chunk_dist; + sl := d * 2 + 1; + + ix := x - world.center_chunk.x + d; + iy := y - world.center_chunk.y + d; + iz := z - world.center_chunk.z + d; + if ix < 0 || ix > 2 * world.chunk_dist do return null; + if iy < 0 || iy > 2 * world.chunk_dist do return null; + if iz < 0 || iz > 2 * world.chunk_dist do return null; + + return world.chunks[ix * sl * sl + iy * sl + iz]; + }, + + (world: ^World, v: Vector3i) => world_get_chunk(world, v.x, v.y, v.z) +} + +world_set_chunk :: #match { + (world: ^World, v: Vector3i, chunk: ^Chunk) -> #auto { return world_set_chunk(world, v.x, v.y, v.z, chunk); }, + + (world: ^World, x, y, z: i32, chunk: ^Chunk) -> bool { + d := world.chunk_dist; + sl := d * 2 + 1; + + ix := x - world.center_chunk.x + d; + iy := y - world.center_chunk.y + d; + iz := z - world.center_chunk.z + d; + if ix < 0 || ix > 2 * world.chunk_dist do return false; + if iy < 0 || iy > 2 * world.chunk_dist do return false; + if iz < 0 || iz > 2 * world.chunk_dist do return false; + + world.chunks[ix * sl * sl + iy * sl + iz] = chunk; + return true; + } +} + +// Getting around the stupidness that is integer arithmetic on modern processors +#local world_coord_to_chunk_and_block :: (world: ^World, x, y, z: i32) -> (chunk: Vector3i, block: Vector3i) { + chunk, block: Vector3i; + chunk.x = x / Chunk_Size; + chunk.y = y / Chunk_Size; + chunk.z = z / Chunk_Size; + block.x = x % Chunk_Size; + block.y = y % Chunk_Size; + block.z = z % Chunk_Size; + + if x < 0 && block.x != 0 { chunk.x -= 1; block.x += Chunk_Size; } + if y < 0 && block.y != 0 { chunk.y -= 1; block.y += Chunk_Size; } + if z < 0 && block.z != 0 { chunk.z -= 1; block.z += Chunk_Size; } + + return chunk, block; +} + +world_position_to_chunk :: (world: ^World, use pos: Vector3) -> Vector3i { + chunk: Vector3i; + chunk.x = ~~math.floor(x / ~~Chunk_Size); + chunk.y = ~~math.floor(y / ~~Chunk_Size); + chunk.z = ~~math.floor(z / ~~Chunk_Size); + return chunk; +} + +world_get_block :: (world: ^World, x, y, z: i32) -> Block { + chunk_pos, block_pos := world_coord_to_chunk_and_block(world, x, y, z); + chunk := world_get_chunk(world, chunk_pos); + if chunk == null do return Block_Empty; + return chunk_get(chunk, block_pos.x, block_pos.y, block_pos.z); +} + +world_set_block :: (world: ^World, x, y, z: i32, block: Block) { + chunk_pos, block_pos := world_coord_to_chunk_and_block(world, x, y, z); + chunk := world_get_chunk(world, chunk_pos); + if chunk == null do return; + chunk_set(chunk, block_pos.x, block_pos.y, block_pos.z, block); +} + +world_move_center :: (use world: ^World, new_center: Vector3i) { + if center_chunk == new_center do return; + + sl := world.chunk_dist * 2 + 1; + current_chunks := memory.copy_slice(world.chunks); + memory.fill_slice(world.chunks, null); + + center_chunk = new_center; + for current_chunks { + if it == null do continue; + if !world_set_chunk(world, it.coord, it) { + world_unload_chunk(world, it); + } + } + + array.clear(^chunks_to_load); + for x: center_chunk.x-world.chunk_dist .. center_chunk.x+world.chunk_dist+1 + do for y: center_chunk.y-world.chunk_dist .. center_chunk.y+world.chunk_dist+1 + do for z: center_chunk.z-world.chunk_dist .. center_chunk.z+world.chunk_dist+1 { + if world_get_chunk(world, x, y, z) == null do world.chunks_to_load << .{x, y, z}; + } + + memory.free_slice(^current_chunks); +} + +world_get_aabbs :: (use world: ^World, center: Vector3, radius: f32, buffer: [] AABB = .[]) -> [] AABB { + is_dynamic := buffer.count == 0; + aabbs: [..] AABB; + buffer_pos := 0; + + pos := Vector3i.{ ~~math.floor(center.x), ~~math.floor(center.y), ~~math.floor(center.z) }; + rad := cast(i32) math.ceil(radius); + + for x: pos.x-rad .. pos.x+rad+1 { + for y: pos.y-rad .. pos.y+rad+1 { + for z: pos.z-rad .. pos.z+rad+1 { + if world_get_block(world, x, y, z) != Block_Empty { + aabb := AABB.{ + ~~x, ~~y, ~~z, + ~~(x+1), ~~(y+1), ~~(z+1) + }; + + if is_dynamic { + aabbs << aabb; + } else { + buffer[buffer_pos] = aabb; + buffer_pos += 1; + if buffer_pos >= buffer.count do break break break; + } + } + } + } + } + + if is_dynamic do return aabbs; + return buffer[0 .. buffer_pos]; +} + +world_update :: (use world: ^World, dt: f32) { + if chunks_to_load.count > 0 { + chunk_to_load := chunks_to_load[0]; + array.delete(^chunks_to_load, 0); + + world_load_chunk(world, chunk_to_load); + } +} + +world_draw :: (use world: ^World) { + for chunks { + if it == null do continue; + chunk_build_mesh(it); + chunk_draw(it); + } +} diff --git a/src/world/worldgen.onyx b/src/world/worldgen.onyx new file mode 100644 index 0000000..3e662e2 --- /dev/null +++ b/src/world/worldgen.onyx @@ -0,0 +1,28 @@ + +use package core +#local perlin :: package perlin + +#local seed :: 8675309 + +generate_chunk :: (world: ^World, chunk: ^Chunk) { + for cz: 32 do for cx: 32 { + t := chunk_coords_to_world(chunk, cx, 0, cz); + px := cast(f64) t.x / 24; + pz := cast(f64) t.z / 24; + + n := perlin.noise(px, ~~(10 * seed), pz); + l := perlin.noise(px, ~~(10 * seed), pz); + l = (math.clamp(math.max(0, l), 0.2, 0.5) - 0.2) / 0.6 + 0.5; + h := cast(i32) (n * 32 + 18); + h = math.max(h, 14); + if n < -0.9 do h += ~~((n + 0.9) * 4); + + h -= chunk.coord.y * Chunk_Size; + if h >= 0 { chunk_set(chunk, cx, h, cz, block_make(0.2, 1, 0.2, ~~l)); h -= 1; } + for cy: 2 .. h+1 do chunk_set(chunk, cx, cy, cz, block_make(0.3, 0.3, 0.1, 1, .{texture_enabled=false})); + for cy: 0 .. math.min(h, 2)+1 do chunk_set(chunk, cx, cy, cz, block_make(0.2, 0.2, 0.2, 0.5, .{texture_enabled=false})); + } + + chunk_set(chunk, 0, 0, 0, block_make(1, 0, 1, 1)); +} + diff --git a/src/worldgen.onyx b/src/worldgen.onyx deleted file mode 100644 index 3e662e2..0000000 --- a/src/worldgen.onyx +++ /dev/null @@ -1,28 +0,0 @@ - -use package core -#local perlin :: package perlin - -#local seed :: 8675309 - -generate_chunk :: (world: ^World, chunk: ^Chunk) { - for cz: 32 do for cx: 32 { - t := chunk_coords_to_world(chunk, cx, 0, cz); - px := cast(f64) t.x / 24; - pz := cast(f64) t.z / 24; - - n := perlin.noise(px, ~~(10 * seed), pz); - l := perlin.noise(px, ~~(10 * seed), pz); - l = (math.clamp(math.max(0, l), 0.2, 0.5) - 0.2) / 0.6 + 0.5; - h := cast(i32) (n * 32 + 18); - h = math.max(h, 14); - if n < -0.9 do h += ~~((n + 0.9) * 4); - - h -= chunk.coord.y * Chunk_Size; - if h >= 0 { chunk_set(chunk, cx, h, cz, block_make(0.2, 1, 0.2, ~~l)); h -= 1; } - for cy: 2 .. h+1 do chunk_set(chunk, cx, cy, cz, block_make(0.3, 0.3, 0.1, 1, .{texture_enabled=false})); - for cy: 0 .. math.min(h, 2)+1 do chunk_set(chunk, cx, cy, cz, block_make(0.2, 0.2, 0.2, 0.5, .{texture_enabled=false})); - } - - chunk_set(chunk, 0, 0, 0, block_make(1, 0, 1, 1)); -} -