organized project
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 22 Feb 2022 22:54:09 +0000 (16:54 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 22 Feb 2022 22:54:09 +0000 (16:54 -0600)
21 files changed:
src/build.onyx
src/camera.onyx [deleted file]
src/chunk.onyx [deleted file]
src/input.onyx [deleted file]
src/logger.onyx [deleted file]
src/physics.onyx [deleted file]
src/player.onyx [deleted file]
src/utils.onyx [deleted file]
src/utils/camera.onyx [new file with mode: 0644]
src/utils/input.onyx [new file with mode: 0644]
src/utils/logger.onyx [new file with mode: 0644]
src/utils/utils.onyx [new file with mode: 0644]
src/utils/vecmath.onyx [new file with mode: 0644]
src/vecmath.onyx [deleted file]
src/world.onyx [deleted file]
src/world/chunk.onyx [new file with mode: 0644]
src/world/physics.onyx [new file with mode: 0644]
src/world/player.onyx [new file with mode: 0644]
src/world/world.onyx [new file with mode: 0644]
src/world/worldgen.onyx [new file with mode: 0644]
src/worldgen.onyx [deleted file]

index 2549a3e22e93bbdc035ed8edf4fcca120baad09f..137d6312d0df09491e32d3ec1c93f63d15731c90 100644 (file)
@@ -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"
 #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 (file)
index c8bc72d..0000000
+++ /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 (file)
index c076957..0000000
+++ /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 (file)
index 2a5f217..0000000
+++ /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 (file)
index 529ddee..0000000
+++ /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 (file)
index 746e39d..0000000
+++ /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 (file)
index c10d6d1..0000000
+++ /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 (file)
index 25a9873..0000000
+++ /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 (file)
index 0000000..c8bc72d
--- /dev/null
@@ -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 (file)
index 0000000..2a5f217
--- /dev/null
@@ -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 (file)
index 0000000..529ddee
--- /dev/null
@@ -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 (file)
index 0000000..25a9873
--- /dev/null
@@ -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 (file)
index 0000000..eb6f639
--- /dev/null
@@ -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 (file)
index eb6f639..0000000
+++ /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 (file)
index 397b380..0000000
+++ /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 (file)
index 0000000..c076957
--- /dev/null
@@ -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 (file)
index 0000000..746e39d
--- /dev/null
@@ -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 (file)
index 0000000..c10d6d1
--- /dev/null
@@ -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 (file)
index 0000000..397b380
--- /dev/null
@@ -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 (file)
index 0000000..3e662e2
--- /dev/null
@@ -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 (file)
index 3e662e2..0000000
+++ /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));
-}
-