Interpolate the biome interpolation based on some weights, instead of choosing the closest one.

This allows making plateaus without having a ramp (from the other biome interpolation) leading up to it.
This commit is contained in:
IntegratedQuantum 2024-07-02 20:06:26 +02:00
parent 4343f6eb18
commit 78db8f0839
2 changed files with 28 additions and 8 deletions

View File

@ -227,6 +227,7 @@ pub const Biome = struct {
minHeight: i32,
maxHeight: i32,
interpolation: Interpolation,
interpolationWeight: f32,
roughness: f32,
hills: f32,
mountains: f32,
@ -266,6 +267,7 @@ pub const Biome = struct {
.hills = json.get(f32, "hills", 0),
.mountains = json.get(f32, "mountains", 0),
.interpolation = std.meta.stringToEnum(Interpolation, json.get([]const u8, "interpolation", "square")) orelse .square,
.interpolationWeight = @max(json.get(f32, "interpolationWeight", 1), std.math.floatMin(f32)),
.caves = json.get(f32, "caves", -0.375),
.crystals = json.get(u32, "crystals", 0),
.stalagmites = json.get(u32, "stalagmites", 0),

View File

@ -10,6 +10,8 @@ const noise = terrain.noise;
const FractalNoise = noise.FractalNoise;
const RandomlyWeightedFractalNoise = noise.RandomlyWeightedFractalNoise;
const PerlinNoise = noise.PerlinNoise;
const vec = main.vec;
const Vec2f = vec.Vec2f;
pub const id = "cubyz:mapgen_v1";
@ -22,25 +24,25 @@ pub fn deinit() void {
}
/// Assumes the 2 points are at tᵢ = (0, 1)
fn interpolationWeights(t: f32, interpolation: terrain.biomes.Interpolation) [2]f32 {
fn interpolationWeights(t: f32, interpolation: terrain.biomes.Interpolation) Vec2f {
switch (interpolation) {
.none => {
if(t < 0.5) {
return [2]f32 {1, 0};
return .{1, 0};
} else {
return [2]f32 {0, 1};
return .{0, 1};
}
},
.linear => {
return [2]f32 {1 - t, t};
return .{1 - t, t};
},
.square => {
if(t < 0.5) {
const tSqr = 2*t*t;
return [2]f32 {1 - tSqr, tSqr};
return .{1 - tSqr, tSqr};
} else {
const tSqr = 2*(1 - t)*(1 - t);
return [2]f32 {tSqr, 1 - tSqr};
return .{tSqr, 1 - tSqr};
}
},
}
@ -113,8 +115,24 @@ pub fn generateMapFragment(map: *MapFragment, worldSeed: u64) void {
closestBiome = biomePositions.get(@intCast(xBiome + 1), @intCast(yBiome + 1)).biome;
}
}
const coefficientsX = interpolationWeights(relXBiome, closestBiome.interpolation);
const coefficientsY = interpolationWeights(relYBiome, closestBiome.interpolation);
const interpolationCoefficientsX = interpolationWeights(relXBiome, .square);
const interpolationCoefficientsY = interpolationWeights(relYBiome, .square);
var coefficientsX: vec.Vec2f = .{0, 0};
var coefficientsY: vec.Vec2f = .{0, 0};
var totalWeight: f32 = 0;
for(0..2) |dx| {
for(0..2) |dy| {
const biomeMapX = @as(usize, @intCast(xBiome)) + dx;
const biomeMapY = @as(usize, @intCast(yBiome)) + dy;
const biomeSample = biomePositions.get(biomeMapX, biomeMapY);
const weight = interpolationCoefficientsX[dx]*interpolationCoefficientsY[dy]*biomeSample.biome.interpolationWeight;
coefficientsX += interpolationWeights(relXBiome, biomeSample.biome.interpolation)*@as(Vec2f, @splat(weight));
coefficientsY += interpolationWeights(relYBiome, biomeSample.biome.interpolation)*@as(Vec2f, @splat(weight));
totalWeight += weight;
}
}
coefficientsX /= @splat(totalWeight);
coefficientsY /= @splat(totalWeight);
for(0..2) |dx| {
for(0..2) |dy| {
const biomeMapX = @as(usize, @intCast(xBiome)) + dx;