Add trail prepass scaffolding

This commit is contained in:
chelsea
2025-12-02 22:18:26 -06:00
parent 317a3f0dcc
commit 483200363e
4 changed files with 225 additions and 3 deletions

Binary file not shown.

View File

@@ -79,5 +79,6 @@ typedef struct {
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);
void worldgen_generate_chunk(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_data *out); void worldgen_generate_chunk(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_data *out);
void worldgen_prepass(worldgen_ctx *ctx, int min_x, int max_x, int min_z, int max_z); void worldgen_prepass(worldgen_ctx *ctx, int min_x, int max_x, int min_z, int max_z);
void worldgen_free_trails(worldgen_ctx *ctx);
#endif #endif

View File

@@ -52,6 +52,9 @@ typedef struct {
pthread_mutex_t *log_mu; pthread_mutex_t *log_mu;
int enable_trails; int enable_trails;
results_buffer *results; results_buffer *results;
struct trail_segment *trail_segments;
size_t trail_segment_count;
int prepass_min_x, prepass_max_x, prepass_min_z, prepass_max_z;
} worker_args; } worker_args;
typedef enum { typedef enum {
@@ -654,6 +657,16 @@ static void *worker_fn(void *ptr) {
worldgen_ctx ctx; worldgen_ctx ctx;
worldgen_init(&ctx, args->world_seed, args->sea_level, args->snow_line); worldgen_init(&ctx, args->world_seed, args->sea_level, args->snow_line);
ctx.enable_trails = args->enable_trails; ctx.enable_trails = args->enable_trails;
if (args->trail_segments && args->trail_segment_count > 0) {
ctx.trail_segments = args->trail_segments;
ctx.trail_segment_count = args->trail_segment_count;
ctx.trail_segment_cap = args->trail_segment_count;
ctx.prepass_done = 1;
ctx.prepass_min_x = args->prepass_min_x;
ctx.prepass_max_x = args->prepass_max_x;
ctx.prepass_min_z = args->prepass_min_z;
ctx.prepass_max_z = args->prepass_max_z;
}
while (1) { while (1) {
size_t idx = atomic_fetch_add(&args->queue->next_index, 1); size_t idx = atomic_fetch_add(&args->queue->next_index, 1);
if (idx >= args->queue->count) break; if (idx >= args->queue->count) break;
@@ -698,6 +711,9 @@ int main(int argc, char **argv) {
const char *out_dir = "output"; const char *out_dir = "output";
output_format format = FORMAT_MCA; output_format format = FORMAT_MCA;
/* Prepass context */
worldgen_ctx pre_ctx;
int prepass_ready = 0;
for (int i = 1; i < argc; ++i) { for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--radius") == 0 && i + 1 < argc) { if (strcmp(argv[i], "--radius") == 0 && i + 1 < argc) {
radius = (int)parse_long(argv[++i]); radius = (int)parse_long(argv[++i]);
@@ -753,6 +769,18 @@ int main(int argc, char **argv) {
snow_line = sea_level + 38; snow_line = sea_level + 38;
} }
/* Global prepass: compute trails based on target area before threading */
worldgen_init(&pre_ctx, world_seed, sea_level, snow_line);
pre_ctx.enable_trails = enable_trails;
int world_min_x = min_x * CHUNK_SIZE;
int world_max_x = max_x * CHUNK_SIZE + (CHUNK_SIZE - 1);
int world_min_z = min_z * CHUNK_SIZE;
int world_max_z = max_z * CHUNK_SIZE + (CHUNK_SIZE - 1);
if (enable_trails) {
worldgen_prepass(&pre_ctx, world_min_x, world_max_x, world_min_z, world_max_z);
prepass_ready = 1;
}
if (!have_rect) { if (!have_rect) {
min_x = center_x - radius; min_x = center_x - radius;
max_x = center_x + radius; max_x = center_x + radius;
@@ -800,7 +828,13 @@ int main(int argc, char **argv) {
.snow_line = snow_line, .snow_line = snow_line,
.log_mu = &log_mu, .log_mu = &log_mu,
.enable_trails = enable_trails, .enable_trails = enable_trails,
.results = &results}; .results = &results,
.trail_segments = prepass_ready ? pre_ctx.trail_segments : NULL,
.trail_segment_count = prepass_ready ? pre_ctx.trail_segment_count : 0,
.prepass_min_x = world_min_x,
.prepass_max_x = world_max_x,
.prepass_min_z = world_min_z,
.prepass_max_z = world_max_z};
for (int i = 0; i < threads; ++i) { for (int i = 0; i < threads; ++i) {
pthread_create(&workers[i], NULL, worker_fn, &args); pthread_create(&workers[i], NULL, worker_fn, &args);
@@ -861,5 +895,8 @@ int main(int argc, char **argv) {
free(results.chunks); free(results.chunks);
free(workers); free(workers);
free(jobs); free(jobs);
if (prepass_ready) {
worldgen_free_trails(&pre_ctx);
}
return 0; return 0;
} }

View File

@@ -131,6 +131,7 @@ static void generate_chunk_grass(worldgen_ctx *ctx, int chunk_x, int chunk_z, co
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 generate_chunk_flowers(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out);
static void generate_chunk_cabins(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out); static void generate_chunk_cabins(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out);
static void ensure_trail_prepass(worldgen_ctx *ctx, int chunk_x, int chunk_z); static void ensure_trail_prepass(worldgen_ctx *ctx, int chunk_x, int chunk_z);
static void append_trail_segment(worldgen_ctx *ctx, int ax, int az, int bx, int bz, int *points, int count);
static void trail_node_position(worldgen_ctx *ctx, int node_x, int node_z, double spacing, double *out_x, double *out_z); 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 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 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); 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);
@@ -141,6 +142,8 @@ static uint16_t generate_normal_ores(worldgen_ctx *ctx, int x, int y, int z, con
static void connect_cabin_to_trail(worldgen_ctx *ctx, int chunk_x, int chunk_z, static void connect_cabin_to_trail(worldgen_ctx *ctx, int chunk_x, int chunk_z,
column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *chunk, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *chunk,
int door_x, int door_z, int door_side, int path_width); int door_x, int door_z, int door_side, int path_width);
static column_data get_column_data(worldgen_ctx *ctx, int x, int z);
static int generate_coal(worldgen_ctx *ctx, int x, int y, int z, int column_height, biome_id biome);
static uint32_t hash_coords(int x, int z, uint32_t seed) { static uint32_t hash_coords(int x, int z, uint32_t seed) {
uint32_t h = (uint32_t)(x * 374761393 + z * 668265263) ^ seed; uint32_t h = (uint32_t)(x * 374761393 + z * 668265263) ^ seed;
@@ -179,6 +182,163 @@ static double land_value(worldgen_ctx *ctx, int x, int z) {
return value; return value;
} }
static void append_trail_segment(worldgen_ctx *ctx, int ax, int az, int bx, int bz, int *points, int count) {
if (ctx->trail_segment_count >= ctx->trail_segment_cap) {
size_t new_cap = ctx->trail_segment_cap ? ctx->trail_segment_cap * 2 : 32;
trail_segment *resized = (trail_segment *)realloc(ctx->trail_segments, new_cap * sizeof(trail_segment));
if (!resized) return;
ctx->trail_segments = resized;
ctx->trail_segment_cap = new_cap;
}
trail_segment *seg = &ctx->trail_segments[ctx->trail_segment_count];
memset(seg, 0, sizeof(*seg));
seg->ax = ax;
seg->az = az;
seg->bx = bx;
seg->bz = bz;
seg->points = points;
seg->count = count;
ctx->trail_segment_count++;
}
void worldgen_prepass(worldgen_ctx *ctx, int min_x, int max_x, int min_z, int max_z) {
if (!ctx) return;
if (ctx->prepass_done) {
if (min_x >= ctx->prepass_min_x && max_x <= ctx->prepass_max_x &&
min_z >= ctx->prepass_min_z && max_z <= ctx->prepass_max_z) {
return;
}
}
for (size_t i = 0; i < ctx->trail_segment_count; ++i) {
free(ctx->trail_segments[i].points);
}
free(ctx->trail_segments);
ctx->trail_segments = NULL;
ctx->trail_segment_count = 0;
ctx->trail_segment_cap = 0;
const int step = 64;
const int max_points = 48;
const double min_spacing = 96.0;
int cap = max_points;
int count = 0;
int *px = (int *)malloc((size_t)cap * sizeof(int));
int *pz = (int *)malloc((size_t)cap * sizeof(int));
double *pv = (double *)malloc((size_t)cap * sizeof(double));
if (!px || !pz || !pv) {
free(px); free(pz); free(pv);
goto done;
}
for (int z = min_z; z <= max_z; z += step) {
for (int x = min_x; x <= max_x; x += step) {
double val = land_value(ctx, x, z);
if (val < 0.05) continue;
int spaced = 1;
for (int i = 0; i < count; ++i) {
double dx = (double)(x - px[i]);
double dz = (double)(z - pz[i]);
if (dx * dx + dz * dz < min_spacing * min_spacing) {
spaced = 0;
break;
}
}
if (!spaced) continue;
if (count < cap) {
px[count] = x;
pz[count] = z;
pv[count] = val;
count++;
}
if (count >= max_points) break;
}
if (count >= max_points) break;
}
if (count == 0) {
px = (int *)realloc(px, sizeof(int));
pz = (int *)realloc(pz, sizeof(int));
pv = (double *)realloc(pv, sizeof(double));
if (!px || !pz || !pv) {
free(px); free(pz); free(pv);
goto done;
}
px[0] = (min_x + max_x) / 2;
pz[0] = (min_z + max_z) / 2;
pv[0] = 1.0;
count = 1;
}
int root = 0;
for (int i = 1; i < count; ++i) {
if (pv[i] > pv[root]) root = i;
}
unsigned char *connected = (unsigned char *)calloc((size_t)count, 1);
if (!connected) {
free(px); free(pz); free(pv);
goto done;
}
connected[root] = 1;
int connected_count = 1;
while (connected_count < count) {
int best_u = -1, best_v = -1;
double best_d2 = DBL_MAX;
for (int u = 0; u < count; ++u) {
if (!connected[u]) continue;
for (int v = 0; v < count; ++v) {
if (connected[v]) continue;
double dx = (double)(px[u] - px[v]);
double dz = (double)(pz[u] - pz[v]);
double d2 = dx * dx + dz * dz;
if (d2 < best_d2) {
best_d2 = d2;
best_u = u;
best_v = v;
}
}
}
if (best_u == -1 || best_v == -1) break;
int *pts = NULL;
int path_count = 0;
if (build_trail_path(ctx, (double)px[best_u], (double)pz[best_u], (double)px[best_v], (double)pz[best_v], &pts, &path_count) && pts && path_count >= 2) {
append_trail_segment(ctx, px[best_u], pz[best_u], px[best_v], pz[best_v], pts, path_count);
} else {
free(pts);
}
connected[best_v] = 1;
connected_count++;
}
free(connected);
free(px);
free(pz);
free(pv);
done:
ctx->prepass_done = 1;
ctx->prepass_min_x = min_x;
ctx->prepass_max_x = max_x;
ctx->prepass_min_z = min_z;
ctx->prepass_max_z = max_z;
}
static void ensure_trail_prepass(worldgen_ctx *ctx, int chunk_x, int chunk_z) {
if (!ctx) return;
if (ctx->prepass_done) return;
int min_x = (chunk_x - 8) * CHUNK_SIZE;
int max_x = (chunk_x + 8) * CHUNK_SIZE + (CHUNK_SIZE - 1);
int min_z = (chunk_z - 8) * CHUNK_SIZE;
int max_z = (chunk_z + 8) * CHUNK_SIZE + (CHUNK_SIZE - 1);
worldgen_prepass(ctx, min_x, max_x, min_z, max_z);
}
void worldgen_free_trails(worldgen_ctx *ctx) {
if (!ctx || !ctx->trail_segments) return;
for (size_t i = 0; i < ctx->trail_segment_count; ++i) {
free(ctx->trail_segments[i].points);
}
free(ctx->trail_segments);
ctx->trail_segments = NULL;
ctx->trail_segment_count = 0;
ctx->trail_segment_cap = 0;
}
static void set_block_with_height(chunk_data *chunk, int local_x, int local_z, int y, uint16_t id) { static void set_block_with_height(chunk_data *chunk, int local_x, int local_z, int y, uint16_t id) {
if (local_x < 0 || local_x >= CHUNK_SIZE || local_z < 0 || local_z >= CHUNK_SIZE) return; if (local_x < 0 || local_x >= CHUNK_SIZE || local_z < 0 || local_z >= CHUNK_SIZE) return;
if (y < 0 || y >= CHUNK_HEIGHT) return; if (y < 0 || y >= CHUNK_HEIGHT) return;
@@ -2636,16 +2796,38 @@ static void carve_trail_span(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_
} }
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_trails(worldgen_ctx *ctx, int chunk_x, int chunk_z, column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *out) {
const double spacing = TRAIL_NODE_SPACING;
int chunk_min_x = chunk_x * CHUNK_SIZE; int chunk_min_x = chunk_x * CHUNK_SIZE;
int chunk_max_x = chunk_min_x + CHUNK_SIZE - 1; int chunk_max_x = chunk_min_x + CHUNK_SIZE - 1;
int chunk_min_z = chunk_z * CHUNK_SIZE; int chunk_min_z = chunk_z * CHUNK_SIZE;
int chunk_max_z = chunk_min_z + CHUNK_SIZE - 1; int chunk_max_z = chunk_min_z + CHUNK_SIZE - 1;
int width = TRAIL_WIDTH;
if (ctx->trail_segment_count > 0) {
for (size_t s = 0; s < ctx->trail_segment_count; ++s) {
trail_segment *seg = &ctx->trail_segments[s];
if (!seg || seg->count < 2) continue;
for (int i = 1; i < seg->count; ++i) {
int x0 = seg->points[(i - 1) * 2];
int z0 = seg->points[(i - 1) * 2 + 1];
int x1 = seg->points[i * 2];
int z1 = seg->points[i * 2 + 1];
int span_min_x = (x0 < x1) ? x0 : x1;
int span_max_x = (x0 > x1) ? x0 : x1;
int span_min_z = (z0 < z1) ? z0 : z1;
int span_max_z = (z0 > z1) ? z0 : z1;
if (span_max_x < chunk_min_x - 4 || span_min_x > chunk_max_x + 4) continue;
if (span_max_z < chunk_min_z - 4 || span_min_z > chunk_max_z + 4) continue;
carve_trail_span(ctx, chunk_x, chunk_z, out, columns, x0, z0, x1, z1, width);
}
}
return;
}
const double spacing = TRAIL_NODE_SPACING;
int min_node_x = (int)floor((chunk_min_x - spacing) / spacing); int min_node_x = (int)floor((chunk_min_x - spacing) / spacing);
int max_node_x = (int)ceil((chunk_max_x + spacing) / spacing); int max_node_x = (int)ceil((chunk_max_x + spacing) / spacing);
int min_node_z = (int)floor((chunk_min_z - spacing) / spacing); int min_node_z = (int)floor((chunk_min_z - spacing) / spacing);
int max_node_z = (int)ceil((chunk_max_z + spacing) / spacing); int max_node_z = (int)ceil((chunk_max_z + spacing) / spacing);
int width = TRAIL_WIDTH;
size_t neighbor_count = sizeof(TRAIL_NEIGHBOR_OFFSETS) / sizeof(TRAIL_NEIGHBOR_OFFSETS[0]); size_t neighbor_count = sizeof(TRAIL_NEIGHBOR_OFFSETS) / sizeof(TRAIL_NEIGHBOR_OFFSETS[0]);
for (int nx = min_node_x; nx <= max_node_x; ++nx) { for (int nx = min_node_x; nx <= max_node_x; ++nx) {
for (int nz = min_node_z; nz <= max_node_z; ++nz) { for (int nz = min_node_z; nz <= max_node_z; ++nz) {
@@ -2695,6 +2877,8 @@ void worldgen_generate_chunk(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_
out->chunk_x = chunk_x; out->chunk_x = chunk_x;
out->chunk_z = chunk_z; out->chunk_z = chunk_z;
ensure_trail_prepass(ctx, chunk_x, chunk_z);
// Precompute column data for base terrain // Precompute column data for base terrain
column_data columns[CHUNK_SIZE][CHUNK_SIZE]; column_data columns[CHUNK_SIZE][CHUNK_SIZE];
for (int dx = 0; dx < CHUNK_SIZE; ++dx) { for (int dx = 0; dx < CHUNK_SIZE; ++dx) {