#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) {
- output->write("(");
- conv.format_any(output, format, .{^v.x, f32});
- output->write(", ");
- conv.format_any(output, format, .{^v.y, f32});
- output->write(")");
+ conv.format(output, "({}, {})", v.x, v.y);
}
format_vector3 :: (output: ^conv.Format_Output, format: ^conv.Format, v: ^Vector3) {
- output->write("(");
- conv.format_any(output, format, .{^v.x, f32});
- output->write(", ");
- conv.format_any(output, format, .{^v.y, f32});
- output->write(", ");
- conv.format_any(output, format, .{^v.z, f32});
- output->write(")");
+ conv.format(output, "({}, {}, {})", v.x, v.y, v.z);
}
format_vector3i :: (output: ^conv.Format_Output, format: ^conv.Format, v: ^Vector3i) {
- output->write("(");
- conv.format_any(output, format, .{^v.x, i32});
- output->write(", ");
- conv.format_any(output, format, .{^v.y, i32});
- output->write(", ");
- conv.format_any(output, format, .{^v.z, i32});
- output->write(")");
+ conv.format(output, "({}, {}, {})", v.x, v.y, v.z);
}
-}
\ No newline at end of file
+}
use package core
World :: struct {
- chunk_dist: u32;
+ 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 = 3;
- memory.alloc_slice(^world.chunks, world.chunk_dist * world.chunk_dist * world.chunk_dist);
+ world.chunk_dist = 2;
+ world.center_chunk = .{0,0,0};
- for x: 0 .. 3 do for y: 0 .. 3 do for z: 0 .. 3 {
- chunk := chunk_make(x, y, z);
- world.chunks[x * world.chunk_dist * world.chunk_dist + y * world.chunk_dist + z] = chunk;
- generate_chunk(world, chunk);
+ sl := world.chunk_dist * 2 + 1;
+ memory.alloc_slice(^world.chunks, sl * sl * sl);
+ memory.fill_slice(world.chunks, null);
+
+ for x: (world.center_chunk.x-world.chunk_dist) .. world.center_chunk.x+world.chunk_dist+1
+ do for y: (world.center_chunk.y-world.chunk_dist) .. world.center_chunk.y+world.chunk_dist+1
+ do for z: (world.center_chunk.z-world.chunk_dist) .. world.center_chunk.z+world.chunk_dist+1 {
+ world.chunks_to_load << .{x, y, z};
}
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, v: Vector3i) -> #auto { return world_get_chunk(world, v.x, v.y, v.z); },
+
+ (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_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 {
- @WorldCenter
- cx := x / Chunk_Size;
- cy := y / Chunk_Size;
- cz := z / Chunk_Size;
- if cx < 0 || cx >= world.chunk_dist do return Block_Empty;
- if cy < 0 || cy >= world.chunk_dist do return Block_Empty;
- if cz < 0 || cz >= world.chunk_dist do return Block_Empty;
- chunk := world.chunks[cx * world.chunk_dist * world.chunk_dist + cy * world.chunk_dist + cz];
- return chunk_get(chunk, x % Chunk_Size, y % Chunk_Size, z % Chunk_Size);
+ 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) {
- @WorldCenter
- cx := x / Chunk_Size;
- cy := y / Chunk_Size;
- cz := z / Chunk_Size;
- if cx < 0 || cx >= world.chunk_dist do return;
- if cy < 0 || cy >= world.chunk_dist do return;
- if cz < 0 || cz >= world.chunk_dist do return;
- chunk := world.chunks[cx * world.chunk_dist * world.chunk_dist + cy * world.chunk_dist + cz];
- chunk_set(chunk, x % Chunk_Size, y % Chunk_Size, z % Chunk_Size, block);
+ 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.alloc_slice(^world.chunks, sl * sl * sl);
+ 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);
+ }
+ }
+
+ 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 {
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);
+ printf("Loading {}\n", chunk_to_load);
-@Relocate // to a utils file
-Ray :: struct {
- origin, direction: Vector3;
-}
-
-@Relocate // to a utils file
-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 };
- }
- }
+ world_load_chunk(world, chunk_to_load);
}
-
- return false;
}
-
world_draw :: (use world: ^World) {
for chunks {
+ if it == null do continue;
chunk_build_mesh(it);
chunk_draw(it);
}