#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"
+++ /dev/null
-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;
-}
-
+++ /dev/null
-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);
-}
-
-
+++ /dev/null
-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);
- }
-}
+++ /dev/null
-//
-// 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 " ";
-}
+++ /dev/null
-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;
-}
-
-
+++ /dev/null
-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,
- };
-}
+++ /dev/null
-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;
-}
-
--- /dev/null
+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;
+}
+
--- /dev/null
+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);
+ }
+}
--- /dev/null
+//
+// 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 " ";
+}
--- /dev/null
+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;
+}
+
--- /dev/null
+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);
+ }
+}
+++ /dev/null
-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);
- }
-}
+++ /dev/null
-
-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);
- }
-}
--- /dev/null
+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);
+}
+
+
--- /dev/null
+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;
+}
+
+
--- /dev/null
+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,
+ };
+}
--- /dev/null
+
+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);
+ }
+}
--- /dev/null
+
+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));
+}
+
+++ /dev/null
-
-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));
-}
-