diff --git a/build.zig b/build.zig index 35a64d24..d9a9af3b 100644 --- a/build.zig +++ b/build.zig @@ -21,10 +21,7 @@ pub fn build(b: *std.Build) void { const chunk_debug_enable = b.option([]const u8, "chunk-debug-enable", "Re-enable one subsystem in chunk-debug-mode (lod, water, caves, decorations)") orelse ""; options.addOption([]const u8, "chunk_debug_enable", chunk_debug_enable); - const world_worldgen_options = b.addOptions(); - world_worldgen_options.addOption(bool, "chunk_debug_mode", chunk_debug_mode); - world_worldgen_options.addOption([]const u8, "chunk_debug_enable", chunk_debug_enable); - const auto_world = b.option([]const u8, "auto-world", "Auto-open a world generator directly (normal, overworld, flat)") orelse ""; + const auto_world = b.option([]const u8, "auto-world", "Auto-open a world generator directly by id or alias (normal, overworld, flat, test)") orelse ""; options.addOption([]const u8, "auto_world", auto_world); const auto_preset = b.option([]const u8, "auto-preset", "Graphics preset to apply for auto-world launches (low, medium, high, ultra, extreme)") orelse ""; @@ -73,7 +70,6 @@ pub fn build(b: *std.Build) void { const shadow_test_variant = b.option([]const u8, "shadow-test-variant", "Shadow test scene variant (dug-cave, bend)") orelse "dug-cave"; options.addOption([]const u8, "shadow_test_variant", shadow_test_variant); - world_worldgen_options.addOption([]const u8, "shadow_test_variant", shadow_test_variant); const benchmark = b.option(bool, "benchmark", "Enable benchmark mode") orelse false; options.addOption(bool, "benchmark", benchmark); @@ -142,6 +138,11 @@ pub fn build(b: *std.Build) void { const engine_lighting = b.createModule(.{ .root_source_file = b.path("modules/engine-lighting/src/root.zig"), .target = target, .optimize = optimize }); const engine_ui = b.createModule(.{ .root_source_file = b.path("modules/engine-ui/src/root.zig"), .target = target, .optimize = optimize }); const world_core = b.createModule(.{ .root_source_file = b.path("modules/world-core/src/root.zig"), .target = target, .optimize = optimize }); + const worldgen_api = b.createModule(.{ .root_source_file = b.path("modules/worldgen-api/src/root.zig"), .target = target, .optimize = optimize }); + const worldgen_common = b.createModule(.{ .root_source_file = b.path("modules/worldgen-common/src/root.zig"), .target = target, .optimize = optimize }); + const worldgen_overworld = b.createModule(.{ .root_source_file = b.path("modules/worldgen-overworld/src/root.zig"), .target = target, .optimize = optimize }); + const worldgen_flat = b.createModule(.{ .root_source_file = b.path("modules/worldgen-flat/src/root.zig"), .target = target, .optimize = optimize }); + const worldgen_test = b.createModule(.{ .root_source_file = b.path("modules/worldgen-test/src/root.zig"), .target = target, .optimize = optimize }); const world_worldgen = b.createModule(.{ .root_source_file = b.path("modules/world-worldgen/src/root.zig"), .target = target, .optimize = optimize }); const world_meshing = b.createModule(.{ .root_source_file = b.path("modules/world-meshing/src/root.zig"), .target = target, .optimize = optimize }); const world_lod = b.createModule(.{ .root_source_file = b.path("modules/world-lod/src/root.zig"), .target = target, .optimize = optimize }); @@ -227,6 +228,31 @@ pub fn build(b: *std.Build) void { addSharedImports(world_core, zig_math, zig_noise, fs_module, sync_module, c_module, options); world_core.addImport("engine-core", engine_core); world_core.addImport("engine-math", engine_math); + addSharedImports(worldgen_api, zig_math, zig_noise, fs_module, sync_module, c_module, options); + worldgen_api.addImport("world-core", world_core); + addSharedImports(worldgen_common, zig_math, zig_noise, fs_module, sync_module, c_module, options); + worldgen_common.addImport("world-core", world_core); + const worldgen_overworld_options = b.addOptions(); + worldgen_overworld_options.addOption(bool, "chunk_debug_mode", chunk_debug_mode); + worldgen_overworld_options.addOption([]const u8, "chunk_debug_enable", chunk_debug_enable); + addSharedImports(worldgen_overworld, zig_math, zig_noise, fs_module, sync_module, c_module, options); + worldgen_overworld.addImport("engine-core", engine_core); + worldgen_overworld.addImport("engine-rhi", engine_rhi); + worldgen_overworld.addImport("world-core", world_core); + worldgen_overworld.addImport("worldgen-api", worldgen_api); + worldgen_overworld.addImport("worldgen-common", worldgen_common); + worldgen_overworld.addOptions("worldgen_overworld_options", worldgen_overworld_options); + addSharedImports(worldgen_flat, zig_math, zig_noise, fs_module, sync_module, c_module, options); + worldgen_flat.addImport("world-core", world_core); + worldgen_flat.addImport("worldgen-api", worldgen_api); + worldgen_flat.addImport("worldgen-common", worldgen_common); + const worldgen_test_options = b.addOptions(); + worldgen_test_options.addOption([]const u8, "shadow_test_variant", shadow_test_variant); + addSharedImports(worldgen_test, zig_math, zig_noise, fs_module, sync_module, c_module, options); + worldgen_test.addImport("world-core", world_core); + worldgen_test.addImport("worldgen-api", worldgen_api); + worldgen_test.addImport("worldgen-common", worldgen_common); + worldgen_test.addOptions("worldgen_test_options", worldgen_test_options); addSharedImports(world_worldgen, zig_math, zig_noise, fs_module, sync_module, c_module, options); addSharedImports(world_meshing, zig_math, zig_noise, fs_module, sync_module, c_module, options); world_meshing.addImport("engine-core", engine_core); @@ -249,7 +275,11 @@ pub fn build(b: *std.Build) void { world_worldgen.addImport("engine-core", engine_core); world_worldgen.addImport("engine-rhi", engine_rhi); world_worldgen.addImport("world-core", world_core); - world_worldgen.addOptions("world_worldgen_options", world_worldgen_options); + world_worldgen.addImport("worldgen-api", worldgen_api); + world_worldgen.addImport("worldgen-common", worldgen_common); + world_worldgen.addImport("worldgen-overworld", worldgen_overworld); + world_worldgen.addImport("worldgen-flat", worldgen_flat); + world_worldgen.addImport("worldgen-test", worldgen_test); addSharedImports(world_runtime, zig_math, zig_noise, fs_module, sync_module, c_module, options); world_runtime.addImport("engine-core", engine_core); diff --git a/modules/game-ui/src/screens/world_list.zig b/modules/game-ui/src/screens/world_list.zig index f872fc53..4280902f 100644 --- a/modules/game-ui/src/screens/world_list.zig +++ b/modules/game-ui/src/screens/world_list.zig @@ -10,6 +10,7 @@ const WorldScreen = @import("world.zig").WorldScreen; const log = @import("engine-core").log; const fs = @import("fs"); const text_input = @import("game-core").text_input; +const registry = @import("world-worldgen").registry; fn getenv(name: [:0]const u8) ?[]const u8 { const value = std.c.getenv(name) orelse return null; @@ -36,11 +37,13 @@ pub const LevelDat = struct { }; pub fn writeLevelDat(allocator: std.mem.Allocator, save_dir: fs.Dir, name: []const u8, seed: u64, generator_index: usize, last_played: i64) !void { + const generator_id = if (generator_index < registry.getGeneratorCount()) registry.getGeneratorId(generator_index) else registry.getGeneratorId(0); const payload = .{ .name = name, .seed = seed, .last_played = last_played, .generator_index = generator_index, + .generator_id = generator_id, }; const json_str = try std.json.Stringify.valueAlloc(allocator, payload, .{ .whitespace = .indent_2 }); defer allocator.free(json_str); @@ -60,6 +63,7 @@ pub fn readLevelDat(allocator: std.mem.Allocator, save_dir: fs.Dir) ?LevelDat { const name_val = obj.get("name") orelse return null; const seed_val = obj.get("seed") orelse return null; const gen_val = obj.get("generator_index") orelse return null; + const gen_id_val = obj.get("generator_id"); const last_val = obj.get("last_played"); const name_str = switch (name_val) { .string => |s| s, @@ -73,10 +77,17 @@ pub fn readLevelDat(allocator: std.mem.Allocator, save_dir: fs.Dir) ?LevelDat { .integer => |i| i, else => 0, } else 0; - const generator_index: usize = switch (gen_val) { + var generator_index: usize = switch (gen_val) { .integer => |i| @intCast(i), else => 0, }; + const generator_id_source = if (gen_id_val) |giv| switch (giv) { + .string => |s| s, + else => "", + } else ""; + if (generator_id_source.len > 0) { + generator_index = registry.findGeneratorIndex(generator_id_source) orelse generator_index; + } const name_copy = allocator.dupe(u8, name_str) catch return null; errdefer allocator.free(name_copy); return .{ diff --git a/modules/world-worldgen/src/generator_interface.zig b/modules/world-worldgen/src/generator_interface.zig index 1a8838c2..c25ad365 100644 --- a/modules/world-worldgen/src/generator_interface.zig +++ b/modules/world-worldgen/src/generator_interface.zig @@ -1,175 +1,17 @@ -const std = @import("std"); -const world_core = @import("world-core"); -const Chunk = world_core.Chunk; -const LODLevel = world_core.LODLevel; -const LODSimplifiedData = world_core.LODSimplifiedData; - -const region_pkg = @import("region.zig"); -const RegionInfo = region_pkg.RegionInfo; -const BiomeId = @import("world-core").BiomeId; - -pub const ColumnInfo = struct { - height: i32, - biome: BiomeId, - is_ocean: bool, - temperature: f32, - humidity: f32, - continentalness: f32, -}; - -/// Options for controlling generation detail level -pub const GenerationOptions = struct { - /// LOD level - higher = more simplified - lod_level: LODLevel = .lod0, - - /// Enable cave generation (worm + noise caves) - enable_caves: bool = true, - - /// Enable worm caves specifically (expensive neighbor checks) - enable_worm_caves: bool = true, - - /// Enable decorations (trees, flowers, grass) - enable_decorations: bool = true, - - /// Enable ore generation - enable_ores: bool = true, - - /// Enable lighting calculation - enable_lighting: bool = true, - - /// Noise octave reduction (0 = full detail, higher = fewer octaves) - octave_reduction: u8 = 0, - - /// Skip biome edge blending - skip_biome_blending: bool = false, - - /// Create options from LOD level with sensible defaults - pub fn fromLOD(lod: LODLevel) GenerationOptions { - const level = @intFromEnum(lod); - return .{ - .lod_level = lod, - .enable_caves = level <= 1, - .enable_worm_caves = level == 0, - .enable_decorations = level <= 1, - .enable_ores = level == 0, - .enable_lighting = level == 0, - .octave_reduction = @intCast(level), - .skip_biome_blending = level > 0, - }; - } -}; - -pub const GeneratorInfo = struct { - /// Human-readable name of the generator (e.g. "Overworld"). - /// Displayed in the UI. Should be kept relatively short (under 32 chars recommended). - name: []const u8, - /// Description of the generator's features and purpose. - description: []const u8, -}; - -/// Pluggable generator interface. -/// Uses a VTable for runtime polymorphism. -pub const Generator = struct { - ptr: *anyopaque, - vtable: *const VTable, - info: GeneratorInfo, - - pub const VTable = struct { - /// Generate a full chunk of terrain. - /// Implementations MUST: - /// 1. Set chunk.generated = false at the very beginning of the function. - /// 2. Respect the stop_flag (if provided) by returning early without setting chunk.generated = true. - /// 3. Set chunk.generated = true ONLY after ALL generation steps (terrain, ores, features, lighting) are complete. - /// Note: If stop_flag is used to return early, the chunk may be left in a partially modified state. - /// The chunk.generated = false flag ensures that other systems (like rendering) do not process this incomplete data. - generate: *const fn (ptr: *anyopaque, chunk: *Chunk, stop_flag: ?*const bool) void, - - /// Generate heightmap-only data for LOD levels. - generateHeightmapOnly: *const fn (ptr: *anyopaque, data: *LODSimplifiedData, region_x: i32, region_z: i32, lod_level: LODLevel) void, - - /// Periodically check if internal caches should be recentered around the player. - /// Returns true if any cache was recentered. - maybeRecenterCache: *const fn (ptr: *anyopaque, player_x: i32, player_z: i32) bool, - - /// Get the world seed used by this generator - getSeed: *const fn (ptr: *anyopaque) u64, - - /// Get region info for a specific world position - getRegionInfo: *const fn (ptr: *anyopaque, world_x: i32, world_z: i32) RegionInfo, - - /// Get detailed column information for a world position (used for mapping) - getColumnInfo: *const fn (ptr: *anyopaque, wx: f32, wz: f32) ColumnInfo, - - /// Clean up generator resources. - /// This MUST be called to free any memory or resources allocated by the generator. - deinit: *const fn (ptr: *anyopaque, allocator: std.mem.Allocator) void, - }; - - pub fn generate(self: Generator, chunk: *Chunk, stop_flag: ?*const bool) void { - self.vtable.generate(self.ptr, chunk, stop_flag); - } - - pub fn generateHeightmapOnly(self: Generator, data: *LODSimplifiedData, region_x: i32, region_z: i32, lod_level: LODLevel) void { - self.vtable.generateHeightmapOnly(self.ptr, data, region_x, region_z, lod_level); - } - - pub fn maybeRecenterCache(self: Generator, player_x: i32, player_z: i32) bool { - return self.vtable.maybeRecenterCache(self.ptr, player_x, player_z); - } - - pub fn getSeed(self: Generator) u64 { - return self.vtable.getSeed(self.ptr); - } - - pub fn getRegionInfo(self: Generator, world_x: i32, world_z: i32) RegionInfo { - return self.vtable.getRegionInfo(self.ptr, world_x, world_z); - } - - pub fn getColumnInfo(self: Generator, wx: f32, wz: f32) ColumnInfo { - return self.vtable.getColumnInfo(self.ptr, wx, wz); - } - - pub fn deinit(self: Generator, allocator: std.mem.Allocator) void { - self.vtable.deinit(self.ptr, allocator); - } -}; - -pub const IChunkGenerator = struct { - generator: Generator, - - pub fn generate(self: IChunkGenerator, chunk: *Chunk, stop_flag: ?*const bool) void { - self.generator.generate(chunk, stop_flag); - } -}; - -pub const ILODHeightmapGenerator = struct { - generator: Generator, - - pub fn generateHeightmapOnly(self: ILODHeightmapGenerator, data: *LODSimplifiedData, region_x: i32, region_z: i32, lod_level: LODLevel) void { - self.generator.generateHeightmapOnly(data, region_x, region_z, lod_level); - } -}; - -pub const IGeneratorInfoProvider = struct { - generator: Generator, - - pub fn getSeed(self: IGeneratorInfoProvider) u64 { - return self.generator.getSeed(); - } - - pub fn getRegionInfo(self: IGeneratorInfoProvider, world_x: i32, world_z: i32) RegionInfo { - return self.generator.getRegionInfo(world_x, world_z); - } - - pub fn getColumnInfo(self: IGeneratorInfoProvider, wx: f32, wz: f32) ColumnInfo { - return self.generator.getColumnInfo(wx, wz); - } -}; - -pub const ICacheRecenterable = struct { - generator: Generator, - - pub fn maybeRecenterCache(self: ICacheRecenterable, player_x: i32, player_z: i32) bool { - return self.generator.maybeRecenterCache(player_x, player_z); - } -}; +const worldgen_api = @import("worldgen-api"); + +pub const ColumnInfo = worldgen_api.ColumnInfo; +pub const CreateContext = worldgen_api.CreateContext; +pub const FeatureFocus = worldgen_api.FeatureFocus; +pub const GenerationOptions = worldgen_api.GenerationOptions; +pub const Generator = worldgen_api.Generator; +pub const GeneratorDescriptor = worldgen_api.GeneratorDescriptor; +pub const GeneratorInfo = worldgen_api.GeneratorInfo; +pub const ICacheRecenterable = worldgen_api.ICacheRecenterable; +pub const IChunkGenerator = worldgen_api.IChunkGenerator; +pub const IGeneratorInfoProvider = worldgen_api.IGeneratorInfoProvider; +pub const ILODHeightmapGenerator = worldgen_api.ILODHeightmapGenerator; +pub const RegionInfo = worldgen_api.RegionInfo; +pub const RegionMood = worldgen_api.RegionMood; +pub const RegionRole = worldgen_api.RegionRole; +pub const RegistryError = worldgen_api.RegistryError; diff --git a/modules/world-worldgen/src/registry.zig b/modules/world-worldgen/src/registry.zig index 41d9efdd..73e7d696 100644 --- a/modules/world-worldgen/src/registry.zig +++ b/modules/world-worldgen/src/registry.zig @@ -2,67 +2,63 @@ const std = @import("std"); const log = @import("engine-core").log; const gen_interface = @import("generator_interface.zig"); const Generator = gen_interface.Generator; -const overworld = @import("overworld_generator.zig"); -const OverworldGenerator = overworld.OverworldGenerator; -const FlatWorldGenerator = @import("flat_world.zig").FlatWorldGenerator; -const ShadowTestWorldGenerator = @import("shadow_test_world.zig").ShadowTestWorldGenerator; -const deco_registry = @import("decoration_registry.zig"); -const build_options = @import("world_worldgen_options"); +const overworld = @import("worldgen-overworld"); +const flat_world = @import("worldgen-flat"); +const shadow_test_world = @import("worldgen-test"); +const worldgen_api = @import("worldgen-api"); pub const RegistryError = error{ InvalidGeneratorIndex, + InvalidGeneratorId, OutOfMemory, }; pub const GeneratorType = struct { + id: []const u8, + aliases: []const []const u8 = &.{}, info: gen_interface.GeneratorInfo, initFn: *const fn (seed: u64, allocator: std.mem.Allocator) RegistryError!Generator, }; pub const GENERATORS = [_]GeneratorType{ .{ - .info = OverworldGenerator.INFO, + .id = overworld.descriptor.id, + .aliases = overworld.descriptor.aliases, + .info = overworld.descriptor.info, .initFn = initOverworld, }, .{ - .info = FlatWorldGenerator.INFO, + .id = flat_world.descriptor.id, + .aliases = flat_world.descriptor.aliases, + .info = flat_world.descriptor.info, .initFn = initFlatWorld, }, .{ - .info = ShadowTestWorldGenerator.INFO, + .id = shadow_test_world.descriptor.id, + .aliases = shadow_test_world.descriptor.aliases, + .info = shadow_test_world.descriptor.info, .initFn = initShadowTestWorld, }, }; fn initOverworld(seed: u64, allocator: std.mem.Allocator) RegistryError!Generator { - const gen = allocator.create(OverworldGenerator) catch return error.OutOfMemory; - const restore_water = chunkDebugRestoreEnabled("water") or chunkDebugRestoreEnabled("watergen"); - const restore_caves = chunkDebugRestoreEnabled("caves"); - const restore_decorations = chunkDebugRestoreEnabled("decorations"); - gen.* = if (build_options.chunk_debug_mode) - OverworldGenerator.initWithParams(seed, allocator, deco_registry.StandardDecorationProvider.provider(), .{ - .terrain_shape = .{ - .sea_level = if (restore_water) 64 else -1, - .ocean_threshold = if (restore_water) 0.37 else -1.0, - .disable_caves = !restore_caves, - }, - .basic_chunks_only = !restore_decorations, - }) - else - OverworldGenerator.init(seed, allocator, deco_registry.StandardDecorationProvider.provider()); - return gen.generator(); + return overworld.descriptor.create(.{ .seed = seed, .allocator = allocator }) catch |err| return mapApiError(err); } fn initFlatWorld(seed: u64, allocator: std.mem.Allocator) RegistryError!Generator { - const gen = allocator.create(FlatWorldGenerator) catch return error.OutOfMemory; - gen.* = FlatWorldGenerator.init(seed, allocator); - return gen.generator(); + return flat_world.descriptor.create(.{ .seed = seed, .allocator = allocator }) catch |err| return mapApiError(err); } fn initShadowTestWorld(seed: u64, allocator: std.mem.Allocator) RegistryError!Generator { - const gen = allocator.create(ShadowTestWorldGenerator) catch return error.OutOfMemory; - gen.* = ShadowTestWorldGenerator.init(seed, allocator); - return gen.generator(); + return shadow_test_world.descriptor.create(.{ .seed = seed, .allocator = allocator }) catch |err| return mapApiError(err); +} + +fn mapApiError(err: worldgen_api.RegistryError) RegistryError { + return switch (err) { + error.InvalidGeneratorIndex => error.InvalidGeneratorIndex, + error.InvalidGeneratorId => error.InvalidGeneratorId, + error.OutOfMemory => error.OutOfMemory, + }; } pub fn getGeneratorCount() usize { @@ -74,6 +70,21 @@ pub fn getGeneratorInfo(index: usize) gen_interface.GeneratorInfo { return GENERATORS[index].info; } +pub fn getGeneratorId(index: usize) []const u8 { + std.debug.assert(index < GENERATORS.len); + return GENERATORS[index].id; +} + +pub fn findGeneratorIndex(id_or_alias: []const u8) ?usize { + for (GENERATORS, 0..) |generator_type, index| { + if (std.ascii.eqlIgnoreCase(id_or_alias, generator_type.id)) return index; + for (generator_type.aliases) |alias| { + if (std.ascii.eqlIgnoreCase(id_or_alias, alias)) return index; + } + } + return null; +} + pub fn createGenerator(index: usize, seed: u64, allocator: std.mem.Allocator) RegistryError!Generator { if (index >= GENERATORS.len) return error.InvalidGeneratorIndex; return GENERATORS[index].initFn(seed, allocator) catch |err| { @@ -82,13 +93,7 @@ pub fn createGenerator(index: usize, seed: u64, allocator: std.mem.Allocator) Re }; } -fn chunkDebugRestoreEnabled(name: []const u8) bool { - if (!build_options.chunk_debug_mode) return false; - - var it = std.mem.tokenizeScalar(u8, build_options.chunk_debug_enable, ','); - while (it.next()) |token| { - const trimmed = std.mem.trim(u8, token, " \t"); - if (std.ascii.eqlIgnoreCase(trimmed, name)) return true; - } - return false; +pub fn createGeneratorById(id_or_alias: []const u8, seed: u64, allocator: std.mem.Allocator) RegistryError!Generator { + const index = findGeneratorIndex(id_or_alias) orelse return error.InvalidGeneratorId; + return createGenerator(index, seed, allocator); } diff --git a/modules/world-worldgen/src/root.zig b/modules/world-worldgen/src/root.zig index d0597393..f9eb5369 100644 --- a/modules/world-worldgen/src/root.zig +++ b/modules/world-worldgen/src/root.zig @@ -1,43 +1,44 @@ -pub const biome = @import("biome.zig"); -pub const biome_color_provider = @import("biome_color_provider.zig"); -pub const biome_edge_detector = @import("biome_edge_detector.zig"); -pub const biome_registry = @import("biome_registry.zig"); -pub const biome_registry_tests = @import("biome_registry_tests.zig"); -pub const biome_selector = @import("biome_selector.zig"); -pub const biome_selector_tests = @import("biome_selector_tests.zig"); -pub const biome_source = @import("biome_source.zig"); -pub const caves = @import("caves.zig"); -pub const caves_tests = @import("caves_tests.zig"); -pub const climate_snapshot = @import("climate_snapshot.zig"); -pub const coastal_generator = @import("coastal_generator.zig"); -pub const coastal_generator_tests = @import("coastal_generator_tests.zig"); -pub const biome_decorator = @import("biome_decorator.zig"); -pub const decoration_provider = @import("decoration_provider.zig"); -pub const decoration_registry = @import("decoration_registry.zig"); -pub const decoration_types = @import("decoration_types.zig"); -pub const flat_world = @import("flat_world.zig"); +pub const overworld = @import("worldgen-overworld"); +pub const biome = overworld.biome; +pub const biome_color_provider = overworld.biome_color_provider; +pub const biome_edge_detector = overworld.biome_edge_detector; +pub const biome_registry = overworld.biome_registry; +pub const biome_registry_tests = overworld.biome_registry_tests; +pub const biome_selector = overworld.biome_selector; +pub const biome_selector_tests = overworld.biome_selector_tests; +pub const biome_source = overworld.biome_source; +pub const caves = overworld.caves; +pub const caves_tests = overworld.caves_tests; +pub const climate_snapshot = overworld.climate_snapshot; +pub const coastal_generator = overworld.coastal_generator; +pub const coastal_generator_tests = overworld.coastal_generator_tests; +pub const biome_decorator = overworld.biome_decorator; +pub const decoration_provider = overworld.decoration_provider; +pub const decoration_registry = overworld.decoration_registry; +pub const decoration_types = overworld.decoration_types; +pub const flat_world = @import("worldgen-flat"); pub const generator_interface = @import("generator_interface.zig"); -pub const gen_region = @import("gen_region.zig"); -pub const height_sampler = @import("height_sampler.zig"); -pub const height_sampler_tests = @import("height_sampler_tests.zig"); -pub const noise_sampler = @import("noise_sampler.zig"); -pub const lighting_computer = @import("lighting_computer.zig"); -pub const lighting_interface = @import("lighting_interface.zig"); -pub const mood = @import("mood.zig"); -pub const noise = @import("noise.zig"); -pub const overworld_generator = @import("overworld_generator.zig"); -pub const region = @import("region.zig"); +pub const gen_region = overworld.gen_region; +pub const height_sampler = overworld.height_sampler; +pub const height_sampler_tests = overworld.height_sampler_tests; +pub const noise_sampler = overworld.noise_sampler; +pub const lighting_computer = overworld.lighting_computer; +pub const lighting_interface = overworld.lighting_interface; +pub const mood = overworld.mood; +pub const noise = overworld.noise; +pub const overworld_generator = overworld.overworld_generator; +pub const region = overworld.region; pub const registry = @import("registry.zig"); -pub const schematics = @import("schematics.zig"); -pub const shadow_test_world = @import("shadow_test_world.zig"); -pub const surface_builder = @import("surface_builder.zig"); -pub const terrain_shape_generator = @import("terrain_shape_generator.zig"); -pub const terrain_shape_generator_tests = @import("terrain_shape_generator_tests.zig"); -pub const terrain_modifier_tests = @import("terrain_modifier_tests.zig"); -pub const terrain_report = @import("terrain_report.zig"); -pub const tree_registry = @import("tree_registry.zig"); -pub const world_class = @import("world_class.zig"); -pub const world_map = @import("world_map.zig"); +pub const schematics = overworld.schematics; +pub const shadow_test_world = @import("worldgen-test"); +pub const surface_builder = overworld.surface_builder; +pub const terrain_shape_generator = overworld.terrain_shape_generator; +pub const terrain_shape_generator_tests = overworld.terrain_shape_generator_tests; +pub const terrain_modifier_tests = overworld.terrain_modifier_tests; +pub const terrain_report = overworld.terrain_report; +pub const tree_registry = overworld.tree_registry; +pub const world_class = overworld.world_class; +pub const world_map = overworld.world_map; pub const BiomeId = biome.BiomeId; pub const CaveCarveMap = caves.CaveCarveMap; @@ -60,8 +61,11 @@ pub const GeneratorType = registry.GeneratorType; pub const GENERATORS = registry.GENERATORS; pub const RegistryError = registry.RegistryError; pub const getGeneratorCount = registry.getGeneratorCount; +pub const getGeneratorId = registry.getGeneratorId; pub const getGeneratorInfo = registry.getGeneratorInfo; +pub const findGeneratorIndex = registry.findGeneratorIndex; pub const createGenerator = registry.createGenerator; +pub const createGeneratorById = registry.createGeneratorById; pub const FeatureFocus = region.FeatureFocus; pub const GenerationOptions = generator_interface.GenerationOptions; pub const Generator = generator_interface.Generator; diff --git a/modules/worldgen-api/build.zig b/modules/worldgen-api/build.zig new file mode 100644 index 00000000..cc1494fa --- /dev/null +++ b/modules/worldgen-api/build.zig @@ -0,0 +1,5 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + _ = b.addModule("worldgen-api", .{ .root_source_file = b.path("src/root.zig") }); +} diff --git a/modules/worldgen-api/build.zig.zon b/modules/worldgen-api/build.zig.zon new file mode 100644 index 00000000..66ca8e50 --- /dev/null +++ b/modules/worldgen-api/build.zig.zon @@ -0,0 +1 @@ +.{ .name = .worldgen_api, .version = "0.1.0", .minimum_zig_version = "0.16.0", .paths = .{ "build.zig", "build.zig.zon", "src" }, .dependencies = .{} } diff --git a/modules/worldgen-api/src/root.zig b/modules/worldgen-api/src/root.zig new file mode 100644 index 00000000..a7bc71ae --- /dev/null +++ b/modules/worldgen-api/src/root.zig @@ -0,0 +1,176 @@ +const std = @import("std"); +const world_core = @import("world-core"); + +const Chunk = world_core.Chunk; +const LODLevel = world_core.LODLevel; +const LODSimplifiedData = world_core.LODSimplifiedData; +const BiomeId = world_core.BiomeId; + +pub const RegionMood = enum { + calm, + sparse, + lush, + wild, +}; + +pub const RegionRole = enum { + transit, + destination, + boundary, +}; + +pub const FeatureFocus = enum { + none, + lake, + forest, + mountain, +}; + +pub const RegionInfo = struct { + mood: RegionMood, + role: RegionRole, + focus: FeatureFocus, + center_x: i32, + center_z: i32, +}; + +pub const ColumnInfo = struct { + height: i32, + biome: BiomeId, + is_ocean: bool, + temperature: f32, + humidity: f32, + continentalness: f32, +}; + +pub const GenerationOptions = struct { + lod_level: LODLevel = .lod0, + enable_caves: bool = true, + enable_worm_caves: bool = true, + enable_decorations: bool = true, + enable_ores: bool = true, + enable_lighting: bool = true, + octave_reduction: u8 = 0, + skip_biome_blending: bool = false, + + pub fn fromLOD(lod: LODLevel) GenerationOptions { + const level = @intFromEnum(lod); + return .{ + .lod_level = lod, + .enable_caves = level <= 1, + .enable_worm_caves = level == 0, + .enable_decorations = level <= 1, + .enable_ores = level == 0, + .enable_lighting = level == 0, + .octave_reduction = @intCast(level), + .skip_biome_blending = level > 0, + }; + } +}; + +pub const GeneratorInfo = struct { + name: []const u8, + description: []const u8, +}; + +pub const Generator = struct { + ptr: *anyopaque, + vtable: *const VTable, + info: GeneratorInfo, + + pub const VTable = struct { + generate: *const fn (ptr: *anyopaque, chunk: *Chunk, stop_flag: ?*const bool) void, + generateHeightmapOnly: *const fn (ptr: *anyopaque, data: *LODSimplifiedData, region_x: i32, region_z: i32, lod_level: LODLevel) void, + maybeRecenterCache: *const fn (ptr: *anyopaque, player_x: i32, player_z: i32) bool, + getSeed: *const fn (ptr: *anyopaque) u64, + getRegionInfo: *const fn (ptr: *anyopaque, world_x: i32, world_z: i32) RegionInfo, + getColumnInfo: *const fn (ptr: *anyopaque, wx: f32, wz: f32) ColumnInfo, + deinit: *const fn (ptr: *anyopaque, allocator: std.mem.Allocator) void, + }; + + pub fn generate(self: Generator, chunk: *Chunk, stop_flag: ?*const bool) void { + self.vtable.generate(self.ptr, chunk, stop_flag); + } + + pub fn generateHeightmapOnly(self: Generator, data: *LODSimplifiedData, region_x: i32, region_z: i32, lod_level: LODLevel) void { + self.vtable.generateHeightmapOnly(self.ptr, data, region_x, region_z, lod_level); + } + + pub fn maybeRecenterCache(self: Generator, player_x: i32, player_z: i32) bool { + return self.vtable.maybeRecenterCache(self.ptr, player_x, player_z); + } + + pub fn getSeed(self: Generator) u64 { + return self.vtable.getSeed(self.ptr); + } + + pub fn getRegionInfo(self: Generator, world_x: i32, world_z: i32) RegionInfo { + return self.vtable.getRegionInfo(self.ptr, world_x, world_z); + } + + pub fn getColumnInfo(self: Generator, wx: f32, wz: f32) ColumnInfo { + return self.vtable.getColumnInfo(self.ptr, wx, wz); + } + + pub fn deinit(self: Generator, allocator: std.mem.Allocator) void { + self.vtable.deinit(self.ptr, allocator); + } +}; + +pub const CreateContext = struct { + seed: u64, + allocator: std.mem.Allocator, +}; + +pub const RegistryError = error{ + InvalidGeneratorIndex, + InvalidGeneratorId, + OutOfMemory, +}; + +pub const GeneratorDescriptor = struct { + id: []const u8, + aliases: []const []const u8 = &.{}, + info: GeneratorInfo, + create: *const fn (context: CreateContext) RegistryError!Generator, +}; + +pub const IChunkGenerator = struct { + generator: Generator, + + pub fn generate(self: IChunkGenerator, chunk: *Chunk, stop_flag: ?*const bool) void { + self.generator.generate(chunk, stop_flag); + } +}; + +pub const ILODHeightmapGenerator = struct { + generator: Generator, + + pub fn generateHeightmapOnly(self: ILODHeightmapGenerator, data: *LODSimplifiedData, region_x: i32, region_z: i32, lod_level: LODLevel) void { + self.generator.generateHeightmapOnly(data, region_x, region_z, lod_level); + } +}; + +pub const IGeneratorInfoProvider = struct { + generator: Generator, + + pub fn getSeed(self: IGeneratorInfoProvider) u64 { + return self.generator.getSeed(); + } + + pub fn getRegionInfo(self: IGeneratorInfoProvider, world_x: i32, world_z: i32) RegionInfo { + return self.generator.getRegionInfo(world_x, world_z); + } + + pub fn getColumnInfo(self: IGeneratorInfoProvider, wx: f32, wz: f32) ColumnInfo { + return self.generator.getColumnInfo(wx, wz); + } +}; + +pub const ICacheRecenterable = struct { + generator: Generator, + + pub fn maybeRecenterCache(self: ICacheRecenterable, player_x: i32, player_z: i32) bool { + return self.generator.maybeRecenterCache(player_x, player_z); + } +}; diff --git a/modules/worldgen-common/build.zig b/modules/worldgen-common/build.zig new file mode 100644 index 00000000..4a0ddb56 --- /dev/null +++ b/modules/worldgen-common/build.zig @@ -0,0 +1,5 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + _ = b.addModule("worldgen-common", .{ .root_source_file = b.path("src/root.zig") }); +} diff --git a/modules/worldgen-common/build.zig.zon b/modules/worldgen-common/build.zig.zon new file mode 100644 index 00000000..f236a177 --- /dev/null +++ b/modules/worldgen-common/build.zig.zon @@ -0,0 +1 @@ +.{ .name = .worldgen_common, .version = "0.1.0", .minimum_zig_version = "0.16.0", .paths = .{ "build.zig", "build.zig.zon", "src" }, .dependencies = .{} } diff --git a/modules/world-worldgen/src/lighting_computer.zig b/modules/worldgen-common/src/lighting_computer.zig similarity index 100% rename from modules/world-worldgen/src/lighting_computer.zig rename to modules/worldgen-common/src/lighting_computer.zig diff --git a/modules/world-worldgen/src/lighting_interface.zig b/modules/worldgen-common/src/lighting_interface.zig similarity index 100% rename from modules/world-worldgen/src/lighting_interface.zig rename to modules/worldgen-common/src/lighting_interface.zig diff --git a/modules/worldgen-common/src/root.zig b/modules/worldgen-common/src/root.zig new file mode 100644 index 00000000..6cd7d7f9 --- /dev/null +++ b/modules/worldgen-common/src/root.zig @@ -0,0 +1,5 @@ +pub const lighting_computer = @import("lighting_computer.zig"); +pub const lighting_interface = @import("lighting_interface.zig"); + +pub const ILightingSystem = lighting_interface.ILightingSystem; +pub const LightingComputer = lighting_computer.LightingComputer; diff --git a/modules/worldgen-flat/build.zig b/modules/worldgen-flat/build.zig new file mode 100644 index 00000000..38c6f0af --- /dev/null +++ b/modules/worldgen-flat/build.zig @@ -0,0 +1,5 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + _ = b.addModule("worldgen-flat", .{ .root_source_file = b.path("src/root.zig") }); +} diff --git a/modules/worldgen-flat/build.zig.zon b/modules/worldgen-flat/build.zig.zon new file mode 100644 index 00000000..b86079f8 --- /dev/null +++ b/modules/worldgen-flat/build.zig.zon @@ -0,0 +1 @@ +.{ .name = .worldgen_flat, .version = "0.1.0", .minimum_zig_version = "0.16.0", .paths = .{ "build.zig", "build.zig.zon", "src" }, .dependencies = .{} } diff --git a/modules/world-worldgen/src/flat_world.zig b/modules/worldgen-flat/src/root.zig similarity index 77% rename from modules/world-worldgen/src/flat_world.zig rename to modules/worldgen-flat/src/root.zig index 134dbc34..e7603f4e 100644 --- a/modules/world-worldgen/src/flat_world.zig +++ b/modules/worldgen-flat/src/root.zig @@ -1,22 +1,18 @@ const std = @import("std"); -const gen_interface = @import("generator_interface.zig"); -const Generator = gen_interface.Generator; -const GeneratorInfo = gen_interface.GeneratorInfo; -const GenerationOptions = gen_interface.GenerationOptions; -const ColumnInfo = gen_interface.ColumnInfo; +const worldgen_api = @import("worldgen-api"); +const Generator = worldgen_api.Generator; +const GeneratorInfo = worldgen_api.GeneratorInfo; +const ColumnInfo = worldgen_api.ColumnInfo; +const RegionInfo = worldgen_api.RegionInfo; const world_core = @import("world-core"); const Chunk = world_core.Chunk; const CHUNK_SIZE_X = world_core.CHUNK_SIZE_X; const CHUNK_SIZE_Y = world_core.CHUNK_SIZE_Y; const CHUNK_SIZE_Z = world_core.CHUNK_SIZE_Z; const BlockType = world_core.BlockType; -const BiomeId = world_core.BiomeId; -const LightingComputer = @import("lighting_computer.zig").LightingComputer; const LODLevel = world_core.LODLevel; const LODSimplifiedData = world_core.LODSimplifiedData; - -const region_pkg = @import("region.zig"); -const RegionInfo = region_pkg.RegionInfo; +const LightingComputer = @import("worldgen-common").LightingComputer; pub const FlatWorldGenerator = struct { seed: u64, @@ -31,17 +27,13 @@ pub const FlatWorldGenerator = struct { }; pub fn init(seed: u64, allocator: std.mem.Allocator) FlatWorldGenerator { - return .{ - .seed = seed, - .allocator = allocator, - }; + return .{ .seed = seed, .allocator = allocator }; } pub fn generate(self: *FlatWorldGenerator, chunk: *Chunk, stop_flag: ?*const bool) void { chunk.generated = false; var local_z: u32 = 0; - while (local_z < CHUNK_SIZE_Z) : (local_z += 1) { if (stop_flag) |sf| if (sf.*) return; var local_x: u32 = 0; @@ -61,7 +53,6 @@ pub const FlatWorldGenerator = struct { .grass else .air; - chunk.setBlock(local_x, @intCast(y), local_z, block); } } @@ -83,11 +74,7 @@ pub const FlatWorldGenerator = struct { @memset(data.biomes, .plains); @memset(data.top_blocks, .grass); @memset(data.colors, GRASS_COLOR); - @memset(data.material_layers, .{ - .surface = .grass, - .subsurface = .dirt, - .foundation = .stone, - }); + @memset(data.material_layers, .{ .surface = .grass, .subsurface = .dirt, .foundation = .stone }); @memset(data.water, world_core.LODWaterState.empty); @memset(data.lighting, world_core.LODLightingHint.daylight); @memset(data.vegetation, world_core.LODVegetationHint.empty); @@ -104,26 +91,20 @@ pub const FlatWorldGenerator = struct { return self.seed; } + pub fn getRegionInfo(self: *const FlatWorldGenerator, world_x: i32, world_z: i32) RegionInfo { + _ = self; + return .{ .mood = .calm, .role = .transit, .focus = .none, .center_x = world_x, .center_z = world_z }; + } + pub fn getColumnInfo(self: *const FlatWorldGenerator, wx: f32, wz: f32) ColumnInfo { _ = self; _ = wx; _ = wz; - return .{ - .height = FLAT_HEIGHT, - .biome = .plains, - .is_ocean = false, - .temperature = 0.5, - .humidity = 0.5, - .continentalness = 0.5, - }; + return .{ .height = FLAT_HEIGHT, .biome = .plains, .is_ocean = false, .temperature = 0.5, .humidity = 0.5, .continentalness = 0.5 }; } pub fn generator(self: *FlatWorldGenerator) Generator { - return .{ - .ptr = self, - .vtable = &VTABLE, - .info = INFO, - }; + return .{ .ptr = self, .vtable = &VTABLE, .info = INFO }; } const VTABLE = Generator.VTable{ @@ -158,7 +139,7 @@ pub const FlatWorldGenerator = struct { fn getRegionInfoWrapper(ptr: *anyopaque, world_x: i32, world_z: i32) RegionInfo { const self: *FlatWorldGenerator = @ptrCast(@alignCast(ptr)); - return region_pkg.getRegion(self.seed, world_x, world_z); + return self.getRegionInfo(world_x, world_z); } fn getColumnInfoWrapper(ptr: *anyopaque, wx: f32, wz: f32) ColumnInfo { @@ -171,3 +152,16 @@ pub const FlatWorldGenerator = struct { allocator.destroy(self); } }; + +pub fn create(context: worldgen_api.CreateContext) worldgen_api.RegistryError!Generator { + const gen = context.allocator.create(FlatWorldGenerator) catch return error.OutOfMemory; + gen.* = FlatWorldGenerator.init(context.seed, context.allocator); + return gen.generator(); +} + +pub const descriptor = worldgen_api.GeneratorDescriptor{ + .id = "zigcraft:flat", + .aliases = &.{"flat"}, + .info = FlatWorldGenerator.INFO, + .create = create, +}; diff --git a/modules/worldgen-overworld/build.zig b/modules/worldgen-overworld/build.zig new file mode 100644 index 00000000..5b877b93 --- /dev/null +++ b/modules/worldgen-overworld/build.zig @@ -0,0 +1,5 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + _ = b.addModule("worldgen-overworld", .{ .root_source_file = b.path("src/root.zig") }); +} diff --git a/modules/worldgen-overworld/build.zig.zon b/modules/worldgen-overworld/build.zig.zon new file mode 100644 index 00000000..3376d8a7 --- /dev/null +++ b/modules/worldgen-overworld/build.zig.zon @@ -0,0 +1 @@ +.{ .name = .worldgen_overworld, .version = "0.1.0", .minimum_zig_version = "0.16.0", .paths = .{ "build.zig", "build.zig.zon", "src" }, .dependencies = .{} } diff --git a/modules/world-worldgen/src/biome.zig b/modules/worldgen-overworld/src/biome.zig similarity index 100% rename from modules/world-worldgen/src/biome.zig rename to modules/worldgen-overworld/src/biome.zig diff --git a/modules/world-worldgen/src/biome_color_provider.zig b/modules/worldgen-overworld/src/biome_color_provider.zig similarity index 100% rename from modules/world-worldgen/src/biome_color_provider.zig rename to modules/worldgen-overworld/src/biome_color_provider.zig diff --git a/modules/world-worldgen/src/biome_decorator.zig b/modules/worldgen-overworld/src/biome_decorator.zig similarity index 100% rename from modules/world-worldgen/src/biome_decorator.zig rename to modules/worldgen-overworld/src/biome_decorator.zig diff --git a/modules/world-worldgen/src/biome_edge_detector.zig b/modules/worldgen-overworld/src/biome_edge_detector.zig similarity index 100% rename from modules/world-worldgen/src/biome_edge_detector.zig rename to modules/worldgen-overworld/src/biome_edge_detector.zig diff --git a/modules/world-worldgen/src/biome_registry.zig b/modules/worldgen-overworld/src/biome_registry.zig similarity index 100% rename from modules/world-worldgen/src/biome_registry.zig rename to modules/worldgen-overworld/src/biome_registry.zig diff --git a/modules/world-worldgen/src/biome_registry_tests.zig b/modules/worldgen-overworld/src/biome_registry_tests.zig similarity index 100% rename from modules/world-worldgen/src/biome_registry_tests.zig rename to modules/worldgen-overworld/src/biome_registry_tests.zig diff --git a/modules/world-worldgen/src/biome_selector.zig b/modules/worldgen-overworld/src/biome_selector.zig similarity index 100% rename from modules/world-worldgen/src/biome_selector.zig rename to modules/worldgen-overworld/src/biome_selector.zig diff --git a/modules/world-worldgen/src/biome_selector_tests.zig b/modules/worldgen-overworld/src/biome_selector_tests.zig similarity index 100% rename from modules/world-worldgen/src/biome_selector_tests.zig rename to modules/worldgen-overworld/src/biome_selector_tests.zig diff --git a/modules/world-worldgen/src/biome_source.zig b/modules/worldgen-overworld/src/biome_source.zig similarity index 100% rename from modules/world-worldgen/src/biome_source.zig rename to modules/worldgen-overworld/src/biome_source.zig diff --git a/modules/world-worldgen/src/caves.zig b/modules/worldgen-overworld/src/caves.zig similarity index 100% rename from modules/world-worldgen/src/caves.zig rename to modules/worldgen-overworld/src/caves.zig diff --git a/modules/world-worldgen/src/caves_tests.zig b/modules/worldgen-overworld/src/caves_tests.zig similarity index 100% rename from modules/world-worldgen/src/caves_tests.zig rename to modules/worldgen-overworld/src/caves_tests.zig diff --git a/modules/world-worldgen/src/climate_snapshot.zig b/modules/worldgen-overworld/src/climate_snapshot.zig similarity index 100% rename from modules/world-worldgen/src/climate_snapshot.zig rename to modules/worldgen-overworld/src/climate_snapshot.zig diff --git a/modules/world-worldgen/src/coastal_generator.zig b/modules/worldgen-overworld/src/coastal_generator.zig similarity index 100% rename from modules/world-worldgen/src/coastal_generator.zig rename to modules/worldgen-overworld/src/coastal_generator.zig diff --git a/modules/world-worldgen/src/coastal_generator_tests.zig b/modules/worldgen-overworld/src/coastal_generator_tests.zig similarity index 100% rename from modules/world-worldgen/src/coastal_generator_tests.zig rename to modules/worldgen-overworld/src/coastal_generator_tests.zig diff --git a/modules/world-worldgen/src/decoration_provider.zig b/modules/worldgen-overworld/src/decoration_provider.zig similarity index 100% rename from modules/world-worldgen/src/decoration_provider.zig rename to modules/worldgen-overworld/src/decoration_provider.zig diff --git a/modules/world-worldgen/src/decoration_registry.zig b/modules/worldgen-overworld/src/decoration_registry.zig similarity index 100% rename from modules/world-worldgen/src/decoration_registry.zig rename to modules/worldgen-overworld/src/decoration_registry.zig diff --git a/modules/world-worldgen/src/decoration_types.zig b/modules/worldgen-overworld/src/decoration_types.zig similarity index 100% rename from modules/world-worldgen/src/decoration_types.zig rename to modules/worldgen-overworld/src/decoration_types.zig diff --git a/modules/world-worldgen/src/gen_region.zig b/modules/worldgen-overworld/src/gen_region.zig similarity index 100% rename from modules/world-worldgen/src/gen_region.zig rename to modules/worldgen-overworld/src/gen_region.zig diff --git a/modules/worldgen-overworld/src/generator_interface.zig b/modules/worldgen-overworld/src/generator_interface.zig new file mode 100644 index 00000000..c25ad365 --- /dev/null +++ b/modules/worldgen-overworld/src/generator_interface.zig @@ -0,0 +1,17 @@ +const worldgen_api = @import("worldgen-api"); + +pub const ColumnInfo = worldgen_api.ColumnInfo; +pub const CreateContext = worldgen_api.CreateContext; +pub const FeatureFocus = worldgen_api.FeatureFocus; +pub const GenerationOptions = worldgen_api.GenerationOptions; +pub const Generator = worldgen_api.Generator; +pub const GeneratorDescriptor = worldgen_api.GeneratorDescriptor; +pub const GeneratorInfo = worldgen_api.GeneratorInfo; +pub const ICacheRecenterable = worldgen_api.ICacheRecenterable; +pub const IChunkGenerator = worldgen_api.IChunkGenerator; +pub const IGeneratorInfoProvider = worldgen_api.IGeneratorInfoProvider; +pub const ILODHeightmapGenerator = worldgen_api.ILODHeightmapGenerator; +pub const RegionInfo = worldgen_api.RegionInfo; +pub const RegionMood = worldgen_api.RegionMood; +pub const RegionRole = worldgen_api.RegionRole; +pub const RegistryError = worldgen_api.RegistryError; diff --git a/modules/world-worldgen/src/height_sampler.zig b/modules/worldgen-overworld/src/height_sampler.zig similarity index 100% rename from modules/world-worldgen/src/height_sampler.zig rename to modules/worldgen-overworld/src/height_sampler.zig diff --git a/modules/world-worldgen/src/height_sampler_tests.zig b/modules/worldgen-overworld/src/height_sampler_tests.zig similarity index 100% rename from modules/world-worldgen/src/height_sampler_tests.zig rename to modules/worldgen-overworld/src/height_sampler_tests.zig diff --git a/modules/world-worldgen/src/mood.zig b/modules/worldgen-overworld/src/mood.zig similarity index 100% rename from modules/world-worldgen/src/mood.zig rename to modules/worldgen-overworld/src/mood.zig diff --git a/modules/world-worldgen/src/noise.zig b/modules/worldgen-overworld/src/noise.zig similarity index 100% rename from modules/world-worldgen/src/noise.zig rename to modules/worldgen-overworld/src/noise.zig diff --git a/modules/world-worldgen/src/noise_sampler.zig b/modules/worldgen-overworld/src/noise_sampler.zig similarity index 100% rename from modules/world-worldgen/src/noise_sampler.zig rename to modules/worldgen-overworld/src/noise_sampler.zig diff --git a/modules/world-worldgen/src/overworld_generator.zig b/modules/worldgen-overworld/src/overworld_generator.zig similarity index 99% rename from modules/world-worldgen/src/overworld_generator.zig rename to modules/worldgen-overworld/src/overworld_generator.zig index e567facf..d468c206 100644 --- a/modules/world-worldgen/src/overworld_generator.zig +++ b/modules/worldgen-overworld/src/overworld_generator.zig @@ -40,7 +40,7 @@ const CoastalSurfaceType = terrain_shape_mod.CoastalSurfaceType; const BiomeSource = @import("biome.zig").BiomeSource; const BiomeDecorator = @import("biome_decorator.zig").BiomeDecorator; const tree_registry = @import("tree_registry.zig"); -const LightingComputer = @import("lighting_computer.zig").LightingComputer; +const LightingComputer = @import("worldgen-common").LightingComputer; const Mutex = sync.Mutex; pub const OverworldGenerator = struct { diff --git a/modules/world-worldgen/src/region.zig b/modules/worldgen-overworld/src/region.zig similarity index 96% rename from modules/world-worldgen/src/region.zig rename to modules/worldgen-overworld/src/region.zig index 194a9e76..52d05826 100644 --- a/modules/world-worldgen/src/region.zig +++ b/modules/worldgen-overworld/src/region.zig @@ -4,26 +4,11 @@ const std = @import("std"); const Vec3f = @import("noise.zig").Vec3f; +const worldgen_api = @import("worldgen-api"); -pub const RegionMood = enum { - calm, // Boring on purpose - sparse, // Empty, lonely - lush, // Abundant vegetation - wild, // Chaos, landmarks -}; - -pub const RegionRole = enum { - transit, // Fast, boring, flat - destination, // One star feature (focus) - boundary, // Awkward, separation -}; - -pub const FeatureFocus = enum { - none, - lake, - forest, - mountain, -}; +pub const RegionMood = worldgen_api.RegionMood; +pub const RegionRole = worldgen_api.RegionRole; +pub const FeatureFocus = worldgen_api.FeatureFocus; pub const PathType = enum { none, @@ -33,13 +18,7 @@ pub const PathType = enum { }; /// Complete region information with role, mood, and feature focus -pub const RegionInfo = struct { - mood: RegionMood, - role: RegionRole, - focus: FeatureFocus, - center_x: i32, - center_z: i32, -}; +pub const RegionInfo = worldgen_api.RegionInfo; // ============================================================================ // FEATURE ALLOW/DENY TABLES (Exact Per Spec) diff --git a/modules/worldgen-overworld/src/root.zig b/modules/worldgen-overworld/src/root.zig new file mode 100644 index 00000000..c5d7fda5 --- /dev/null +++ b/modules/worldgen-overworld/src/root.zig @@ -0,0 +1,80 @@ +const std = @import("std"); +const worldgen_api = @import("worldgen-api"); + +pub const biome = @import("biome.zig"); +pub const biome_color_provider = @import("biome_color_provider.zig"); +pub const biome_edge_detector = @import("biome_edge_detector.zig"); +pub const biome_registry = @import("biome_registry.zig"); +pub const biome_registry_tests = @import("biome_registry_tests.zig"); +pub const biome_selector = @import("biome_selector.zig"); +pub const biome_selector_tests = @import("biome_selector_tests.zig"); +pub const biome_source = @import("biome_source.zig"); +pub const caves = @import("caves.zig"); +pub const caves_tests = @import("caves_tests.zig"); +pub const climate_snapshot = @import("climate_snapshot.zig"); +pub const coastal_generator = @import("coastal_generator.zig"); +pub const coastal_generator_tests = @import("coastal_generator_tests.zig"); +pub const biome_decorator = @import("biome_decorator.zig"); +pub const decoration_provider = @import("decoration_provider.zig"); +pub const decoration_registry = @import("decoration_registry.zig"); +pub const decoration_types = @import("decoration_types.zig"); +pub const gen_region = @import("gen_region.zig"); +pub const height_sampler = @import("height_sampler.zig"); +pub const height_sampler_tests = @import("height_sampler_tests.zig"); +pub const noise_sampler = @import("noise_sampler.zig"); +pub const lighting_computer = @import("worldgen-common").lighting_computer; +pub const lighting_interface = @import("worldgen-common").lighting_interface; +pub const mood = @import("mood.zig"); +pub const noise = @import("noise.zig"); +pub const overworld_generator = @import("overworld_generator.zig"); +pub const region = @import("region.zig"); +pub const schematics = @import("schematics.zig"); +pub const surface_builder = @import("surface_builder.zig"); +pub const terrain_shape_generator = @import("terrain_shape_generator.zig"); +pub const terrain_shape_generator_tests = @import("terrain_shape_generator_tests.zig"); +pub const terrain_modifier_tests = @import("terrain_modifier_tests.zig"); +pub const terrain_report = @import("terrain_report.zig"); +pub const tree_registry = @import("tree_registry.zig"); +pub const world_class = @import("world_class.zig"); +pub const world_map = @import("world_map.zig"); + +pub const OverworldGenerator = overworld_generator.OverworldGenerator; + +const build_options = @import("worldgen_overworld_options"); + +pub fn create(context: worldgen_api.CreateContext) worldgen_api.RegistryError!worldgen_api.Generator { + const gen = context.allocator.create(OverworldGenerator) catch return error.OutOfMemory; + const restore_water = chunkDebugRestoreEnabled("water") or chunkDebugRestoreEnabled("watergen"); + const restore_caves = chunkDebugRestoreEnabled("caves"); + const restore_decorations = chunkDebugRestoreEnabled("decorations"); + gen.* = if (build_options.chunk_debug_mode) + OverworldGenerator.initWithParams(context.seed, context.allocator, decoration_registry.StandardDecorationProvider.provider(), .{ + .terrain_shape = .{ + .sea_level = if (restore_water) 64 else -1, + .ocean_threshold = if (restore_water) 0.37 else -1.0, + .disable_caves = !restore_caves, + }, + .basic_chunks_only = !restore_decorations, + }) + else + OverworldGenerator.init(context.seed, context.allocator, decoration_registry.StandardDecorationProvider.provider()); + return gen.generator(); +} + +pub const descriptor = worldgen_api.GeneratorDescriptor{ + .id = "zigcraft:overworld", + .aliases = &.{ "normal", "overworld" }, + .info = OverworldGenerator.INFO, + .create = create, +}; + +fn chunkDebugRestoreEnabled(name: []const u8) bool { + if (!build_options.chunk_debug_mode) return false; + + var it = std.mem.tokenizeScalar(u8, build_options.chunk_debug_enable, ','); + while (it.next()) |token| { + const trimmed = std.mem.trim(u8, token, " \t"); + if (std.ascii.eqlIgnoreCase(trimmed, name)) return true; + } + return false; +} diff --git a/modules/world-worldgen/src/schematics.zig b/modules/worldgen-overworld/src/schematics.zig similarity index 100% rename from modules/world-worldgen/src/schematics.zig rename to modules/worldgen-overworld/src/schematics.zig diff --git a/modules/world-worldgen/src/surface_builder.zig b/modules/worldgen-overworld/src/surface_builder.zig similarity index 100% rename from modules/world-worldgen/src/surface_builder.zig rename to modules/worldgen-overworld/src/surface_builder.zig diff --git a/modules/world-worldgen/src/terrain_modifier_tests.zig b/modules/worldgen-overworld/src/terrain_modifier_tests.zig similarity index 100% rename from modules/world-worldgen/src/terrain_modifier_tests.zig rename to modules/worldgen-overworld/src/terrain_modifier_tests.zig diff --git a/modules/world-worldgen/src/terrain_report.zig b/modules/worldgen-overworld/src/terrain_report.zig similarity index 100% rename from modules/world-worldgen/src/terrain_report.zig rename to modules/worldgen-overworld/src/terrain_report.zig diff --git a/modules/world-worldgen/src/terrain_shape_generator.zig b/modules/worldgen-overworld/src/terrain_shape_generator.zig similarity index 100% rename from modules/world-worldgen/src/terrain_shape_generator.zig rename to modules/worldgen-overworld/src/terrain_shape_generator.zig diff --git a/modules/world-worldgen/src/terrain_shape_generator_tests.zig b/modules/worldgen-overworld/src/terrain_shape_generator_tests.zig similarity index 100% rename from modules/world-worldgen/src/terrain_shape_generator_tests.zig rename to modules/worldgen-overworld/src/terrain_shape_generator_tests.zig diff --git a/modules/world-worldgen/src/tree_registry.zig b/modules/worldgen-overworld/src/tree_registry.zig similarity index 100% rename from modules/world-worldgen/src/tree_registry.zig rename to modules/worldgen-overworld/src/tree_registry.zig diff --git a/modules/world-worldgen/src/world_class.zig b/modules/worldgen-overworld/src/world_class.zig similarity index 100% rename from modules/world-worldgen/src/world_class.zig rename to modules/worldgen-overworld/src/world_class.zig diff --git a/modules/world-worldgen/src/world_map.zig b/modules/worldgen-overworld/src/world_map.zig similarity index 100% rename from modules/world-worldgen/src/world_map.zig rename to modules/worldgen-overworld/src/world_map.zig diff --git a/modules/worldgen-test/build.zig b/modules/worldgen-test/build.zig new file mode 100644 index 00000000..ab6cb3ea --- /dev/null +++ b/modules/worldgen-test/build.zig @@ -0,0 +1,5 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + _ = b.addModule("worldgen-test", .{ .root_source_file = b.path("src/root.zig") }); +} diff --git a/modules/worldgen-test/build.zig.zon b/modules/worldgen-test/build.zig.zon new file mode 100644 index 00000000..7ce7a53d --- /dev/null +++ b/modules/worldgen-test/build.zig.zon @@ -0,0 +1 @@ +.{ .name = .worldgen_test, .version = "0.1.0", .minimum_zig_version = "0.16.0", .paths = .{ "build.zig", "build.zig.zon", "src" }, .dependencies = .{} } diff --git a/modules/world-worldgen/src/shadow_test_world.zig b/modules/worldgen-test/src/root.zig similarity index 86% rename from modules/world-worldgen/src/shadow_test_world.zig rename to modules/worldgen-test/src/root.zig index ccf668f3..379ae6ba 100644 --- a/modules/world-worldgen/src/shadow_test_world.zig +++ b/modules/worldgen-test/src/root.zig @@ -1,21 +1,19 @@ const std = @import("std"); -const gen_interface = @import("generator_interface.zig"); -const Generator = gen_interface.Generator; -const GeneratorInfo = gen_interface.GeneratorInfo; -const ColumnInfo = gen_interface.ColumnInfo; +const worldgen_api = @import("worldgen-api"); +const Generator = worldgen_api.Generator; +const GeneratorInfo = worldgen_api.GeneratorInfo; +const ColumnInfo = worldgen_api.ColumnInfo; +const RegionInfo = worldgen_api.RegionInfo; const world_core = @import("world-core"); const Chunk = world_core.Chunk; const CHUNK_SIZE_X = world_core.CHUNK_SIZE_X; const CHUNK_SIZE_Y = world_core.CHUNK_SIZE_Y; const CHUNK_SIZE_Z = world_core.CHUNK_SIZE_Z; const BlockType = world_core.BlockType; -const BiomeId = world_core.BiomeId; -const LightingComputer = @import("lighting_computer.zig").LightingComputer; -const build_options = @import("world_worldgen_options"); const LODLevel = world_core.LODLevel; const LODSimplifiedData = world_core.LODSimplifiedData; -const region_pkg = @import("region.zig"); -const RegionInfo = region_pkg.RegionInfo; +const build_options = @import("worldgen_test_options"); +const LightingComputer = @import("worldgen-common").LightingComputer; pub const ShadowTestWorldGenerator = struct { seed: u64, @@ -173,11 +171,7 @@ pub const ShadowTestWorldGenerator = struct { @memset(data.biomes, .plains); @memset(data.top_blocks, .grass); @memset(data.colors, GRASS_COLOR); - @memset(data.material_layers, .{ - .surface = .grass, - .subsurface = .dirt, - .foundation = .stone, - }); + @memset(data.material_layers, .{ .surface = .grass, .subsurface = .dirt, .foundation = .stone }); @memset(data.water, world_core.LODWaterState.empty); @memset(data.lighting, world_core.LODLightingHint.daylight); @memset(data.vegetation, world_core.LODVegetationHint.empty); @@ -194,26 +188,20 @@ pub const ShadowTestWorldGenerator = struct { return self.seed; } + pub fn getRegionInfo(self: *const ShadowTestWorldGenerator, world_x: i32, world_z: i32) RegionInfo { + _ = self; + return .{ .mood = .calm, .role = .destination, .focus = .none, .center_x = world_x, .center_z = world_z }; + } + pub fn getColumnInfo(self: *const ShadowTestWorldGenerator, wx: f32, wz: f32) ColumnInfo { _ = self; _ = wx; _ = wz; - return .{ - .height = GROUND_Y, - .biome = .plains, - .is_ocean = false, - .temperature = 0.5, - .humidity = 0.5, - .continentalness = 0.5, - }; + return .{ .height = GROUND_Y, .biome = .plains, .is_ocean = false, .temperature = 0.5, .humidity = 0.5, .continentalness = 0.5 }; } pub fn generator(self: *ShadowTestWorldGenerator) Generator { - return .{ - .ptr = self, - .vtable = &VTABLE, - .info = INFO, - }; + return .{ .ptr = self, .vtable = &VTABLE, .info = INFO }; } const VTABLE = Generator.VTable{ @@ -248,7 +236,7 @@ pub const ShadowTestWorldGenerator = struct { fn getRegionInfoWrapper(ptr: *anyopaque, world_x: i32, world_z: i32) RegionInfo { const self: *ShadowTestWorldGenerator = @ptrCast(@alignCast(ptr)); - return region_pkg.getRegion(self.seed, world_x, world_z); + return self.getRegionInfo(world_x, world_z); } fn getColumnInfoWrapper(ptr: *anyopaque, wx: f32, wz: f32) ColumnInfo { @@ -261,3 +249,16 @@ pub const ShadowTestWorldGenerator = struct { allocator.destroy(self); } }; + +pub fn create(context: worldgen_api.CreateContext) worldgen_api.RegistryError!Generator { + const gen = context.allocator.create(ShadowTestWorldGenerator) catch return error.OutOfMemory; + gen.* = ShadowTestWorldGenerator.init(context.seed, context.allocator); + return gen.generator(); +} + +pub const descriptor = worldgen_api.GeneratorDescriptor{ + .id = "zigcraft:shadow-test", + .aliases = &.{ "test", "shadow-test", "lighting-test" }, + .info = ShadowTestWorldGenerator.INFO, + .create = create, +}; diff --git a/src/game/app.zig b/src/game/app.zig index 381c5213..88d66c0c 100644 --- a/src/game/app.zig +++ b/src/game/app.zig @@ -21,6 +21,7 @@ const SettingsManager = @import("game-core").SettingsManager; const Settings = @import("game-core").Settings; const InputSettings = @import("game-core").InputSettings; const BuildConfig = @import("game-core").BuildConfig; +const worldgen_registry = @import("world-worldgen").registry; const screen_pkg = @import("game-ui").screen; const ScreenManager = screen_pkg.ScreenManager; @@ -539,18 +540,10 @@ fn applyShadowTestPreset(settings: *Settings) void { } fn resolveAutoWorldGenerator() ?usize { - if (build_options.shadow_test_scene) return 2; + if (build_options.shadow_test_scene) return worldgen_registry.findGeneratorIndex("test") orelse 0; if (build_options.auto_world.len == 0) return null; - if (std.ascii.eqlIgnoreCase(build_options.auto_world, "normal") or std.ascii.eqlIgnoreCase(build_options.auto_world, "overworld")) { - return 0; - } - if (std.ascii.eqlIgnoreCase(build_options.auto_world, "flat")) { - return 1; - } - if (std.ascii.eqlIgnoreCase(build_options.auto_world, "shadow-test") or std.ascii.eqlIgnoreCase(build_options.auto_world, "lighting-test")) { - return 2; - } + if (worldgen_registry.findGeneratorIndex(build_options.auto_world)) |index| return index; log.log.warn("Unknown -Dauto-world value '{s}', defaulting to overworld", .{build_options.auto_world}); - return 0; + return worldgen_registry.findGeneratorIndex("overworld") orelse 0; }