13 Commits

Author SHA1 Message Date
943013b83d feat(player): add configurable fly Y speed 2026-06-21 17:53:23 +08:00
47f13ca995 feat(world): add ChunkLoadStyle enum and rename chunk_pos to get_chunk_pos 2026-06-21 17:42:59 +08:00
4f6c5303ec feat(world): add thread pool size management and UI controls 2026-06-21 16:59:11 +08:00
34d9439466 feat(world): add dynamic thread pool resizing 2026-06-21 16:36:15 +08:00
2386d98217 refactor: remove unused uniforms and set cameraPos uniform outside lambda 2026-06-21 16:04:44 +08:00
790f4a5aa4 fix: add thread safety for cave and river path mutexes 2026-06-21 15:56:58 +08:00
9a7fe1bfe9 feat(gameplay): add temporary chunk flag to prevent path clearing 2026-06-21 15:00:50 +08:00
8929af888a fix(renderer): correct shader uniform name and remove unused uniform 2026-06-20 22:08:16 +08:00
a72b0dd677 feat(world): integrate thread pool and async chunk generation 2026-06-20 21:57:27 +08:00
4b617612e8 fix(gameplay): use gen_phase_one to get seed 2026-06-20 17:12:48 +08:00
5cfd663566 refactor(gameplay): move chunk generation phases into gen_chunk method
Consolidate multiple phase generation calls into a single gen_chunk() method on Chunk, which handles neighbor generation and ensures thread safety. Simplify World::gen_chunks_internal by using gen_chunk() instead of manual phase orchestration.
2026-06-20 17:11:05 +08:00
d69e1895d4 refactor(gameplay): remove dead code and simplify chunk neighbor context
Remove the large commented-out `init_chunks()` function, and eliminate the `affected_neighbor` tracking in `gen_chunks_internal()`. This simplifies the neighbor context building and removes unused vertex data regeneration for affected neighbors.
2026-06-20 16:38:36 +08:00
be17846c16 feat(renderer): add fog effect based on render distance 2026-06-20 15:49:00 +08:00
19 changed files with 488 additions and 601 deletions

View File

@@ -25,6 +25,10 @@ uniform float minRadius;
uniform float maxRadius; uniform float maxRadius;
uniform bool enablePBR; uniform bool enablePBR;
uniform bool flipY; uniform bool flipY;
uniform int renderDistance;
uniform vec3 skyColor;
const vec2 poissonDisk32[32] = vec2[]( const vec2 poissonDisk32[32] = vec2[](
vec2(-0.975402, -0.071138), vec2(-0.975402, -0.071138),
vec2(-0.920347, -0.411420), vec2(-0.920347, -0.411420),
@@ -342,7 +346,16 @@ void main(void) {
float shadow = ShadowCalculation(FragPosLightSpace, norm, lightDir); float shadow = ShadowCalculation(FragPosLightSpace, norm, lightDir);
// fog
float dist = length(cameraPos - vert_pos);
vec4 fogColor = vec4(skyColor, 1.0);
float fogStart = renderDistance * 16 * 0.9;
float fogEnd = renderDistance * 16;
float fogFactor = smoothstep(fogEnd, fogStart, dist);
color = vec4((ambient + (1.0 - shadow) * (diffuse)) * objectColor.rgb + (1.0-shadow) * specular * objectColor.rgb, objectColor.a); color = vec4((ambient + (1.0 - shadow) * (diffuse)) * objectColor.rgb + (1.0-shadow) * specular * objectColor.rgb, objectColor.a);
color = mix(fogColor, color, fogFactor);
//color = vec4(normal * 0.5 + 0.5, 1.0); //color = vec4(normal * 0.5 + 0.5, 1.0);
//color = vec4(tangent * 0.5 + 0.5, 1.0);; //color = vec4(tangent * 0.5 + 0.5, 1.0);;
//color = vec4(norm * 0.5 + 0.5, 1.0); //color = vec4(norm * 0.5 + 0.5, 1.0);

View File

@@ -22,7 +22,6 @@ uniform float ambientStrength;
uniform vec3 sunlightColor; uniform vec3 sunlightColor;
uniform vec3 ambientColor; uniform vec3 ambientColor;
uniform vec3 sunlightDir; uniform vec3 sunlightDir;
uniform vec3 cameraPos;
uniform bool shader_on; uniform bool shader_on;
uniform float specularStrength; uniform float specularStrength;

View File

@@ -26,7 +26,7 @@ constexpr float DEFAULT_G = 22.5f;
constexpr int SIZE_X = CHUNK_SIZE; constexpr int SIZE_X = CHUNK_SIZE;
constexpr int SIZE_Y = WORLD_SIZE_Y; constexpr int SIZE_Y = WORLD_SIZE_Y;
constexpr int SIZE_Z = CHUNK_SIZE; constexpr int SIZE_Z = CHUNK_SIZE;
constexpr int RESERVED_THREADS = 3;
constexpr ChunkPos CHUNK_DIR[]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}, constexpr ChunkPos CHUNK_DIR[]{{1, 0}, {-1, 0}, {0, 1}, {0, -1},
{1, 1}, {-1, 1}, {1, -1}, {-1, -1}}; {1, 1}, {-1, 1}, {1, -1}, {-1, -1}};

View File

@@ -48,6 +48,8 @@ private:
int m_pre_set_tick_speed = 1; int m_pre_set_tick_speed = 1;
bool m_tick_frezze = false; bool m_tick_frezze = false;
int m_samples_idx = 1; int m_samples_idx = 1;
int m_threads = 1;
int m_chunk_style = 0;
void show_about_table_bar(); void show_about_table_bar();
void show_biome_table_bar(); void show_biome_table_bar();
void show_time_table_bar(); void show_time_table_bar();

View File

@@ -17,11 +17,13 @@ public:
int cave_sum() const; int cave_sum() const;
float& cave_probability(); float& cave_probability();
std::shared_mutex& path_mutex();
private: private:
CaveHashMap m_paths; CaveHashMap m_paths;
unsigned m_seed = 0; unsigned m_seed = 0;
Random m_random; Random m_random;
float m_cave_probability = 0.035f; float m_cave_probability = 0.035f;
std::shared_mutex m_path_mutex;
}; };
} // namespace Cubed } // namespace Cubed

View File

@@ -23,6 +23,9 @@ private:
std::atomic<bool> m_dirty{false}; std::atomic<bool> m_dirty{false};
std::atomic<bool> m_need_upload{true}; std::atomic<bool> m_need_upload{true};
std::atomic<bool> m_is_on_gen_vertex_data{false}; std::atomic<bool> m_is_on_gen_vertex_data{false};
std::atomic<bool> m_gening{false};
std::atomic<bool> m_temp_chunk{false};
std::atomic<BiomeType> m_biome = BiomeType::PLAIN; std::atomic<BiomeType> m_biome = BiomeType::PLAIN;
std::mutex m_vertexs_data_mutex; std::mutex m_vertexs_data_mutex;
@@ -54,7 +57,7 @@ private:
BlockType id); BlockType id);
public: public:
Chunk(World& world, ChunkPos chunk_pos); Chunk(World& world, ChunkPos chunk_pos, bool temp_chunk = false);
~Chunk(); ~Chunk();
Chunk(const Chunk&) = delete; Chunk(const Chunk&) = delete;
Chunk& operator=(const Chunk&) = delete; Chunk& operator=(const Chunk&) = delete;
@@ -124,7 +127,9 @@ public:
void need_upload(); void need_upload();
void set_chunk_block(int index, unsigned id); void set_chunk_block(int index, unsigned id);
// ensure thread safe!
void gen_chunk();
bool is_temp_chunk() const;
ChunkPos chunk_pos() const; ChunkPos chunk_pos() const;
BiomeType biome() const; BiomeType biome() const;
void biome(BiomeType b); void biome(BiomeType b);

View File

@@ -34,6 +34,7 @@ private:
float m_max_speed = m_max_walk_speed; float m_max_speed = m_max_walk_speed;
float m_y_speed = 0.0f; float m_y_speed = 0.0f;
float m_fly_y_speed = 7.5f;
bool can_up = true; bool can_up = true;
float space_on_time = 0.0f; float space_on_time = 0.0f;
@@ -99,6 +100,7 @@ public:
float& acceleration(); float& acceleration();
float& deceleration(); float& deceleration();
float& g(); float& g();
float& fly_y_speed();
unsigned place_block() const; unsigned place_block() const;

View File

@@ -12,6 +12,7 @@ class RiverWorm {
public: public:
RiverWorm(); RiverWorm();
~RiverWorm();
RiverHashMap& paths(); RiverHashMap& paths();
void init(unsigned world_seed); void init(unsigned world_seed);
void reload(unsigned world_seed); void reload(unsigned world_seed);
@@ -21,12 +22,14 @@ public:
int river_sum() const; int river_sum() const;
float& river_probability(); float& river_probability();
std::shared_mutex& paths_mutex();
private: private:
RiverHashMap m_paths; RiverHashMap m_paths;
unsigned m_seed = 0; unsigned m_seed = 0;
Random m_random; Random m_random;
float m_probability = 0.01f; float m_probability = 0.01f;
std::shared_mutex m_paths_mutex;
}; };
}; // namespace Cubed }; // namespace Cubed

View File

@@ -4,6 +4,7 @@
#include "Cubed/gameplay/chunk.hpp" #include "Cubed/gameplay/chunk.hpp"
#include "Cubed/gameplay/game_time.hpp" #include "Cubed/gameplay/game_time.hpp"
#include "Cubed/gameplay/river_worm.hpp" #include "Cubed/gameplay/river_worm.hpp"
#include "Cubed/tools/thread_pool.hpp"
#include <atomic> #include <atomic>
#include <condition_variable> #include <condition_variable>
@@ -34,15 +35,24 @@ class Player;
class TextureManager; class TextureManager;
class World { class World {
private: private:
enum class ChunkLoadStyle { RANDOM, CENTER };
struct PendingChunk {
Chunk chunk;
std::future<void> future;
};
using OptionalBlockVectorArray = using OptionalBlockVectorArray =
std::array<std::optional<std::vector<BlockType>>, 4>; std::array<std::optional<std::vector<BlockType>>, 4>;
using ChunkPtrUpdateList = std::vector<std::pair<ChunkPos, Chunk*>>; using ChunkPtrUpdateList = std::vector<std::pair<ChunkPos, Chunk*>>;
using ChunkPairVector = std::vector<std::pair<ChunkPos, Chunk>>; using ChunkPairVector = std::vector<std::pair<ChunkPos, Chunk>>;
using ChunkPairQueue = std::queue<std::pair<ChunkPos, Chunk>>;
using ConstChunkMap = using ConstChunkMap =
std::unordered_map<ChunkPos, const Chunk*, ChunkPos::Hash>; std::unordered_map<ChunkPos, const Chunk*, ChunkPos::Hash>;
using ChunkPosSet = std::unordered_set<ChunkPos, ChunkPos::Hash>; using ChunkPosSet = std::unordered_set<ChunkPos, ChunkPos::Hash>;
using ChunkHashMap = std::unordered_map<ChunkPos, Chunk, ChunkPos::Hash>; using ChunkHashMap = std::unordered_map<ChunkPos, Chunk, ChunkPos::Hash>;
using PendingChunkHashMap =
std::unordered_map<ChunkPos, PendingChunk, ChunkPos::Hash>;
glm::vec3 m_gen_player_pos{0.0f, 0.0f, 0.0f}; glm::vec3 m_gen_player_pos{0.0f, 0.0f, 0.0f};
ChunkHashMap m_chunks; ChunkHashMap m_chunks;
std::unordered_map<std::size_t, Player> m_players; std::unordered_map<std::size_t, Player> m_players;
@@ -50,7 +60,7 @@ private:
std::thread m_gen_thread; std::thread m_gen_thread;
std::thread m_server_thread; std::thread m_server_thread;
std::atomic<std::shared_ptr<ThreadPool>> m_gen_thread_pool;
std::stop_source m_server_stop_source; std::stop_source m_server_stop_source;
std::atomic<int> m_per_tick_time = DEFAULT_PER_TICK_TIME; // ms std::atomic<int> m_per_tick_time = DEFAULT_PER_TICK_TIME; // ms
@@ -59,7 +69,7 @@ private:
mutable std::mutex m_chunks_mutex; mutable std::mutex m_chunks_mutex;
std::mutex m_gen_signal_mutex; std::mutex m_gen_signal_mutex;
std::mutex m_new_chunk_queue_mutex; std::mutex m_new_chunk_mutex;
std::mutex m_delete_vbo_mutex; std::mutex m_delete_vbo_mutex;
std::mutex m_delete_vao_mutex; std::mutex m_delete_vao_mutex;
std::mutex m_gen_player_pos_mutex; std::mutex m_gen_player_pos_mutex;
@@ -74,13 +84,15 @@ private:
std::atomic<bool> m_tick_running{true}; std::atomic<bool> m_tick_running{true};
std::atomic<int> m_rendering_distance{24}; std::atomic<int> m_rendering_distance{24};
std::atomic<float> m_chunk_gen_fraction{0.0f}; std::atomic<float> m_chunk_gen_fraction{0.0f};
std::atomic<int> m_pool_threads{1};
std::atomic<int> m_max_threads{1};
std::atomic<TickType> m_game_ticks{0}; std::atomic<TickType> m_game_ticks{0};
std::atomic<ChunkLoadStyle> m_chunk_load_style{ChunkLoadStyle::RANDOM};
std::vector<ChunkPos> m_dirty_queue; std::vector<ChunkPos> m_dirty_queue;
std::vector<ChunkRenderSnapshot> m_render_snapshots; std::vector<ChunkRenderSnapshot> m_render_snapshots;
std::vector<std::pair<ChunkPos, Chunk>> m_new_chunk; std::vector<std::pair<ChunkPos, Chunk>> m_new_finished_chunk;
std::vector<std::pair<ChunkPos, Chunk>> m_new_chunk_queue; // Can only be used in the gen thread
PendingChunkHashMap new_chunks;
CaveCarver m_cave_carcer; CaveCarver m_cave_carcer;
RiverWorm m_river_worm; RiverWorm m_river_worm;
@@ -88,18 +100,14 @@ private:
void gen_chunks_internal(); void gen_chunks_internal();
void sync_player_pos(glm::vec3& player_pos); void sync_player_pos(glm::vec3& player_pos);
void void compute_required_chunks(ChunkPosSet& required_chunks,
compute_required_chunks(ChunkPosSet& required_chunks, ChunkPairVector& temp_neighbor);
ChunkPairVector& temp_neighbor,
std::vector<ChunkPos>& need_gen_temp_chunks_pos);
void sync_and_collect_missing_chunks(std::vector<ChunkPos>&, void sync_and_collect_missing_chunks(std::vector<ChunkPos>&,
const ChunkPosSet&); const ChunkPosSet&);
void
build_neighbor_context_for_new_chunks(ConstChunkMap& new_chunks_neighbor, void submit_new_chunks();
ChunkPtrUpdateList& affected_neighbor, void poll_finished_chunks();
const ChunkPairVector& new_chunks); void wait_all_chunk_tasks();
void build_neighbor_context_for_affected_neighbors(ChunkPtrUpdateList&,
ConstChunkMap&);
public: public:
World(); World();
@@ -118,7 +126,7 @@ public:
bool is_solid(const glm::ivec3& block_pos) const; bool is_solid(const glm::ivec3& block_pos) const;
bool can_pass_block(const glm::ivec3& block_pos) const; bool can_pass_block(const glm::ivec3& block_pos) const;
BlockType get_block_tpye(const glm::ivec3& block_pos) const; BlockType get_block_tpye(const glm::ivec3& block_pos) const;
static ChunkPos chunk_pos(int world_x, int world_z); static ChunkPos get_chunk_pos(int world_x, int world_z);
void need_gen(); void need_gen();
@@ -154,6 +162,11 @@ public:
bool is_tick_running() const; bool is_tick_running() const;
void tick_running(bool run); void tick_running(bool run);
int pool_threads() const;
int max_threads() const;
void change_pool_threads(int threads);
int chunk_load_style() const;
void set_chunk_load_style(int id);
}; };
} // namespace Cubed } // namespace Cubed

View File

@@ -0,0 +1,123 @@
#pragma once
#include <condition_variable>
#include <cstddef>
#include <functional>
#include <future>
#include <mutex>
#include <queue>
#include <thread>
#include <vector>
namespace Cubed {
class ThreadPool {
private:
std::vector<std::jthread> m_workers;
std::queue<std::function<void()>> m_tasks;
std::mutex m_mtx;
std::condition_variable_any m_cv;
std::atomic<bool> m_stopping{false};
std::atomic<size_t> m_thread_sum{0};
public:
ThreadPool(const ThreadPool&) = delete;
ThreadPool(ThreadPool&&) = delete;
ThreadPool& operator=(const ThreadPool&) = delete;
ThreadPool& operator=(ThreadPool&&) = delete;
explicit ThreadPool(size_t thread_sum) : m_thread_sum(thread_sum) {
for (size_t i = 0; i < thread_sum; i++) {
m_workers.emplace_back([this](std::stop_token stoken) {
while (true) {
std::function<void()> task;
{
std::unique_lock lock(m_mtx);
m_cv.wait(lock, stoken,
[this, stoken] { return !m_tasks.empty(); });
if (stoken.stop_requested() && m_tasks.empty()) {
return;
}
task = std::move(m_tasks.front());
m_tasks.pop();
}
task();
}
});
}
}
~ThreadPool() { stop(); }
template <typename F> auto enqueue(F&& f) {
using R = std::invoke_result_t<F>;
auto task =
std::make_shared<std::packaged_task<R()>>(std::forward<F>(f));
auto fut = task->get_future();
{
std::lock_guard lock(m_mtx);
if (m_stopping)
throw std::runtime_error("thread pool stopped");
m_tasks.emplace([task] { (*task)(); });
}
m_cv.notify_one();
return fut;
}
void stop() {
m_stopping = true;
for (auto& w : m_workers) {
w.request_stop();
}
m_cv.notify_all();
for (auto& w : m_workers) {
if (w.joinable()) {
w.join();
}
}
}
size_t thread_sum() const { return m_thread_sum.load(); }
};
template <std::random_access_iterator Iter, typename F>
void parallel_do(ThreadPool& pool, Iter first, Iter last, size_t max_threads,
F&& f) {
max_threads = std::max<size_t>(1, max_threads);
max_threads = std::min(max_threads, pool.thread_sum());
std::decay_t<F> fn(std::forward<F>(f));
size_t length = std::distance(first, last);
if (!length) {
return;
}
constexpr size_t MIN_PER_THREAD = 25;
size_t num_blocks =
std::min(max_threads, (length + MIN_PER_THREAD - 1) / MIN_PER_THREAD);
num_blocks = std::max<size_t>(1, num_blocks);
size_t block_size = (length + num_blocks - 1) / num_blocks;
std::vector<std::future<void>> futures;
futures.reserve(num_blocks - 1);
Iter block_start = first;
for (size_t i = 0; i < num_blocks - 1; ++i) {
Iter block_end = block_start;
auto remain = std::distance(block_start, last);
std::advance(block_end, std::min<size_t>(block_size, remain));
futures.emplace_back(pool.enqueue([block_start, block_end, &fn]() {
for (auto it = block_start; it != block_end; ++it) {
fn(*it);
}
}));
block_start = block_end;
}
for (auto it = block_start; it != last; ++it) {
fn(*it);
}
for (auto& fut : futures) {
fut.get();
}
};
} // namespace Cubed

View File

@@ -5,3 +5,4 @@ leak:libdecor-gtk.so
leak:libgtk-3.so leak:libgtk-3.so
leak:libwayland-client.so leak:libwayland-client.so
leak:libglfw.so leak:libglfw.so
leak:libEGL_nvidia.so

View File

@@ -463,6 +463,29 @@ void DevPanel::show_world_tab_item() {
if (ImGui::SliderInt("Render Distance", &rendering_distance, 2, 128)) { if (ImGui::SliderInt("Render Distance", &rendering_distance, 2, 128)) {
m_app.world().rendering_distance(rendering_distance); m_app.world().rendering_distance(rendering_distance);
} }
ImGui::Text(
"Pool Threads %d Max Support Threads %d Reserved Threads %d",
m_app.world().pool_threads(), m_app.world().max_threads(),
RESERVED_THREADS);
ImGui::SliderInt("Set Pool Threads", &m_threads, 1,
m_app.world().max_threads());
ImGui::SameLine();
if (ImGui::Button("Set")) {
m_app.world().change_pool_threads(m_threads);
}
if (m_threads > m_app.world().max_threads() - RESERVED_THREADS) {
ImGui::TextColored(
ImVec4(1.0f, 1.0f, 0.0f, 1.0f),
"Waring: When the threads in the thread pool exceed \n(maximum "
"threads minus reserved threads), \nit may cause stuttering.");
}
static const char* chunk_load_style[] = {"Random", "Center"};
m_chunk_style = m_app.world().chunk_load_style();
if (ImGui::Combo("ChunkLoadStyle", &m_chunk_style, chunk_load_style,
IM_ARRAYSIZE(chunk_load_style))) {
m_app.world().set_chunk_load_style(m_chunk_style);
}
if (ImGui::Button("Rebuild World")) { if (ImGui::Button("Rebuild World")) {
m_app.world().rebuild_world(); m_app.world().rebuild_world();
} }
@@ -542,6 +565,8 @@ void DevPanel::show_player_tab_item() {
m_player_profile.pos[1], m_player_profile.pos[1],
m_player_profile.pos[2]}); m_player_profile.pos[2]});
} }
ImGui::SliderFloat("Fly Y Speed", &m_player->fly_y_speed(), 0.0f,
100.0f);
ImGui::SliderFloat("Acceleration", &m_player->acceleration(), 1.0f, ImGui::SliderFloat("Acceleration", &m_player->acceleration(), 1.0f,
200.0f); 200.0f);
ImGui::SliderFloat("Deceleration", &m_player->deceleration(), 1.0f, ImGui::SliderFloat("Deceleration", &m_player->deceleration(), 1.0f,

View File

@@ -49,11 +49,13 @@ void CaveCarver::try_to_add_path(const ChunkPos& chunk_pos,
void CaveCarver::cleanup_finished_caves() { void CaveCarver::cleanup_finished_caves() {
std::vector<unsigned int> finished_keys; std::vector<unsigned int> finished_keys;
for (const auto& pair : m_paths) { for (const auto& pair : m_paths) {
if (pair.second.is_finished()) { if (pair.second.is_finished()) {
finished_keys.push_back(pair.first); finished_keys.push_back(pair.first);
} }
} }
for (const auto& key : finished_keys) { for (const auto& key : finished_keys) {
m_paths.erase(key); m_paths.erase(key);
} }
@@ -61,4 +63,5 @@ void CaveCarver::cleanup_finished_caves() {
int CaveCarver::cave_sum() const { return m_paths.size(); } int CaveCarver::cave_sum() const { return m_paths.size(); }
float& CaveCarver::cave_probability() { return m_cave_probability; } float& CaveCarver::cave_probability() { return m_cave_probability; }
std::shared_mutex& CaveCarver::path_mutex() { return m_path_mutex; }
} // namespace Cubed } // namespace Cubed

View File

@@ -8,8 +8,8 @@
namespace Cubed { namespace Cubed {
Chunk::Chunk(World& world, ChunkPos chunk_pos) Chunk::Chunk(World& world, ChunkPos chunk_pos, bool temp_chunk)
: m_chunk_pos(chunk_pos), m_world(world) { : m_temp_chunk(temp_chunk), m_chunk_pos(chunk_pos), m_world(world) {
for (int i = 0; i < VERTEX_DATA_SUM; i++) { for (int i = 0; i < VERTEX_DATA_SUM; i++) {
m_vertex_data.emplace_back(m_world); m_vertex_data.emplace_back(m_world);
} }
@@ -299,7 +299,7 @@ void Chunk::gen_vertices(const OptionalBlockVectorArray& neighbor_block) {
int world_nz = world_z + DIR[face].z; int world_nz = world_z + DIR[face].z;
auto [neighbor_x, neighbor_z] = auto [neighbor_x, neighbor_z] =
World::chunk_pos(world_nx, world_nz); World::get_chunk_pos(world_nx, world_nz);
auto is_culled = auto is_culled =
[&](const std::optional<std::vector<BlockType>>& [&](const std::optional<std::vector<BlockType>>&
@@ -455,6 +455,42 @@ void Chunk::gen_cross_plane_vertices(int world_x, int world_y, int world_z,
} }
} }
void Chunk::gen_chunk() {
if (m_gening.exchange(true))
return;
m_gening = true;
if (m_blocks.size() != 0) {
Logger::warn(
"Request Generator Chunk {} {} ,but the Blocks size is Not 0",
m_chunk_pos.x, m_chunk_pos.z);
}
std::vector<Chunk> neighbor;
for (int i = 0; i < 4; i++) {
neighbor.emplace_back(m_world, m_chunk_pos + CHUNK_DIR[i], true);
}
for (auto& chunk : neighbor) {
chunk.gen_phase_one();
chunk.gen_phase_three();
chunk.gen_phase_five();
chunk.gen_phase_seven();
}
gen_phase_one();
gen_phase_three();
gen_phase_five();
OptionalBlockVectorArray neightbor_blocks;
for (int i = 0; i < 4; i++) {
neightbor_blocks[i] = neighbor[i].get_chunk_blocks();
}
gen_phase_six(neightbor_blocks);
gen_phase_seven();
for (int i = 0; i < 4; i++) {
neightbor_blocks[i] = neighbor[i].get_chunk_blocks();
}
gen_vertex_data(neightbor_blocks);
}
// Logger::info("Cross Sum {}", m_cross_vertices_sum.load()); // Logger::info("Cross Sum {}", m_cross_vertices_sum.load());
bool Chunk::is_temp_chunk() const { return m_temp_chunk.load(); }
} // namespace Cubed } // namespace Cubed

View File

@@ -723,19 +723,25 @@ void ChunkGenerator::generate_cave() {
auto& paths = cave_carver.paths(); auto& paths = cave_carver.paths();
const auto& chunk_pos = m_chunk.chunk_pos(); const auto& chunk_pos = m_chunk.chunk_pos();
auto& blocks = m_chunk.blocks(); auto& blocks = m_chunk.blocks();
{
std::shared_lock lock(cave_carver.path_mutex());
for (auto& [id, path] : paths) { for (auto& [id, path] : paths) {
carve_worm(path.points(), chunk_pos, [&](int x, int y, int z) -> void { carve_worm(path.points(), chunk_pos,
[&](int x, int y, int z) -> void {
int idx = Chunk::index(x, y, z); int idx = Chunk::index(x, y, z);
if (blocks[idx] == 7) if (blocks[idx] == 7)
return; return;
if (y < WORLD_SIZE_Y - 1 && blocks[Chunk::index(x, y + 1, z)] == 7) if (y < WORLD_SIZE_Y - 1 &&
blocks[Chunk::index(x, y + 1, z)] == 7)
return; return;
blocks[idx] = 0; blocks[idx] = 0;
}); });
if (!m_chunk.is_temp_chunk()) {
path.clear_chunk(chunk_pos); path.clear_chunk(chunk_pos);
} }
}
}
} }
void ChunkGenerator::generate_river() { void ChunkGenerator::generate_river() {
@@ -744,16 +750,19 @@ void ChunkGenerator::generate_river() {
auto& paths = river_worm.paths(); auto& paths = river_worm.paths();
const auto& chunk_pos = m_chunk.chunk_pos(); const auto& chunk_pos = m_chunk.chunk_pos();
auto& blocks = m_chunk.blocks(); auto& blocks = m_chunk.blocks();
bool is_river = false; bool is_river = false;
{
std::shared_lock lock(river_worm.paths_mutex());
for (auto& [id, path] : paths) { for (auto& [id, path] : paths) {
if ((m_chunk.biome() == BiomeType::DESERT) || if ((m_chunk.biome() == BiomeType::DESERT) ||
(m_chunk.biome() == BiomeType::OCEAN)) { (m_chunk.biome() == BiomeType::OCEAN)) {
if (!m_chunk.is_temp_chunk()) {
path.clear_chunk(chunk_pos); path.clear_chunk(chunk_pos);
}
continue; continue;
} }
carve_worm(path.points(), chunk_pos, [&](int x, int y, int z) -> void { carve_worm(path.points(), chunk_pos,
[&](int x, int y, int z) -> void {
int idx = Chunk::index(x, y, z); int idx = Chunk::index(x, y, z);
if (y > SEA_LEVEL) { if (y > SEA_LEVEL) {
blocks[idx] = 0; blocks[idx] = 0;
@@ -765,8 +774,12 @@ void ChunkGenerator::generate_river() {
} }
blocks[idx] = 7; blocks[idx] = 7;
}); });
if (!m_chunk.is_temp_chunk()) {
path.clear_chunk(chunk_pos); path.clear_chunk(chunk_pos);
} }
}
}
if (is_river) { if (is_river) {
m_chunk.biome(RIVER); m_chunk.biome(RIVER);
} }

View File

@@ -247,7 +247,7 @@ void Player::update_front_vec(float offset_x, float offset_y) {
} }
void Player::check_player_chunk_transition() { void Player::check_player_chunk_transition() {
ChunkPos cur_pos = m_world.chunk_pos(m_player_pos.x, m_player_pos.z); ChunkPos cur_pos = m_world.get_chunk_pos(m_player_pos.x, m_player_pos.z);
if (cur_pos != m_player_chunk_pos) { if (cur_pos != m_player_chunk_pos) {
m_world.need_gen(); m_world.need_gen();
m_player_chunk_pos = cur_pos; m_player_chunk_pos = cur_pos;
@@ -372,11 +372,11 @@ void Player::update_move(float delta_time) {
if (is_fly) { if (is_fly) {
if (m_move_state.up) { if (m_move_state.up) {
m_y_speed = 7.5f; m_y_speed = m_fly_y_speed;
} }
if (m_move_state.down) { if (m_move_state.down) {
m_y_speed = -7.5f; m_y_speed = -m_fly_y_speed;
} }
if (!m_move_state.down && !m_move_state.up) { if (!m_move_state.down && !m_move_state.up) {
@@ -544,6 +544,7 @@ float& Player::max_speed() { return m_max_speed; }
float& Player::acceleration() { return m_acceleration; } float& Player::acceleration() { return m_acceleration; }
float& Player::deceleration() { return m_deceleration; } float& Player::deceleration() { return m_deceleration; }
float& Player::g() { return m_g; } float& Player::g() { return m_g; }
float& Player::fly_y_speed() { return m_fly_y_speed; }
unsigned Player::place_block() const { return m_place_block; }; unsigned Player::place_block() const { return m_place_block; };
Gait& Player::gait() { return m_gait; } Gait& Player::gait() { return m_gait; }
GameMode& Player::game_mode() { return m_game_mode; } GameMode& Player::game_mode() { return m_game_mode; }

View File

@@ -4,7 +4,7 @@
namespace Cubed { namespace Cubed {
RiverWorm::RiverWorm() {} RiverWorm::RiverWorm() {}
RiverWorm::~RiverWorm() {}
RiverWorm::RiverHashMap& RiverWorm::paths() { return m_paths; } RiverWorm::RiverHashMap& RiverWorm::paths() { return m_paths; }
void RiverWorm::init(unsigned world_seed) { void RiverWorm::init(unsigned world_seed) {
@@ -46,11 +46,13 @@ void RiverWorm::try_to_add_path(const ChunkPos& chunk_pos,
void RiverWorm::cleanup_finished_rivers() { void RiverWorm::cleanup_finished_rivers() {
std::vector<unsigned> finished_keys; std::vector<unsigned> finished_keys;
for (const auto& pair : m_paths) { for (const auto& pair : m_paths) {
if (pair.second.is_finished()) { if (pair.second.is_finished()) {
finished_keys.push_back(pair.first); finished_keys.push_back(pair.first);
} }
} }
for (const auto& key : finished_keys) { for (const auto& key : finished_keys) {
m_paths.erase(key); m_paths.erase(key);
} }
@@ -58,4 +60,6 @@ void RiverWorm::cleanup_finished_rivers() {
int RiverWorm::river_sum() const { return m_paths.size(); } int RiverWorm::river_sum() const { return m_paths.size(); }
float& RiverWorm::river_probability() { return m_probability; } float& RiverWorm::river_probability() { return m_probability; }
std::shared_mutex& RiverWorm::paths_mutex() { return m_paths_mutex; }
} // namespace Cubed } // namespace Cubed

View File

@@ -5,10 +5,10 @@
#include "Cubed/tools/cubed_assert.hpp" #include "Cubed/tools/cubed_assert.hpp"
#include "Cubed/tools/cubed_hash.hpp" #include "Cubed/tools/cubed_hash.hpp"
#include <execution>
#include <glm/gtc/constants.hpp> #include <glm/gtc/constants.hpp>
#include <numbers> #include <numbers>
using namespace std::chrono; using namespace std::chrono;
using namespace std::chrono_literals;
namespace Cubed { namespace Cubed {
@@ -22,6 +22,13 @@ World::World() {}
World::~World() { World::~World() {
stop_gen_thread(); stop_gen_thread();
stop_server_thread(); stop_server_thread();
wait_all_chunk_tasks();
auto pool_ptr = m_gen_thread_pool.load();
if (pool_ptr) {
pool_ptr->stop();
}
m_gen_thread_pool.store(nullptr);
m_chunks.clear(); m_chunks.clear();
{ {
std::lock_guard lk(m_delete_vbo_mutex); std::lock_guard lk(m_delete_vbo_mutex);
@@ -39,6 +46,12 @@ World::~World() {
} }
} }
void World::wait_all_chunk_tasks() {
for (auto& [pos, task] : new_chunks) {
task.future.get();
}
}
bool World::can_move(const AABB& player_box) const { return true; } bool World::can_move(const AABB& player_box) const { return true; }
const std::optional<LookBlock>& const std::optional<LookBlock>&
@@ -77,10 +90,11 @@ void World::init_world() {
m_cave_carcer.init(ChunkGenerator::seed()); m_cave_carcer.init(ChunkGenerator::seed());
m_river_worm.init(ChunkGenerator::seed()); m_river_worm.init(ChunkGenerator::seed());
m_chunks.reserve(MAX_DISTANCE * MAX_DISTANCE * 4); m_chunks.reserve(MAX_DISTANCE * MAX_DISTANCE * 4);
int max_thread = std::thread::hardware_concurrency();
change_pool_threads(max_thread - RESERVED_THREADS);
auto t1 = std::chrono::system_clock::now(); auto t1 = std::chrono::system_clock::now();
Logger::info("Max Support Thread is {}",
std::thread::hardware_concurrency());
// init players // init players
m_players.emplace(HASH::str("TestPlayer"), Player(*this, "TestPlayer")); m_players.emplace(HASH::str("TestPlayer"), Player(*this, "TestPlayer"));
@@ -102,210 +116,7 @@ void World::init_chunks() {
} }
} }
/* ChunkPos World::get_chunk_pos(int world_x, int world_z) {
void World::init_chunks() {
int dis_x = PRE_LOAD_DISTANCE;
int dis_z = PRE_LOAD_DISTANCE;
for (int x = 0; x < dis_x; x++) {
for (int z = 0; z < dis_z; z++) {
int nx = x - dis_x / 2;
int nz = z - dis_z / 2;
ChunkPos pos{nx, nz};
auto it = m_chunks.find(pos);
if (it == m_chunks.end()) {
m_chunks.emplace(pos, Chunk(*this, pos));
}
}
}
ChunkHashMap temp_neighbor;
for (int x = 0; x < dis_x + 2; x++) {
for (int z = 0; z < dis_z + 2; z++) {
int nx = x - (dis_x + 2) / 2;
int nz = z - (dis_z + 2) / 2;
ChunkPos pos{nx, nz};
auto it = m_chunks.find(pos);
if (it == m_chunks.end()) {
auto it = temp_neighbor.find(pos);
if (it == temp_neighbor.end()) {
temp_neighbor.emplace(pos, Chunk(*this, pos));
}
}
}
}
for (auto& [pos, chunk] : m_chunks) {
chunk.gen_phase_one();
m_cave_carcer.try_to_add_path(pos, chunk.seed());
}
for (auto& [pos, chunk] : temp_neighbor) {
chunk.gen_phase_one();
m_cave_carcer.try_to_add_path(pos, chunk.seed());
}
std::array<const Chunk*, 8> neighbor_chunks;
for (auto& [pos, chunks] : m_chunks) {
for (int i = 0; i < 8; i++) {
auto neighbor_pos = pos + CHUNK_DIR[i];
auto it = m_chunks.find(neighbor_pos);
if (it == m_chunks.end()) {
auto it = temp_neighbor.find(neighbor_pos);
if (it == temp_neighbor.end()) {
neighbor_chunks[i] = nullptr;
ASSERT_MSG(false, "Neighbor Chunk is nullptr");
} else {
neighbor_chunks[i] = &it->second;
}
continue;
}
neighbor_chunks[i] = &it->second;
}
chunks.gen_phase_two(neighbor_chunks);
}
for (auto& [pos, chunks] : temp_neighbor) {
for (int i = 0; i < 8; i++) {
auto neighbor_pos = pos + CHUNK_DIR[i];
auto it = m_chunks.find(neighbor_pos);
if (it == m_chunks.end()) {
auto it = temp_neighbor.find(neighbor_pos);
if (it == temp_neighbor.end()) {
neighbor_chunks[i] = nullptr;
} else {
neighbor_chunks[i] = &it->second;
}
continue;
}
neighbor_chunks[i] = &it->second;
}
chunks.gen_phase_two(neighbor_chunks);
}
for (auto& [pos, chunks] : m_chunks) {
chunks.gen_phase_three();
}
for (auto& [pos, chunks] : temp_neighbor) {
chunks.gen_phase_three();
}
for (int i = 0; i < 4; i++) {
for (auto& [pos, chunks] : temp_neighbor) {
std::array<std::optional<HeightMapArray>, 8>
neighbor_chunk_heightmap;
std::array<BiomeType, 8> neighbor_biome;
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()) {
auto it = temp_neighbor.find(neighbor_pos);
if (it == temp_neighbor.end()) {
neighbor_chunk_heightmap[i] = std::nullopt;
neighbor_biome[i] = BiomeType::NONE;
} else {
neighbor_chunk_heightmap[i] =
it->second.get_heightmap();
neighbor_biome[i] = it->second.biome();
}
continue;
}
neighbor_chunk_heightmap[i] = it->second.get_heightmap();
neighbor_biome[i] = it->second.biome();
}
chunks.gen_phase_four(neighbor_chunk_heightmap, neighbor_biome);
}
for (auto& [pos, chunks] : m_chunks) {
std::array<std::optional<HeightMapArray>, 8>
neighbor_chunk_heightmap;
std::array<BiomeType, 8> neighbor_biome;
for (int i = 0; i < 8; i++) {
auto neighbor_pos = pos + CHUNK_DIR[i];
auto it = m_chunks.find(neighbor_pos);
if (it == m_chunks.end()) {
auto it = temp_neighbor.find(neighbor_pos);
if (it == temp_neighbor.end()) {
neighbor_chunk_heightmap[i] = std::nullopt;
neighbor_biome[i] = BiomeType::NONE;
ASSERT_MSG(false, "Neighbor Chunk is nullptr");
} else {
neighbor_chunk_heightmap[i] =
it->second.get_heightmap();
neighbor_biome[i] = it->second.biome();
}
continue;
}
neighbor_chunk_heightmap[i] = it->second.get_heightmap();
neighbor_biome[i] = it->second.biome();
}
chunks.gen_phase_four(neighbor_chunk_heightmap, neighbor_biome);
}
}
for (auto& [pos, chunks] : m_chunks) {
chunks.gen_phase_five();
}
for (auto& [pos, chunks] : temp_neighbor) {
chunks.gen_phase_five();
}
std::array<std::optional<std::vector<BlockType>>, 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()) {
auto it = temp_neighbor.find(neighbor_pos);
if (it == temp_neighbor.end()) {
neighbor_block[i] = std::nullopt;
ASSERT_MSG(false, "Neighbor Chunk is nullptr");
} else {
neighbor_block[i] = it->second.get_chunk_blocks();
}
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<int> sync{0};
sync.store(1, std::memory_order_release);
sync.load(std::memory_order_acquire);
m_cave_carcer.cleanup_finished_caves();
std::vector<ChunkRenderData> pending_gen_data;
pending_gen_data.reserve(m_chunks.size());
for (auto& [pos, chunk] : m_chunks) {
ChunkRenderData data;
data.chunk = &chunk;
for (int i = 0; i < 4; i++) {
auto it = m_chunks.find(pos + CHUNK_DIR[i]);
if (it != m_chunks.end()) {
data.neighbor_block[i] = &(it->second.get_chunk_blocks());
} else {
data.neighbor_block[i] = nullptr;
}
}
pending_gen_data.emplace_back(std::move(data));
}
std::for_each(std::execution::par, pending_gen_data.begin(),
pending_gen_data.end(), [](ChunkRenderData& data) {
if (!data.chunk) {
return;
}
data.chunk->gen_vertex_data(data.neighbor_block);
});
for (auto& chunk_map : m_chunks) {
auto& [chunk_pos, chunk] = chunk_map;
chunk.upload_to_gpu();
}
}
*/
ChunkPos World::chunk_pos(int world_x, int world_z) {
int chunk_x, chunk_z; int chunk_x, chunk_z;
if (world_x < 0) { if (world_x < 0) {
chunk_x = (world_x + 1) / CHUNK_SIZE - 1; chunk_x = (world_x + 1) / CHUNK_SIZE - 1;
@@ -325,13 +136,19 @@ ChunkPos World::chunk_pos(int world_x, int world_z) {
#pragma region ChunkGenerate #pragma region ChunkGenerate
void World::gen_chunks_internal() { void World::gen_chunks_internal() {
// Logger::info("gen_chunks_internal");
m_chunk_gen_fraction = 0.0f; m_chunk_gen_fraction = 0.0f;
m_chunk_gen_finished = false; m_chunk_gen_finished = false;
/*
if (!new_chunks.empty()) {
submit_new_chunks();
return;
}*/
ChunkPosSet required_chunks; ChunkPosSet required_chunks;
ChunkPairVector temp_neighbor; ChunkPairVector temp_neighbor;
std::vector<ChunkPos> need_gen_temp_chunks_pos;
compute_required_chunks(required_chunks, temp_neighbor, compute_required_chunks(required_chunks, temp_neighbor);
need_gen_temp_chunks_pos);
ASSERT_MSG(!required_chunks.empty(), "required chunks is empty!!"); ASSERT_MSG(!required_chunks.empty(), "required chunks is empty!!");
@@ -348,256 +165,36 @@ void World::gen_chunks_internal() {
} }
m_chunk_gen_fraction = 0.1f; m_chunk_gen_fraction = 0.1f;
ChunkPairVector new_chunks;
ChunkHashMap new_temp_chunks;
for (auto& pos : need_gen_chunks_pos) { for (auto& pos : need_gen_chunks_pos) {
new_chunks.push_back({pos, Chunk(*this, pos)}); new_chunks.emplace(pos, Chunk(*this, pos));
} }
for (auto& pos : need_gen_temp_chunks_pos) { auto t1 = system_clock::now();
new_temp_chunks.emplace(pos, Chunk(*this, pos)); {
std::scoped_lock lock{m_cave_carcer.path_mutex(),
m_river_worm.paths_mutex()};
auto pool_ptr = m_gen_thread_pool.load();
if (!pool_ptr) {
return;
} }
ConstChunkMap new_chunks_neighbor; parallel_do(*pool_ptr, temp_neighbor.begin(), temp_neighbor.end(),
// affected neighbor pool_ptr->thread_sum(),
ChunkPtrUpdateList affected_neighbor;
build_neighbor_context_for_new_chunks(new_chunks_neighbor,
affected_neighbor, new_chunks);
// build new chunk, but the neighbor in m_chunks also need to re-build
std::for_each(std::execution::par, new_chunks.begin(), new_chunks.end(),
[this](std::pair<ChunkPos, Chunk>& new_chunk) { [this](std::pair<ChunkPos, Chunk>& new_chunk) {
auto& [pos, chunk] = new_chunk; auto& [pos, chunk] = new_chunk;
chunk.gen_phase_one(); chunk.gen_phase_one();
m_cave_carcer.try_to_add_path(pos, chunk.seed()); m_cave_carcer.try_to_add_path(pos, chunk.seed());
m_river_worm.try_to_add_path(pos, chunk.seed()); m_river_worm.try_to_add_path(pos, chunk.seed());
}); });
std::for_each(new_temp_chunks.begin(), new_temp_chunks.end(),
[](std::pair<const ChunkPos, Chunk>& new_chunk) {
auto& [pos, chunk] = new_chunk;
chunk.gen_phase_one();
});
// precompute path to ensure the continuity of the path
std::for_each(std::execution::par, temp_neighbor.begin(),
temp_neighbor.end(),
[this](std::pair<ChunkPos, Chunk>& new_chunk) {
auto& [pos, chunk] = new_chunk;
chunk.gen_phase_one();
m_cave_carcer.try_to_add_path(pos, chunk.seed());
m_river_worm.try_to_add_path(pos, chunk.seed());
});
m_chunk_gen_fraction = 0.2f;
/*
std::array<const Chunk*, 8> neighbor_chunks;
for (auto& [pos, chunks] : new_chunks) {
for (int i = 0; i < 8; i++) {
auto neighbor_pos = pos + CHUNK_DIR[i];
auto it = new_chunks_neighbor.find(neighbor_pos);
if (it == new_chunks_neighbor.end()) {
neighbor_chunks[i] = nullptr;
// ASSERT_MSG(false, "Cant Find Neighbot");
continue;
}
neighbor_chunks[i] = it->second;
}
chunks.gen_phase_two(neighbor_chunks);
}
*/
/*
for (auto& [pos, chunks] : temp_neighbor) {
for (int i = 0; i < 8; i++) {
auto neighbor_pos = pos + CHUNK_DIR[i];
auto it = new_chunks_neighbor.find(neighbor_pos);
if (it == new_chunks_neighbor.end()) {
neighbor_chunks[i] = nullptr;
continue;
}
neighbor_chunks[i] = it->second;
}
chunks.gen_phase_two(neighbor_chunks);
}
*/
m_chunk_gen_fraction = 0.3f;
std::for_each(std::execution::par, new_chunks.begin(), new_chunks.end(),
[](std::pair<ChunkPos, Chunk>& pair) {
auto& [pos, chunks] = pair;
chunks.gen_phase_three();
});
for (auto& [pos, chunk] : new_temp_chunks) {
chunk.gen_phase_three();
}
// for (auto& [pos, chunks] : temp_neighbor) {
// chunks.gen_phase_three();
// }
/*
for (int i = 0; i < 4; i++) {
for (auto& [pos, chunks] : temp_neighbor) {
std::array<std::optional<HeightMapArray>, 8>
neighbor_chunk_heightmap;
// std::lock_guard lk(m_chunks_mutex);
std::array<BiomeType, 8> neighbor_biome;
for (int i = 0; i < 8; i++) {
auto neighbor_pos = pos + CHUNK_DIR[i];
auto it = new_chunks_neighbor.find(neighbor_pos);
if (it == new_chunks_neighbor.end()) {
neighbor_chunk_heightmap[i] = std::nullopt;
neighbor_biome[i] = BiomeType::NONE;
continue;
}
neighbor_chunk_heightmap[i] = it->second->get_heightmap();
neighbor_biome[i] = it->second->biome();
}
chunks.gen_phase_four(neighbor_chunk_heightmap, neighbor_biome);
}
for (auto& [pos, chunks] : new_chunks) {
std::array<std::optional<HeightMapArray>, 8>
neighbor_chunk_heightmap;
// std::lock_guard lk(m_chunks_mutex);
std::array<BiomeType, 8> neighbor_biome;
for (int i = 0; i < 8; i++) {
auto neighbor_pos = pos + CHUNK_DIR[i];
auto it = new_chunks_neighbor.find(neighbor_pos);
if (it == new_chunks_neighbor.end()) {
neighbor_chunk_heightmap[i] = std::nullopt;
neighbor_biome[i] = BiomeType::NONE;
ASSERT_MSG(false, "Cant Find Neighbot");
continue;
}
neighbor_chunk_heightmap[i] = it->second->get_heightmap();
neighbor_biome[i] = it->second->biome();
}
chunks.gen_phase_four(neighbor_chunk_heightmap, neighbor_biome);
}
}
*/
m_chunk_gen_fraction = 0.4f;
for (auto& [pos, chunks] : new_chunks) {
chunks.gen_phase_five();
}
m_chunk_gen_fraction = 0.45f;
for (auto& [pos, chunk] : new_temp_chunks) {
chunk.gen_phase_five();
}
m_chunk_gen_fraction = 0.5f;
/*
for (auto& [pos, chunks] : temp_neighbor) {
chunks.gen_phase_five();
}
*/
std::vector<std::pair<Chunk*, OptionalBlockVectorArray>>
new_chunks_surface_blend_data(new_chunks.size());
for (size_t idx = 0; idx < new_chunks.size(); idx++) {
auto& [pos, chunk] = new_chunks[idx];
new_chunks_surface_blend_data[idx].first = &chunk;
{
// 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()) {
auto it = new_temp_chunks.find(neighbor_pos);
if (it == new_temp_chunks.end()) {
new_chunks_surface_blend_data[idx].second[i] =
std::nullopt;
Logger::warn(
"Can't find neighbor for chunk surface blend");
continue;
}
new_chunks_surface_blend_data[idx].second[i] =
it->second.get_chunk_blocks();
continue;
}
new_chunks_surface_blend_data[idx].second[i] =
it->second->get_chunk_blocks();
}
}
}
std::for_each(
std::execution::par, new_chunks_surface_blend_data.begin(),
new_chunks_surface_blend_data.end(),
[](std::pair<Chunk*, OptionalBlockVectorArray>& new_chunk_data) {
auto& [chunk, neighbor_data] = new_chunk_data;
chunk->gen_phase_six(neighbor_data);
});
m_chunk_gen_fraction = 0.55f;
std::for_each(std::execution::par, new_chunks.begin(), new_chunks.end(),
[](std::pair<ChunkPos, Chunk>& new_chunk) {
auto& [pos, chunk] = new_chunk;
chunk.gen_phase_seven();
});
m_chunk_gen_fraction = 0.6f;
std::vector<std::pair<Chunk*, OptionalBlockVectorArray>>
new_chunk_vertices_data(new_chunks.size());
for (size_t idx = 0; idx < new_chunks.size(); idx++) {
auto& [pos, chunk] = new_chunks[idx];
new_chunk_vertices_data[idx].first = &chunk;
for (int i = 0; i < 4; i++) {
auto it = new_chunks_neighbor.find(pos + CHUNK_DIR[i]);
if (it != new_chunks_neighbor.end()) {
new_chunk_vertices_data[idx].second[i] =
(it->second->get_chunk_blocks());
} else {
new_chunk_vertices_data[idx].second[i] = std::nullopt;
}
}
}
std::for_each(
std::execution::par, new_chunk_vertices_data.begin(),
new_chunk_vertices_data.end(),
[](std::pair<Chunk*, OptionalBlockVectorArray>& new_chunk_data) {
auto& [chunk, neighbor_data] = new_chunk_data;
chunk->gen_vertex_data(neighbor_data);
});
m_chunk_gen_fraction = 0.7f;
build_neighbor_context_for_affected_neighbors(affected_neighbor,
new_chunks_neighbor);
m_chunk_gen_fraction = 0.8f;
OptionalBlockVectorArray neighbor_block;
for (auto& [pos, chunk] : affected_neighbor) {
for (int i = 0; i < 4; i++) {
auto it = new_chunks_neighbor.find(pos + CHUNK_DIR[i]);
if (it != new_chunks_neighbor.end()) {
neighbor_block[i] = (it->second->get_chunk_blocks());
} else {
neighbor_block[i] = std::nullopt;
}
}
chunk->gen_vertex_data(neighbor_block);
chunk->need_upload();
}
m_chunk_gen_fraction = 0.9f;
{
std::lock_guard lk(m_new_chunk_queue_mutex);
for (auto& x : new_chunks) {
m_new_chunk_queue.emplace_back(std::move(x));
}
}
m_cave_carcer.cleanup_finished_caves(); m_cave_carcer.cleanup_finished_caves();
m_river_worm.cleanup_finished_rivers(); m_river_worm.cleanup_finished_rivers();
}
auto t2 = system_clock::now();
Logger::info("Temp Neighbor Add Path Consum {}",
duration_cast<milliseconds>(t2 - t1));
m_chunk_gen_fraction = 0.9f;
m_chunk_gen_fraction = 1.0f; m_chunk_gen_fraction = 1.0f;
submit_new_chunks();
m_chunk_gen_finished = true; m_chunk_gen_finished = true;
} }
@@ -606,15 +203,14 @@ void World::sync_player_pos(glm::vec3& player_pos) {
player_pos = m_gen_player_pos; player_pos = m_gen_player_pos;
} }
void World::compute_required_chunks( void World::compute_required_chunks(ChunkPosSet& required_chunks,
ChunkPosSet& required_chunks, ChunkPairVector& temp_neighbor, ChunkPairVector& temp_neighbor) {
std::vector<ChunkPos>& need_gen_temp_chunks_pos) {
glm::vec3 player_pos; glm::vec3 player_pos;
sync_player_pos(player_pos); sync_player_pos(player_pos);
int x = std::floor(player_pos.x); int x = std::floor(player_pos.x);
int z = std::floor(player_pos.z); int z = std::floor(player_pos.z);
auto [chunk_x, chunk_z] = chunk_pos(x, z); auto [chunk_x, chunk_z] = get_chunk_pos(x, z);
int radius = m_rendering_distance; int radius = m_rendering_distance;
int r2 = radius * radius; int r2 = radius * radius;
required_chunks.reserve(radius * radius); required_chunks.reserve(radius * radius);
@@ -626,20 +222,6 @@ void World::compute_required_chunks(
} }
} }
} }
int new_radius = radius + 1;
int new_r2 = new_radius * new_radius;
for (int dx = -new_radius; dx <= new_radius; ++dx) {
for (int dz = -new_radius; dz <= new_radius; ++dz) {
if (dx * dx + dz * dz <= new_r2) {
int nx = chunk_x + dx;
int nz = chunk_z + dz;
auto it = required_chunks.find({nx, nz});
if (it == required_chunks.end()) {
need_gen_temp_chunks_pos.push_back({nx, nz});
}
}
}
}
int max_path_len = std::max(CavePath::step_max(), RiverPath::step_max()); int max_path_len = std::max(CavePath::step_max(), RiverPath::step_max());
radius = max_path_len / 2; radius = max_path_len / 2;
r2 = radius * radius; r2 = radius * radius;
@@ -647,10 +229,6 @@ void World::compute_required_chunks(
for (int dz = -radius; dz <= radius; ++dz) { for (int dz = -radius; dz <= radius; ++dz) {
if (dx * dx + dz * dz <= r2) { if (dx * dx + dz * dz <= r2) {
ChunkPos pos{chunk_x + dx, chunk_z + dz}; ChunkPos pos{chunk_x + dx, chunk_z + dz};
auto it = required_chunks.find(pos);
if (it != required_chunks.end()) {
continue;
}
temp_neighbor.emplace_back(pos, Chunk(*this, pos)); temp_neighbor.emplace_back(pos, Chunk(*this, pos));
} }
} }
@@ -677,37 +255,71 @@ void World::sync_and_collect_missing_chunks(
} }
} }
void World::build_neighbor_context_for_new_chunks( void World::submit_new_chunks() {
ConstChunkMap& new_chunks_neighbor, ChunkPtrUpdateList& affected_neighbor, using enum ChunkLoadStyle;
const ChunkPairVector& new_chunks) { std::lock_guard lock(m_new_chunk_mutex);
{ auto pool_ptr = m_gen_thread_pool.load();
std::lock_guard lk(m_chunks_mutex); if (!pool_ptr) {
for (auto& [pos, chunk] : new_chunks) { return;
for (auto& dir : CHUNK_DIR) { }
auto it = m_chunks.find(pos + dir); switch (m_chunk_load_style) {
if (it != m_chunks.end()) { case RANDOM:
new_chunks_neighbor.insert({it->first, &(it->second)}); for (auto& [pos, task] : new_chunks) {
affected_neighbor.push_back({it->first, &(it->second)}); if (!task.future.valid()) {
task.future =
pool_ptr->enqueue([&task]() { task.chunk.gen_chunk(); });
}
}
break;
case CENTER: {
std::vector<std::pair<ChunkPos, PendingChunk*>> tasks;
for (auto& [pos, task] : new_chunks) {
if (!task.future.valid()) {
tasks.emplace_back(pos, &task);
}
}
glm::vec3 player_pos;
sync_player_pos(player_pos);
auto dist2 = [player_pos](ChunkPos chunk_pos) {
ChunkPos player_chunk_pos =
get_chunk_pos(player_pos.x, player_pos.z);
float dx = player_chunk_pos.x - chunk_pos.x;
float dz = player_chunk_pos.z - chunk_pos.z;
return dx * dx + dz * dz;
};
std::sort(tasks.begin(), tasks.end(),
[&dist2](const auto& a, const auto& b) {
return dist2(a.first) < dist2(b.first);
});
for (auto& [pos, task] : tasks) {
if (!task->future.valid()) {
task->future =
pool_ptr->enqueue([task]() { task->chunk.gen_chunk(); });
} }
} }
} }
} }
for (auto& [pos, chunk] : new_chunks) {
new_chunks_neighbor.insert({pos, &chunk});
}
} }
void World::build_neighbor_context_for_affected_neighbors( void World::poll_finished_chunks() {
ChunkPtrUpdateList& affected_neighbor, ConstChunkMap& new_chunks_neighbor) { m_new_finished_chunk.clear();
std::lock_guard lk(m_chunks_mutex); std::lock_guard lock(m_new_chunk_mutex);
for (auto& [pos, chunk] : affected_neighbor) { std::erase_if(
for (auto& dir : CHUNK_DIR) { new_chunks, [&](std::pair<const ChunkPos, PendingChunk>& pair) {
auto it = m_chunks.find(pos + dir); auto& pending = pair.second;
if (it != m_chunks.end()) { if (!pending.future.valid()) {
new_chunks_neighbor.insert({it->first, &(it->second)}); return false;
}
} }
if (pending.future.wait_for(0ms) != std::future_status::ready) {
return false;
} }
pending.future.get();
m_new_finished_chunk.emplace_back(pair.first,
std::move(pending.chunk));
return true;
});
} }
#pragma endregion #pragma endregion
@@ -767,10 +379,12 @@ void World::serever_run(std::stop_token stoken) {
} }
void World::need_gen() { void World::need_gen() {
if (!m_could_gen) { if (!m_could_gen) {
Logger::warn("It is generating or consuming new chunks"); Logger::warn("It is generating or consuming new chunks");
return; return;
} }
m_could_gen = false; m_could_gen = false;
{ {
std::lock_guard lk(m_gen_player_pos_mutex); std::lock_guard lk(m_gen_player_pos_mutex);
@@ -778,11 +392,12 @@ void World::need_gen() {
} }
m_need_gen_chunk = true; m_need_gen_chunk = true;
m_gen_cv.notify_one(); m_gen_cv.notify_one();
} }
int World::get_block(const glm::ivec3& block_pos) const { int World::get_block(const glm::ivec3& block_pos) const {
auto [chunk_x, chunk_z] = chunk_pos(block_pos.x, block_pos.z); auto [chunk_x, chunk_z] = get_chunk_pos(block_pos.x, block_pos.z);
std::lock_guard lk(m_chunks_mutex); std::lock_guard lk(m_chunks_mutex);
auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z}); auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z});
@@ -800,7 +415,7 @@ int World::get_block(const glm::ivec3& block_pos) const {
} }
bool World::is_solid(const glm::ivec3& block_pos) const { bool World::is_solid(const glm::ivec3& block_pos) const {
auto [chunk_x, chunk_z] = chunk_pos(block_pos.x, block_pos.z); auto [chunk_x, chunk_z] = get_chunk_pos(block_pos.x, block_pos.z);
std::lock_guard lk(m_chunks_mutex); std::lock_guard lk(m_chunks_mutex);
auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z}); auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z});
@@ -822,7 +437,7 @@ bool World::is_solid(const glm::ivec3& block_pos) const {
} }
bool World::can_pass_block(const glm::ivec3& block_pos) const { bool World::can_pass_block(const glm::ivec3& block_pos) const {
auto [chunk_x, chunk_z] = chunk_pos(block_pos.x, block_pos.z); auto [chunk_x, chunk_z] = get_chunk_pos(block_pos.x, block_pos.z);
std::lock_guard lk(m_chunks_mutex); std::lock_guard lk(m_chunks_mutex);
auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z}); auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z});
@@ -840,21 +455,21 @@ bool World::can_pass_block(const glm::ivec3& block_pos) const {
} }
BlockType World::get_block_tpye(const glm::ivec3& block_pos) const { BlockType World::get_block_tpye(const glm::ivec3& block_pos) const {
auto [chunk_x, chunk_z] = chunk_pos(block_pos.x, block_pos.z); auto [chunk_x, chunk_z] = get_chunk_pos(block_pos.x, block_pos.z);
std::lock_guard lk(m_chunks_mutex); std::lock_guard lk(m_chunks_mutex);
auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z}); auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z});
if (it == m_chunks.end()) { if (it == m_chunks.end()) {
Logger::error("Can't Find Block {} {} {}", block_pos.x, block_pos.y, // Logger::error("Can't Find Block {} {} {}", block_pos.x, block_pos.y,
block_pos.z); // block_pos.z);
return 0; return 0;
} }
const auto& chunk_blocks = it->second.get_chunk_blocks(); const auto& chunk_blocks = it->second.get_chunk_blocks();
auto [x, y, z] = Chunk::world_to_block(block_pos, {chunk_x, chunk_z}); auto [x, y, z] = Chunk::world_to_block(block_pos, {chunk_x, chunk_z});
if (x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y || if (x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y ||
z >= CHUNK_SIZE) { z >= CHUNK_SIZE) {
Logger::error("Can't Find Block {} {} {}", block_pos.x, block_pos.y, // Logger::error("Can't Find Block {} {} {}", block_pos.x, block_pos.y,
block_pos.z); // block_pos.z);
return 0; return 0;
} }
return chunk_blocks[Chunk::index(x, y, z)]; return chunk_blocks[Chunk::index(x, y, z)];
@@ -867,7 +482,7 @@ void World::set_block(const glm::ivec3& block_pos, unsigned id) {
world_y = block_pos.y; world_y = block_pos.y;
world_z = block_pos.z; world_z = block_pos.z;
auto [chunk_x, chunk_z] = chunk_pos(world_x, world_z); auto [chunk_x, chunk_z] = get_chunk_pos(world_x, world_z);
std::lock_guard lk(m_chunks_mutex); std::lock_guard lk(m_chunks_mutex);
auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z}); auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z});
@@ -890,7 +505,7 @@ void World::set_block(const glm::ivec3& block_pos, unsigned id) {
for (const auto& dir : NEIGHBOR_DIRS) { for (const auto& dir : NEIGHBOR_DIRS) {
glm::ivec3 neighbor = block_pos + dir; glm::ivec3 neighbor = block_pos + dir;
auto [cx, cz] = chunk_pos(neighbor.x, neighbor.z); auto [cx, cz] = get_chunk_pos(neighbor.x, neighbor.z);
auto it = m_chunks.find({cx, cz}); auto it = m_chunks.find({cx, cz});
if (it != m_chunks.end()) { if (it != m_chunks.end()) {
it->second.mark_dirty(); it->second.mark_dirty();
@@ -918,16 +533,9 @@ void World::update(float delta_time) {
m_pending_delete_vao.clear(); m_pending_delete_vao.clear();
} }
{ poll_finished_chunks();
std::scoped_lock lk(m_chunks_mutex, m_new_chunk_queue_mutex);
m_new_chunk.clear();
for (auto& x : m_new_chunk_queue) {
m_new_chunk.emplace_back(std::move(x));
}
m_new_chunk_queue.clear();
}
for (auto& x : m_new_chunk) { for (auto& x : m_new_finished_chunk) {
x.second.upload_to_gpu(); x.second.upload_to_gpu();
} }
@@ -936,7 +544,7 @@ void World::update(float delta_time) {
std::lock_guard lk(m_chunks_mutex); std::lock_guard lk(m_chunks_mutex);
bool consumed = false; bool consumed = false;
for (auto& x : m_new_chunk) { for (auto& x : m_new_finished_chunk) {
m_chunks.insert_or_assign(x.first, std::move(x.second)); m_chunks.insert_or_assign(x.first, std::move(x.second));
consumed = true; consumed = true;
} }
@@ -1011,9 +619,9 @@ void World::rebuild_world() {
m_cave_carcer.reload(ChunkGenerator::seed()); m_cave_carcer.reload(ChunkGenerator::seed());
m_river_worm.reload(ChunkGenerator::seed()); m_river_worm.reload(ChunkGenerator::seed());
{ {
std::scoped_lock lk(m_chunks_mutex, m_new_chunk_queue_mutex); std::scoped_lock lk(m_chunks_mutex);
m_chunks.clear(); m_chunks.clear();
m_new_chunk_queue.clear(); m_new_finished_chunk.clear();
} }
m_could_gen = true; m_could_gen = true;
ChunkGenerator::reload(); ChunkGenerator::reload();
@@ -1023,20 +631,6 @@ void World::rebuild_world() {
m_is_rebuilding = false; m_is_rebuilding = false;
} }
float World::chunk_gen_fraction() const { return m_chunk_gen_fraction.load(); }
int World::rendering_distance() const { return m_rendering_distance.load(); }
void World::rendering_distance(int rendering_distance) {
m_rendering_distance = rendering_distance;
}
CaveCarver& World::cave_carcer() { return m_cave_carcer; }
RiverWorm& World::river_worm() { return m_river_worm; }
std::vector<glm::vec4>& World::planes() { return m_planes; }
std::vector<ChunkRenderSnapshot>& World::render_snapshots() {
return m_render_snapshots;
};
/* /*
glm::vec3 World::sunlight_dir() const { glm::vec3 World::sunlight_dir() const {
float t = static_cast<float>(m_day_tick) / DAY_TIME; float t = static_cast<float>(m_day_tick) / DAY_TIME;
@@ -1071,6 +665,21 @@ glm::vec3 World::sunlight_dir() const {
return glm::normalize(-dir); return glm::normalize(-dir);
} }
float World::chunk_gen_fraction() const { return m_chunk_gen_fraction.load(); }
int World::rendering_distance() const { return m_rendering_distance.load(); }
void World::rendering_distance(int rendering_distance) {
m_rendering_distance = rendering_distance;
}
CaveCarver& World::cave_carcer() { return m_cave_carcer; }
RiverWorm& World::river_worm() { return m_river_worm; }
std::vector<glm::vec4>& World::planes() { return m_planes; }
std::vector<ChunkRenderSnapshot>& World::render_snapshots() {
return m_render_snapshots;
};
TickType World::game_tick() const { return m_game_ticks.load(); } TickType World::game_tick() const { return m_game_ticks.load(); }
TickType World::day_tick() const { return m_day_tick.load(); } TickType World::day_tick() const { return m_day_tick.load(); }
void World::day_tick(TickType tick) { void World::day_tick(TickType tick) {
@@ -1082,4 +691,38 @@ void World::per_tick_time(int ms) { m_per_tick_time = ms; }
bool World::is_tick_running() const { return m_tick_running.load(); } bool World::is_tick_running() const { return m_tick_running.load(); }
void World::tick_running(bool run) { m_tick_running = run; } void World::tick_running(bool run) { m_tick_running = run; }
int World::pool_threads() const { return m_pool_threads.load(); }
int World::max_threads() const { return m_max_threads.load(); }
void World::change_pool_threads(int threads) {
m_max_threads = std::thread::hardware_concurrency();
if (m_max_threads < 1) {
Logger::warn("Can't Get Max Support Threads, Set Max Threads to 4");
m_max_threads = 4;
}
int used_thread = std::clamp(threads, 1, m_max_threads.load());
Logger::info("Create New Thread Pool Use {} Threads", used_thread);
m_gen_thread_pool.store(std::make_shared<ThreadPool>(used_thread));
m_pool_threads = used_thread;
}
int World::chunk_load_style() const {
return std::to_underlying(m_chunk_load_style.load());
}
void World::set_chunk_load_style(int id) {
using enum ChunkLoadStyle;
using std::to_underlying;
switch (m_chunk_load_style.load()) {
case RANDOM:
if (id == to_underlying(RANDOM)) {
m_chunk_load_style = RANDOM;
return;
}
case CENTER:
if (id == to_underlying(CENTER)) {
m_chunk_load_style = CENTER;
return;
}
}
Logger::error("Can,t Find Chunk Load Style Id {}, Nothing Will Do", id);
}
} // namespace Cubed } // namespace Cubed

View File

@@ -419,7 +419,7 @@ void Renderer::render_ui() {
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
shader.set_loc("mv_matrix", m_ui_m_matrix); shader.set_loc("m_matrix", m_ui_m_matrix);
shader.set_loc("proj_matrix", m_ui_proj); shader.set_loc("proj_matrix", m_ui_proj);
glBindVertexArray(m_vao[3]); glBindVertexArray(m_vao[3]);
@@ -719,7 +719,6 @@ void Renderer::render_world() {
normal_block_shader.set_loc("sunlightDir", light_dir_view); normal_block_shader.set_loc("sunlightDir", light_dir_view);
normal_block_shader.set_loc("shadowMode", m_shadow_mode); normal_block_shader.set_loc("shadowMode", m_shadow_mode);
normal_block_shader.set_loc("shader_on", m_shader_on); normal_block_shader.set_loc("shader_on", m_shader_on);
normal_block_shader.set_loc("texelsPerUnit", texels_per_unit);
normal_block_shader.set_loc("lightSizeUV", normal_block_shader.set_loc("lightSizeUV",
static_cast<float>(m_light_size_uv)); static_cast<float>(m_light_size_uv));
normal_block_shader.set_loc("minRadius", m_min_radius); normal_block_shader.set_loc("minRadius", m_min_radius);
@@ -728,6 +727,8 @@ void Renderer::render_world() {
normal_block_shader.set_loc("specularStrength", m_specular_strength); normal_block_shader.set_loc("specularStrength", m_specular_strength);
normal_block_shader.set_loc("cameraPos", m_camera.get_camera_pos()); normal_block_shader.set_loc("cameraPos", m_camera.get_camera_pos());
normal_block_shader.set_loc("flipY", m_flip_y); normal_block_shader.set_loc("flipY", m_flip_y);
normal_block_shader.set_loc("renderDistance", m_world.rendering_distance());
normal_block_shader.set_loc("skyColor", m_sky_uniform.sky_top);
m_mvp_mat = m_p_mat * m_mv_mat; m_mvp_mat = m_p_mat * m_mv_mat;
auto& m_planes = m_world.planes(); auto& m_planes = m_world.planes();
@@ -818,7 +819,6 @@ void Renderer::render_world() {
accum_shader.set_loc("mv_matrix", m_mv_mat); accum_shader.set_loc("mv_matrix", m_mv_mat);
accum_shader.set_loc("proj_matrix", m_p_mat); accum_shader.set_loc("proj_matrix", m_p_mat);
accum_shader.set_loc("norm_matrix", m_norm_mat); accum_shader.set_loc("norm_matrix", m_norm_mat);
accum_shader.set_loc("lightSpaceMatrix", light_space_matrix);
accum_shader.set_loc("ambientStrength", m_ambient_strength); accum_shader.set_loc("ambientStrength", m_ambient_strength);
accum_shader.set_loc("sunlightColor", accum_shader.set_loc("sunlightColor",
m_parallel_light.directional_light_color); m_parallel_light.directional_light_color);
@@ -827,14 +827,13 @@ void Renderer::render_world() {
accum_shader.set_loc("sunlightDir", light_dir_view); accum_shader.set_loc("sunlightDir", light_dir_view);
accum_shader.set_loc("shader_on", m_shader_on); accum_shader.set_loc("shader_on", m_shader_on);
accum_shader.set_loc("specularStrength", m_specular_strength); accum_shader.set_loc("specularStrength", m_specular_strength);
accum_shader.set_loc("cameraPos", m_camera.get_camera_pos());
}; };
auto& accum_shader = get_shader("accum"); auto& accum_shader = get_shader("accum");
accum_shader.use(); accum_shader.use();
set_accum_loc(accum_shader); set_accum_loc(accum_shader);
accum_shader.set_loc("cameraPos", m_camera.get_camera_pos());
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_manager.get_texture_array()); glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_manager.get_texture_array());
for (const auto& snapshot : m_render_snapshots) { for (const auto& snapshot : m_render_snapshots) {