feat: basic world state, manual chunkgen for now

This commit is contained in:
catangent 2025-08-10 12:02:12 +01:00
parent 06ca8a5c0d
commit dd41aabf0f
4 changed files with 132 additions and 46 deletions

View file

@ -7,6 +7,8 @@ const v3 = raylib_helper.v3;
const znoise = @import("znoise"); const znoise = @import("znoise");
const chunks = @import("world/chunk.zig"); const chunks = @import("world/chunk.zig");
const WorldState = @import("world/world_state.zig").WorldState;
const chunk_generators = @import("world/chunk_generators.zig");
const TILE_TEXTURE_RESOLUTION = 16; const TILE_TEXTURE_RESOLUTION = 16;
@ -29,36 +31,20 @@ pub fn moveCamera(camera: *raylib.Camera3D, vec: raylib.Vector3) void {
camera.target = v3.add(camera.target, vec); 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 { pub fn main() !void {
if (!debug) raylib.SetTraceLogLevel(raylib.LOG_ERROR); if (!debug) raylib.SetTraceLogLevel(raylib.LOG_ERROR);
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const a7r = gpa.allocator(); const allocator = gpa.allocator();
defer { defer {
const status = gpa.deinit(); const status = gpa.deinit();
if (status == .leak) std.debug.print("MEMORY LEAK DETECTED!!!!!!!!!!!!!!!!!!!!!!\n", .{}) else std.debug.print("no leaks detected.\n", .{}); if (status == .leak) std.debug.print("MEMORY LEAK DETECTED!!!!!!!!!!!!!!!!!!!!!!\n", .{}) else std.debug.print("no leaks detected.\n", .{});
} }
var thread_pool: std.Thread.Pool = undefined;
try thread_pool.init(.{.allocator = allocator});
defer thread_pool.deinit();
raylib.SetConfigFlags(raylib.FLAG_WINDOW_RESIZABLE | raylib.FLAG_FULLSCREEN_MODE); raylib.SetConfigFlags(raylib.FLAG_WINDOW_RESIZABLE | raylib.FLAG_FULLSCREEN_MODE);
const display = raylib.GetCurrentMonitor(); const display = raylib.GetCurrentMonitor();
@ -93,16 +79,13 @@ pub fn main() !void {
defer raylib.UnloadShader(shader); 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); 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 world_state: WorldState = WorldState.init(thread_pool, allocator);
defer chunk.deinit(); defer world_state.deinit();
const model = raylib.LoadChunkModelFromMesh(try chunk.createMesh(tile_rows, tile_columns)); for (0..10) |x| for (0..10) |z| {
defer raylib.UnloadChunkModel(model); _ = try world_state.generateChunk(.{@intCast(x), 0, @intCast(z)});
model.materials[0].maps[raylib.MATERIAL_MAP_DIFFUSE].texture = texture; _ = try world_state.generateChunkModel(.{@intCast(x), 0, @intCast(z)}, tile_rows, tile_columns, texture, ambient_occlusion_texture, shader);
model.materials[0].shader = shader; };
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;
while (!raylib.WindowShouldClose()) { while (!raylib.WindowShouldClose()) {
raylib.ClearBackground(raylib.BLACK); raylib.ClearBackground(raylib.BLACK);
@ -140,18 +123,21 @@ pub fn main() !void {
raylib.BeginDrawing(); raylib.BeginDrawing();
defer raylib.EndDrawing(); defer raylib.EndDrawing();
const model_position = v3.new(0, 0, 0);
{ {
raylib.BeginMode3D(camera); raylib.BeginMode3D(camera);
defer raylib.EndMode3D(); defer raylib.EndMode3D();
raylib.BeginShaderMode(shader);
defer raylib.EndShaderMode(); world_state.chunks_access_mutex.lock();
var chunks_iterator = world_state.chunks.valueIterator();
raylib.SetShaderValueTexture(shader, raylib.GetShaderLocation(shader, "ambientOcclusionTexture"), ambient_occlusion_texture); while (chunks_iterator.next()) |entry| {
raylib.DrawChunkModel(model, model_position, 0.5, raylib.WHITE); if (entry.*.model) |model| {
const model_position = v3.new(@floatFromInt(entry.position[0]*16), @floatFromInt(entry.position[1]*16), @floatFromInt(entry.position[2]*16));
raylib.DrawChunkModel(model, model_position, 0.5, raylib.WHITE);
}
}
world_state.chunks_access_mutex.unlock();
} }
raylib.DrawFPS(10, 10); raylib.DrawFPS(10, 10);
try drawCameraPosition(camera, 10, 30); try drawCameraPosition(camera, 10, 30);
} }

View file

@ -2,7 +2,7 @@ const std = @import("std");
const raylib_helper = @import("../lib_helpers/raylib_helper.zig"); const raylib_helper = @import("../lib_helpers/raylib_helper.zig");
const raylib = raylib_helper.raylib; const raylib = raylib_helper.raylib;
const v3 = raylib_helper.v3; const v3 = raylib_helper.v3;
const A7r = std.mem.Allocator; const Allocator = std.mem.Allocator;
const comptimePrint = std.fmt.comptimePrint; const comptimePrint = std.fmt.comptimePrint;
const VERTICES_BLOCK_SIZE = 2 * 3 * 3; const VERTICES_BLOCK_SIZE = 2 * 3 * 3;
@ -56,19 +56,19 @@ comptime {
pub const Chunk = struct { pub const Chunk = struct {
tiles: []u32, tiles: []u32,
a7r: A7r, allocator: Allocator,
pub fn init(a7r: A7r) !Chunk { pub fn init(allocator: Allocator) !Chunk {
const self = Chunk{ const self = Chunk{
.a7r = a7r, .allocator = allocator,
.tiles = try a7r.alloc(u32, 32 * 32 * 32), .tiles = try allocator.alloc(u32, 32 * 32 * 32),
}; };
@memset(self.tiles, 0); @memset(self.tiles, 0);
return self; return self;
} }
pub fn deinit(self: Chunk) void { pub fn deinit(self: Chunk) void {
self.a7r.free(self.tiles); self.allocator.free(self.tiles);
} }
// 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]. // 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].
@ -291,7 +291,7 @@ pub const Chunk = struct {
} }
fn scanForRawQuads(chunk: Chunk) !std.ArrayList(RawQuad) { fn scanForRawQuads(chunk: Chunk) !std.ArrayList(RawQuad) {
var raw_quads = try std.ArrayList(RawQuad).initCapacity(chunk.a7r, 4096); var raw_quads = try std.ArrayList(RawQuad).initCapacity(chunk.allocator, 4096);
// Begin scanning the chunk for tile surfaces to make raw quads. // 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. inline for (0..3) |dimension| { // Iterate over the 3 dimensions, X, Y and Z.

View file

@ -0,0 +1,23 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const znoise = @import("znoise");
const chunks = @import("chunk.zig");
pub fn createChunk(allocator: Allocator, chunk_pos: @Vector(3, i64)) !chunks.Chunk {
var chunk = try chunks.Chunk.init(allocator);
const height_generator = znoise.FnlGenerator{ .seed = 413445 };
for (0..32) |raw_x| for (0..32) |raw_y| for (0..32) |raw_z| {
const local_x: u5 = @intCast(raw_x);
const local_y: u5 = @intCast(raw_y);
const local_z: u5 = @intCast(raw_z);
const x: f32 = @floatFromInt(@as(i64, @intCast(raw_x)) + chunk_pos[0]*32);
const y: f32 = @floatFromInt(@as(i64, @intCast(raw_y)) + chunk_pos[1]*32);
const z: f32 = @floatFromInt(@as(i64, @intCast(raw_z)) + chunk_pos[2]*32);
const height: f32 = (height_generator.noise2(x, z) + 1) * 16;
const tile_type: u32 = if(height >= y + 1) 2 else 1;
if (height >= y) chunk.setTile(local_x, local_y, local_z, tile_type);
};
return chunk;
}

77
src/world/world_state.zig Normal file
View file

@ -0,0 +1,77 @@
const std = @import("std");
const Thread = std.Thread;
const Allocator = std.mem.Allocator;
const AutoHashMap = std.AutoHashMap;
const Chunk = @import("chunk.zig").Chunk;
const chunk_generators = @import("chunk_generators.zig");
const raylib_helper = @import("../lib_helpers/raylib_helper.zig");
const raylib = raylib_helper.raylib;
pub const WorldStateError = error{
ChunkNotGeneratedError,
};
pub const ChunkEntry = struct {
chunk: ?Chunk = null,
model: ?raylib.ChunkModel = null,
position: @Vector(3, i64),
};
pub const WorldState = struct {
pool: Thread.Pool = undefined,
allocator: Allocator = undefined,
// TODO: we can do better than a hashmap and a mutex
chunks: AutoHashMap(@Vector(3, i64), ChunkEntry) = undefined,
chunks_access_mutex: Thread.Mutex = .{},
pub fn init(global_pool: Thread.Pool, allocator: Allocator) WorldState {
var world_state: WorldState = undefined;
world_state.pool = global_pool;
world_state.allocator = allocator;
world_state.chunks = AutoHashMap(@Vector(3, i64), ChunkEntry).init(allocator);
return world_state;
}
pub fn deinit(self: *WorldState) void {
var chunk_iterator = self.chunks.valueIterator();
while(chunk_iterator.next()) |entry| {
if(entry.*.chunk) |chunk| {
chunk.deinit();
}
if(entry.*.model) |model| {
raylib.UnloadChunkModel(model);
}
}
self.chunks.deinit();
}
pub fn generateChunk(self: *WorldState, pos: @Vector(3, i64)) !Chunk {
const chunk = try chunk_generators.createChunk(self.allocator, pos);
self.chunks_access_mutex.lock();
try self.chunks.put(pos, .{.chunk = chunk, .position = pos});
self.chunks_access_mutex.unlock();
return chunk;
}
pub fn generateChunkModel(self: *WorldState, pos: @Vector(3, i64), tile_rows: u32, tile_columns: u32, texture: raylib.Texture, ambient_occlusion_texture: raylib.Texture, shader: raylib.Shader) !raylib.ChunkModel {
self.chunks_access_mutex.lock();
const chunk_entry = (try self.chunks.getOrPutValue(pos, .{.position = pos})).value_ptr;
self.chunks_access_mutex.unlock();
const chunk = chunk_entry.chunk;
if(chunk == null) return WorldStateError.ChunkNotGeneratedError;
const model = raylib.LoadChunkModelFromMesh(try chunk.?.createMesh(tile_rows, tile_columns));
model.materials[0].maps[raylib.MATERIAL_MAP_DIFFUSE].texture = texture;
model.materials[0].shader = shader;
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;
chunk_entry.model = model;
return model;
}
};