Remove river carving and add flowers

This commit is contained in:
chelsea
2025-12-02 19:56:27 -06:00
parent 2482740b89
commit f5afe3d69b
4 changed files with 136 additions and 68 deletions

View File

@@ -119,11 +119,12 @@ static inline double clamp01(double v) {
}
static int ground_slope(worldgen_ctx *ctx, int x, int z);
static void apply_flat_river(worldgen_ctx *ctx, int x, int z, column_data *data);
static uint16_t select_surface_block(worldgen_ctx *ctx, const column_data *data, int world_x, int world_z);
static void generate_chunk_trails(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out);
static void generate_chunk_grass(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out);
static void generate_chunk_flowers(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out);
static void trail_node_position(worldgen_ctx *ctx, int node_x, int node_z, double spacing, double *out_x, double *out_z);
static void carve_trail_pad(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_data *out, column_data columns[CHUNK_SIZE][CHUNK_SIZE], int center_x, int center_z, int radius, int target_height);
static double old_growth_plains_mask(worldgen_ctx *ctx, int x, int z);
static double old_growth_grove_mask(worldgen_ctx *ctx, int x, int z);
@@ -343,36 +344,10 @@ static column_data get_column_data(worldgen_ctx *ctx, int x, int z) {
}
}
apply_flat_river(ctx, x, z, &data);
data.biome = classify_biome(ctx, x, z, data.height);
return data;
}
static void apply_flat_river(worldgen_ctx *ctx, int x, int z, column_data *data) {
if (data->is_local_basin) return;
if (data->height > ctx->sea_level + 8) return;
double warp_x = x + simplex_noise2(&ctx->noise, x * 0.004, z * 0.004) * 25.0;
double warp_z = z + simplex_noise2(&ctx->noise, (x + 12000) * 0.004, (z - 12000) * 0.004) * 25.0;
double river_noise = simplex_noise2(&ctx->noise, warp_x * 0.0005, warp_z * 0.0005);
double channel = 1.0 - fabs(river_noise);
if (channel < 0.985) return;
double strength = (channel - 0.985) / 0.015;
if (strength > 1.0) strength = 1.0;
if (strength < 0.0) strength = 0.0;
int surface = ctx->sea_level - 1;
if (surface < 2) surface = 2;
int depth = 2 + (int)(strength * 3.5);
int target_height = surface - depth;
if (target_height < 1) target_height = 1;
if (target_height >= data->height) return;
data->height = target_height;
data->has_water = 1;
data->water_surface = surface;
data->basin_rim = surface;
data->basin_depth = surface - target_height;
data->is_local_basin = 0;
}
// ---------------------------------------------------------------------------
// Ore generation (coal seams)
// ---------------------------------------------------------------------------
@@ -1270,7 +1245,60 @@ static void generate_chunk_grass(worldgen_ctx *ctx, int chunk_x, int chunk_z, co
}
}
static void generate_chunk_trees(worldgen_ctx *ctx, int chunk_x, int chunk_z, block_list *out) {
static void generate_chunk_flowers(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out) {
for (int dx = 0; dx < CHUNK_SIZE; ++dx) {
for (int dz = 0; dz < CHUNK_SIZE; ++dz) {
column_data cd = columns[dx][dz];
if (cd.height <= 0 || cd.height >= CHUNK_HEIGHT - 2) continue;
int world_x = chunk_x * CHUNK_SIZE + dx;
int world_z = chunk_z * CHUNK_SIZE + dz;
if (out->blocks[cd.height][dx][dz] != BLOCK_GRASS) continue;
if (out->blocks[cd.height + 1][dx][dz] != BLOCK_AIR) continue;
int slope = ground_slope(ctx, world_x, world_z);
if (slope > 3) continue;
double meadow = simplex_noise2(&ctx->noise, (world_x + 5400) * 0.012, (world_z - 5400) * 0.012) * 0.5 + 0.5;
double bloom = simplex_noise2(&ctx->noise, (world_x - 8100) * 0.0035, (world_z + 8100) * 0.0035) * 0.5 + 0.5;
double humidity = simplex_noise2(&ctx->noise, (world_x + 12000) * 0.0045, (world_z - 12000) * 0.0045) * 0.5 + 0.5;
double fertility = clamp01(meadow * 0.6 + humidity * 0.4);
double chance = 0.03 + fertility * 0.18 + bloom * 0.12;
if (cd.height < ctx->sea_level) chance *= 0.6;
double slope_penalty = clamp01((double)slope / 4.0);
chance *= (1.0 - slope_penalty * 0.7);
uint32_t h = hash_coords(world_x + 30000, world_z - 30000, (uint32_t)(ctx->world_seed ^ 0xA511E9B5u));
double roll = (double)(h & 0xFFFF) / 65535.0;
if (roll > chance) continue;
out->blocks[cd.height + 1][dx][dz] = BLOCK_WILDFLOWER;
static const int offsets[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
for (int i = 0; i < 4; ++i) {
int nx = dx + offsets[i][0];
int nz = dz + offsets[i][1];
if (nx < 0 || nx >= CHUNK_SIZE || nz < 0 || nz >= CHUNK_SIZE) continue;
column_data nd = columns[nx][nz];
if (nd.height <= 0 || nd.height >= CHUNK_HEIGHT - 2) continue;
if (out->blocks[nd.height][nx][nz] != BLOCK_GRASS) continue;
if (out->blocks[nd.height + 1][nx][nz] != BLOCK_AIR) continue;
uint32_t nh = hash_coords(world_x + offsets[i][0] + 60000, world_z + offsets[i][1] - 60000, (uint32_t)(ctx->world_seed ^ 0xF00DBAAu));
if ((nh & 0xFFFF) > 24000) continue;
out->blocks[nd.height + 1][nx][nz] = BLOCK_WILDFLOWER;
}
}
}
}
static int column_has_trail_surface(chunk_data *chunk, int chunk_x, int chunk_z, int world_x, int world_z) {
int local_x = world_x - chunk_x * CHUNK_SIZE;
int local_z = world_z - chunk_z * CHUNK_SIZE;
if (local_x < 0 || local_x >= CHUNK_SIZE || local_z < 0 || local_z >= CHUNK_SIZE) return 0;
for (int y = CHUNK_HEIGHT - 2; y >= 1; --y) {
uint16_t block = chunk->blocks[y][local_x][local_z];
if (block == BLOCK_AIR) continue;
if (block == BLOCK_WATER) return 0;
return block == BLOCK_GRAVEL;
}
return 0;
}
static void generate_chunk_trees(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_data *chunk, block_list *out) {
const int grid = 5;
const int margin = 4;
const int min_tree_alt = ctx->sea_level - 2;
@@ -1345,6 +1373,10 @@ static void generate_chunk_trees(worldgen_ctx *ctx, int chunk_x, int chunk_z, bl
if (surface == BLOCK_SNOW) {
base_y = data.height;
}
if (column_has_trail_surface(chunk, chunk_x, chunk_z, candidate_x, candidate_z)) {
block_list_free(&tmp);
continue;
}
generate_tree(ctx, arch, candidate_x, base_y, candidate_z, &rng, &tmp);
if (tmp.count == 0) {
block_list_free(&tmp);
@@ -1408,6 +1440,7 @@ static uint32_t trail_segment_hash(int ax, int az, int bx, int bz, uint32_t seed
static int should_connect_trail_nodes(worldgen_ctx *ctx, int node_x0, int node_z0, int node_x1, int node_z1) {
if (node_x0 == node_x1 && node_z0 == node_z1) return 0;
uint32_t seg_hash = trail_segment_hash(node_x0, node_z0, node_x1, node_z1, (uint32_t)ctx->world_seed ^ 0xB37D4A91u);
double ax, az, bx, bz;
trail_node_position(ctx, node_x0, node_z0, TRAIL_NODE_SPACING, &ax, &az);
trail_node_position(ctx, node_x1, node_z1, TRAIL_NODE_SPACING, &bx, &bz);
@@ -1447,20 +1480,28 @@ static int should_connect_trail_nodes(worldgen_ctx *ctx, int node_x0, int node_z
} else {
base += 0.05;
}
if (dx == 0.0 || dz == 0.0) {
base += 0.05;
}
if (dx == dz && dx > 0.1) {
base += 0.03;
int axis_edge = (dx == 0.0 || dz == 0.0);
if (axis_edge) {
double corridor = simplex_noise2(&ctx->noise, (mid_x + 50000.0) * 0.0007, (mid_z - 50000.0) * 0.0007) * 0.5 + 0.5;
double penalty = 0.22 + corridor * 0.3;
base -= penalty;
if (grid_len <= 1.05) {
double axis_gate = 0.22 + corridor * 0.35;
double axis_rand = ((seg_hash >> 8) & 0xFFFF) / 65535.0;
if (axis_rand > axis_gate) {
return 0;
}
}
} else if (dx == dz && dx > 0.1) {
base += 0.07;
}
base += (alignment - 0.5) * 0.2;
base += (ridge_bias - 0.5) * 0.15;
double texture = simplex_noise2(&ctx->noise, mid_x * 0.0012, mid_z * 0.0012) * 0.5 + 0.5;
base += (texture - 0.5) * 0.15;
if (base < 0.02) base = 0.02;
if (base > 0.9) base = 0.9;
uint32_t h = trail_segment_hash(node_x0, node_z0, node_x1, node_z1, (uint32_t)ctx->world_seed ^ 0xB37D4A91u);
double r = (h + 1.0) / 4294967296.0;
if (base > 0.85) base = 0.85;
double r = (seg_hash + 1.0) / 4294967296.0;
return r < base;
}
@@ -1670,18 +1711,46 @@ static void place_trail_column(chunk_data *out, column_data columns[CHUNK_SIZE][
columns[local_x][local_z].height = target_height;
}
static void carve_trail_pad(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_data *out, column_data columns[CHUNK_SIZE][CHUNK_SIZE], int center_x, int center_z, int radius, int target_height) {
if (radius < 1) radius = 1;
(void)ctx;
int radius_sq = radius * radius;
for (int dz = -radius; dz <= radius; ++dz) {
for (int dx = -radius; dx <= radius; ++dx) {
if (dx * dx + dz * dz > radius_sq) continue;
int wx = center_x + dx;
int wz = center_z + dz;
place_trail_column(out, columns, chunk_x, chunk_z, wx, wz, target_height);
}
}
}
static void carve_trail_span(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_data *out, column_data columns[CHUNK_SIZE][CHUNK_SIZE], int x0, int z0, int x1, int z1, int width) {
int dx = abs(x1 - x0);
int sx = (x0 < x1) ? 1 : -1;
int dz = abs(z1 - z0);
int sz = (z0 < z1) ? 1 : -1;
int err = dx - dz;
int last_height = INT32_MIN;
double tx = (double)(x1 - x0);
double tz = (double)(z1 - z0);
double len = sqrt(tx * tx + tz * tz);
const int max_step_up = 1;
const int max_step_down = 1;
const double carry_weight = 0.65;
while (1) {
if (len < 0.0001) {
column_data data = get_column_data(ctx, x0, z0);
carve_trail_pad(ctx, chunk_x, chunk_z, out, columns, x0, z0, width - 1, data.height);
return;
}
int steps = (int)ceil(len / 0.5);
if (steps < 1) steps = 1;
double nx = -tz / len;
double nz = tx / len;
int last_height = INT32_MIN;
int start_height = INT32_MIN;
double half_width = (double)width / 2.0;
for (int i = 0; i <= steps; ++i) {
double t = (double)i / (double)steps;
double px = x0 + tx * t;
double pz = z0 + tz * t;
int wx = (int)llround(px);
int wz = (int)llround(pz);
column_data data = get_column_data(ctx, wx, wz);
int target = data.height;
if (last_height != INT32_MIN) {
double blended = carry_weight * (double)last_height + (1.0 - carry_weight) * (double)target;
@@ -1691,32 +1760,28 @@ static void carve_trail_span(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_
} else if (target < last_height - max_step_down) {
target = last_height - max_step_down;
}
} else {
start_height = target;
}
last_height = target;
double tx = x1 - x0;
double tz = z1 - z0;
double len = sqrt(tx * tx + tz * tz);
double nx = 0.0, nz = 0.0;
if (len > 0.0001) {
nx = -tz / len;
nz = tx / len;
}
for (int w = -width / 2; w <= width / 2; ++w) {
int wx = x0 + (int)llround(nx * w);
int wz = z0 + (int)llround(nz * w);
place_trail_column(out, columns, chunk_x, chunk_z, wx, wz, target);
}
if (x0 == x1 && z0 == z1) break;
int err2 = 2 * err;
if (err2 > -dz) {
err -= dz;
x0 += sx;
}
if (err2 < dx) {
err += dx;
z0 += sz;
for (double w = -half_width; w <= half_width + 0.01; w += 0.5) {
double ox = px + nx * w;
double oz = pz + nz * w;
int fx = (int)llround(ox);
int fz = (int)llround(oz);
place_trail_column(out, columns, chunk_x, chunk_z, fx, fz, target);
}
}
int pad_radius = width - 1;
int dx = x1 - x0;
int dz = z1 - z0;
int use_pads = (abs(dx) > 0 && abs(dz) > 0);
if (use_pads && start_height != INT32_MIN) {
carve_trail_pad(ctx, chunk_x, chunk_z, out, columns, x0, z0, pad_radius, start_height);
}
if (use_pads && last_height != INT32_MIN) {
carve_trail_pad(ctx, chunk_x, chunk_z, out, columns, x1, z1, pad_radius, last_height);
}
}
static void generate_chunk_trails(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out) {
@@ -1822,11 +1887,12 @@ void worldgen_generate_chunk(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_
}
generate_chunk_grass(ctx, chunk_x, chunk_z, columns, out);
generate_chunk_flowers(ctx, chunk_x, chunk_z, columns, out);
// Tree overlay
block_list trees;
block_list_init(&trees);
generate_chunk_trees(ctx, chunk_x, chunk_z, &trees);
generate_chunk_trees(ctx, chunk_x, chunk_z, out, &trees);
for (size_t i = 0; i < trees.count; ++i) {
int lx = trees.items[i].x - chunk_x * CHUNK_SIZE;
int lz = trees.items[i].z - chunk_z * CHUNK_SIZE;