Add trail prepass scaffolding
This commit is contained in:
@@ -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