diff --git a/build.zig b/build.zig index 72988cc..7d520d5 100644 --- a/build.zig +++ b/build.zig @@ -37,6 +37,8 @@ pub fn build(b: *std.Build) void { .optimize = optimize, }); + exe_unit_tests.linkLibrary(rl.artifact("raylib")); + const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); const test_step = b.step("test", "Run unit tests"); diff --git a/src/main.zig b/src/main.zig index 5c6ddbe..76bf7ac 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4,6 +4,9 @@ const raylib = rh.raylib; const v3 = rh.v3; const chunks = @import("world/chunk.zig"); +const benchmark_chunk_meshing = false; +const debug = false; + pub fn drawCameraPosition(camera: raylib.Camera3D, x: i32, y: i32) !void { var buf: [256:0]u8 = undefined; @@ -22,6 +25,8 @@ pub fn moveCamera(camera: *raylib.Camera3D, vec: raylib.Vector3) void { } pub fn main() !void { + if (!debug) raylib.SetTraceLogLevel(raylib.LOG_ERROR); + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const a7r = gpa.allocator(); defer { @@ -53,6 +58,7 @@ pub fn main() !void { var chunk = try chunks.Chunk.init(a7r); defer chunk.deinit(); + for (0..32) |raw_x| for (0..32) |raw_z| { const x: u5 = @intCast(raw_x); const z: u5 = @intCast(raw_z); @@ -66,6 +72,19 @@ pub fn main() !void { chunk.setTile(31 - a, a, a, 1); chunk.setTile(31 - a, 31 - a, a, 1); } + if (benchmark_chunk_meshing) { + var tmp: u64 = 0; + for (0..500) |_| { + const start = try std.time.Instant.now(); + const model = raylib.LoadModelFromMesh(try chunk.createMesh()); + defer raylib.UnloadModel(model); + const end = try std.time.Instant.now(); + tmp += end.since(start); + } + std.debug.print("\nchunk meshing time: {d:.3}ms\n\n", .{ + @as(f64, @floatFromInt(tmp)) / std.time.ns_per_ms / 500, + }); + } const model = raylib.LoadModelFromMesh(try chunk.createMesh()); defer raylib.UnloadModel(model); @@ -112,9 +131,4 @@ pub fn main() !void { } } -test "simple test" { - var list = std.ArrayList(i32).init(std.testing.allocator); - defer list.deinit(); - try list.append(42); - try std.testing.expectEqual(@as(i32, 42), list.pop()); -} +test "empty test xd" {} diff --git a/src/util/raylib_helper.zig b/src/util/raylib_helper.zig index 556d07b..6071cb7 100644 --- a/src/util/raylib_helper.zig +++ b/src/util/raylib_helper.zig @@ -7,6 +7,15 @@ pub const v3 = struct { pub inline fn new(x: f32, y: f32, z: f32) raylib.Vector3 { return raylib.Vector3{ .x = x, .y = y, .z = z }; } + pub inline fn newShifted(x: f32, y: f32, z: f32, comptime d: comptime_int) raylib.Vector3 { + if (d % 3 == 0) { + return new(x, y, z); + } else if (d % 3 == 1) { + return new(y, z, x); + } else if (d % 3 == 2) { + return new(z, x, y); + } + } pub inline fn add(a: raylib.Vector3, b: raylib.Vector3) raylib.Vector3 { return raylib.Vector3Add(a, b); } diff --git a/src/world/chunk.zig b/src/world/chunk.zig index 14696c4..2e62236 100644 --- a/src/world/chunk.zig +++ b/src/world/chunk.zig @@ -11,6 +11,8 @@ const RawQuad = struct { bottom_right: raylib.Vector3, bottom_left: raylib.Vector3, normal: raylib.Vector3, + width: f32, + height: f32, }; pub const Chunk = struct { @@ -38,148 +40,76 @@ pub const Chunk = struct { self.tiles[@as(u15, x) << 10 | @as(u15, y) << 5 | @as(u15, z)] = tile; } + fn getTileRaw(self: Chunk, x: u5, y: u5, z: u5) i32 { + return self.tiles[@as(u15, x) << 10 | @as(u15, y) << 5 | @as(u15, z)]; + } + + inline fn getTileRawShifted(self: Chunk, x: u5, y: u5, z: u5, comptime d: comptime_int) i32 { + if (d % 3 == 0) { + return self.getTileRaw(x, y, z); + } else if (d % 3 == 1) { + return self.getTileRaw(y, z, x); + } else if (d % 3 == 2) { + return self.getTileRaw(z, x, y); + } + } + pub fn createMesh(chunk: Chunk) !raylib.Mesh { var raw_quads = try std.ArrayList(RawQuad).initCapacity(chunk.a7r, 4096); defer raw_quads.deinit(); - for (0..32) |raw_x| { - const x: u5 = @intCast(raw_x); - var positive_tile_surfaces: [32][32]i32 = .{.{0} ** 32} ** 32; - var negative_tile_surfaces: [32][32]i32 = .{.{0} ** 32} ** 32; - for (0..32) |raw_y| for (0..32) |raw_z| { - const y: u5 = @intCast(raw_y); - const z: u5 = @intCast(raw_z); - const tile: i32 = chunk.getTile(x, y, z); - if (tile == 0) continue; - if (x == 31 or chunk.getTile(x + 1, y, z) == 0) positive_tile_surfaces[y][z] = tile; - if (x == 0 or chunk.getTile(x - 1, y, z) == 0) negative_tile_surfaces[y][z] = tile; - }; - const xf: f32 = @floatFromInt(raw_x); - for (0..32) |y| for (0..32) |z| { - const surface = positive_tile_surfaces[y][z]; - if (surface == 0) continue; - positive_tile_surfaces[y][z] = 0; - const yf: f32 = @floatFromInt(y); - const zf: f32 = @floatFromInt(z); - const raw_quad: RawQuad = .{ - .tile = surface, - .top_left = v3.new(xf + 0.5, yf - 0.5, zf - 0.5), - .top_right = v3.new(xf + 0.5, yf + 0.5, zf - 0.5), - .bottom_left = v3.new(xf + 0.5, yf - 0.5, zf + 0.5), - .bottom_right = v3.new(xf + 0.5, yf + 0.5, zf + 0.5), - .normal = v3.new(1, 0, 0), - }; - try raw_quads.append(raw_quad); - }; - for (0..32) |y| for (0..32) |z| { - const surface = negative_tile_surfaces[y][z]; - if (surface == 0) continue; - negative_tile_surfaces[y][z] = 0; - const yf: f32 = @floatFromInt(y); - const zf: f32 = @floatFromInt(z); - const raw_quad: RawQuad = .{ - .tile = surface, - .top_left = v3.new(xf - 0.5, yf + 0.5, zf - 0.5), - .top_right = v3.new(xf - 0.5, yf - 0.5, zf - 0.5), - .bottom_left = v3.new(xf - 0.5, yf + 0.5, zf + 0.5), - .bottom_right = v3.new(xf - 0.5, yf - 0.5, zf + 0.5), - .normal = v3.new(-1, 0, 0), - }; - try raw_quads.append(raw_quad); - }; - } - for (0..32) |raw_z| { - const z: u5 = @intCast(raw_z); - var positive_tile_surfaces: [32][32]i32 = .{.{0} ** 32} ** 32; - var negative_tile_surfaces: [32][32]i32 = .{.{0} ** 32} ** 32; - for (0..32) |raw_y| for (0..32) |raw_x| { - const y: u5 = @intCast(raw_y); + inline for (0..3) |d| { + for (0..32) |raw_x| { const x: u5 = @intCast(raw_x); - const tile: i32 = chunk.getTile(x, y, z); - if (tile == 0) continue; - if (z == 31 or chunk.getTile(x, y, z + 1) == 0) positive_tile_surfaces[y][x] = tile; - if (z == 0 or chunk.getTile(x, y, z - 1) == 0) negative_tile_surfaces[y][x] = tile; - }; - const zf: f32 = @floatFromInt(raw_z); - for (0..32) |y| for (0..32) |x| { - const surface = positive_tile_surfaces[y][x]; - if (surface == 0) continue; - positive_tile_surfaces[y][x] = 0; - const yf: f32 = @floatFromInt(y); - const xf: f32 = @floatFromInt(x); - const raw_quad: RawQuad = .{ - .tile = surface, - .top_left = v3.new(xf - 0.5, yf - 0.5, zf + 0.5), - .top_right = v3.new(xf + 0.5, yf - 0.5, zf + 0.5), - .bottom_left = v3.new(xf - 0.5, yf + 0.5, zf + 0.5), - .bottom_right = v3.new(xf + 0.5, yf + 0.5, zf + 0.5), - .normal = v3.new(0, 0, 1), + var positive_tile_surfaces: [32][32]i32 = .{.{0} ** 32} ** 32; + var negative_tile_surfaces: [32][32]i32 = .{.{0} ** 32} ** 32; + for (0..32) |raw_y| for (0..32) |raw_z| { + const y: u5 = @intCast(raw_y); + const z: u5 = @intCast(raw_z); + const tile: i32 = chunk.getTileRawShifted(x, y, z, d); + if (tile == 0) continue; + if (x == 31 or chunk.getTileRawShifted(x + 1, y, z, d) == 0) positive_tile_surfaces[y][z] = tile; + if (x == 0 or chunk.getTileRawShifted(x - 1, y, z, d) == 0) negative_tile_surfaces[y][z] = tile; }; - try raw_quads.append(raw_quad); - }; - for (0..32) |y| for (0..32) |x| { - const surface = negative_tile_surfaces[y][x]; - if (surface == 0) continue; - negative_tile_surfaces[y][x] = 0; - const yf: f32 = @floatFromInt(y); - const xf: f32 = @floatFromInt(x); - const raw_quad: RawQuad = .{ - .tile = surface, - .top_left = v3.new(xf - 0.5, yf + 0.5, zf - 0.5), - .top_right = v3.new(xf + 0.5, yf + 0.5, zf - 0.5), - .bottom_left = v3.new(xf - 0.5, yf - 0.5, zf - 0.5), - .bottom_right = v3.new(xf + 0.5, yf - 0.5, zf - 0.5), - .normal = v3.new(0, 0, -1), - }; - try raw_quads.append(raw_quad); - }; - } - - for (0..32) |raw_y| { - const y: u5 = @intCast(raw_y); - var positive_tile_surfaces: [32][32]i32 = .{.{0} ** 32} ** 32; - var negative_tile_surfaces: [32][32]i32 = .{.{0} ** 32} ** 32; - for (0..32) |raw_x| for (0..32) |raw_z| { - const x: u5 = @intCast(raw_x); - const z: u5 = @intCast(raw_z); - const tile: i32 = chunk.getTile(x, y, z); - if (tile == 0) continue; - if (y == 31 or chunk.getTile(x, y + 1, z) == 0) positive_tile_surfaces[x][z] = tile; - if (y == 0 or chunk.getTile(x, y - 1, z) == 0) negative_tile_surfaces[x][z] = tile; - }; - const yf: f32 = @floatFromInt(raw_y); - for (0..32) |x| for (0..32) |z| { - const surface = positive_tile_surfaces[x][z]; - if (surface == 0) continue; - positive_tile_surfaces[x][z] = 0; - const xf: f32 = @floatFromInt(x); - const zf: f32 = @floatFromInt(z); - const raw_quad: RawQuad = .{ - .tile = surface, - .top_left = v3.new(xf - 0.5, yf + 0.5, zf - 0.5), - .top_right = v3.new(xf - 0.5, yf + 0.5, zf + 0.5), - .bottom_left = v3.new(xf + 0.5, yf + 0.5, zf - 0.5), - .bottom_right = v3.new(xf + 0.5, yf + 0.5, zf + 0.5), - .normal = v3.new(0, 1, 0), - }; - try raw_quads.append(raw_quad); - }; - for (0..32) |x| for (0..32) |z| { - const surface = negative_tile_surfaces[x][z]; - if (surface == 0) continue; - negative_tile_surfaces[x][z] = 0; - const xf: f32 = @floatFromInt(x); - const zf: f32 = @floatFromInt(z); - const raw_quad: RawQuad = .{ - .tile = surface, - .top_left = v3.new(xf + 0.5, yf - 0.5, zf - 0.5), - .top_right = v3.new(xf + 0.5, yf - 0.5, zf + 0.5), - .bottom_left = v3.new(xf - 0.5, yf - 0.5, zf - 0.5), - .bottom_right = v3.new(xf - 0.5, yf - 0.5, zf + 0.5), - .normal = v3.new(0, -1, 0), - }; - try raw_quads.append(raw_quad); - }; + const xf: f32 = @floatFromInt(raw_x); + inline for (.{ -1, 1 }) |sign| { + var tile_surfaces = if (sign == 1) positive_tile_surfaces else negative_tile_surfaces; + for (0..32) |y| for (0..32) |z| { + const surface = tile_surfaces[y][z]; + if (surface == 0) continue; + var y2 = y + 1; + while (y2 <= 31 and tile_surfaces[y2][z] == surface) : (y2 += 1) { + tile_surfaces[y2][z] = 0; + } + var z2 = z + 1; + zloop: while (z2 <= 31) : (z2 += 1) { + for (y..y2) |ytmp| if (tile_surfaces[ytmp][z2] != surface) break :zloop; + for (y..y2) |ytmp| tile_surfaces[ytmp][z2] = 0; + tile_surfaces[y][z2] = 0; + } + y2 -= 1; + z2 -= 1; + tile_surfaces[y][z] = 0; + const ymin: f32 = @as(f32, @floatFromInt(y)) - 0.5; + const ymax: f32 = @as(f32, @floatFromInt(y2)) + 0.5; + const zmin: f32 = @as(f32, @floatFromInt(z)) - 0.5; + const zmax: f32 = @as(f32, @floatFromInt(z2)) + 0.5; + const yleft: f32 = if (sign == 1) ymin else ymax; + const yright: f32 = if (sign == 1) ymax else ymin; + const raw_quad: RawQuad = .{ + .tile = surface, + .top_left = v3.newShifted(xf + 0.5 * sign, yleft, zmin, d), + .top_right = v3.newShifted(xf + 0.5 * sign, yright, zmin, d), + .bottom_left = v3.newShifted(xf + 0.5 * sign, yleft, zmax, d), + .bottom_right = v3.newShifted(xf + 0.5 * sign, yright, zmax, d), + .normal = v3.newShifted(sign, 0, 0, d), + .width = ymax - ymin, + .height = zmax - zmin, + }; + try raw_quads.append(raw_quad); + }; + } + } } const triangle_count: c_int = @intCast(raw_quads.items.len * 2); @@ -206,31 +136,31 @@ pub const Chunk = struct { vertices[18 * i + 3] = raw_quad.top_right.x; vertices[18 * i + 4] = raw_quad.top_right.y; vertices[18 * i + 5] = raw_quad.top_right.z; - texcoords[12 * i + 2] = 1.0; + texcoords[12 * i + 2] = raw_quad.width; texcoords[12 * i + 3] = 0.0; vertices[18 * i + 6] = raw_quad.bottom_left.x; vertices[18 * i + 7] = raw_quad.bottom_left.y; vertices[18 * i + 8] = raw_quad.bottom_left.z; texcoords[12 * i + 4] = 0.0; - texcoords[12 * i + 5] = 1.0; + texcoords[12 * i + 5] = raw_quad.height; vertices[18 * i + 9] = raw_quad.bottom_right.x; vertices[18 * i + 10] = raw_quad.bottom_right.y; vertices[18 * i + 11] = raw_quad.bottom_right.z; - texcoords[12 * i + 6] = 1.0; - texcoords[12 * i + 7] = 1.0; + texcoords[12 * i + 6] = raw_quad.width; + texcoords[12 * i + 7] = raw_quad.height; vertices[18 * i + 12] = raw_quad.bottom_left.x; vertices[18 * i + 13] = raw_quad.bottom_left.y; vertices[18 * i + 14] = raw_quad.bottom_left.z; texcoords[12 * i + 8] = 0.0; - texcoords[12 * i + 9] = 1.0; + texcoords[12 * i + 9] = raw_quad.height; vertices[18 * i + 15] = raw_quad.top_right.x; vertices[18 * i + 16] = raw_quad.top_right.y; vertices[18 * i + 17] = raw_quad.top_right.z; - texcoords[12 * i + 10] = 1.0; + texcoords[12 * i + 10] = raw_quad.width; texcoords[12 * i + 11] = 0.0; }