Compare commits

..

No commits in common. "main" and "gl_test" have entirely different histories.

5 changed files with 140 additions and 153 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Before After
Before After

View file

@ -60,5 +60,10 @@ 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,5 +1,4 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const raylib_helper = @import("lib_helpers/raylib_helper.zig");
const raylib = raylib_helper.raylib;
@ -10,6 +9,7 @@ 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,26 +29,6 @@ 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);
@ -93,9 +73,38 @@ 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 createDefaultChunk(a7r);
var chunk = try chunks.Chunk.init(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;
@ -104,6 +113,10 @@ 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,100 +198,11 @@ 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;
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) {
// 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 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.
@ -379,15 +290,95 @@ pub const Chunk = struct {
}
}
}
return raw_quads;
}
// 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();
// Create OpenGL buffers
const triangle_count: i32 = @as(i32, @intCast(raw_quads.items.len)) * 2;
var mesh = packMeshFromRawQuads(raw_quads, tile_columns, tile_rows);
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,
};
raylib.UploadChunkMesh(@ptrCast(&mesh), false);

42
todo.md
View file

@ -1,33 +1,11 @@
# 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
# 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
# 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.
# future tasks
implement chunk meshing cache to reduce delay on block placement
investigate binary/SIMD meshing for performance