Speed up terrain generation
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#define CHUNK_SIZE 16
|
#define CHUNK_SIZE 16
|
||||||
#define CHUNK_HEIGHT 256
|
#define CHUNK_HEIGHT 256
|
||||||
|
#define WORLDGEN_HEIGHT_CACHE_SIZE 32768
|
||||||
|
|
||||||
enum BlockId {
|
enum BlockId {
|
||||||
BLOCK_BEDROCK = 0,
|
BLOCK_BEDROCK = 0,
|
||||||
@@ -64,6 +65,13 @@ typedef struct {
|
|||||||
uint16_t blocks[CHUNK_HEIGHT][CHUNK_SIZE][CHUNK_SIZE];
|
uint16_t blocks[CHUNK_HEIGHT][CHUNK_SIZE][CHUNK_SIZE];
|
||||||
} chunk_data;
|
} chunk_data;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int x;
|
||||||
|
int z;
|
||||||
|
int value;
|
||||||
|
uint8_t valid;
|
||||||
|
} worldgen_height_cache_entry;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
simplex_noise noise;
|
simplex_noise noise;
|
||||||
int sea_level;
|
int sea_level;
|
||||||
@@ -75,6 +83,7 @@ typedef struct {
|
|||||||
size_t trail_segment_cap;
|
size_t trail_segment_cap;
|
||||||
int prepass_done;
|
int prepass_done;
|
||||||
int prepass_min_x, prepass_max_x, prepass_min_z, prepass_max_z;
|
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;
|
} worldgen_ctx;
|
||||||
|
|
||||||
void worldgen_init(worldgen_ctx *ctx, int world_seed, int sea_level, int snow_line);
|
void worldgen_init(worldgen_ctx *ctx, int world_seed, int sea_level, int snow_line);
|
||||||
|
|||||||
@@ -682,14 +682,12 @@ static void *worker_fn(void *ptr) {
|
|||||||
chunk_data chunk;
|
chunk_data chunk;
|
||||||
worldgen_generate_chunk(&ctx, job.x, job.z, &chunk);
|
worldgen_generate_chunk(&ctx, job.x, job.z, &chunk);
|
||||||
if (args->results) {
|
if (args->results) {
|
||||||
pthread_mutex_lock(&args->results->mutex);
|
|
||||||
size_t result_idx = atomic_fetch_add(&args->results->count, 1);
|
size_t result_idx = atomic_fetch_add(&args->results->count, 1);
|
||||||
if (result_idx < args->results->capacity) {
|
if (result_idx < args->results->capacity) {
|
||||||
args->results->chunks[result_idx].chunk_x = chunk.chunk_x;
|
args->results->chunks[result_idx].chunk_x = chunk.chunk_x;
|
||||||
args->results->chunks[result_idx].chunk_z = chunk.chunk_z;
|
args->results->chunks[result_idx].chunk_z = chunk.chunk_z;
|
||||||
memcpy(&args->results->chunks[result_idx].data, &chunk, sizeof(chunk_data));
|
memcpy(&args->results->chunks[result_idx].data, &chunk, sizeof(chunk_data));
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&args->results->mutex);
|
|
||||||
} else {
|
} else {
|
||||||
write_chunk_file(args->out_dir, &chunk);
|
write_chunk_file(args->out_dir, &chunk);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -619,7 +619,7 @@ static void block_list_push(block_list *list, int x, int y, int z, uint16_t id)
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Core worldgen functions
|
// 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 region = region_blend(ctx, x, z);
|
||||||
double old_growth = old_growth_plains_mask(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;
|
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;
|
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) {
|
static column_data get_column_data(worldgen_ctx *ctx, int x, int z) {
|
||||||
column_data data;
|
column_data data;
|
||||||
data.height = column_height(ctx, x, z);
|
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;
|
if (ground_block != BLOCK_GRASS && ground_block != BLOCK_SNOW && ground_block != BLOCK_DIRT) return 0;
|
||||||
int top = y + trunk_height + crown_extra;
|
int top = y + trunk_height + crown_extra;
|
||||||
if (top >= CHUNK_HEIGHT) return 0;
|
if (top >= CHUNK_HEIGHT) return 0;
|
||||||
for (int cy = y; cy <= top; ++cy) {
|
|
||||||
for (int dx = -radius; dx <= radius; ++dx) {
|
for (int dx = -radius; dx <= radius; ++dx) {
|
||||||
for (int dz = -radius; dz <= radius; ++dz) {
|
for (int dz = -radius; dz <= radius; ++dz) {
|
||||||
if (cy < y && (dx != 0 || dz != 0)) continue;
|
column_data data = get_column_data(ctx, x + dx, z + dz);
|
||||||
int block = sample_block(ctx, x + dx, cy, z + dz);
|
int natural_top = data.height;
|
||||||
if (block != BLOCK_AIR && block != BLOCK_SNOW) {
|
if (data.has_water && data.water_surface > natural_top) {
|
||||||
return 0;
|
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_done = 0;
|
||||||
ctx->prepass_min_x = ctx->prepass_max_x = 0;
|
ctx->prepass_min_x = ctx->prepass_max_x = 0;
|
||||||
ctx->prepass_min_z = ctx->prepass_max_z = 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);
|
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));
|
memset(out, 0, sizeof(*out));
|
||||||
out->chunk_x = chunk_x;
|
out->chunk_x = chunk_x;
|
||||||
out->chunk_z = chunk_z;
|
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);
|
ensure_trail_prepass(ctx, chunk_x, chunk_z);
|
||||||
|
|
||||||
@@ -3574,26 +3602,29 @@ void worldgen_generate_chunk(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_
|
|||||||
column_data cd = columns[dx][dz];
|
column_data cd = columns[dx][dz];
|
||||||
int world_x = chunk_x * CHUNK_SIZE + dx;
|
int world_x = chunk_x * CHUNK_SIZE + dx;
|
||||||
int world_z = chunk_z * CHUNK_SIZE + dz;
|
int world_z = chunk_z * CHUNK_SIZE + dz;
|
||||||
for (int y = 0; y < CHUNK_HEIGHT; ++y) {
|
out->blocks[0][dx][dz] = BLOCK_BEDROCK;
|
||||||
int id;
|
int stone_top = cd.height - 3;
|
||||||
if (y == 0) {
|
if (stone_top > CHUNK_HEIGHT) stone_top = CHUNK_HEIGHT;
|
||||||
id = BLOCK_BEDROCK;
|
for (int y = 1; y < stone_top; ++y) {
|
||||||
} else if (y < cd.height - 3) {
|
|
||||||
if (generate_coal(ctx, world_x, y, world_z, cd.height, cd.biome)) {
|
if (generate_coal(ctx, world_x, y, world_z, cd.height, cd.biome)) {
|
||||||
id = BLOCK_COAL;
|
out->blocks[y][dx][dz] = BLOCK_COAL;
|
||||||
} else {
|
} else {
|
||||||
id = (int)generate_normal_ores(ctx, world_x, y, world_z, &cd);
|
out->blocks[y][dx][dz] = 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;
|
|
||||||
} else {
|
|
||||||
id = BLOCK_AIR;
|
|
||||||
}
|
}
|
||||||
out->blocks[y][dx][dz] = (uint16_t)id;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user