From 1addbada63dfd6774824fb53485751996db39fba Mon Sep 17 00:00:00 2001 From: chelsea Date: Sat, 2 May 2026 19:03:57 -0500 Subject: [PATCH] Speed up terrain generation --- worldgen-c/include/worldgen.h | 9 ++++ worldgen-c/src/main.c | 2 - worldgen-c/src/worldgen.c | 87 ++++++++++++++++++++++++----------- 3 files changed, 68 insertions(+), 30 deletions(-) diff --git a/worldgen-c/include/worldgen.h b/worldgen-c/include/worldgen.h index dfb3283..cc9d06e 100644 --- a/worldgen-c/include/worldgen.h +++ b/worldgen-c/include/worldgen.h @@ -7,6 +7,7 @@ #define CHUNK_SIZE 16 #define CHUNK_HEIGHT 256 +#define WORLDGEN_HEIGHT_CACHE_SIZE 32768 enum BlockId { BLOCK_BEDROCK = 0, @@ -64,6 +65,13 @@ typedef struct { uint16_t blocks[CHUNK_HEIGHT][CHUNK_SIZE][CHUNK_SIZE]; } chunk_data; +typedef struct { + int x; + int z; + int value; + uint8_t valid; +} worldgen_height_cache_entry; + typedef struct { simplex_noise noise; int sea_level; @@ -75,6 +83,7 @@ typedef struct { size_t trail_segment_cap; int prepass_done; int prepass_min_x, prepass_max_x, prepass_min_z, prepass_max_z; + worldgen_height_cache_entry height_cache[WORLDGEN_HEIGHT_CACHE_SIZE]; } worldgen_ctx; void worldgen_init(worldgen_ctx *ctx, int world_seed, int sea_level, int snow_line); diff --git a/worldgen-c/src/main.c b/worldgen-c/src/main.c index cc6fc69..93985fa 100644 --- a/worldgen-c/src/main.c +++ b/worldgen-c/src/main.c @@ -682,14 +682,12 @@ static void *worker_fn(void *ptr) { chunk_data chunk; worldgen_generate_chunk(&ctx, job.x, job.z, &chunk); if (args->results) { - pthread_mutex_lock(&args->results->mutex); size_t result_idx = atomic_fetch_add(&args->results->count, 1); if (result_idx < args->results->capacity) { args->results->chunks[result_idx].chunk_x = chunk.chunk_x; args->results->chunks[result_idx].chunk_z = chunk.chunk_z; memcpy(&args->results->chunks[result_idx].data, &chunk, sizeof(chunk_data)); } - pthread_mutex_unlock(&args->results->mutex); } else { write_chunk_file(args->out_dir, &chunk); } diff --git a/worldgen-c/src/worldgen.c b/worldgen-c/src/worldgen.c index 3f7e043..3e68cd5 100644 --- a/worldgen-c/src/worldgen.c +++ b/worldgen-c/src/worldgen.c @@ -619,7 +619,7 @@ static void block_list_push(block_list *list, int x, int y, int z, uint16_t id) // --------------------------------------------------------------------------- // Core worldgen functions // --------------------------------------------------------------------------- -static int column_height(worldgen_ctx *ctx, int x, int z) { +static int raw_column_height(worldgen_ctx *ctx, int x, int z) { double region = region_blend(ctx, x, z); double old_growth = old_growth_plains_mask(ctx, x, z); double warp1_x = x + simplex_noise2(&ctx->noise, x * 0.001, z * 0.001) * 50.0; @@ -686,6 +686,21 @@ static int column_height(worldgen_ctx *ctx, int x, int z) { return (int)height; } +static int column_height(worldgen_ctx *ctx, int x, int z) { + uint32_t h = hash_coords(x, z, (uint32_t)ctx->world_seed ^ 0xA53C9E21u); + size_t idx = (size_t)(h & (WORLDGEN_HEIGHT_CACHE_SIZE - 1)); + worldgen_height_cache_entry *entry = &ctx->height_cache[idx]; + if (entry->valid && entry->x == x && entry->z == z) { + return entry->value; + } + int value = raw_column_height(ctx, x, z); + entry->x = x; + entry->z = z; + entry->value = value; + entry->valid = 1; + return value; +} + static column_data get_column_data(worldgen_ctx *ctx, int x, int z) { column_data data; data.height = column_height(ctx, x, z); @@ -991,14 +1006,19 @@ static int can_place_tree(worldgen_ctx *ctx, int x, int y, int z, int trunk_heig if (ground_block != BLOCK_GRASS && ground_block != BLOCK_SNOW && ground_block != BLOCK_DIRT) return 0; int top = y + trunk_height + crown_extra; if (top >= CHUNK_HEIGHT) return 0; - for (int cy = y; cy <= top; ++cy) { - for (int dx = -radius; dx <= radius; ++dx) { - for (int dz = -radius; dz <= radius; ++dz) { - if (cy < y && (dx != 0 || dz != 0)) continue; - int block = sample_block(ctx, x + dx, cy, z + dz); - if (block != BLOCK_AIR && block != BLOCK_SNOW) { - return 0; + for (int dx = -radius; dx <= radius; ++dx) { + for (int dz = -radius; dz <= radius; ++dz) { + column_data data = get_column_data(ctx, x + dx, z + dz); + int natural_top = data.height; + if (data.has_water && data.water_surface > natural_top) { + natural_top = data.water_surface; + } + if (natural_top >= y && natural_top <= top) { + if (natural_top == y && natural_top == data.height && + select_surface_block(ctx, &data, x + dx, z + dz) == BLOCK_SNOW) { + continue; } + return 0; } } } @@ -3547,6 +3567,7 @@ void worldgen_init(worldgen_ctx *ctx, int world_seed, int sea_level, int snow_li ctx->prepass_done = 0; ctx->prepass_min_x = ctx->prepass_max_x = 0; ctx->prepass_min_z = ctx->prepass_max_z = 0; + memset(ctx->height_cache, 0, sizeof(ctx->height_cache)); simplex_init(&ctx->noise, (uint32_t)world_seed); } @@ -3554,6 +3575,13 @@ void worldgen_generate_chunk(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_ memset(out, 0, sizeof(*out)); out->chunk_x = chunk_x; out->chunk_z = chunk_z; + for (int y = 0; y < CHUNK_HEIGHT; ++y) { + for (int dx = 0; dx < CHUNK_SIZE; ++dx) { + for (int dz = 0; dz < CHUNK_SIZE; ++dz) { + out->blocks[y][dx][dz] = BLOCK_AIR; + } + } + } ensure_trail_prepass(ctx, chunk_x, chunk_z); @@ -3572,28 +3600,31 @@ void worldgen_generate_chunk(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_ for (int dx = 0; dx < CHUNK_SIZE; ++dx) { for (int dz = 0; dz < CHUNK_SIZE; ++dz) { column_data cd = columns[dx][dz]; - int world_x = chunk_x * CHUNK_SIZE + dx; - int world_z = chunk_z * CHUNK_SIZE + dz; - for (int y = 0; y < CHUNK_HEIGHT; ++y) { - int id; - if (y == 0) { - id = BLOCK_BEDROCK; - } else if (y < cd.height - 3) { - if (generate_coal(ctx, world_x, y, world_z, cd.height, cd.biome)) { - id = BLOCK_COAL; - } else { - id = (int)generate_normal_ores(ctx, world_x, y, world_z, &cd); - } - } else if (y < cd.height) { - id = BLOCK_DIRT; - } else if (y == cd.height) { - id = select_surface_block(ctx, &cd, world_x, world_z); - } else if (cd.has_water && y <= cd.water_surface && y > cd.height) { - id = BLOCK_WATER; + int world_x = chunk_x * CHUNK_SIZE + dx; + int world_z = chunk_z * CHUNK_SIZE + dz; + out->blocks[0][dx][dz] = BLOCK_BEDROCK; + int stone_top = cd.height - 3; + if (stone_top > CHUNK_HEIGHT) stone_top = CHUNK_HEIGHT; + for (int y = 1; y < stone_top; ++y) { + if (generate_coal(ctx, world_x, y, world_z, cd.height, cd.biome)) { + out->blocks[y][dx][dz] = BLOCK_COAL; } else { - id = BLOCK_AIR; + out->blocks[y][dx][dz] = generate_normal_ores(ctx, world_x, y, world_z, &cd); + } + } + int dirt_start = stone_top > 1 ? stone_top : 1; + for (int y = dirt_start; y < cd.height && y < CHUNK_HEIGHT; ++y) { + out->blocks[y][dx][dz] = BLOCK_DIRT; + } + if (cd.height >= 0 && cd.height < CHUNK_HEIGHT) { + out->blocks[cd.height][dx][dz] = select_surface_block(ctx, &cd, world_x, world_z); + } + if (cd.has_water) { + int water_top = cd.water_surface; + if (water_top >= CHUNK_HEIGHT) water_top = CHUNK_HEIGHT - 1; + for (int y = cd.height + 1; y <= water_top; ++y) { + if (y > 0) out->blocks[y][dx][dz] = BLOCK_WATER; } - out->blocks[y][dx][dz] = (uint16_t)id; } } }