Speed up terrain generation

This commit is contained in:
chelsea
2026-05-02 19:03:57 -05:00
parent d5fd7e6bb5
commit 1addbada63
3 changed files with 68 additions and 30 deletions

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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;
}
}
}