implement custom chunk meshes, models and shaders.

This commit is contained in:
catangent 2025-04-14 20:29:41 +01:00
parent e4d536bc87
commit e805891885
7 changed files with 800 additions and 57 deletions

View file

@ -4,7 +4,6 @@ const raylib = raylib_helper.raylib;
const v3 = raylib_helper.v3;
const A7r = std.mem.Allocator;
const comptimePrint = std.fmt.comptimePrint;
const VERTICES_BLOCK_SIZE = 2 * 3 * 3;
const TEXCOORDS_BLOCK_SIZE = 2 * 2 * 3;
@ -43,7 +42,7 @@ const Metadata1 = packed struct {
comptime {
if (@bitSizeOf(Metadata1) != 128) {
@compileError(comptimePrint("Metadata 1 has wrong size. Expected 128 bits, found {}", .{@bitSizeOf(Metadata1)}));
@compileError(comptimePrint("Metadata 1 has wrong size. Expected 128 bytes, found {}", .{@bitSizeOf(Metadata1)}));
}
}
@ -91,7 +90,18 @@ pub const Chunk = struct {
}
// Create a raw quad with specified parameters and surface, accounting for dimension and sign. Surface is the block ID.
fn pack_raw_quad(x: f32, y_start: usize, y_end: usize, z_start: usize, z_end: usize, sign: comptime_int, d: comptime_int, surface: u32) RawQuad {
fn pack_raw_quad(
x: f32,
y_start: usize, y_end: usize,
z_start: usize, z_end: usize,
sign: comptime_int,
d: comptime_int,
surface: u32,
y_minus_obscuring_pattern: u32,
y_plus_obscuring_pattern: u32,
z_minus_obscuring_pattern: u32,
z_plus_obscuring_pattern: u32,
) RawQuad {
const ymin: f32 = @as(f32, @floatFromInt(y_start)) - 0.5;
const ymax: f32 = @as(f32, @floatFromInt(y_end)) - 0.5;
const zmin: f32 = @as(f32, @floatFromInt(z_start)) - 0.5;
@ -113,10 +123,10 @@ pub const Chunk = struct {
.width = zmax - zmin,
.height = ymax - ymin,
.top_obscuring_pattern = 0,
.left_obscuring_pattern = 0,
.right_obscuring_pattern = 0,
.bottom_obscuring_pattern = 0,
.top_obscuring_pattern = z_plus_obscuring_pattern,
.left_obscuring_pattern = z_minus_obscuring_pattern,
.right_obscuring_pattern = y_plus_obscuring_pattern,
.bottom_obscuring_pattern = y_minus_obscuring_pattern,
.top_left_obscured = false,
.top_right_obscured = false,
.bottom_right_obscured = false,
@ -155,10 +165,10 @@ pub const Chunk = struct {
.width = zmax - zmin,
.height = ymax - ymin,
.top_obscuring_pattern = 0xffffffff,
.left_obscuring_pattern = 0xffffffff,
.right_obscuring_pattern = 0xffffffff,
.bottom_obscuring_pattern = 0xffffffff,
.top_obscuring_pattern = 0,
.left_obscuring_pattern = 0,
.right_obscuring_pattern = 0,
.bottom_obscuring_pattern = 0,
.top_left_obscured = false,
.top_right_obscured = false,
.bottom_right_obscured = false,
@ -171,7 +181,7 @@ pub const Chunk = struct {
}
// Create mesh of a chunk. tile_rows and tile_columns are the dimensions of the tiles.png file, in terms of individual tile textures.
pub fn createMesh(chunk: Chunk, tile_rows: u32, tile_columns: u32) !raylib.Mesh {
pub fn createMesh(chunk: Chunk, tile_rows: u32, tile_columns: u32) !raylib.ChunkMesh {
var raw_quads = try std.ArrayList(RawQuad).initCapacity(chunk.a7r, 4096);
defer raw_quads.deinit();
@ -200,6 +210,7 @@ pub const Chunk = struct {
// The end coordinates of the quad. The quad is therefore covers rectangle from start coordinates (inclusive) to end coordinates (exclusive).
var y_end = y_start + 1;
var z_end = z_start + 1;
// todo: meshing can be optimized with SIMD stuff!!
// Greedy meshing: Extend the quad in the +y direction, until we hit a tile of a different type or the end of the chunk.
while (y_end <= 31 and tile_surfaces[y_end][z_start] == surface) : (y_end += 1) {
tile_surfaces[y_end][z_start] = 0;
@ -209,8 +220,41 @@ pub const Chunk = struct {
for (y_start..y_end) |y| if (tile_surfaces[y][z_end] != surface) break :zloop; // Stop extending if we hit a tile of incorrect type.
for (y_start..y_end) |y| tile_surfaces[y][z_end] = 0;
}
// todo: scan tiles around quad surface for ambient occlusion
const raw_quad = pack_raw_quad(@floatFromInt(raw_x), y_start, y_end, z_start, z_end, sign, d, surface);
// Scan the line of tiles adjacent to the quad for ambient occlusion
var z_minus_obscuring_pattern: u32 = 0;
if (x != (if (sign == 1) 31 else 0) and z_start != 0) {
for (y_start..y_end) |raw_y| {
const y: u5 = @intCast(raw_y);
z_minus_obscuring_pattern <<= 1;
if (chunk.getTileRawShifted(if (sign == 1) x+1 else x-1, y, @intCast(z_start - 1), d) != 0) z_minus_obscuring_pattern |= 1;
}
}
var z_plus_obscuring_pattern: u32 = 0;
if (x != (if (sign == 1) 31 else 0) and z_end != 32) {
for (y_start..y_end) |raw_y| {
const y: u5 = @intCast(raw_y);
z_plus_obscuring_pattern <<= 1;
if (chunk.getTileRawShifted(if (sign == 1) x+1 else x-1, y, @intCast(z_end), d) != 0) z_plus_obscuring_pattern |= 1;
}
}
var y_minus_obscuring_pattern: u32 = 0;
if (x != (if (sign == 1) 31 else 0) and y_start != 0) {
for (z_start..z_end) |raw_z| {
const z: u5 = @intCast(raw_z);
y_minus_obscuring_pattern <<= 1;
if (chunk.getTileRawShifted(if (sign == 1) x+1 else x-1, @intCast(y_start - 1), z, d) != 0) y_minus_obscuring_pattern |= 1;
}
}
var y_plus_obscuring_pattern: u32 = 0;
if (x != (if (sign == 1) 31 else 0) and y_end != 32) {
for (z_start..z_end) |raw_z| {
const z: u5 = @intCast(raw_z);
y_plus_obscuring_pattern <<= 1;
if (chunk.getTileRawShifted(if (sign == 1) x+1 else x-1, @intCast(y_end), z, d) != 0) y_plus_obscuring_pattern |= 1;
}
}
// std.debug.print("{}\n", .{z_minus_obscuring_pattern});
const raw_quad = pack_raw_quad(@floatFromInt(raw_x), y_start, y_end, z_start, z_end, sign, d, surface, y_minus_obscuring_pattern, y_plus_obscuring_pattern, z_minus_obscuring_pattern, z_plus_obscuring_pattern);
try raw_quads.append(raw_quad);
};
}
@ -224,7 +268,7 @@ pub const Chunk = struct {
const vertices: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 3)));
const texcoords: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 2)));
const texcoords2: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 2)));
const tiletexcoords: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 2)));
const normals: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 3)));
const metadata1_packed: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 4)));
@ -249,8 +293,8 @@ pub const Chunk = struct {
const vertex_corners = .{ raw_quad.top_left, raw_quad.bottom_left, raw_quad.top_right, raw_quad.bottom_right, raw_quad.top_right, raw_quad.bottom_left };
const texcoords_x = .{ left_uv, left_uv, right_uv, right_uv, right_uv, left_uv };
const texcoords_y = .{ top_uv, bottom_uv } ** 3;
const texcoords2_x = .{ 0.0, 0.0, raw_quad.width, raw_quad.width, raw_quad.width, 0.0 };
const texcoords2_y = .{ 0.0, raw_quad.height } ** 3;
const tiletexcoords_x = .{ 0.0, 0.0, raw_quad.width, raw_quad.width, raw_quad.width, 0.0 };
const tiletexcoords_y = .{ 0.0, raw_quad.height } ** 3;
inline for (0..6) |corner_id| {
vertices[VERTICES_BLOCK_SIZE * i + corner_id * 3 + 0] = vertex_corners[corner_id].x;
@ -258,8 +302,8 @@ pub const Chunk = struct {
vertices[VERTICES_BLOCK_SIZE * i + corner_id * 3 + 2] = vertex_corners[corner_id].z;
texcoords[TEXCOORDS_BLOCK_SIZE * i + corner_id * 2 + 0] = texcoords_x[corner_id];
texcoords[TEXCOORDS_BLOCK_SIZE * i + corner_id * 2 + 1] = texcoords_y[corner_id];
texcoords2[TEXCOORDS_BLOCK_SIZE * i + corner_id * 2 + 0] = texcoords2_x[corner_id];
texcoords2[TEXCOORDS_BLOCK_SIZE * i + corner_id * 2 + 1] = texcoords2_y[corner_id];
tiletexcoords[TEXCOORDS_BLOCK_SIZE * i + corner_id * 2 + 0] = tiletexcoords_x[corner_id];
tiletexcoords[TEXCOORDS_BLOCK_SIZE * i + corner_id * 2 + 1] = tiletexcoords_y[corner_id];
}
// Store metadata into OpenGL buffers.
@ -296,21 +340,21 @@ pub const Chunk = struct {
}
// Create mesh using the buffers.
var mesh = raylib.Mesh{
var mesh = raylib.ChunkMesh{
.triangleCount = triangle_count,
.vertexCount = triangle_count * 3,
.vertices = vertices,
.texcoords = texcoords,
.texcoords2 = texcoords2,
.tiletexcoords = tiletexcoords,
.normals = normals,
.tangents = metadata1_packed,
.metadata1 = metadata1_packed,
.vaoId = 0,
.vboId = null,
};
raylib.UploadMesh(@ptrCast(&mesh), false);
raylib.UploadChunkMesh(@ptrCast(&mesh), false);
return mesh;
}