diff --git a/include/Cubed/gameplay/chunk.hpp b/include/Cubed/gameplay/chunk.hpp index b39a087..d7b497a 100644 --- a/include/Cubed/gameplay/chunk.hpp +++ b/include/Cubed/gameplay/chunk.hpp @@ -73,8 +73,10 @@ public: void gen_phase_four(const std::array, 4>& neighbor_heightmap); // Generate Block void gen_phase_five(); + // Adjust Block; + void gen_phase_six(const std::array>, 4>& neighbor_block); // Generate Structure - void gen_phase_six(); + void gen_phase_seven(); //void gen_vertex_data(); // 0 : (1, 0) // 1 : (-1, 0) diff --git a/src/gameplay/biome.cpp b/src/gameplay/biome.cpp index bc9044f..e450e9b 100644 --- a/src/gameplay/biome.cpp +++ b/src/gameplay/biome.cpp @@ -23,7 +23,7 @@ static ForestParams forest { Biome::FOREST, {0.5f, 1.0f}, {0.5f, 1.0f}, - {0.004f, 0.012f, 0.022f}, + {0.004f, 0.010f, 0.020f}, {64, 12} }, 0.1f @@ -93,13 +93,13 @@ Biome get_biome_from_noise(float temp, float humid) { */ Biome get_biome_from_noise(float temp, float humid) { using enum Biome; - if (plain.temp.first <= temp && temp <= plain.temp.second && plain.humid.first <= humid && humid <= plain.humid.second) { + if (plain.temp.first <= temp && temp < plain.temp.second && plain.humid.first <= humid && humid < plain.humid.second) { return PLAIN; } - if (forest.temp.first <= temp && temp <= forest.temp.second && forest.humid.first <= humid && humid <= forest.humid.second) { + if (forest.temp.first <= temp && temp < forest.temp.second && forest.humid.first <= humid && humid < forest.humid.second) { return FOREST; } - if (desert.temp.first <= temp && temp <= desert.temp.second && desert.humid.first <= humid && humid <= desert.humid.second) { + if (desert.temp.first <= temp && temp < desert.temp.second && desert.humid.first <= humid && humid < desert.humid.second) { return DESERT; } if (mountain.temp.first <= temp && temp <= mountain.temp.second && mountain.humid.first <= humid && humid <= mountain.humid.second) { diff --git a/src/gameplay/chunk.cpp b/src/gameplay/chunk.cpp index 952d67c..c1bfeaa 100644 --- a/src/gameplay/chunk.cpp +++ b/src/gameplay/chunk.cpp @@ -377,7 +377,109 @@ void Chunk::gen_phase_five() { } -void Chunk::gen_phase_six() { +void Chunk::gen_phase_six(const std::array>, 4>& neighbor_block) { + constexpr int BLEND_RADIUS = 12; + constexpr int WORLD_HEIGHT = WORLD_SIZE_Y; + + // Helper lambda: get top block type from a neighbor's block data at (nx, nz) + auto get_top_block_from_neighbor = [&](const std::vector& blocks, int nx, int nz) -> uint8_t { + // Search from topmost y downwards for the first non-zero block + for (int y = WORLD_HEIGHT - 1; y >= 0; --y) { + int idx = get_index(nx, y, nz); // linear index: y * area + z * size + x + if (idx >= 0 && idx < static_cast(blocks.size()) && blocks[idx] != 0) { + return blocks[idx]; + } + } + return 0; // fallback, should not happen for valid chunks + }; + + // For each column (x, z) + for (int x = 0; x < CHUCK_SIZE; ++x) { + for (int z = 0; z < CHUCK_SIZE; ++z) { + // Get the current top block type of this column from m_blocks + uint8_t type_self = 0; + int top_y = -1; + top_y = m_heightmap[x][z]; + type_self = m_blocks[get_index(x, top_y, z)]; + + if (top_y == -1) continue; // no block? skip + + // Weight map: type -> total weight + std::unordered_map weights; + weights[type_self] = 1.0f; // self weight + + // --- Right neighbor (index 0) --- + if (neighbor_block[0] && x >= CHUCK_SIZE - BLEND_RADIUS) { + int dist = (CHUCK_SIZE - 1) - x; + float t = 1.0f - static_cast(dist) / BLEND_RADIUS; + t = t * t * (3.0f - 2.0f * t); // smoothstep + if (t > 0.0f) { + uint8_t type_neighbor = get_top_block_from_neighbor(*neighbor_block[0], 0, z); + weights[type_neighbor] += t; + } + } + + // --- Left neighbor (index 1) --- + if (neighbor_block[1] && x < BLEND_RADIUS) { + int dist = x; + float t = 1.0f - static_cast(dist) / BLEND_RADIUS; + t = t * t * (3.0f - 2.0f * t); + if (t > 0.0f) { + uint8_t type_neighbor = get_top_block_from_neighbor(*neighbor_block[1], CHUCK_SIZE - 1, z); + weights[type_neighbor] += t; + } + } + + // --- Front neighbor (index 2) --- + if (neighbor_block[2] && z >= CHUCK_SIZE - BLEND_RADIUS) { + int dist = (CHUCK_SIZE - 1) - z; + float t = 1.0f - static_cast(dist) / BLEND_RADIUS; + t = t * t * (3.0f - 2.0f * t); + if (t > 0.0f) { + uint8_t type_neighbor = get_top_block_from_neighbor(*neighbor_block[2], x, 0); + weights[type_neighbor] += t; + } + } + + // --- Back neighbor (index 3) --- + if (neighbor_block[3] && z < BLEND_RADIUS) { + int dist = z; + float t = 1.0f - static_cast(dist) / BLEND_RADIUS; + t = t * t * (3.0f - 2.0f * t); + if (t > 0.0f) { + uint8_t type_neighbor = get_top_block_from_neighbor(*neighbor_block[3], x, CHUCK_SIZE - 1); + weights[type_neighbor] += t; + } + } + + // Find type with maximum total weight + uint8_t final_type = type_self; + float max_weight = weights[type_self]; + for (const auto& [type, w] : weights) { + if (w > max_weight) { + max_weight = w; + final_type = type; + } + } + + // Update the top block if the type changed + if (final_type != type_self) { + m_blocks[get_index(x, top_y, z)] = final_type; + unsigned fill_type = 2; + if (final_type == 1) { + fill_type = 2; + } else if (final_type == 4) { + fill_type = 4; + } + for (int y = top_y - 5; y < top_y; y++) { + m_blocks[get_index(x, y, z)] = fill_type; + } + } + } + } +} + +void Chunk::gen_phase_seven() { if (m_biome == Biome::FOREST) { std::array x_arr; std::iota(x_arr.begin(), x_arr.end(), 0); diff --git a/src/gameplay/world.cpp b/src/gameplay/world.cpp index f427e33..a1cf37e 100644 --- a/src/gameplay/world.cpp +++ b/src/gameplay/world.cpp @@ -183,10 +183,25 @@ void World::init_chunks() { } for (auto& [pos, chunks] : m_chunks) { - chunks.gen_phase_five(); - chunks.gen_phase_six(); + chunks.gen_phase_five(); + } + std::array>, 4> neighbor_block; + for (auto& [pos, chunks] : m_chunks) { + for (int i = 0; i < 4; i++) { + auto neighbor_pos = pos + CHUNK_DIR[i]; + auto it = m_chunks.find(neighbor_pos); + if (it == m_chunks.end()) { + neighbor_block[i] = std::nullopt; + continue; + } + neighbor_block[i] = it->second.get_chunk_blocks(); + + } + chunks.gen_phase_six(neighbor_block); + } + for (auto& [pos, chunks] : m_chunks) { + chunks.gen_phase_seven(); } - std::atomic sync{0}; sync.store(1, std::memory_order_release); sync.load(std::memory_order_acquire); @@ -336,7 +351,25 @@ void World::gen_chunks_internal() { m_chunk_gen_fraction = 0.5f; for (auto& [pos, chunks] : new_chunks) { chunks.gen_phase_five(); - chunks.gen_phase_six(); + } + std::array>, 4> neighbor_blocks_data; + for (auto& [pos, chunks] : new_chunks) { + { + //std::lock_guard lk(m_chunks_mutex); + for (int i = 0; i < 4; i++) { + auto neighbor_pos = pos + CHUNK_DIR[i]; + auto it = new_chunks_neighbor.find(neighbor_pos); + if (it == new_chunks_neighbor.end()) { + neighbor_blocks_data[i] = std::nullopt; + continue; + } + neighbor_blocks_data[i] = it->second->get_chunk_blocks(); + } + } + chunks.gen_phase_six(neighbor_blocks_data); + } + for (auto& [pos, chunks] : new_chunks) { + chunks.gen_phase_seven(); } m_chunk_gen_fraction = 0.6f; for (auto& [pos, chunk] : new_chunks) {