Add trail prepass scaffolding
This commit is contained in:
Binary file not shown.
@@ -79,5 +79,6 @@ typedef struct {
|
||||
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_prepass(worldgen_ctx *ctx, int min_x, int max_x, int min_z, int max_z);
|
||||
void worldgen_free_trails(worldgen_ctx *ctx);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -52,6 +52,9 @@ typedef struct {
|
||||
pthread_mutex_t *log_mu;
|
||||
int enable_trails;
|
||||
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;
|
||||
|
||||
typedef enum {
|
||||
@@ -654,6 +657,16 @@ static void *worker_fn(void *ptr) {
|
||||
worldgen_ctx ctx;
|
||||
worldgen_init(&ctx, args->world_seed, args->sea_level, args->snow_line);
|
||||
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) {
|
||||
size_t idx = atomic_fetch_add(&args->queue->next_index, 1);
|
||||
if (idx >= args->queue->count) break;
|
||||
@@ -698,6 +711,9 @@ int main(int argc, char **argv) {
|
||||
const char *out_dir = "output";
|
||||
output_format format = FORMAT_MCA;
|
||||
|
||||
/* Prepass context */
|
||||
worldgen_ctx pre_ctx;
|
||||
int prepass_ready = 0;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (strcmp(argv[i], "--radius") == 0 && i + 1 < argc) {
|
||||
radius = (int)parse_long(argv[++i]);
|
||||
@@ -753,6 +769,18 @@ int main(int argc, char **argv) {
|
||||
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) {
|
||||
min_x = center_x - radius;
|
||||
max_x = center_x + radius;
|
||||
@@ -800,7 +828,13 @@ int main(int argc, char **argv) {
|
||||
.snow_line = snow_line,
|
||||
.log_mu = &log_mu,
|
||||
.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) {
|
||||
pthread_create(&workers[i], NULL, worker_fn, &args);
|
||||
@@ -861,5 +895,8 @@ int main(int argc, char **argv) {
|
||||
free(results.chunks);
|
||||
free(workers);
|
||||
free(jobs);
|
||||
if (prepass_ready) {
|
||||
worldgen_free_trails(&pre_ctx);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -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_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 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 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);
|
||||
@@ -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,
|
||||
column_data columns[CHUNK_SIZE][CHUNK_SIZE], chunk_data *chunk,
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (local_x < 0 || local_x >= CHUNK_SIZE || local_z < 0 || local_z >= CHUNK_SIZE) 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) {
|
||||
const double spacing = TRAIL_NODE_SPACING;
|
||||
int chunk_min_x = chunk_x * CHUNK_SIZE;
|
||||
int chunk_max_x = chunk_min_x + CHUNK_SIZE - 1;
|
||||
int chunk_min_z = chunk_z * CHUNK_SIZE;
|
||||
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 max_node_x = (int)ceil((chunk_max_x + 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 width = TRAIL_WIDTH;
|
||||
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 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_z = chunk_z;
|
||||
|
||||
ensure_trail_prepass(ctx, chunk_x, chunk_z);
|
||||
|
||||
// Precompute column data for base terrain
|
||||
column_data columns[CHUNK_SIZE][CHUNK_SIZE];
|
||||
for (int dx = 0; dx < CHUNK_SIZE; ++dx) {
|
||||
|
||||
Reference in New Issue
Block a user