Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 39 additions & 31 deletions modules/world-worldgen/src/biome_registry.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

const std = @import("std");
const BlockType = @import("world-core").BlockType;
const DecorationRule = @import("decoration_types.zig").DecorationRule;
const tree_registry = @import("tree_registry.zig");
pub const TreeType = tree_registry.TreeType;

Expand Down Expand Up @@ -43,16 +44,8 @@ pub const ColorTints = struct {

/// Vegetation profile for biome-driven placement
pub const VegetationProfile = struct {
tree_types: []const TreeType = &.{.oak},
tree_density: f32 = 0.05, // Probability per attempt
bush_density: f32 = 0.0,
grass_density: f32 = 0.0,
cactus_density: f32 = 0.0,
dead_bush_density: f32 = 0.0,
bamboo_density: f32 = 0.0,
melon_density: f32 = 0.0,
red_mushroom_density: f32 = 0.0,
brown_mushroom_density: f32 = 0.0,
tree_types: []const TreeType = &.{},
decoration_rules: []const DecorationRule = &.{},
};

/// Terrain modifiers applied during height computation
Expand Down Expand Up @@ -233,7 +226,7 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.continentalness = .{ .min = 0.0, .max = 0.20 },
.priority = 2,
.surface = .{ .top = .gravel, .filler = .gravel, .depth_range = 4 },
.vegetation = .{ .tree_types = &.{}, .tree_density = 0 },
.vegetation = .{ .tree_types = &.{} },
.colors = .{ .water = .{ 0.1, 0.2, 0.5 } },
},
.{
Expand All @@ -245,7 +238,7 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.continentalness = .{ .min = 0.0, .max = 0.35 },
.priority = 1,
.surface = .{ .top = .sand, .filler = .sand, .depth_range = 3 },
.vegetation = .{ .tree_types = &.{}, .tree_density = 0 },
.vegetation = .{ .tree_types = &.{} },
},
.{
.id = .beach,
Expand All @@ -257,7 +250,7 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.max_slope = 2,
.priority = 10,
.surface = .{ .top = .sand, .filler = .sand, .depth_range = 2 },
.vegetation = .{ .tree_types = &.{}, .tree_density = 0 },
.vegetation = .{ .tree_types = &.{} },
},

// === Land Biomes (continentalness > 0.45) ===
Expand All @@ -271,7 +264,7 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.ruggedness = Range.any(),
.priority = 0, // Fallback
.surface = .{ .top = .grass, .filler = .dirt, .depth_range = 3 },
.vegetation = .{ .tree_types = &.{.sparse_oak}, .tree_density = 0.02, .grass_density = 0.3 },
.vegetation = .{ .tree_types = &.{.sparse_oak}, .decoration_rules = &.{.{ .block = .tall_grass, .place_on = &.{.grass}, .chance = 0.3 }} },
.terrain = .{ .height_amplitude = 0.7, .smoothing = 0.2 },
},
.{
Expand All @@ -284,7 +277,7 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.ruggedness = .{ .min = 0.0, .max = 0.60 },
.priority = 5,
.surface = .{ .top = .grass, .filler = .dirt, .depth_range = 3 },
.vegetation = .{ .tree_types = &.{ .oak, .birch, .dense_oak }, .tree_density = 0.12, .bush_density = 0.05, .grass_density = 0.4 },
.vegetation = .{ .tree_types = &.{ .oak, .birch, .dense_oak }, .decoration_rules = &.{.{ .block = .tall_grass, .place_on = &.{.grass}, .chance = 0.4 }} },
.colors = .{ .grass = .{ 0.18, 0.64, 0.16 }, .foliage = .{ 0.12, 0.52, 0.12 } },
},
.{
Expand All @@ -296,7 +289,7 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.continentalness = .{ .min = 0.45, .max = 1.0 },
.priority = 6,
.surface = .{ .top = .grass, .filler = .dirt, .depth_range = 3 },
.vegetation = .{ .tree_types = &.{.spruce}, .tree_density = 0.10, .grass_density = 0.2 },
.vegetation = .{ .tree_types = &.{.spruce}, .decoration_rules = &.{.{ .block = .tall_grass, .place_on = &.{.grass}, .chance = 0.2 }} },
.colors = .{ .grass = .{ 0.24, 0.56, 0.24 }, .foliage = .{ 0.18, 0.46, 0.18 } },
},
.{
Expand All @@ -311,7 +304,10 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.max_slope = 4,
.priority = 6,
.surface = .{ .top = .sand, .filler = .sand, .depth_range = 6 },
.vegetation = .{ .tree_types = &.{}, .tree_density = 0, .cactus_density = 0.015, .dead_bush_density = 0.02 },
.vegetation = .{ .tree_types = &.{}, .decoration_rules = &.{
.{ .block = .cactus, .place_on = &.{.sand}, .chance = 0.015 },
.{ .block = .dead_bush, .place_on = &.{.sand}, .chance = 0.02 },
} },
.terrain = .{ .height_amplitude = 0.5, .smoothing = 0.4 },
.colors = .{ .grass = .{ 0.75, 0.70, 0.35 } },
},
Expand All @@ -326,7 +322,7 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.max_slope = 3,
.priority = 5,
.surface = .{ .top = .grass, .filler = .dirt, .depth_range = 2 },
.vegetation = .{ .tree_types = &.{.swamp_oak}, .tree_density = 0.08 },
.vegetation = .{ .tree_types = &.{.swamp_oak} },
.terrain = .{ .clamp_to_sea_level = true, .height_offset = -2 },
.colors = .{
.grass = .{ 0.24, 0.54, 0.20 },
Expand All @@ -345,7 +341,7 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.max_slope = 255,
.priority = 4,
.surface = .{ .top = .snow_block, .filler = .dirt, .depth_range = 3 },
.vegetation = .{ .tree_types = &.{.spruce}, .tree_density = 0.01 },
.vegetation = .{ .tree_types = &.{.spruce} },
.colors = .{ .grass = .{ 0.7, 0.75, 0.8 } },
},

Expand All @@ -362,7 +358,7 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.min_ridge_mask = 0.1,
.priority = 2,
.surface = .{ .top = .stone, .filler = .stone, .depth_range = 1 },
.vegetation = .{ .tree_types = &.{.sparse_oak}, .tree_density = 0 },
.vegetation = .{ .tree_types = &.{.sparse_oak} },
.terrain = .{ .height_amplitude = 1.5 },
},
.{
Expand All @@ -377,7 +373,7 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.max_slope = 255,
.priority = 2,
.surface = .{ .top = .snow_block, .filler = .stone, .depth_range = 1 },
.vegetation = .{ .tree_types = &.{}, .tree_density = 0 },
.vegetation = .{ .tree_types = &.{} },
.terrain = .{ .height_amplitude = 1.4 },
.colors = .{ .grass = .{ 0.85, 0.90, 0.95 } },
},
Expand All @@ -392,7 +388,7 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.continentalness = .{ .min = 0.45, .max = 0.60 }, // Coastal swamp
.priority = 6,
.surface = .{ .top = .mud, .filler = .mud, .depth_range = 4 },
.vegetation = .{ .tree_types = &.{.mangrove}, .tree_density = 0.15 },
.vegetation = .{ .tree_types = &.{.mangrove} },
.terrain = .{ .clamp_to_sea_level = true, .height_offset = -1 },
.colors = .{ .grass = .{ 0.26, 0.58, 0.18 }, .foliage = .{ 0.22, 0.52, 0.16 }, .water = .{ 0.16, 0.38, 0.30 } },
},
Expand All @@ -405,7 +401,10 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.continentalness = .{ .min = 0.60, .max = 1.0 }, // Inland
.priority = 5,
.surface = .{ .top = .grass, .filler = .dirt, .depth_range = 3 },
.vegetation = .{ .tree_types = &.{.jungle}, .tree_density = 0.20, .bamboo_density = 0.08, .melon_density = 0.04 },
.vegetation = .{ .tree_types = &.{.jungle}, .decoration_rules = &.{
.{ .block = .bamboo, .place_on = &.{.grass}, .chance = 0.08 },
.{ .block = .melon, .place_on = &.{.grass}, .chance = 0.04 },
} },
.colors = .{ .grass = .{ 0.10, 0.76, 0.08 }, .foliage = .{ 0.08, 0.62, 0.08 } },
},
.{
Expand All @@ -417,7 +416,10 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.continentalness = .{ .min = 0.55, .max = 1.0 }, // Inland (less restrictive)
.priority = 5, // Higher priority to win over plains in hot zones
.surface = .{ .top = .grass, .filler = .dirt, .depth_range = 3 },
.vegetation = .{ .tree_types = &.{.acacia}, .tree_density = 0.015, .grass_density = 0.5, .dead_bush_density = 0.01 },
.vegetation = .{ .tree_types = &.{.acacia}, .decoration_rules = &.{
.{ .block = .tall_grass, .place_on = &.{.grass}, .chance = 0.5 },
.{ .block = .dead_bush, .place_on = &.{.grass}, .chance = 0.01 },
} },
.colors = .{ .grass = .{ 0.55, 0.55, 0.30 }, .foliage = .{ 0.50, 0.50, 0.28 } },
},
.{
Expand All @@ -430,7 +432,7 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.ruggedness = .{ .min = 0.4, .max = 1.0 },
.priority = 6,
.surface = .{ .top = .red_sand, .filler = .terracotta, .depth_range = 5 },
.vegetation = .{ .cactus_density = 0.02 },
.vegetation = .{ .tree_types = &.{}, .decoration_rules = &.{.{ .block = .cactus, .place_on = &.{.red_sand}, .chance = 0.02 }} },
.colors = .{ .grass = .{ 0.5, 0.4, 0.3 } },
},
.{
Expand All @@ -442,7 +444,10 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.max_height = 50,
.priority = 20,
.surface = .{ .top = .mycelium, .filler = .dirt, .depth_range = 3 },
.vegetation = .{ .tree_types = &.{ .huge_red_mushroom, .huge_brown_mushroom }, .tree_density = 0.05, .red_mushroom_density = 0.1, .brown_mushroom_density = 0.1 },
.vegetation = .{ .tree_types = &.{ .huge_red_mushroom, .huge_brown_mushroom }, .decoration_rules = &.{
.{ .block = .red_mushroom_block, .place_on = &.{.mycelium}, .chance = 0.1 },
.{ .block = .brown_mushroom_block, .place_on = &.{.mycelium}, .chance = 0.1 },
} },
.colors = .{ .grass = .{ 0.4, 0.8, 0.4 } },
},
.{
Expand All @@ -455,7 +460,7 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.continentalness = .{ .min = -1.0, .max = -0.5 },
.priority = 15,
.surface = .{ .top = .sand, .filler = .sand, .depth_range = 2 },
.vegetation = .{ .tree_types = &.{}, .tree_density = 0 },
.vegetation = .{ .tree_types = &.{} },
},

// === Transition Micro-Biomes ===
Expand All @@ -472,7 +477,7 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.ruggedness = .{ .min = 0.30, .max = 0.80 },
.priority = 0, // Lowest priority
.surface = .{ .top = .grass, .filler = .dirt, .depth_range = 3 },
.vegetation = .{ .tree_types = &.{ .sparse_oak, .spruce }, .tree_density = 0.08, .grass_density = 0.4 },
.vegetation = .{ .tree_types = &.{ .sparse_oak, .spruce }, .decoration_rules = &.{.{ .block = .tall_grass, .place_on = &.{.grass}, .chance = 0.4 }} },
.terrain = .{ .height_amplitude = 1.1, .smoothing = 0.1 },
.colors = .{ .grass = .{ 0.24, 0.62, 0.22 }, .foliage = .{ 0.18, 0.50, 0.16 } },
},
Expand All @@ -486,7 +491,7 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.ruggedness = .{ .min = 0.0, .max = 0.30 },
.priority = 0, // Lowest priority
.surface = .{ .top = .grass, .filler = .dirt, .depth_range = 2 },
.vegetation = .{ .tree_types = &.{.swamp_oak}, .tree_density = 0.04, .grass_density = 0.5 },
.vegetation = .{ .tree_types = &.{.swamp_oak}, .decoration_rules = &.{.{ .block = .tall_grass, .place_on = &.{.grass}, .chance = 0.5 }} },
.terrain = .{ .height_offset = -1, .smoothing = 0.3 },
.colors = .{
.grass = .{ 0.20, 0.58, 0.20 },
Expand All @@ -504,7 +509,10 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.ruggedness = .{ .min = 0.0, .max = 0.40 },
.priority = 0, // Lowest priority
.surface = .{ .top = .grass, .filler = .dirt, .depth_range = 3 },
.vegetation = .{ .tree_types = &.{.acacia}, .tree_density = 0.005, .grass_density = 0.3, .dead_bush_density = 0.02 },
.vegetation = .{ .tree_types = &.{.acacia}, .decoration_rules = &.{
.{ .block = .tall_grass, .place_on = &.{.grass}, .chance = 0.3 },
.{ .block = .dead_bush, .place_on = &.{.grass}, .chance = 0.02 },
} },
.terrain = .{ .height_amplitude = 0.6, .smoothing = 0.25 },
.colors = .{ .grass = .{ 0.55, 0.50, 0.28 } }, // Less yellow, more natural
},
Expand All @@ -518,7 +526,7 @@ pub const BIOME_REGISTRY: []const BiomeDefinition = &.{
.ruggedness = .{ .min = 0.0, .max = 0.35 },
.priority = 0, // Lowest priority
.surface = .{ .top = .grass, .filler = .dirt, .depth_range = 3 },
.vegetation = .{ .tree_types = &.{}, .tree_density = 0, .grass_density = 0.4 }, // No trees
.vegetation = .{ .tree_types = &.{}, .decoration_rules = &.{.{ .block = .tall_grass, .place_on = &.{.grass}, .chance = 0.4 }} }, // No trees
.terrain = .{ .height_amplitude = 0.5, .smoothing = 0.3 },
.colors = .{ .grass = .{ 0.24, 0.66, 0.24 }, .foliage = .{ 0.18, 0.52, 0.16 } },
},
Expand Down
24 changes: 22 additions & 2 deletions modules/world-worldgen/src/decoration_registry.zig
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub const SimpleDecoration = types.SimpleDecoration;
pub const SchematicBlock = types.SchematicBlock;
pub const Schematic = types.Schematic;
pub const SchematicDecoration = types.SchematicDecoration;
pub const DecorationRule = types.DecorationRule;
pub const Decoration = types.Decoration;
pub const DecorationProvider = @import("decoration_provider.zig").DecorationProvider;
pub const DecorationContext = @import("decoration_provider.zig").DecorationProvider.DecorationContext;
Expand Down Expand Up @@ -107,6 +108,7 @@ fn variantAllowed(variant: f32, allow_subbiomes: bool, min: f32, max: f32) bool

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;

pub const StandardDecorationProvider = struct {
Expand Down Expand Up @@ -169,13 +171,31 @@ pub const StandardDecorationProvider = struct {

// 1. Static decorations (flowers, grass)
if (chooseStaticSimpleDecoration(biome, surface_block, variant, allow_subbiomes, veg_mult, random)) |simple| {
chunk.setBlock(local_x, @intCast(surface_y + 1), local_z, simple.block);
const place_y = surface_y + 1;
if (place_y >= 0 and place_y < CHUNK_SIZE_Y) {
chunk.setBlock(local_x, @intCast(place_y), local_z, simple.block);
}
}

// 2. Dynamic Tree Registry (from Biome Definition)
// 2. Biome-defined block decoration rules
const biome_def = biome_mod.getBiomeDefinition(biome);
const vegetation = biome_def.vegetation;

for (vegetation.decoration_rules) |rule| {
if (!rule.isAllowed(surface_block, surface_y, variant, allow_subbiomes)) continue;

const prob = @min(1.0, rule.chance * veg_mult);
if (random.float(f32) >= prob) continue;

const place_y = surface_y + 1;
if (place_y < 0 or place_y >= CHUNK_SIZE_Y) continue;
if (chunk.getBlockSafe(@intCast(local_x), place_y, @intCast(local_z)) != .air) continue;

chunk.setBlock(local_x, @intCast(place_y), local_z, rule.block);
break;
}

// 3. Dynamic Tree Registry (from Biome Definition)
if (vegetation.tree_types.len > 0) {
for (vegetation.tree_types) |tree_type| {
if (tree_registry.getTreeDefinition(tree_type)) |tree_def| {
Expand Down
17 changes: 17 additions & 0 deletions modules/world-worldgen/src/decoration_types.zig
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,23 @@ pub const SchematicDecoration = struct {
}
};

pub const DecorationRule = struct {
block: BlockType,
place_on: []const BlockType,
chance: f32,
y_min: i32 = 1,
y_max: i32 = 256,
variant_min: f32 = -1.0,
variant_max: f32 = 1.0,

pub fn isAllowed(self: DecorationRule, surface_block: BlockType, surface_y: i32, variant: f32, allow_subbiomes: bool) bool {
if (!isBlockAllowed(self.place_on, surface_block)) return false;
if (surface_y + 1 < self.y_min or surface_y + 1 > self.y_max) return false;
if (allow_subbiomes) return variant >= self.variant_min and variant <= self.variant_max;
return self.variant_min == -1.0 and self.variant_max == 1.0;
}
};

pub const Decoration = union(enum) {
simple: SimpleDecoration,
schematic: SchematicDecoration,
Expand Down
4 changes: 2 additions & 2 deletions src/worldgen_tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,8 @@ test "WorldGen stable chunk fingerprints for known seed" {
};

const expected = [_]u64{
12740687495258888450,
14029817033549788541,
16067496184749289293,
10106071382427144696,
4422434882344650145,
};

Expand Down
Loading