2024-09-09 18:05:14 +01:00
|
|
|
const std = @import("std");
|
2024-12-16 23:19:26 +00:00
|
|
|
const raylib_helper = @import("../lib_helpers/raylib_helper.zig");
|
|
|
|
|
const raylib = raylib_helper.raylib;
|
|
|
|
|
const v3 = raylib_helper.v3;
|
2024-09-09 18:05:14 +01:00
|
|
|
const A7r = std.mem.Allocator;
|
2024-12-23 18:45:07 +00:00
|
|
|
const comptimePrint = std.fmt.comptimePrint;
|
2025-07-03 14:11:35 +01:00
|
|
|
|
2025-04-09 20:00:20 +01:00
|
|
|
const VERTICES_BLOCK_SIZE = 2 * 3 * 3;
|
|
|
|
|
const TEXCOORDS_BLOCK_SIZE = 2 * 2 * 3;
|
2025-04-09 19:15:08 +01:00
|
|
|
|
2025-07-03 14:11:35 +01:00
|
|
|
const X_DIRECTION = 0;
|
|
|
|
|
const Y_DIRECTION = 1;
|
|
|
|
|
const Z_DIRECTION = 2;
|
|
|
|
|
|
2024-09-12 22:51:16 +01:00
|
|
|
const RawQuad = struct {
|
2024-12-16 23:19:26 +00:00
|
|
|
tile: u32,
|
2024-09-12 22:51:16 +01:00
|
|
|
top_left: raylib.Vector3,
|
|
|
|
|
top_right: raylib.Vector3,
|
|
|
|
|
bottom_right: raylib.Vector3,
|
|
|
|
|
bottom_left: raylib.Vector3,
|
|
|
|
|
normal: raylib.Vector3,
|
2024-09-15 21:25:30 +01:00
|
|
|
width: f32,
|
|
|
|
|
height: f32,
|
2025-04-09 19:15:08 +01:00
|
|
|
|
|
|
|
|
top_obscuring_pattern: u32,
|
|
|
|
|
left_obscuring_pattern: u32,
|
|
|
|
|
right_obscuring_pattern: u32,
|
|
|
|
|
bottom_obscuring_pattern: u32,
|
|
|
|
|
top_left_obscured: bool,
|
|
|
|
|
top_right_obscured: bool,
|
|
|
|
|
bottom_right_obscured: bool,
|
|
|
|
|
bottom_left_obscured: bool,
|
2024-09-12 22:51:16 +01:00
|
|
|
};
|
|
|
|
|
|
2025-04-10 15:56:19 +01:00
|
|
|
// Quad shader metadata. Has to be 128 bytes in size.
|
2024-12-23 18:45:07 +00:00
|
|
|
const Metadata1 = packed struct {
|
2025-07-03 14:11:35 +01:00
|
|
|
top_left_obscured: bool,
|
|
|
|
|
top_right_obscured: bool,
|
|
|
|
|
bottom_right_obscured: bool,
|
|
|
|
|
bottom_left_obscured: bool,
|
2024-12-23 18:45:07 +00:00
|
|
|
quad_height: u6,
|
|
|
|
|
quad_width: u6,
|
2025-07-03 14:11:35 +01:00
|
|
|
unused: u16 = 0,
|
2024-12-23 18:45:07 +00:00
|
|
|
unused_2: u32 = 0,
|
2025-07-03 14:11:35 +01:00
|
|
|
unused_3: u32 = 0,
|
|
|
|
|
unused_4: u32 = 0,
|
2024-12-23 18:45:07 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
comptime {
|
|
|
|
|
if (@bitSizeOf(Metadata1) != 128) {
|
2025-04-14 20:29:41 +01:00
|
|
|
@compileError(comptimePrint("Metadata 1 has wrong size. Expected 128 bytes, found {}", .{@bitSizeOf(Metadata1)}));
|
2024-12-23 18:45:07 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-09 18:05:14 +01:00
|
|
|
pub const Chunk = struct {
|
2024-12-16 23:19:26 +00:00
|
|
|
tiles: []u32,
|
2024-09-09 18:05:14 +01:00
|
|
|
a7r: A7r,
|
|
|
|
|
|
|
|
|
|
pub fn init(a7r: A7r) !Chunk {
|
2024-09-12 22:51:16 +01:00
|
|
|
const self = Chunk{
|
2024-09-09 18:05:14 +01:00
|
|
|
.a7r = a7r,
|
2024-12-16 23:19:26 +00:00
|
|
|
.tiles = try a7r.alloc(u32, 32 * 32 * 32),
|
2024-09-09 18:05:14 +01:00
|
|
|
};
|
2024-09-12 22:51:16 +01:00
|
|
|
@memset(self.tiles, 0);
|
|
|
|
|
return self;
|
2024-09-09 18:05:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn deinit(self: Chunk) void {
|
|
|
|
|
self.a7r.free(self.tiles);
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-10 15:56:19 +01:00
|
|
|
// Fetch the tile at (x, y, z), but with potential side effects. If you imagine tiles to be a 3-dimensional array, this would be tiles[x][y][z].
|
2024-12-16 23:19:26 +00:00
|
|
|
pub fn getTile(self: Chunk, x: u5, y: u5, z: u5) u32 {
|
2024-09-09 18:05:14 +01:00
|
|
|
return self.tiles[@as(u15, x) << 10 | @as(u15, y) << 5 | @as(u15, z)];
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-10 15:56:19 +01:00
|
|
|
// Set the tile at (x, y, z). If you imagine tiles to be a 3-dimensional array, this would be tiles[x][y][z] = tile.
|
2024-12-16 23:19:26 +00:00
|
|
|
pub fn setTile(self: Chunk, x: u5, y: u5, z: u5, tile: u32) void {
|
2024-09-09 18:05:14 +01:00
|
|
|
self.tiles[@as(u15, x) << 10 | @as(u15, y) << 5 | @as(u15, z)] = tile;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-10 15:56:19 +01:00
|
|
|
// Fetch the tile at (x, y, z) without changin anything. If you imagine tiles to be a 3-dimensional array, this would be tiles[x][y][z].
|
2024-12-16 23:19:26 +00:00
|
|
|
fn getTileRaw(self: Chunk, x: u5, y: u5, z: u5) u32 {
|
2024-09-15 21:25:30 +01:00
|
|
|
return self.tiles[@as(u15, x) << 10 | @as(u15, y) << 5 | @as(u15, z)];
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-10 15:56:19 +01:00
|
|
|
// This cyclically permutes the x, y, z coordinates at compile time. Useful when iterating over x, y, and z axis.
|
2025-07-03 14:11:35 +01:00
|
|
|
inline fn getTileRawShifted(self: Chunk, x: u5, y: u5, z: u5, comptime dimention: comptime_int) u32 {
|
|
|
|
|
if (dimention % 3 == 0) {
|
2024-09-15 21:25:30 +01:00
|
|
|
return self.getTileRaw(x, y, z);
|
2025-07-03 14:11:35 +01:00
|
|
|
} else if (dimention % 3 == 1) {
|
2024-09-15 21:25:30 +01:00
|
|
|
return self.getTileRaw(y, z, x);
|
2025-07-03 14:11:35 +01:00
|
|
|
} else if (dimention % 3 == 2) {
|
2024-09-15 21:25:30 +01:00
|
|
|
return self.getTileRaw(z, x, y);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-10 15:56:19 +01:00
|
|
|
// Create a raw quad with specified parameters and surface, accounting for dimension and sign. Surface is the block ID.
|
2025-07-03 14:11:35 +01:00
|
|
|
fn packRawQuad(
|
2025-04-14 20:29:41 +01:00
|
|
|
x: f32,
|
|
|
|
|
y_start: usize, y_end: usize,
|
|
|
|
|
z_start: usize, z_end: usize,
|
|
|
|
|
sign: comptime_int,
|
2025-07-03 14:11:35 +01:00
|
|
|
dimention: comptime_int,
|
2025-04-14 20:29:41 +01:00
|
|
|
surface: u32,
|
|
|
|
|
y_minus_obscuring_pattern: u32,
|
|
|
|
|
y_plus_obscuring_pattern: u32,
|
|
|
|
|
z_minus_obscuring_pattern: u32,
|
|
|
|
|
z_plus_obscuring_pattern: u32,
|
|
|
|
|
) RawQuad {
|
2025-04-10 15:56:19 +01:00
|
|
|
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;
|
|
|
|
|
const zmax: f32 = @as(f32, @floatFromInt(z_end)) - 0.5;
|
2025-04-09 19:15:08 +01:00
|
|
|
const yleft: f32 = if (sign == 1) ymin else ymax;
|
|
|
|
|
const yright: f32 = if (sign == 1) ymax else ymin;
|
|
|
|
|
const zleft: f32 = if (sign == 1) zmin else zmax;
|
|
|
|
|
const zright: f32 = if (sign == 1) zmax else zmin;
|
2025-07-03 14:11:35 +01:00
|
|
|
|
|
|
|
|
_ = y_minus_obscuring_pattern;
|
|
|
|
|
_ = y_plus_obscuring_pattern;
|
|
|
|
|
_ = z_plus_obscuring_pattern;
|
|
|
|
|
if(dimention == 0 and z_minus_obscuring_pattern == 0b10000){
|
|
|
|
|
std.debug.print("z_minus_obscuring_pattern {b}\n", .{z_minus_obscuring_pattern});
|
|
|
|
|
std.debug.print("dimension {}\n", .{dimention});
|
|
|
|
|
std.debug.print("x {}\n", .{x});
|
|
|
|
|
std.debug.print("y {}-{}\n", .{y_start, y_end});
|
|
|
|
|
std.debug.print("z {}-{}\n", .{z_start, z_end});
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-09 19:15:08 +01:00
|
|
|
var raw_quad: RawQuad = undefined;
|
2025-07-03 14:11:35 +01:00
|
|
|
switch (dimention) {
|
|
|
|
|
X_DIRECTION => {
|
2025-04-09 19:15:08 +01:00
|
|
|
raw_quad = .{
|
|
|
|
|
.tile = surface,
|
2025-04-10 15:56:19 +01:00
|
|
|
.top_left = v3.new(x + 0.5 * sign, ymax, zright),
|
|
|
|
|
.top_right = v3.new(x + 0.5 * sign, ymax, zleft),
|
|
|
|
|
.bottom_left = v3.new(x + 0.5 * sign, ymin, zright),
|
|
|
|
|
.bottom_right = v3.new(x + 0.5 * sign, ymin, zleft),
|
2025-04-09 19:15:08 +01:00
|
|
|
.normal = v3.new(sign, 0, 0),
|
|
|
|
|
.width = zmax - zmin,
|
|
|
|
|
.height = ymax - ymin,
|
|
|
|
|
|
2025-07-03 14:11:35 +01:00
|
|
|
.top_obscuring_pattern = 0, //z_plus_obscuring_pattern,
|
|
|
|
|
.left_obscuring_pattern = if(z_minus_obscuring_pattern == 0b10000) 0x80 else 0,
|
|
|
|
|
.right_obscuring_pattern = 0, //y_plus_obscuring_pattern,
|
|
|
|
|
.bottom_obscuring_pattern = 0, //y_minus_obscuring_pattern,
|
2025-04-09 19:15:08 +01:00
|
|
|
.top_left_obscured = false,
|
|
|
|
|
.top_right_obscured = false,
|
|
|
|
|
.bottom_right_obscured = false,
|
|
|
|
|
.bottom_left_obscured = false,
|
|
|
|
|
};
|
|
|
|
|
},
|
2025-07-03 14:11:35 +01:00
|
|
|
Y_DIRECTION => {
|
2025-04-09 19:15:08 +01:00
|
|
|
raw_quad = .{
|
|
|
|
|
.tile = surface,
|
2025-04-10 15:56:19 +01:00
|
|
|
.bottom_left = v3.new(yleft, zmin, x + 0.5 * sign),
|
|
|
|
|
.top_left = v3.new(yleft, zmax, x + 0.5 * sign),
|
|
|
|
|
.bottom_right = v3.new(yright, zmin, x + 0.5 * sign),
|
|
|
|
|
.top_right = v3.new(yright, zmax, x + 0.5 * sign),
|
2025-04-09 19:15:08 +01:00
|
|
|
.normal = v3.new(0, 0, sign),
|
|
|
|
|
.height = zmax - zmin,
|
|
|
|
|
.width = ymax - ymin,
|
|
|
|
|
|
|
|
|
|
.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,
|
|
|
|
|
.bottom_left_obscured = false,
|
|
|
|
|
};
|
|
|
|
|
},
|
2025-07-03 14:11:35 +01:00
|
|
|
Z_DIRECTION => {
|
2025-04-09 19:15:08 +01:00
|
|
|
raw_quad = .{
|
|
|
|
|
.tile = surface,
|
2025-04-10 15:56:19 +01:00
|
|
|
.top_left = v3.new(zleft, x + 0.5 * sign, ymin),
|
|
|
|
|
.top_right = v3.new(zright, x + 0.5 * sign, ymin),
|
|
|
|
|
.bottom_left = v3.new(zleft, x + 0.5 * sign, ymax),
|
|
|
|
|
.bottom_right = v3.new(zright, x + 0.5 * sign, ymax),
|
2025-04-09 19:15:08 +01:00
|
|
|
.normal = v3.new(0, sign, 0),
|
|
|
|
|
.width = zmax - zmin,
|
|
|
|
|
.height = ymax - ymin,
|
|
|
|
|
|
2025-04-14 20:29:41 +01:00
|
|
|
.top_obscuring_pattern = 0,
|
|
|
|
|
.left_obscuring_pattern = 0,
|
|
|
|
|
.right_obscuring_pattern = 0,
|
|
|
|
|
.bottom_obscuring_pattern = 0,
|
2025-04-09 19:15:08 +01:00
|
|
|
.top_left_obscured = false,
|
|
|
|
|
.top_right_obscured = false,
|
|
|
|
|
.bottom_right_obscured = false,
|
|
|
|
|
.bottom_left_obscured = false,
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
else => unreachable,
|
|
|
|
|
}
|
|
|
|
|
return raw_quad;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-10 15:56:19 +01:00
|
|
|
// Create mesh of a chunk. tile_rows and tile_columns are the dimensions of the tiles.png file, in terms of individual tile textures.
|
2025-04-14 20:29:41 +01:00
|
|
|
pub fn createMesh(chunk: Chunk, tile_rows: u32, tile_columns: u32) !raylib.ChunkMesh {
|
2024-09-12 22:51:16 +01:00
|
|
|
var raw_quads = try std.ArrayList(RawQuad).initCapacity(chunk.a7r, 4096);
|
|
|
|
|
defer raw_quads.deinit();
|
2024-09-09 18:05:14 +01:00
|
|
|
|
2025-04-10 15:56:19 +01:00
|
|
|
// Begin scanning the chunk for tile surfaces to make raw quads.
|
2025-07-03 14:11:35 +01:00
|
|
|
inline for (0..3) |dimention| { // Iterate over the 3 dimensions, X, Y and Z.
|
2024-09-15 21:25:30 +01:00
|
|
|
for (0..32) |raw_x| {
|
2024-09-12 22:51:16 +01:00
|
|
|
const x: u5 = @intCast(raw_x);
|
2025-04-10 15:56:19 +01:00
|
|
|
// Create surface arrays for the +x side of the layer and the -x side.
|
2024-12-16 23:19:26 +00:00
|
|
|
var positive_tile_surfaces: [32][32]u32 = .{.{0} ** 32} ** 32;
|
|
|
|
|
var negative_tile_surfaces: [32][32]u32 = .{.{0} ** 32} ** 32;
|
2024-09-15 21:25:30 +01:00
|
|
|
for (0..32) |raw_y| for (0..32) |raw_z| {
|
|
|
|
|
const y: u5 = @intCast(raw_y);
|
|
|
|
|
const z: u5 = @intCast(raw_z);
|
2025-07-03 14:11:35 +01:00
|
|
|
const tile: u32 = chunk.getTileRawShifted(x, y, z, dimention);
|
2025-04-10 15:56:19 +01:00
|
|
|
if (tile == 0) continue; // If air, there is no surface.
|
|
|
|
|
// If either at the edge of the chunk or the tile is exposed, create a tile surface.
|
2025-07-03 14:11:35 +01:00
|
|
|
if (x == 31 or chunk.getTileRawShifted(x + 1, y, z, dimention) == 0) positive_tile_surfaces[y][z] = tile;
|
|
|
|
|
if (x == 0 or chunk.getTileRawShifted(x - 1, y, z, dimention) == 0) negative_tile_surfaces[y][z] = tile;
|
2024-09-12 22:51:16 +01:00
|
|
|
};
|
2024-09-15 21:25:30 +01:00
|
|
|
inline for (.{ -1, 1 }) |sign| {
|
|
|
|
|
var tile_surfaces = if (sign == 1) positive_tile_surfaces else negative_tile_surfaces;
|
2025-04-10 15:56:19 +01:00
|
|
|
for (0..32) |y_start| for (0..32) |z_start| {
|
|
|
|
|
const surface = tile_surfaces[y_start][z_start]; // Starting surface tile type.
|
|
|
|
|
if (surface == 0) continue; // No surface if air.
|
|
|
|
|
tile_surfaces[y_start][z_start] = 0; // Replace this surface with air, since the corresponding quad will be created.
|
|
|
|
|
// 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;
|
2025-04-14 20:29:41 +01:00
|
|
|
// todo: meshing can be optimized with SIMD stuff!!
|
2025-04-10 15:56:19 +01:00
|
|
|
// 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;
|
2024-09-15 21:25:30 +01:00
|
|
|
}
|
2025-04-10 15:56:19 +01:00
|
|
|
// Greedy meshing: Extend the quad in the +z direction, until the next line does not consist of tiles of correct type.
|
|
|
|
|
zloop: while (z_end <= 31) : (z_end += 1) {
|
|
|
|
|
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;
|
2024-09-15 21:25:30 +01:00
|
|
|
}
|
2025-04-14 20:29:41 +01:00
|
|
|
// 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;
|
2025-07-03 14:11:35 +01:00
|
|
|
if (chunk.getTileRawShifted(if (sign == 1) x+1 else x-1, y, @intCast(z_start - 1), dimention) != 0) z_minus_obscuring_pattern |= 1;
|
2025-04-14 20:29:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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;
|
2025-07-03 14:11:35 +01:00
|
|
|
if (chunk.getTileRawShifted(if (sign == 1) x+1 else x-1, y, @intCast(z_end), dimention) != 0) z_plus_obscuring_pattern |= 1;
|
2025-04-14 20:29:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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;
|
2025-07-03 14:11:35 +01:00
|
|
|
if (chunk.getTileRawShifted(if (sign == 1) x+1 else x-1, @intCast(y_start - 1), z, dimention) != 0) y_minus_obscuring_pattern |= 1;
|
2025-04-14 20:29:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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;
|
2025-07-03 14:11:35 +01:00
|
|
|
if (chunk.getTileRawShifted(if (sign == 1) x+1 else x-1, @intCast(y_end), z, dimention) != 0) y_plus_obscuring_pattern |= 1;
|
2025-04-14 20:29:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// std.debug.print("{}\n", .{z_minus_obscuring_pattern});
|
2025-07-03 14:11:35 +01:00
|
|
|
const raw_quad = packRawQuad(@floatFromInt(raw_x), y_start, y_end, z_start, z_end, sign, dimention, surface, y_minus_obscuring_pattern, y_plus_obscuring_pattern, z_minus_obscuring_pattern, z_plus_obscuring_pattern);
|
2024-09-15 21:25:30 +01:00
|
|
|
try raw_quads.append(raw_quad);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-09-12 22:51:16 +01:00
|
|
|
}
|
|
|
|
|
|
2025-04-09 20:00:20 +01:00
|
|
|
// Create OpenGL buffers
|
2024-12-23 18:49:37 +00:00
|
|
|
const triangle_count: i32 = @as(i32, @intCast(raw_quads.items.len)) * 2;
|
2024-09-12 22:51:16 +01:00
|
|
|
|
2024-12-23 18:49:37 +00:00
|
|
|
const arr_size: u32 = @as(u32, @intCast(triangle_count)) * 3 * @sizeOf(f32);
|
2024-09-09 18:05:14 +01:00
|
|
|
|
|
|
|
|
const vertices: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 3)));
|
|
|
|
|
const texcoords: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 2)));
|
2025-04-14 20:29:41 +01:00
|
|
|
const tiletexcoords: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 2)));
|
2024-09-09 18:05:14 +01:00
|
|
|
const normals: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 3)));
|
2025-07-03 14:11:35 +01:00
|
|
|
const metadata1_packed: [*]u32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 4)));
|
|
|
|
|
const occlusion_sides: [*]u32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 4)));
|
2024-09-09 18:05:14 +01:00
|
|
|
|
2024-09-12 22:51:16 +01:00
|
|
|
for (raw_quads.items, 0..) |raw_quad, i| {
|
2025-04-09 20:00:20 +01:00
|
|
|
if (raw_quad.tile <= 0) continue; // air tile, no texture
|
2024-12-16 23:19:26 +00:00
|
|
|
const tile = raw_quad.tile;
|
2024-09-19 21:33:32 +01:00
|
|
|
|
2025-04-09 20:00:20 +01:00
|
|
|
// Set normals for the quads (same as the triangles.)
|
2024-09-12 22:51:16 +01:00
|
|
|
for (0..6) |j| {
|
|
|
|
|
normals[18 * i + 3 * j + 0] = raw_quad.normal.x;
|
|
|
|
|
normals[18 * i + 3 * j + 1] = raw_quad.normal.y;
|
|
|
|
|
normals[18 * i + 3 * j + 2] = raw_quad.normal.z;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-09 20:00:20 +01:00
|
|
|
// Find UV coordinates of corresponding tiles.
|
2024-09-19 21:33:32 +01:00
|
|
|
const left_uv = @as(f32, @floatFromInt(tile % tile_columns)) / @as(f32, @floatFromInt(tile_columns));
|
|
|
|
|
const right_uv = @as(f32, @floatFromInt(tile % tile_columns + 1)) / @as(f32, @floatFromInt(tile_columns));
|
|
|
|
|
const top_uv = @as(f32, @floatFromInt(tile / tile_columns)) / @as(f32, @floatFromInt(tile_rows));
|
|
|
|
|
const bottom_uv = @as(f32, @floatFromInt(tile / tile_columns + 1)) / @as(f32, @floatFromInt(tile_rows));
|
|
|
|
|
|
2025-04-09 19:15:08 +01:00
|
|
|
// Unwrap raw quads vertex coordinates and UV coordinates into OpenGL buffers.
|
|
|
|
|
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;
|
2025-04-14 20:29:41 +01:00
|
|
|
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;
|
2024-12-23 18:45:07 +00:00
|
|
|
|
2025-04-09 19:15:08 +01:00
|
|
|
inline for (0..6) |corner_id| {
|
|
|
|
|
vertices[VERTICES_BLOCK_SIZE * i + corner_id * 3 + 0] = vertex_corners[corner_id].x;
|
|
|
|
|
vertices[VERTICES_BLOCK_SIZE * i + corner_id * 3 + 1] = vertex_corners[corner_id].y;
|
|
|
|
|
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];
|
2025-04-14 20:29:41 +01:00
|
|
|
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];
|
2025-04-09 19:15:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store metadata into OpenGL buffers.
|
2025-07-03 14:11:35 +01:00
|
|
|
for (0..6) |j| {
|
2025-04-09 19:15:08 +01:00
|
|
|
const metadata1 = Metadata1{
|
2025-07-03 14:11:35 +01:00
|
|
|
.top_left_obscured = raw_quad.top_left_obscured,
|
|
|
|
|
.top_right_obscured = raw_quad.top_right_obscured,
|
|
|
|
|
.bottom_left_obscured = raw_quad.bottom_left_obscured,
|
|
|
|
|
.bottom_right_obscured = raw_quad.bottom_right_obscured,
|
2025-04-09 19:15:08 +01:00
|
|
|
.quad_height = @intFromFloat(raw_quad.height),
|
|
|
|
|
.quad_width = @intFromFloat(raw_quad.width),
|
|
|
|
|
};
|
|
|
|
|
const metadata1_baked: [4]f32 = @bitCast(metadata1);
|
|
|
|
|
for (0..4) |k| {
|
|
|
|
|
metadata1_packed[24 * i + 4 * j + k] = @bitCast(@as(f32, metadata1_baked[k]));
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-03 14:11:35 +01:00
|
|
|
|
|
|
|
|
// Store ambient occlusion sides into OpenGL buffers.
|
|
|
|
|
for (0..6) |j| {
|
|
|
|
|
occlusion_sides[24 * i + 4 * j + 0] = raw_quad.left_obscuring_pattern;
|
|
|
|
|
occlusion_sides[24 * i + 4 * j + 1] = raw_quad.right_obscuring_pattern;
|
|
|
|
|
occlusion_sides[24 * i + 4 * j + 2] = raw_quad.top_obscuring_pattern;
|
|
|
|
|
occlusion_sides[24 * i + 4 * j + 3] = raw_quad.bottom_obscuring_pattern;
|
2024-12-23 18:45:07 +00:00
|
|
|
}
|
2024-09-12 22:51:16 +01:00
|
|
|
}
|
2024-09-09 18:05:14 +01:00
|
|
|
|
2025-04-10 15:56:19 +01:00
|
|
|
// Create mesh using the buffers.
|
2025-04-14 20:29:41 +01:00
|
|
|
var mesh = raylib.ChunkMesh{
|
2024-09-09 18:05:14 +01:00
|
|
|
.triangleCount = triangle_count,
|
|
|
|
|
.vertexCount = triangle_count * 3,
|
|
|
|
|
|
|
|
|
|
.vertices = vertices,
|
|
|
|
|
.texcoords = texcoords,
|
2025-04-14 20:29:41 +01:00
|
|
|
.tiletexcoords = tiletexcoords,
|
2024-09-09 18:05:14 +01:00
|
|
|
.normals = normals,
|
2025-04-14 20:29:41 +01:00
|
|
|
.metadata1 = metadata1_packed,
|
2025-07-03 14:11:35 +01:00
|
|
|
.occlusion_sides = occlusion_sides,
|
2024-12-16 23:19:26 +00:00
|
|
|
|
2024-09-09 18:05:14 +01:00
|
|
|
.vaoId = 0,
|
|
|
|
|
.vboId = null,
|
|
|
|
|
};
|
|
|
|
|
|
2025-04-14 20:29:41 +01:00
|
|
|
raylib.UploadChunkMesh(@ptrCast(&mesh), false);
|
2024-09-09 18:05:14 +01:00
|
|
|
|
|
|
|
|
return mesh;
|
|
|
|
|
}
|
|
|
|
|
};
|