Compare commits

...
Sign in to create a new pull request.

11 commits

Author SHA1 Message Date
06ca8a5c0d chore: more todos 2025-08-04 12:03:31 +01:00
3158098ad4 more todos 2025-08-04 12:02:10 +01:00
cbe80ca2df amend todos 2025-08-04 11:01:34 +01:00
4d22b8783f refactor: remove stupidity 2025-08-03 19:01:25 +01:00
35a81340b1 refactor: split large functions into smaller ones 2025-08-03 18:52:11 +01:00
c7a194c0d3 fix merge conflict 2025-08-03 17:57:40 +01:00
Radonchnk
6bf75f87ab dimention 2025-04-19 17:46:27 +01:00
1cc01e7cf9 fix 2 iq 2025-04-19 17:38:53 +01:00
Radonchnk
92085b9228 lil refactor 2025-04-19 14:48:48 +01:00
Radonchnk
4999b300b2 Merge branch 'main' of https://forge.irithice.cc/irithice/voxel-test
deez nuts
2025-04-19 14:28:01 +01:00
Radonchnk
5994325858 disable vdsinc 2025-04-19 14:27:35 +01:00
5 changed files with 153 additions and 140 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Before After
Before After

View file

@ -60,10 +60,5 @@ void main()
outColor *= texture(occlusionMap, (vec2(ao_square, 0)+fract(fragTileTexCoord))/vec2(256, 1));
outColor.a = 1;
//uint bit = uint(fragTileTexCoord * 32);
//outColor.g = (((uint(quadWidth) >> bit) & uint(1)) == uint(1)) ?
// ((bit % uint(2) == uint(0)) ? 1.0 : 0.8):
// ((bit % uint(2) == uint(0)) ? 0.0 : 0.2);
}

View file

@ -1,4 +1,5 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const raylib_helper = @import("lib_helpers/raylib_helper.zig");
const raylib = raylib_helper.raylib;
@ -9,7 +10,6 @@ const chunks = @import("world/chunk.zig");
const TILE_TEXTURE_RESOLUTION = 16;
const benchmark_chunk_meshing = false;
const debug = true;
pub fn drawCameraPosition(camera: raylib.Camera3D, x: i32, y: i32) !void {
@ -29,6 +29,26 @@ pub fn moveCamera(camera: *raylib.Camera3D, vec: raylib.Vector3) void {
camera.target = v3.add(camera.target, vec);
}
pub fn createDefaultChunk(a7r: Allocator) !chunks.Chunk {
var chunk = try chunks.Chunk.init(a7r);
const height_generator = znoise.FnlGenerator{ .seed = 413445 };
const tile_type_generator = znoise.FnlGenerator{ .seed = 4435, .frequency = 0.1 };
for (0..32) |raw_x| for (0..32) |raw_y| for (0..32) |raw_z| {
const x: u5 = @intCast(raw_x);
const y: u5 = @intCast(raw_y);
const z: u5 = @intCast(raw_z);
const xf: f32 = @floatFromInt(raw_x);
const yf: f32 = @floatFromInt(raw_y);
const zf: f32 = @floatFromInt(raw_z);
const tile_type: u32 = if (tile_type_generator.noise3(xf, yf, zf) > 0) 1 else 2;
const height: f32 = (height_generator.noise2(xf, zf) + 1) * 16;
if (height >= yf) chunk.setTile(x, y, z, tile_type);
// if((xf-16)*(xf-16)+(yf-16)*(yf-16)+(zf-16)*(zf-16) < 16*16) chunk.setTile(x, y, z, tile_type);
};
return chunk;
}
pub fn main() !void {
if (!debug) raylib.SetTraceLogLevel(raylib.LOG_ERROR);
@ -73,38 +93,9 @@ pub fn main() !void {
defer raylib.UnloadShader(shader);
raylib.SetShaderValue(shader, raylib.GetShaderLocation(shader, "textureTiling"), &.{ @as(f32, @floatFromInt(tile_columns)), @as(f32, @floatFromInt(tile_rows)) }, raylib.SHADER_UNIFORM_VEC2);
var chunk = try chunks.Chunk.init(a7r);
var chunk = try createDefaultChunk(a7r);
defer chunk.deinit();
// const height_generator = znoise.FnlGenerator{ .seed = 413445 };
const tile_type_generator = znoise.FnlGenerator{ .seed = 4435, .frequency = 0.1 };
for (0..32) |raw_x| for (0..32) |raw_y| for (0..32) |raw_z| {
const x: u5 = @intCast(raw_x);
const y: u5 = @intCast(raw_y);
const z: u5 = @intCast(raw_z);
const xf: f32 = @floatFromInt(raw_x);
const yf: f32 = @floatFromInt(raw_y);
const zf: f32 = @floatFromInt(raw_z);
const tile_type: u32 = if (tile_type_generator.noise3(xf, yf, zf) > 0) 2 else 2;
// const height: f32 = (height_generator.noise2(xf, zf) + 1) * 16 + @as(f32, if(x > 24) 4.0 else 0.0) + @as(f32, if(z < 8) 4.0 else 0.0);
// if (height >= yf) chunk.setTile(x, y, z, tile_type);
if((xf-16)*(xf-16)+(yf-16)*(yf-16)+(zf-16)*(zf-16) < 16*16) chunk.setTile(x, y, z, tile_type);
};
if (benchmark_chunk_meshing) {
var tmp: u64 = 0;
for (0..500) |_| {
const start = try std.time.Instant.now();
const model = raylib.LoadChunkModelFromMesh(try chunk.createMesh(tile_rows, tile_columns));
defer raylib.UnloadChunkModel(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.LoadChunkModelFromMesh(try chunk.createMesh(tile_rows, tile_columns));
defer raylib.UnloadChunkModel(model);
model.materials[0].maps[raylib.MATERIAL_MAP_DIFFUSE].texture = texture;
@ -113,10 +104,6 @@ pub fn main() !void {
model.materials[0].shader.locs[raylib.SHADER_LOC_MAP_DIFFUSE+1] = raylib.GetShaderLocation(shader, "occlusionMap");
raylib.SetShaderValueTexture(shader, model.materials[0].shader.locs[raylib.SHADER_LOC_MAP_DIFFUSE+1], ambient_occlusion_texture); model.materials[0].maps[raylib.MATERIAL_MAP_DIFFUSE+1].texture = ambient_occlusion_texture;
// for (0..32) |i|{
// std.debug.print("shader loc {}: {}\n", .{i, shader.locs[i]});
// }
while (!raylib.WindowShouldClose()) {
raylib.ClearBackground(raylib.BLACK);

View file

@ -198,11 +198,100 @@ pub const Chunk = struct {
}
return raw_quad;
}
fn packMeshFromRawQuads(raw_quads: std.ArrayList(RawQuad), tile_columns: u32, tile_rows: u32) raylib.ChunkMesh {
// Create OpenGL buffers
const triangle_count: i32 = @as(i32, @intCast(raw_quads.items.len)) * 2;
// 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.ChunkMesh {
const arr_size: u32 = @as(u32, @intCast(triangle_count)) * 3 * @sizeOf(f32);
const vertices: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 3)));
const texcoords: [*]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: [*]u32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 4)));
const occlusion_sides: [*]u32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 4)));
for (raw_quads.items, 0..) |raw_quad, i| {
if (raw_quad.tile <= 0) continue; // air tile, no texture
const tile = raw_quad.tile;
// Set normals for the quads (same as the triangles.)
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;
}
// Find UV coordinates of corresponding tiles.
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));
// 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;
const tiletexcoords_x = if(raw_quad.flip_x)
.{raw_quad.width, raw_quad.width, 0.0, 0.0, 0.0, raw_quad.width} else
.{ 0.0, 0.0, raw_quad.width, raw_quad.width, raw_quad.width, 0.0 };
const tiletexcoords_y = if(raw_quad.flip_y) .{ raw_quad.height, 0.0 } ** 3 else .{ 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;
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];
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.
for (0..6) |j| {
const metadata1 = Metadata1{
.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,
.quad_height = @intFromFloat(raw_quad.height),
.quad_width = @intFromFloat(raw_quad.width),
};
const metadata1_baked: [4]u32 = @bitCast(metadata1);
for (0..4) |k| {
metadata1_packed[24 * i + 4 * j + k] = metadata1_baked[k];
}
}
// 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;
}
}
// Create mesh using the buffers.
return raylib.ChunkMesh{
.triangleCount = triangle_count,
.vertexCount = triangle_count * 3,
.vertices = vertices,
.texcoords = texcoords,
.tiletexcoords = tiletexcoords,
.normals = normals,
.metadata1 = metadata1_packed,
.occlusion_sides = occlusion_sides,
.vaoId = 0,
.vboId = null,
};
}
fn scanForRawQuads(chunk: Chunk) !std.ArrayList(RawQuad) {
var raw_quads = try std.ArrayList(RawQuad).initCapacity(chunk.a7r, 4096);
defer raw_quads.deinit();
// Begin scanning the chunk for tile surfaces to make raw quads.
inline for (0..3) |dimension| { // Iterate over the 3 dimensions, X, Y and Z.
@ -290,95 +379,15 @@ pub const Chunk = struct {
}
}
}
return raw_quads;
}
// Create OpenGL buffers
const triangle_count: i32 = @as(i32, @intCast(raw_quads.items.len)) * 2;
// 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.ChunkMesh {
var raw_quads = try scanForRawQuads(chunk);
defer raw_quads.deinit();
const arr_size: u32 = @as(u32, @intCast(triangle_count)) * 3 * @sizeOf(f32);
const vertices: [*]f32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 3)));
const texcoords: [*]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: [*]u32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 4)));
const occlusion_sides: [*]u32 = @ptrCast(@alignCast(raylib.MemAlloc(arr_size * 4)));
for (raw_quads.items, 0..) |raw_quad, i| {
if (raw_quad.tile <= 0) continue; // air tile, no texture
const tile = raw_quad.tile;
// Set normals for the quads (same as the triangles.)
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;
}
// Find UV coordinates of corresponding tiles.
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));
// 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;
const tiletexcoords_x = if(raw_quad.flip_x)
.{raw_quad.width, raw_quad.width, 0.0, 0.0, 0.0, raw_quad.width} else
.{ 0.0, 0.0, raw_quad.width, raw_quad.width, raw_quad.width, 0.0 };
const tiletexcoords_y = if(raw_quad.flip_y) .{ raw_quad.height, 0.0 } ** 3 else .{ 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;
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];
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.
for (0..6) |j| {
const metadata1 = Metadata1{
.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,
.quad_height = @intFromFloat(raw_quad.height),
.quad_width = @intFromFloat(raw_quad.width),
};
const metadata1_baked: [4]u32 = @bitCast(metadata1);
for (0..4) |k| {
metadata1_packed[24 * i + 4 * j + k] = metadata1_baked[k];
}
}
// 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;
}
}
// Create mesh using the buffers.
var mesh = raylib.ChunkMesh{
.triangleCount = triangle_count,
.vertexCount = triangle_count * 3,
.vertices = vertices,
.texcoords = texcoords,
.tiletexcoords = tiletexcoords,
.normals = normals,
.metadata1 = metadata1_packed,
.occlusion_sides = occlusion_sides,
.vaoId = 0,
.vboId = null,
};
var mesh = packMeshFromRawQuads(raw_quads, tile_columns, tile_rows);
raylib.UploadChunkMesh(@ptrCast(&mesh), false);

42
todo.md
View file

@ -1,11 +1,33 @@
# current tasks
yoink implementation of mesh into meshes.zig so i could modify it to add ambient occlusion vars
implement ambient occlusion either
- via passing extra info to shader (can use 132 bits to indicate what tiles around the quad are obsuring light. need to pass as an extra variable)
implement animations (smooth or minecraft like), random orientation/mirroring, metallicity (shine on edges)
implement second layer of textures on top of first one for more details
limit vertical camera rotations
# world generation
1. world state "object" which will keep track of chunks to generate, to remove, to keep, those that are currently being generated in a thread pool.
2. parallelised creeping chunk generation in a radius.
3. removing of chunks when they go outside the radius.
4. interlinked chunks; each chunk should have a ptr to the 26 neighbor chunks or null.
5. world state linking and unlinking chunks automatically.
6. ambient occlusion spanning between chunks.
7. chunk loading, unloading, saving/loading from files; world state keeping track of chunks loaded and unloaded as well
8. generation of (LOD) simplified chunk models
9. generation of simplified chunks
10. separate LOD radiuses for different LOD levels
11. partial chunk loading and unloading.
12. block change handling: recalculate only the relevant quads
# future tasks
implement chunk meshing cache to reduce delay on block placement
investigate binary/SIMD meshing for performance
# misc graphics
1. implement possibilities for 6 different surfaces for a tile, e.g a branch tile should have 4 bark surfaces and 2 inner surfaces
2. implement random rotation and reflection of surfaces
3. implement fixed rotation and reflection of surfaces
4. implement custom models, e.g a tall grass tile is a model with two planes crossing in the middle (see: minecraft). implement optional greedy meshing for such models; e.g tall grass textures could fuse together diagonally and grass could have 2 layers who could mesh as well.
5. implement metallicity (e.g block of iron) (?, discuss)
6. implement random shininess (e.g rough diamond block, would sparkle as you move across, see purple rocks in PEAK's Alpine biome)
7. implement emissive textures (e.g a computer screen, see minecraft's enderman/spider eyes and sculk blocks)
8. implement bloom effect (see gregtech's glowing EBF's)
# directional sun/moonlight
1. discuss implementation
# directional central lightning, e.g torches
1. discuss implementation
# tooling
1. benchmarking step in build.zig: benchmark chunk meshing, generation, and similar tasks which will need to be performed at scale
2. texturepack compiler: something that would compile minecraft-style texturepacks into a format that could be more suited for in game usage.