Tune redwoods cabins and chunk heightmaps

This commit is contained in:
chelsea
2026-05-02 15:13:15 -05:00
parent 2f11a5b5fa
commit 7f10f3536a
5 changed files with 102 additions and 53 deletions

View File

@@ -128,6 +128,7 @@ static const block_state BLOCK_STATE_TABLE[] = {
[BLOCK_SPRUCE_LOG_X] = {"minecraft:spruce_log", PROPS_LOG_AXIS_X, 1},
[BLOCK_SPRUCE_LOG_Z] = {"minecraft:spruce_log", PROPS_LOG_AXIS_Z, 1},
[BLOCK_GLASS_PANE] = {"minecraft:glass_pane", NULL, 0},
[BLOCK_GLASS] = {"minecraft:glass", NULL, 0},
[BLOCK_SPRUCE_DOOR_S_LOWER] = {"minecraft:spruce_door", PROPS_DOOR_S_LOWER, 5},
[BLOCK_SPRUCE_DOOR_S_UPPER] = {"minecraft:spruce_door", PROPS_DOOR_S_UPPER, 5},
[BLOCK_SPRUCE_DOOR_N_LOWER] = {"minecraft:spruce_door", PROPS_DOOR_N_LOWER, 5},
@@ -314,13 +315,19 @@ static void pack_bits(const uint16_t *indices, size_t count, int bits_per_value,
// Chunk -> NBT helpers
// ---------------------------------------------------------------------------
static void pack_heightmap(const chunk_data *chunk, int64_t *out_longs, size_t out_count) {
uint16_t values[CHUNK_SIZE * CHUNK_SIZE];
memset(out_longs, 0, out_count * sizeof(int64_t));
const int bits = 9;
const int values_per_long = 64 / bits;
for (int z = 0; z < CHUNK_SIZE; ++z) {
for (int x = 0; x < CHUNK_SIZE; ++x) {
values[x + z * CHUNK_SIZE] = chunk->heightmap[x][z];
size_t idx = (size_t)(x + z * CHUNK_SIZE);
size_t long_id = idx / (size_t)values_per_long;
size_t offset = (idx % (size_t)values_per_long) * (size_t)bits;
if (long_id >= out_count) continue;
uint64_t *target = (uint64_t *)&out_longs[long_id];
*target |= ((uint64_t)chunk->heightmap[x][z] & 0x1FFu) << offset;
}
}
pack_bits(values, CHUNK_SIZE * CHUNK_SIZE, 9, out_longs, out_count);
for (size_t i = 0; i < out_count; ++i) {
out_longs[i] = to_signed64((uint64_t)out_longs[i]);
}
@@ -419,8 +426,8 @@ static void build_chunk_nbt(const chunk_data *chunk, buf *out) {
int32_t biomes[256];
for (int i = 0; i < 256; ++i) biomes[i] = 1; // Plains biome
int64_t heightmap[36];
pack_heightmap(chunk, heightmap, 36);
int64_t heightmap[37];
pack_heightmap(chunk, heightmap, 37);
nbt_start_compound(out, "");
nbt_write_int(out, "DataVersion", DATA_VERSION);
@@ -434,7 +441,7 @@ static void build_chunk_nbt(const chunk_data *chunk, buf *out) {
nbt_write_byte(out, "isLightOn", 1);
nbt_start_compound(out, "Heightmaps");
nbt_write_long_array(out, "MOTION_BLOCKING", heightmap, 36);
nbt_write_long_array(out, "MOTION_BLOCKING", heightmap, 37);
nbt_end_compound(out);
nbt_write_int_array(out, "Biomes", biomes, 256);

View File

@@ -426,7 +426,7 @@ static int is_cabin_structure_block(uint16_t block) {
return block == BLOCK_SPRUCE_PLANKS || block == BLOCK_OAK_PLANKS ||
block == BLOCK_OAK_LOG_X || block == BLOCK_OAK_LOG_Z ||
block == BLOCK_SPRUCE_LOG_X || block == BLOCK_SPRUCE_LOG_Z ||
block == BLOCK_GLASS_PANE || block == BLOCK_SPRUCE_STAIRS_E || block == BLOCK_SPRUCE_STAIRS_W ||
block == BLOCK_GLASS_PANE || block == BLOCK_GLASS || block == BLOCK_SPRUCE_STAIRS_E || block == BLOCK_SPRUCE_STAIRS_W ||
block == BLOCK_LADDER_N || block == BLOCK_LADDER_S || block == BLOCK_LADDER_E || block == BLOCK_LADDER_W ||
block == BLOCK_SPRUCE_DOOR_N_LOWER || block == BLOCK_SPRUCE_DOOR_N_UPPER ||
block == BLOCK_SPRUCE_DOOR_S_LOWER || block == BLOCK_SPRUCE_DOOR_S_UPPER ||
@@ -1442,41 +1442,37 @@ static void build_maple_ancient(worldgen_ctx *ctx, int x, int y, int z, int heig
static void build_redwood_titan(worldgen_ctx *ctx, int x, int y, int z, int height, rng_state *rng, block_list *out, const tree_archetype *arch) {
(void)ctx;
int extra = rng_range_inclusive(rng, 20, 32);
int core_height = height + extra;
if (core_height < 60) core_height = 60;
if (y + core_height + 16 >= CHUNK_HEIGHT) {
core_height = CHUNK_HEIGHT - y - 16;
if (core_height < 40) core_height = 40;
int core_height = height + rng_range_inclusive(rng, 2, 7);
if (core_height < 30) core_height = 30;
if (core_height > 44) core_height = 44;
if (y + core_height + 10 >= CHUNK_HEIGHT) {
core_height = CHUNK_HEIGHT - y - 10;
if (core_height < 24) core_height = 24;
}
int taper_start = core_height - 12;
int taper_start = core_height - 6;
for (int dy = 0; dy < core_height; ++dy) {
int radius = 3;
if (dy > taper_start) radius = 2;
if (dy > taper_start + 6) radius = 1;
int radius = (dy >= taper_start) ? 0 : 1;
for (int dx = -radius; dx <= radius; ++dx) {
for (int dz = -radius; dz <= radius; ++dz) {
if (radius >= 3 && abs(dx) == radius && abs(dz) == radius && dy > 4) continue;
if (radius == 2 && abs(dx) == 2 && abs(dz) == 2 && dy > taper_start) continue;
block_list_push(out, x + dx, y + dy, z + dz, (uint16_t)arch->log_block);
}
}
}
int spire = 6 + rng_range_inclusive(rng, 0, 3);
int spire = 3 + rng_range_inclusive(rng, 0, 2);
place_log_column(x, y + core_height, z, spire, arch->log_block, out);
int canopy_base = y + core_height - 6;
for (int ring = 0; ring < 5; ++ring) {
int radius = 6 - ring;
for (int ring = 0; ring < 4; ++ring) {
int radius = 4 - ring;
if (radius < 2) radius = 2;
place_leaf_circle(x, canopy_base + ring, z, radius, arch->leaf_block, rng, 0.1, out);
place_leaf_circle(x, canopy_base + ring + 1, z, radius - 1, arch->leaf_block, rng, 0.18, out);
}
place_leaf_circle(x, canopy_base + 6, z, 2, arch->leaf_block, rng, 0.0, out);
place_leaf_circle(x, canopy_base + 7, z, 1, arch->leaf_block, rng, 0.0, out);
place_leaf_circle(x, canopy_base + 5, z, 2, arch->leaf_block, rng, 0.0, out);
place_leaf_circle(x, canopy_base + 6, z, 1, arch->leaf_block, rng, 0.0, out);
const int dirs[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
for (int i = 0; i < 4; ++i) {
int buttress_height = 4 + rng_range_inclusive(rng, 0, 3);
for (int step = 0; step < 4; ++step) {
int buttress_height = 3 + rng_range_inclusive(rng, 0, 2);
for (int step = 0; step < 2; ++step) {
int px = x + dirs[i][0] * (2 + step);
int pz = z + dirs[i][1] * (2 + step);
int stack = buttress_height - step;
@@ -1488,10 +1484,10 @@ static void build_redwood_titan(worldgen_ctx *ctx, int x, int y, int z, int heig
}
for (int i = 0; i < 4; ++i) {
int branch_y = canopy_base - 3 - rng_range_inclusive(rng, 0, 3);
int branch_len = 5 + rng_range_inclusive(rng, 0, 3);
place_branch_span(x, branch_y, z, dirs[i][0], dirs[i][1], branch_len, 3, arch->log_block, arch->leaf_block, rng, out);
int branch_len = 3 + rng_range_inclusive(rng, 0, 2);
place_branch_span(x, branch_y, z, dirs[i][0], dirs[i][1], branch_len, 2, arch->log_block, arch->leaf_block, rng, out);
}
place_leaf_blob(x, canopy_base - 2, z, 5, 4, arch->leaf_block, rng, out);
place_leaf_blob(x, canopy_base - 2, z, 4, 3, arch->leaf_block, rng, out);
}
typedef enum {
@@ -1540,7 +1536,7 @@ static void build_redwood_titan(worldgen_ctx *ctx, int x, int y, int z, int heig
[TREE_OAK_ANCIENT] = {"oak_ancient", TREE_SPECIES_OAK, BLOCK_OAK_LOG, BLOCK_OAK_LEAVES, 12, 16, 6, 7, build_oak_ancient},
[TREE_SPRUCE_ANCIENT] = {"spruce_ancient", TREE_SPECIES_SPRUCE, BLOCK_OAK_LOG, BLOCK_OAK_LEAVES, 12, 16, 5, 7, build_spruce_ancient},
[TREE_MAPLE_ANCIENT] = {"maple_ancient", TREE_SPECIES_MAPLE, BLOCK_OAK_LOG, BLOCK_OAK_LEAVES, 11, 15, 6, 6, build_maple_ancient},
[TREE_REDWOOD_TITAN] = {"redwood_titan", TREE_SPECIES_PINE, BLOCK_OAK_LOG, BLOCK_OAK_LEAVES, 70, 86, 7, 24, build_redwood_titan},
[TREE_REDWOOD_TITAN] = {"redwood_titan", TREE_SPECIES_PINE, BLOCK_OAK_LOG, BLOCK_OAK_LEAVES, 28, 38, 4, 10, build_redwood_titan},
};
static const int TREE_VARIANTS_OAK[] = {TREE_OAK_ROUND, TREE_OAK_SPRAWL, TREE_OAK_COLUMNAR, TREE_OAK_ANCIENT};
@@ -1871,7 +1867,7 @@ static const cabin_blueprint CABIN_BLUEPRINTS[] = {
BLOCK_OAK_LOG,
BLOCK_SPRUCE_STAIRS_E,
BLOCK_SPRUCE_STAIRS_W,
BLOCK_GLASS_PANE,
BLOCK_GLASS,
BLOCK_STONE,
0.04,
0.25,
@@ -1893,7 +1889,7 @@ static const cabin_blueprint CABIN_BLUEPRINTS[] = {
BLOCK_OAK_LOG,
BLOCK_SPRUCE_STAIRS_E,
BLOCK_SPRUCE_STAIRS_W,
BLOCK_GLASS_PANE,
BLOCK_GLASS,
BLOCK_STONE,
0.05,
0.3,
@@ -1915,7 +1911,7 @@ static const cabin_blueprint CABIN_BLUEPRINTS[] = {
BLOCK_OAK_LOG,
BLOCK_SPRUCE_STAIRS_E,
BLOCK_SPRUCE_STAIRS_W,
BLOCK_GLASS_PANE,
BLOCK_GLASS,
BLOCK_STONE,
0.04,
0.22,
@@ -1967,6 +1963,39 @@ static void mark_rect_occupancy(unsigned char occupancy[CHUNK_SIZE][CHUNK_SIZE],
}
}
static int cabin_site_height_range(const cabin_blueprint *bp, int chunk_origin_x, int chunk_origin_z,
column_data columns[CHUNK_SIZE][CHUNK_SIZE],
int center_x, int center_z, int padding,
int *out_min_h, int *out_max_h) {
int min_h = INT_MAX;
int max_h = INT_MIN;
for (size_t i = 0; i < bp->rect_count; ++i) {
const cabin_rect *rect = &bp->rects[i];
int rcx = center_x + rect->offset_x;
int rcz = center_z + rect->offset_z;
int x0 = rcx - rect->half_w - padding;
int x1 = rcx + rect->half_w + padding;
int z0 = rcz - rect->half_l - padding;
int z1 = rcz + rect->half_l + padding;
if (x0 < chunk_origin_x || x1 >= chunk_origin_x + CHUNK_SIZE) return 0;
if (z0 < chunk_origin_z || z1 >= chunk_origin_z + CHUNK_SIZE) return 0;
for (int wz = z0; wz <= z1; ++wz) {
for (int wx = x0; wx <= x1; ++wx) {
int lx = wx - chunk_origin_x;
int lz = wz - chunk_origin_z;
column_data col = columns[lx][lz];
if (col.has_water && col.height < col.water_surface) return 0;
if (col.height < min_h) min_h = col.height;
if (col.height > max_h) max_h = col.height;
}
}
}
if (min_h == INT_MAX) return 0;
*out_min_h = min_h;
*out_max_h = max_h;
return 1;
}
static void build_cabin_roof(const cabin_blueprint *bp, int chunk_x, int chunk_z, chunk_data *chunk,
int rect_center_x, int rect_center_z, int half_w, int half_l,
int roof_base_y, rng_state *rng) {
@@ -2302,7 +2331,11 @@ static int try_place_cabin(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_da
}
}
if (min_h == INT_MAX) return 0;
if (max_h - min_h > 3) return 0;
if (max_h - min_h > 1) return 0;
int pad_min_h = INT_MAX;
int pad_max_h = INT_MIN;
if (!cabin_site_height_range(bp, chunk_origin_x, chunk_origin_z, columns, center_x, center_z, 2, &pad_min_h, &pad_max_h)) return 0;
if (pad_max_h - pad_min_h > 2) return 0;
int base_floor_y = min_h + 1;
if (base_floor_y + bp->wall_height * bp->stories + 6 >= CHUNK_HEIGHT) return 0;
@@ -2364,7 +2397,7 @@ static void generate_chunk_cabins(worldgen_ctx *ctx, int chunk_x, int chunk_z, c
size_t blueprint_count = sizeof(CABIN_BLUEPRINTS) / sizeof(CABIN_BLUEPRINTS[0]);
unsigned char occupancy[CHUNK_SIZE][CHUNK_SIZE];
memset(occupancy, 0, sizeof(occupancy));
int attempts = 1 + (rng_next_f64(&rng) < 0.2 ? 1 : 0);
int attempts = 3 + (rng_next_f64(&rng) < 0.35 ? 1 : 0);
while (attempts-- > 0) {
const cabin_blueprint *bp = &CABIN_BLUEPRINTS[rng_range_inclusive(&rng, 0, (int)blueprint_count - 1)];
int min_local_x = 2;
@@ -2386,13 +2419,22 @@ static void generate_chunk_cabins(worldgen_ctx *ctx, int chunk_x, int chunk_z, c
int best_local_x = min_local_x;
int best_local_z = min_local_z;
double best_score = -1e9;
const int samples = 12;
const int samples = 24;
for (int s = 0; s < samples; ++s) {
int local_cx = rng_range_inclusive(&rng, min_local_x, max_local_x);
int local_cz = rng_range_inclusive(&rng, min_local_z, max_local_z);
int world_cx = chunk_x * CHUNK_SIZE + local_cx;
int world_cz = chunk_z * CHUNK_SIZE + local_cz;
double score = land_value(ctx, world_cx, world_cz);
int flat_min_h = INT_MAX;
int flat_max_h = INT_MIN;
if (!cabin_site_height_range(bp, chunk_x * CHUNK_SIZE, chunk_z * CHUNK_SIZE, columns,
world_cx, world_cz, 2, &flat_min_h, &flat_max_h)) {
continue;
}
int flat_range = flat_max_h - flat_min_h;
if (flat_range > 3) continue;
score += 1.2 - (double)flat_range * 0.45;
if (ctx->enable_trails) {
double dist2 = nearest_trail_distance2(ctx, world_cx, world_cz, 1.0e12);
double bonus = 0.0;
@@ -3414,11 +3456,10 @@ void worldgen_generate_chunk(worldgen_ctx *ctx, int chunk_x, int chunk_z, chunk_
}
}
generate_chunk_cabins(ctx, chunk_x, chunk_z, columns, out);
if (ctx->enable_trails) {
generate_chunk_trails(ctx, chunk_x, chunk_z, columns, out);
}
generate_chunk_cabins(ctx, chunk_x, chunk_z, columns, out);
generate_chunk_redwood_floor(ctx, chunk_x, chunk_z, columns, out);
generate_chunk_grass(ctx, chunk_x, chunk_z, columns, out);
generate_chunk_flowers(ctx, chunk_x, chunk_z, columns, out);