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
9 changes: 9 additions & 0 deletions modules/world-worldgen/src/biome_registry.zig
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ pub const TerrainModifier = struct {
clamp_to_sea_level: bool = false,
/// Additional height offset
height_offset: f32 = 0.0,

/// Apply biome terrain shaping to a sampled height without mutating the
/// live generation pipeline.
pub fn applyHeight(self: TerrainModifier, base_height: f32, sea_level: f32) f32 {
var height = sea_level + (base_height - sea_level) * self.height_amplitude;
height += (sea_level - height) * self.smoothing;
if (self.clamp_to_sea_level) height = sea_level;
return height + self.height_offset;
}
};

/// Surface block configuration
Expand Down
1 change: 1 addition & 0 deletions modules/world-worldgen/src/root.zig
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ 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 tree_registry = @import("tree_registry.zig");
pub const world_class = @import("world_class.zig");
pub const world_map = @import("world_map.zig");
Expand Down
56 changes: 56 additions & 0 deletions modules/world-worldgen/src/terrain_modifier_tests.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//! Unit tests for terrain modifier height semantics.

const std = @import("std");
const testing = std.testing;
const TerrainModifier = @import("biome_registry.zig").TerrainModifier;

const SEA_LEVEL: f32 = 62.0;

test "TerrainModifier defaults preserve sampled height" {
const modifier = TerrainModifier{};

try testing.expectApproxEqAbs(@as(f32, 62.0), modifier.applyHeight(62.0, SEA_LEVEL), 0.0001);
try testing.expectApproxEqAbs(@as(f32, 144.0), modifier.applyHeight(144.0, SEA_LEVEL), 0.0001);
}

test "TerrainModifier height_amplitude scales relief around sea level" {
const flat = TerrainModifier{ .height_amplitude = 0.0 };
const doubled = TerrainModifier{ .height_amplitude = 2.0 };

try testing.expectApproxEqAbs(@as(f32, 62.0), flat.applyHeight(144.0, SEA_LEVEL), 0.0001);
try testing.expectApproxEqAbs(@as(f32, 226.0), doubled.applyHeight(144.0, SEA_LEVEL), 0.0001);
try testing.expectApproxEqAbs(@as(f32, 42.0), doubled.applyHeight(52.0, SEA_LEVEL), 0.0001);
}

test "TerrainModifier smoothing blends height toward sea level" {
const unchanged = TerrainModifier{ .smoothing = 0.0 };
const half = TerrainModifier{ .smoothing = 0.5 };
const full = TerrainModifier{ .smoothing = 1.0 };

try testing.expectApproxEqAbs(@as(f32, 102.0), unchanged.applyHeight(102.0, SEA_LEVEL), 0.0001);
try testing.expectApproxEqAbs(@as(f32, 82.0), half.applyHeight(102.0, SEA_LEVEL), 0.0001);
try testing.expectApproxEqAbs(@as(f32, 62.0), full.applyHeight(102.0, SEA_LEVEL), 0.0001);
}

test "TerrainModifier clamp_to_sea_level forces height before offset" {
const modifier = TerrainModifier{ .clamp_to_sea_level = true, .height_offset = -2.0 };

try testing.expectApproxEqAbs(@as(f32, 60.0), modifier.applyHeight(61.0, SEA_LEVEL), 0.0001);
try testing.expectApproxEqAbs(@as(f32, 60.0), modifier.applyHeight(180.0, SEA_LEVEL), 0.0001);
}

test "TerrainModifier height_offset applies after shaping" {
const raised = TerrainModifier{ .height_offset = 8.0 };
const lowered = TerrainModifier{ .height_offset = -3.0 };

try testing.expectApproxEqAbs(@as(f32, 70.0), raised.applyHeight(62.0, SEA_LEVEL), 0.0001);
try testing.expectApproxEqAbs(@as(f32, 141.0), lowered.applyHeight(144.0, SEA_LEVEL), 0.0001);
}

test "TerrainModifier combines amplitude smoothing clamp and offset deterministically" {
const shaped = TerrainModifier{ .height_amplitude = 1.5, .smoothing = 0.5, .height_offset = 4.0 };
const clamped = TerrainModifier{ .height_amplitude = 1.5, .smoothing = 0.5, .clamp_to_sea_level = true, .height_offset = 4.0 };

try testing.expectApproxEqAbs(@as(f32, 96.0), shaped.applyHeight(102.0, SEA_LEVEL), 0.0001);
try testing.expectApproxEqAbs(@as(f32, 66.0), clamped.applyHeight(102.0, SEA_LEVEL), 0.0001);
}
1 change: 1 addition & 0 deletions src/tests.zig
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ test {
_ = @import("world-worldgen").biome_registry_tests;
_ = @import("world-worldgen").biome_selector_tests;
_ = @import("world-worldgen").height_sampler_tests;
_ = @import("world-worldgen").terrain_modifier_tests;
_ = @import("world-worldgen").terrain_shape_generator_tests;
_ = @import("world-lod").lod_manager_tests;
_ = @import("world-lod").lod_seam;
Expand Down
Loading