diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..4921432 --- /dev/null +++ b/.clang-format @@ -0,0 +1,20 @@ +--- +BasedOnStyle: Google +--- +Language: Cpp +IndentWidth: 4 +ColumnLimit: 150 +BreakBeforeBraces: Allman +PackConstructorInitializers: Never +SpaceBeforeCtorInitializerColon: true +BreakConstructorInitializers: AfterColon +AllowShortFunctionsOnASingleLine: Inline +BreakBeforeBinaryOperators: None +AlignAfterOpenBracket: BlockIndent +AllowShortLambdasOnASingleLine: Empty +BinPackArguments: false +BinPackParameters: false +NamespaceIndentation: All +ReferenceAlignment: Left +AllowAllParametersOfDeclarationOnNextLine: true +--- diff --git a/src/Game.cpp b/src/Game.cpp index f34cc75..a12fb5c 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -4,149 +4,158 @@ using namespace explo; -Game::Game(GlfwWindow& window) : - /* Misc utils */ - m_main_thread_executor{}, - m_thread_pool(std::thread::hardware_concurrency()), +Game::Game(GlfwWindow &window) : + /* Misc utils */ + m_main_thread_executor{}, + m_thread_pool(std::thread::hardware_concurrency()), /* Video */ m_window(window) { - m_debug_ui = std::make_unique(RenderApi::renderer()); - - RenderApi::ui_draw([this]() { m_debug_ui->display(); }); - - glfwSetWindowUserPointer(m_window.handle(), this); - - glfwSetKeyCallback(m_window.handle(), [](GLFWwindow* window, int key, int scancode, int action, int mods) - { - Game* game = static_cast(glfwGetWindowUserPointer(window)); - game->on_key_change(key, action); - }); - - run_on_main_thread([this]() { late_initialize(); }); + m_debug_ui = std::make_unique(RenderApi::renderer()); + + RenderApi::ui_draw( + [this]() + { + m_debug_ui->display(); + } + ); + + glfwSetWindowUserPointer(m_window.handle(), this); + + glfwSetKeyCallback( + m_window.handle(), + [](GLFWwindow *window, int key, int scancode, int action, int mods) + { + Game *game = static_cast(glfwGetWindowUserPointer(window)); + game->on_key_change(key, action); + } + ); + + run_on_main_thread( + [this]() + { + late_initialize(); + } + ); } Game::~Game() { - RenderApi::ui_draw([]() {}); + RenderApi::ui_draw([]() {}); } void Game::late_initialize() { - m_world = std::make_shared( - m_volume_generator, - m_surface_generator - ); + m_world = std::make_shared(m_volume_generator, m_surface_generator); - m_player = std::make_shared(*m_world, glm::vec3(0, 10, 0)); - m_player_controller = std::make_unique(*m_player); + m_player = std::make_shared(*m_world, glm::vec3(0, 10, 0)); + m_player_controller = std::make_unique(*m_player); - const glm::ivec3 k_render_distance(20, 0, 20); - m_player->recreate_world_view(k_render_distance); + const glm::ivec3 k_render_distance(20, 0, 20); + m_player->recreate_world_view(k_render_distance); - RenderApi::camera_set_position(m_player->get_position()); - RenderApi::camera_set_rotation(m_player->get_yaw(), m_player->get_pitch()); - RenderApi::camera_set_projection_params(90.0f, 1.0f, 0.01f, 1000.0f); + RenderApi::camera_set_position(m_player->get_position()); + RenderApi::camera_set_rotation(m_player->get_yaw(), m_player->get_pitch()); + RenderApi::camera_set_projection_params(90.0f, 1.0f, 0.01f, 1000.0f); } -void Game::run_on_main_thread(std::function const& job) +void Game::run_on_main_thread(std::function const &job) { - m_main_thread_executor.enqueue_job(job); + m_main_thread_executor.enqueue_job(job); } -void Game::run_async(std::function const& job) +void Game::run_async(std::function const &job) { - m_thread_pool.enqueue_job(job); + m_thread_pool.enqueue_job(job); } void Game::on_window_resize(uint32_t width, uint32_t height) { - RenderApi::window_resize(width, height); + RenderApi::window_resize(width, height); } void Game::render() { - m_fps_counter++; + m_fps_counter++; - // FPS - if (!m_last_fps_time || (glfwGetTime() - *m_last_fps_time) >= 1.0f) - { - m_fps = m_fps_counter; - m_fps_counter = 0; - m_last_fps_time = (float) glfwGetTime(); - } + // FPS + if (!m_last_fps_time || (glfwGetTime() - *m_last_fps_time) >= 1.0f) + { + m_fps = m_fps_counter; + m_fps_counter = 0; + m_last_fps_time = (float)glfwGetTime(); + } - // dt - if (m_last_frame_time) - { - m_dt = float(glfwGetTime()) - *m_last_frame_time; - } + // dt + if (m_last_frame_time) + { + m_dt = float(glfwGetTime()) - *m_last_frame_time; + } - m_last_frame_time = (float) glfwGetTime(); + m_last_frame_time = (float)glfwGetTime(); - m_main_thread_executor.process(); // Process main thread jobs + m_main_thread_executor.process(); // Process main thread jobs - if (m_player_controller->update_position()) - RenderApi::camera_set_position(m_player->get_position()); + if (m_player_controller->update_position()) RenderApi::camera_set_position(m_player->get_position()); - if (m_player_controller->update_rotation()) - RenderApi::camera_set_rotation(m_player->get_yaw(), m_player->get_pitch()); + if (m_player_controller->update_rotation()) RenderApi::camera_set_rotation(m_player->get_yaw(), m_player->get_pitch()); - RenderApi::render(); + RenderApi::render(); } void Game::on_key_change(int key, int action) { - // Cursor logic - if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) - { - if (m_window.is_cursor_disabled()) - { - m_window.enable_cursor(); - } - else m_window.close(); - } - else if (key == GLFW_KEY_ENTER && action == GLFW_PRESS) - { - if (!m_window.is_cursor_disabled()) m_window.disable_cursor(); - } + // Cursor logic + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + { + if (m_window.is_cursor_disabled()) + { + m_window.enable_cursor(); + } + else + m_window.close(); + } + else if (key == GLFW_KEY_ENTER && action == GLFW_PRESS) + { + if (!m_window.is_cursor_disabled()) m_window.disable_cursor(); + } } // -------------------------------------------------------------------------------------------------------------------------------- std::unique_ptr s_game; -void explo::init(GlfwWindow& window) +void explo::init(GlfwWindow &window) { - RenderApi::init(window.handle()); + RenderApi::init(window.handle()); - s_game = std::make_unique(window); + s_game = std::make_unique(window); } void explo::shutdown() { - s_game.reset(); + s_game.reset(); - RenderApi::destroy(); + RenderApi::destroy(); } void explo::render() { - s_game->render(); + s_game->render(); } -Game& explo::game() +Game &explo::game() { - return *s_game; + return *s_game; } -void explo::run_on_main_thread(std::function const& job) +void explo::run_on_main_thread(std::function const &job) { - s_game->run_on_main_thread(job); + s_game->run_on_main_thread(job); } -void explo::run_async(std::function const& job) +void explo::run_async(std::function const &job) { - s_game->run_async(job); + s_game->run_async(job); } diff --git a/src/Game.hpp b/src/Game.hpp index b26464f..19a5c60 100644 --- a/src/Game.hpp +++ b/src/Game.hpp @@ -1,79 +1,79 @@ #pragma once +#include "GlfwWindow.hpp" #include "input/EntityController.hpp" -#include "world/BlockRegistry.hpp" -#include "world/Entity.hpp" -#include "video/DebugUi.hpp" #include "util/SyncJobExecutor.hpp" #include "util/ThreadPool.hpp" -#include "world/volume/SinCosVolumeGenerator.hpp" -#include "world/volume/PerlinNoiseGenerator.hpp" +#include "video/DebugUi.hpp" +#include "world/BlockRegistry.hpp" +#include "world/Entity.hpp" #include "world/surface/BlockySurfaceGenerator.hpp" -#include "GlfwWindow.hpp" +#include "world/volume/PerlinNoiseGenerator.hpp" +#include "world/volume/SinCosVolumeGenerator.hpp" namespace explo { class Game { - public: - /* Misc utils */ - SyncJobExecutor m_main_thread_executor; - ThreadPool m_thread_pool; + public: + /* Misc utils */ + SyncJobExecutor m_main_thread_executor; + ThreadPool m_thread_pool; /* World */ - BlockRegistry m_block_registry; - PerlinNoiseGenerator m_volume_generator; - BlockySurfaceGenerator m_surface_generator; + BlockRegistry m_block_registry; + PerlinNoiseGenerator m_volume_generator; + BlockySurfaceGenerator m_surface_generator; /* Video */ - GlfwWindow& m_window; - std::unique_ptr m_debug_ui; + GlfwWindow &m_window; + std::unique_ptr m_debug_ui; - float m_dt = 0.0f; - std::optional m_last_frame_time; + float m_dt = 0.0f; + std::optional m_last_frame_time; - // FPS - std::optional m_last_fps_time; - int m_fps_counter = 0; - int m_fps = 0; + // FPS + std::optional m_last_fps_time; + int m_fps_counter = 0; + int m_fps = 0; - /* Late initialize */ - std::shared_ptr m_world; - std::shared_ptr m_player; - std::unique_ptr m_player_controller; + /* Late initialize */ + std::shared_ptr m_world; + std::shared_ptr m_player; + std::unique_ptr m_player_controller; - public: - explicit Game(GlfwWindow& window); + public: + explicit Game(GlfwWindow &window); ~Game(); - GlfwWindow& get_window() { return m_window; }; - float get_dt() const { return m_dt; } + GlfwWindow &get_window() { return m_window; }; + float get_dt() const { return m_dt; } - /* Job executors */ + /* Job executors */ - void run_on_main_thread(std::function const& job); - void run_async(std::function const& job); + void run_on_main_thread(std::function const &job); + void run_async(std::function const &job); - void on_window_resize(uint32_t width, uint32_t height); + void on_window_resize(uint32_t width, uint32_t height); void render(); - private: - void late_initialize(); + private: + void late_initialize(); - void on_key_change(int key, int action); + void on_key_change(int key, int action); }; - // -------------------------------------------------------------------------------------------------------------------------------- + // -------------------------------------------------------------------------------------------------------------------------------- - void init(GlfwWindow& window); - void shutdown(); + void init(GlfwWindow &window); + void shutdown(); - void render(); + void render(); - Game& game(); + Game &game(); - /* Helpers */ + /* Helpers */ - void run_on_main_thread(std::function const& job); - void run_async(std::function const& job); -} + void run_on_main_thread(std::function const &job); + void run_async(std::function const &job); +} // namespace explo diff --git a/src/GlfwWindow.cpp b/src/GlfwWindow.cpp index 9b1a8bb..00ea912 100644 --- a/src/GlfwWindow.cpp +++ b/src/GlfwWindow.cpp @@ -2,70 +2,70 @@ using namespace explo; -GlfwWindow::GlfwWindow(GLFWwindow* handle) : - m_handle(handle) +GlfwWindow::GlfwWindow(GLFWwindow *handle) : + m_handle(handle) { } GlfwWindow::~GlfwWindow() { - glfwDestroyWindow(m_handle); + glfwDestroyWindow(m_handle); } bool GlfwWindow::is_key_pressed(int key_code) const { - return glfwGetKey(m_handle, key_code) == GLFW_PRESS; + return glfwGetKey(m_handle, key_code) == GLFW_PRESS; } glm::vec2 GlfwWindow::get_cursor_position() const { - glm::dvec2 cursor_pos; - glfwGetCursorPos(m_handle, &cursor_pos.x, &cursor_pos.y); - return glm::vec2(cursor_pos); + glm::dvec2 cursor_pos; + glfwGetCursorPos(m_handle, &cursor_pos.x, &cursor_pos.y); + return glm::vec2(cursor_pos); } void GlfwWindow::enable_cursor() { - glfwSetInputMode(m_handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + glfwSetInputMode(m_handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } void GlfwWindow::disable_cursor() { - glfwSetInputMode(m_handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + glfwSetInputMode(m_handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); } bool GlfwWindow::is_cursor_disabled() const { - return glfwGetInputMode(m_handle, GLFW_CURSOR) == GLFW_CURSOR_DISABLED; + return glfwGetInputMode(m_handle, GLFW_CURSOR) == GLFW_CURSOR_DISABLED; } bool GlfwWindow::is_opened() const { - return !glfwWindowShouldClose(m_handle); + return !glfwWindowShouldClose(m_handle); } void GlfwWindow::close() { - glfwSetWindowShouldClose(m_handle, true); + glfwSetWindowShouldClose(m_handle, true); } glm::ivec2 GlfwWindow::get_size() const { - glm::ivec2 size{}; - glfwGetFramebufferSize(m_handle, &size.x, &size.y); - return size; + glm::ivec2 size{}; + glfwGetFramebufferSize(m_handle, &size.x, &size.y); + return size; } -std::unique_ptr GlfwWindow::create(int width, int height, std::string const& title) +std::unique_ptr GlfwWindow::create(int width, int height, std::string const &title) { - GLFWwindow* handle = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr); - return std::make_unique(handle); + GLFWwindow *handle = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr); + return std::make_unique(handle); } std::unique_ptr GlfwWindow::create_headless(int width, int height) { - glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); - GLFWwindow* handle = glfwCreateWindow(width, height, "", nullptr, nullptr); - return std::make_unique(handle); + GLFWwindow *handle = glfwCreateWindow(width, height, "", nullptr, nullptr); + return std::make_unique(handle); } diff --git a/src/GlfwWindow.hpp b/src/GlfwWindow.hpp index 6c692ee..a7ebd1f 100644 --- a/src/GlfwWindow.hpp +++ b/src/GlfwWindow.hpp @@ -1,40 +1,40 @@ #pragma once -#include -#include - #include + #include +#include +#include namespace explo { - class GlfwWindow - { - private: - GLFWwindow* m_handle; + class GlfwWindow + { + private: + GLFWwindow *m_handle; - public: - explicit GlfwWindow(GLFWwindow* handle); - GlfwWindow(GlfwWindow const& other) = delete; - ~GlfwWindow(); + public: + explicit GlfwWindow(GLFWwindow *handle); + GlfwWindow(GlfwWindow const &other) = delete; + ~GlfwWindow(); - GLFWwindow* handle() const { return m_handle; }; + GLFWwindow *handle() const { return m_handle; }; - /// \param key_code the key. Currently matches `GLFW_KEY_*`. - bool is_key_pressed(int key_code) const; + /// \param key_code the key. Currently matches `GLFW_KEY_*`. + bool is_key_pressed(int key_code) const; - glm::vec2 get_cursor_position() const; + glm::vec2 get_cursor_position() const; - void enable_cursor(); - void disable_cursor(); - bool is_cursor_disabled() const; + void enable_cursor(); + void disable_cursor(); + bool is_cursor_disabled() const; - bool is_opened() const; - void close(); + bool is_opened() const; + void close(); - glm::ivec2 get_size() const; + glm::ivec2 get_size() const; - static std::unique_ptr create(int width, int height, std::string const& title); - static std::unique_ptr create_headless(int width, int height); - }; -} // namespace explo \ No newline at end of file + static std::unique_ptr create(int width, int height, std::string const &title); + static std::unique_ptr create_headless(int width, int height); + }; +} // namespace explo \ No newline at end of file diff --git a/src/input/EntityController.cpp b/src/input/EntityController.cpp index a3b7278..e853ae4 100644 --- a/src/input/EntityController.cpp +++ b/src/input/EntityController.cpp @@ -1,89 +1,84 @@ #include "EntityController.hpp" #include "GLFW/glfw3.h" - #include "Game.hpp" using namespace explo; -EntityController::EntityController(Entity& entity) : +EntityController::EntityController(Entity &entity) : m_entity(entity) { } -EntityController::~EntityController() -{ -} +EntityController::~EntityController() {} bool EntityController::update_position() { - if (!is_input_mode_enabled()) - return false; + if (!is_input_mode_enabled()) return false; - GlfwWindow& window = game().get_window(); - float dt = game().get_dt(); + GlfwWindow &window = game().get_window(); + float dt = game().get_dt(); - bool updated = false; + bool updated = false; - glm::vec3 position = m_entity.get_position(); - glm::vec3 dp{}; + glm::vec3 position = m_entity.get_position(); + glm::vec3 dp{}; - if (window.is_key_pressed(GLFW_KEY_A)) dp += -m_entity.get_right(), updated = true; - if (window.is_key_pressed(GLFW_KEY_D)) dp += m_entity.get_right(), updated = true; - if (window.is_key_pressed(GLFW_KEY_S)) dp += -m_entity.get_forward(), updated = true; - if (window.is_key_pressed(GLFW_KEY_W)) dp += m_entity.get_forward(), updated = true; - if (window.is_key_pressed(GLFW_KEY_SPACE)) dp += m_entity.get_up(), updated = true; - if (window.is_key_pressed(GLFW_KEY_LEFT_SHIFT)) dp += -m_entity.get_up(), updated = true; + if (window.is_key_pressed(GLFW_KEY_A)) dp += -m_entity.get_right(), updated = true; + if (window.is_key_pressed(GLFW_KEY_D)) dp += m_entity.get_right(), updated = true; + if (window.is_key_pressed(GLFW_KEY_S)) dp += -m_entity.get_forward(), updated = true; + if (window.is_key_pressed(GLFW_KEY_W)) dp += m_entity.get_forward(), updated = true; + if (window.is_key_pressed(GLFW_KEY_SPACE)) dp += m_entity.get_up(), updated = true; + if (window.is_key_pressed(GLFW_KEY_LEFT_SHIFT)) dp += -m_entity.get_up(), updated = true; - if (updated) - { - position += dp * k_movement_sensitivity * (window.is_key_pressed(GLFW_KEY_LEFT_CONTROL) ? k_movement_speed_boost : 1.0f) * dt; - m_entity.set_position(position); - } + if (updated) + { + position += dp * k_movement_sensitivity * (window.is_key_pressed(GLFW_KEY_LEFT_CONTROL) ? k_movement_speed_boost : 1.0f) * dt; + m_entity.set_position(position); + } - return updated; + return updated; } bool EntityController::update_rotation() { - if (!is_input_mode_enabled()) - return false; + if (!is_input_mode_enabled()) return false; - GlfwWindow& window = game().get_window(); - float dt = game().get_dt(); + GlfwWindow &window = game().get_window(); + float dt = game().get_dt(); - bool updated = false; + bool updated = false; - glm::vec2 cursor_pos = window.get_cursor_position(); + glm::vec2 cursor_pos = window.get_cursor_position(); - if (m_last_cursor_x && m_last_cursor_y) - { - float yaw = m_entity.get_yaw(); - float pitch = m_entity.get_pitch(); + if (m_last_cursor_x && m_last_cursor_y) + { + float yaw = m_entity.get_yaw(); + float pitch = m_entity.get_pitch(); - float cursor_dx = static_cast(cursor_pos.x - *m_last_cursor_x); - float cursor_dy = static_cast(cursor_pos.y - *m_last_cursor_y); + float cursor_dx = static_cast(cursor_pos.x - *m_last_cursor_x); + float cursor_dy = static_cast(cursor_pos.y - *m_last_cursor_y); - if (cursor_dx || cursor_dy) - { - yaw += cursor_dx * k_rotation_sensitivity * dt; - pitch += -cursor_dy * k_rotation_sensitivity * dt; + if (cursor_dx || cursor_dy) + { + yaw += cursor_dx * k_rotation_sensitivity * dt; + pitch += -cursor_dy * k_rotation_sensitivity * dt; - pitch = glm::clamp(pitch, -glm::pi() / 2.0f, glm::pi() / 2.0f); + pitch = glm::clamp(pitch, -glm::pi() / 2.0f, glm::pi() / 2.0f); - m_entity.set_rotation(yaw, pitch); + m_entity.set_rotation(yaw, pitch); - updated = true; - } - } + updated = true; + } + } - m_last_cursor_x = cursor_pos.x; - m_last_cursor_y = cursor_pos.y; + m_last_cursor_x = cursor_pos.x; + m_last_cursor_y = cursor_pos.y; - return updated; + return updated; } bool EntityController::is_input_mode_enabled() const { - return game().get_window().is_cursor_disabled(); + return game().get_window().is_cursor_disabled(); } diff --git a/src/input/EntityController.hpp b/src/input/EntityController.hpp index 07cc432..c88d1a1 100644 --- a/src/input/EntityController.hpp +++ b/src/input/EntityController.hpp @@ -4,30 +4,30 @@ namespace explo { - // Forward decl - class Entity; + // Forward decl + class Entity; class EntityController { - public: - static constexpr float k_movement_sensitivity = 5.0f; // m/s - static constexpr float k_movement_speed_boost = 10.0f; - static constexpr float k_rotation_sensitivity = 0.1f; + public: + static constexpr float k_movement_sensitivity = 5.0f; // m/s + static constexpr float k_movement_speed_boost = 10.0f; + static constexpr float k_rotation_sensitivity = 0.1f; - private: - Entity& m_entity; + private: + Entity &m_entity; std::optional m_last_cursor_x, m_last_cursor_y; std::optional m_last_timestamp; - public: - explicit EntityController(Entity& entity); + public: + explicit EntityController(Entity &entity); ~EntityController(); - bool update_position(); - bool update_rotation(); + bool update_position(); + bool update_rotation(); - private: - bool is_input_mode_enabled() const; + private: + bool is_input_mode_enabled() const; }; -} +} // namespace explo diff --git a/src/log.hpp b/src/log.hpp index 651ee40..087bfc9 100644 --- a/src/log.hpp +++ b/src/log.hpp @@ -1,91 +1,99 @@ #pragma once -#include +#include +#include + #include -#include +#include #include +#include #include -#include -#include - #define EXPLO_LOG_LEVEL_ERROR 0 -#define EXPLO_LOG_LEVEL_WARN 1 -#define EXPLO_LOG_LEVEL_INFO 2 +#define EXPLO_LOG_LEVEL_WARN 1 +#define EXPLO_LOG_LEVEL_INFO 2 #define EXPLO_LOG_LEVEL_DEBUG 3 #define EXPLO_LOG_LEVEL EXPLO_LOG_LEVEL_DEBUG namespace explo { - namespace internal - { - inline void log_level_prompt(int log_level) - { - switch (log_level) - { - case EXPLO_LOG_LEVEL_ERROR: fmt::print(fmt::fg(fmt::color::indian_red), "ERROR"); break; - case EXPLO_LOG_LEVEL_WARN: fmt::print(fmt::fg(fmt::color::yellow), "WARN"); break; - case EXPLO_LOG_LEVEL_INFO: fmt::print(fmt::fg(fmt::color::white), "INFO"); break; - case EXPLO_LOG_LEVEL_DEBUG: fmt::print(fmt::fg(fmt::color::gray), "DEBUG"); break; - default: - throw std::runtime_error("Illegal argument"); - } - } - } // namespace internal - - template - void log(int log_level, char const* context, char const* format_string, _args_t&&... args) - { - static std::mutex s_mutex; - std::lock_guard lock(s_mutex); - - // [explo] - fmt::print(fmt::fg(fmt::color::aqua), "[explo] "); - - // [context] - if (context && strcmp(context, "")) - { - uint32_t context_color = (std::hash{}(context) % (0xEEEEEE - 0x707070)) + 0x707070; - fmt::text_style context_style = fmt::fg(static_cast(context_color)); - - fmt::print(context_style, "["); - fmt::print(context_style, context); - fmt::print(context_style, "] "); - } - - // LOG_LEVEL: - internal::log_level_prompt(log_level); - fmt::print(": "); - - // Formatted message - fmt::print<_args_t...>(format_string, std::forward<_args_t>(args)...); - fmt::print("\n"); - - fflush(stdout); - } -} // namespace explo + namespace internal + { + inline void log_level_prompt(int log_level) + { + switch (log_level) + { + case EXPLO_LOG_LEVEL_ERROR: + fmt::print(fmt::fg(fmt::color::indian_red), "ERROR"); + break; + case EXPLO_LOG_LEVEL_WARN: + fmt::print(fmt::fg(fmt::color::yellow), "WARN"); + break; + case EXPLO_LOG_LEVEL_INFO: + fmt::print(fmt::fg(fmt::color::white), "INFO"); + break; + case EXPLO_LOG_LEVEL_DEBUG: + fmt::print(fmt::fg(fmt::color::gray), "DEBUG"); + break; + default: + throw std::runtime_error("Illegal argument"); + } + } + } // namespace internal + + template + void log(int log_level, char const *context, char const *format_string, _args_t &&...args) + { + static std::mutex s_mutex; + std::lock_guard lock(s_mutex); + + // [explo] + fmt::print(fmt::fg(fmt::color::aqua), "[explo] "); + + // [context] + if (context && strcmp(context, "")) + { + uint32_t context_color = (std::hash{}(context) % (0xEEEEEE - 0x707070)) + 0x707070; + fmt::text_style context_style = fmt::fg(static_cast(context_color)); + + fmt::print(context_style, "["); + fmt::print(context_style, context); + fmt::print(context_style, "] "); + } + + // LOG_LEVEL: + internal::log_level_prompt(log_level); + fmt::print(": "); + + // Formatted message + fmt::print<_args_t...>(format_string, std::forward<_args_t>(args)...); + fmt::print("\n"); + + fflush(stdout); + } +} // namespace explo #if EXPLO_LOG_LEVEL >= EXPLO_LOG_LEVEL_ERROR -# define LOG_E(context, format, ...) explo::log(EXPLO_LOG_LEVEL_ERROR, context, format, __VA_ARGS__) +#define LOG_E(context, format, ...) explo::log(EXPLO_LOG_LEVEL_ERROR, context, format, __VA_ARGS__) #else -# define LOG_E(context, format, ...) +#define LOG_E(context, format, ...) #endif #if EXPLO_LOG_LEVEL >= EXPLO_LOG_LEVEL_WARN -# define LOG_W(context, format, ...) explo::log(EXPLO_LOG_LEVEL_WARN, context, format, __VA_ARGS__) +#define LOG_W(context, format, ...) explo::log(EXPLO_LOG_LEVEL_WARN, context, format, __VA_ARGS__) #else -# define LOG_W(context, format, ...) +#define LOG_W(context, format, ...) #endif #if EXPLO_LOG_LEVEL >= EXPLO_LOG_LEVEL_INFO -# define LOG_I(context, format, ...) explo::log(EXPLO_LOG_LEVEL_INFO, context, format, __VA_ARGS__) +#define LOG_I(context, format, ...) explo::log(EXPLO_LOG_LEVEL_INFO, context, format, __VA_ARGS__) #else -# define LOG_I(context, format, ...) +#define LOG_I(context, format, ...) #endif #if EXPLO_LOG_LEVEL >= EXPLO_LOG_LEVEL_DEBUG -# define LOG_D(context, format, ...) explo::log(EXPLO_LOG_LEVEL_DEBUG, context, format, __VA_ARGS__) +#define LOG_D(context, format, ...) explo::log(EXPLO_LOG_LEVEL_DEBUG, context, format, __VA_ARGS__) #else -# define LOG_D(context, format, ...) +#define LOG_D(context, format, ...) #endif diff --git a/src/main.cpp b/src/main.cpp index a106e39..089aea3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,67 +1,66 @@ -#include - #include +#include #include -#include "log.hpp" #include "Game.hpp" #include "GlfwWindow.hpp" +#include "log.hpp" using namespace explo; -void glfw_error_callback(int error_code, const char* description) +void glfw_error_callback(int error_code, const char *description) { - fprintf(stderr, "GLFW error (code %d): %s\n", error_code, description); + fprintf(stderr, "GLFW error (code %d): %s\n", error_code, description); } -int main(int argc, char* argv[]) +int main(int argc, char *argv[]) { - setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); - // Init GLFW - glfwSetErrorCallback(glfw_error_callback); + // Init GLFW + glfwSetErrorCallback(glfw_error_callback); - if (!glfwInit()) - { - fprintf(stderr, "Failed to initialize GLFW\n"); - exit(1); - } + if (!glfwInit()) + { + fprintf(stderr, "Failed to initialize GLFW\n"); + exit(1); + } - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); - glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE); + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE); - /* */ + /* */ - std::unique_ptr window = GlfwWindow::create(100, 100, "explo"); - explo::init(*window); + std::unique_ptr window = GlfwWindow::create(100, 100, "explo"); + explo::init(*window); - glm::ivec2 last_window_size = glm::ivec2(-1); + glm::ivec2 last_window_size = glm::ivec2(-1); - while (window->is_opened()) - { - glfwPollEvents(); + while (window->is_opened()) + { + glfwPollEvents(); - glm::ivec2 window_size = window->get_size(); // Handle resize + glm::ivec2 window_size = window->get_size(); // Handle resize - if (window_size.x != last_window_size.x || window_size.y != last_window_size.y) - { - explo::game().on_window_resize(window_size.x, window_size.y); + if (window_size.x != last_window_size.x || window_size.y != last_window_size.y) + { + explo::game().on_window_resize(window_size.x, window_size.y); - last_window_size.x = window_size.x; - last_window_size.y = window_size.y; - } + last_window_size.x = window_size.x; + last_window_size.y = window_size.y; + } - explo::render(); - } + explo::render(); + } - explo::shutdown(); - window.reset(); + explo::shutdown(); + window.reset(); - LOG_I("", "Bye bye!"); + LOG_I("", "Bye bye!"); - glfwTerminate(); + glfwTerminate(); - return 0; + return 0; } diff --git a/src/util/Aabb.cpp b/src/util/Aabb.cpp index 0b6ddfa..67b3a60 100644 --- a/src/util/Aabb.cpp +++ b/src/util/Aabb.cpp @@ -2,26 +2,21 @@ using namespace explo; -Aabb::Aabb(glm::vec3 const& min, glm::vec3 const& max) : - m_min(min), - m_max(max) +Aabb::Aabb(glm::vec3 const &min, glm::vec3 const &max) : + m_min(min), + m_max(max) { } -Aabb::~Aabb() -{ -} +Aabb::~Aabb() {} -bool Aabb::is_inside(glm::vec3 const& point) const +bool Aabb::is_inside(glm::vec3 const &point) const { - return point.x >= m_min.x && point.y >= m_min.y && point.z >= m_min.z && - point.x <= m_max.x && point.y <= m_max.y && point.z <= m_max.z; + return point.x >= m_min.x && point.y >= m_min.y && point.z >= m_min.z && point.x <= m_max.x && point.y <= m_max.y && point.z <= m_max.z; } -bool Aabb::intersect(Aabb const& aabb) const +bool Aabb::intersect(Aabb const &aabb) const { - return - (m_min.x <= aabb.m_max.x && m_max.x >= aabb.m_min.x) && - (m_min.y <= aabb.m_max.y && m_max.y >= aabb.m_min.y) && - (m_min.z <= aabb.m_max.z && m_max.z >= aabb.m_min.z); + return (m_min.x <= aabb.m_max.x && m_max.x >= aabb.m_min.x) && (m_min.y <= aabb.m_max.y && m_max.y >= aabb.m_min.y) && + (m_min.z <= aabb.m_max.z && m_max.z >= aabb.m_min.z); } diff --git a/src/util/Aabb.hpp b/src/util/Aabb.hpp index f641b4f..494c16a 100644 --- a/src/util/Aabb.hpp +++ b/src/util/Aabb.hpp @@ -4,16 +4,16 @@ namespace explo { - struct Aabb - { - glm::vec3 m_min; - glm::vec3 m_max; + struct Aabb + { + glm::vec3 m_min; + glm::vec3 m_max; - explicit Aabb(glm::vec3 const& min, glm::vec3 const& max); - ~Aabb(); + explicit Aabb(glm::vec3 const &min, glm::vec3 const &max); + ~Aabb(); - bool is_inside(glm::vec3 const& point) const; - bool intersect(Aabb const& aabb) const; - }; + bool is_inside(glm::vec3 const &point) const; + bool intersect(Aabb const &aabb) const; + }; -} // namespace explo +} // namespace explo diff --git a/src/util/CircularImage3d.hpp b/src/util/CircularImage3d.hpp index 044956d..2ad68df 100644 --- a/src/util/CircularImage3d.hpp +++ b/src/util/CircularImage3d.hpp @@ -1,47 +1,35 @@ #pragma once -#include "glm/glm.hpp" - #include "Image3d.hpp" +#include "glm/glm.hpp" namespace explo { - /// A 3d image equipped with a start offset for every axis, like a circular buffer. - template - class CircularImage3d - { - private: - Image3d<_T>& m_image_3d; - - glm::ivec3 m_size; - glm::ivec3 m_offset; - - public: - explicit CircularImage3d(Image3d<_T>& image_3d, glm::uvec3 const& size) : - m_image_3d(image_3d), - m_size(size) - {} - ~CircularImage3d() = default; - - _T& get_pixel(glm::ivec3 const& position) const - { - return m_image_3d.get_pixel(to_image_position(position)); - } - - void set_pixel(glm::ivec3 const& position, _T const& value) - { - m_image_3d.set_pixel(to_image_position(position), value); - } - - void shift(glm::ivec3 const& offset) - { - m_offset += offset; - } - - private: - glm::uvec3 to_image_position(glm::ivec3 const& position) - { - return (position + m_offset) % m_size; - } - }; -} // namespace explo + /// A 3d image equipped with a start offset for every axis, like a circular buffer. + template + class CircularImage3d + { + private: + Image3d<_T> &m_image_3d; + + glm::ivec3 m_size; + glm::ivec3 m_offset; + + public: + explicit CircularImage3d(Image3d<_T> &image_3d, glm::uvec3 const &size) : + m_image_3d(image_3d), + m_size(size) + { + } + ~CircularImage3d() = default; + + _T &get_pixel(glm::ivec3 const &position) const { return m_image_3d.get_pixel(to_image_position(position)); } + + void set_pixel(glm::ivec3 const &position, _T const &value) { m_image_3d.set_pixel(to_image_position(position), value); } + + void shift(glm::ivec3 const &offset) { m_offset += offset; } + + private: + glm::uvec3 to_image_position(glm::ivec3 const &position) { return (position + m_offset) % m_size; } + }; +} // namespace explo diff --git a/src/util/Image3d.hpp b/src/util/Image3d.hpp index e948e23..6cb40a0 100644 --- a/src/util/Image3d.hpp +++ b/src/util/Image3d.hpp @@ -1,45 +1,42 @@ #pragma once -#include "glm/glm.hpp" - #include +#include "glm/glm.hpp" + namespace explo { - template - class Image3d - { - protected: - glm::uvec3 m_size; - std::vector<_T> m_image_data; - - public: - explicit Image3d(glm::uvec3 const& size) : - m_size(size) - { - m_image_data.resize(m_size.x * m_size.y * m_size.z); - } - - ~Image3d() = default; - - glm::uvec3 const& get_size() const { return m_size; } - _T const* get_data() const { return m_image_data.data(); } - - uint32_t flatten_3d_index(glm::uvec3 const& position) - { - return position.x * (m_size.x * m_size.y) + position.y * m_size.x + position.z; - } - - _T& get_pixel(glm::uvec3 const& position) const - { - uint32_t i = flatten_3d_index(position); - return m_image_data.at(i); - } - - void set_pixel(glm::uvec3 const& position, _T const& pixel) - { - uint32_t i = flatten_3d_index(position); - m_image_data[i] = pixel; - } - }; -} // namespace explo + template + class Image3d + { + protected: + glm::uvec3 m_size; + std::vector<_T> m_image_data; + + public: + explicit Image3d(glm::uvec3 const &size) : + m_size(size) + { + m_image_data.resize(m_size.x * m_size.y * m_size.z); + } + + ~Image3d() = default; + + glm::uvec3 const &get_size() const { return m_size; } + _T const *get_data() const { return m_image_data.data(); } + + uint32_t flatten_3d_index(glm::uvec3 const &position) { return position.x * (m_size.x * m_size.y) + position.y * m_size.x + position.z; } + + _T &get_pixel(glm::uvec3 const &position) const + { + uint32_t i = flatten_3d_index(position); + return m_image_data.at(i); + } + + void set_pixel(glm::uvec3 const &position, _T const &pixel) + { + uint32_t i = flatten_3d_index(position); + m_image_data[i] = pixel; + } + }; +} // namespace explo diff --git a/src/util/JobChain.cpp b/src/util/JobChain.cpp index aba6e3e..d8cf3e1 100644 --- a/src/util/JobChain.cpp +++ b/src/util/JobChain.cpp @@ -2,43 +2,40 @@ using namespace explo; -JobChain::JobChain() -{ -} +JobChain::JobChain() {} -JobChain::~JobChain() -{ -} +JobChain::~JobChain() {} -JobChain& JobChain::then(JobT const& job) +JobChain &JobChain::then(JobT const &job) { - m_jobs.push_back(job); - return *this; + m_jobs.push_back(job); + return *this; } void JobChain::dispatch() const { - for (JobT const& job : m_jobs) - job(); + for (JobT const &job : m_jobs) job(); } -void enqueue_on_thread_pool(ThreadPool& thread_pool, std::list const& jobs) +void enqueue_on_thread_pool(ThreadPool &thread_pool, std::list const &jobs) { - thread_pool.enqueue_job([&thread_pool, jobs = jobs]() mutable - { - if (!jobs.empty()) - { - JobChain::JobT job = jobs.front(); - jobs.pop_front(); - - job(); - - enqueue_on_thread_pool(thread_pool, jobs); - } - }); + thread_pool.enqueue_job( + [&thread_pool, jobs = jobs]() mutable + { + if (!jobs.empty()) + { + JobChain::JobT job = jobs.front(); + jobs.pop_front(); + + job(); + + enqueue_on_thread_pool(thread_pool, jobs); + } + } + ); } -void JobChain::dispatch(ThreadPool& thread_pool) const +void JobChain::dispatch(ThreadPool &thread_pool) const { - enqueue_on_thread_pool(thread_pool, m_jobs); + enqueue_on_thread_pool(thread_pool, m_jobs); } diff --git a/src/util/JobChain.hpp b/src/util/JobChain.hpp index 79f7724..f37c8e6 100644 --- a/src/util/JobChain.hpp +++ b/src/util/JobChain.hpp @@ -6,24 +6,23 @@ namespace explo { - /// An utility class used to avoid callbacks nesting for sequential jobs dispatch. - class JobChain - { - public: - using JobT = std::function; + /// An utility class used to avoid callbacks nesting for sequential jobs dispatch. + class JobChain + { + public: + using JobT = std::function; - private: - std::list m_jobs; + private: + std::list m_jobs; - public: - explicit JobChain(); - ~JobChain(); + public: + explicit JobChain(); + ~JobChain(); - JobChain& then(JobT const& job); + JobChain &then(JobT const &job); - void dispatch() const; - void dispatch(ThreadPool& thread_pool) const; - }; + void dispatch() const; + void dispatch(ThreadPool &thread_pool) const; + }; - -} // namespace explo +} // namespace explo diff --git a/src/util/PerlinNoise.hpp b/src/util/PerlinNoise.hpp index 9dc3106..51003ba 100644 --- a/src/util/PerlinNoise.hpp +++ b/src/util/PerlinNoise.hpp @@ -11,10 +11,10 @@ // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions : -// +// // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE @@ -25,635 +25,637 @@ // //---------------------------------------------------------------------------------------- -# pragma once -# include -# include -# include -# include -# include -# include -# include - -# if __has_include() && defined(__cpp_concepts) -# include -# endif +#pragma once +#include +#include +#include +#include +#include +#include +#include +#if __has_include() && defined(__cpp_concepts) +#include +#endif // Library major version -# define SIVPERLIN_VERSION_MAJOR 3 +#define SIVPERLIN_VERSION_MAJOR 3 // Library minor version -# define SIVPERLIN_VERSION_MINOR 0 +#define SIVPERLIN_VERSION_MINOR 0 // Library revision version -# define SIVPERLIN_VERSION_REVISION 0 +#define SIVPERLIN_VERSION_REVISION 0 // Library version -# define SIVPERLIN_VERSION ((SIVPERLIN_VERSION_MAJOR * 100 * 100) + (SIVPERLIN_VERSION_MINOR * 100) + (SIVPERLIN_VERSION_REVISION)) - +#define SIVPERLIN_VERSION ((SIVPERLIN_VERSION_MAJOR * 100 * 100) + (SIVPERLIN_VERSION_MINOR * 100) + (SIVPERLIN_VERSION_REVISION)) // [[nodiscard]] for constructors -# if (201907L <= __has_cpp_attribute(nodiscard)) -# define SIVPERLIN_NODISCARD_CXX20 [[nodiscard]] -# else -# define SIVPERLIN_NODISCARD_CXX20 -# endif - +#if (201907L <= __has_cpp_attribute(nodiscard)) +#define SIVPERLIN_NODISCARD_CXX20 [[nodiscard]] +#else +#define SIVPERLIN_NODISCARD_CXX20 +#endif // std::uniform_random_bit_generator concept -# if __cpp_lib_concepts -# define SIVPERLIN_CONCEPT_URBG template -# define SIVPERLIN_CONCEPT_URBG_ template -# else -# define SIVPERLIN_CONCEPT_URBG template , std::is_unsigned>>>* = nullptr> -# define SIVPERLIN_CONCEPT_URBG_ template , std::is_unsigned>>>*> -# endif - +#if __cpp_lib_concepts +#define SIVPERLIN_CONCEPT_URBG template +#define SIVPERLIN_CONCEPT_URBG_ template +#else +#define SIVPERLIN_CONCEPT_URBG \ + template , std::is_unsigned>>> * = nullptr> +#define SIVPERLIN_CONCEPT_URBG_ \ + template , std::is_unsigned>>> *> +#endif // arbitrary value for increasing entropy -# ifndef SIVPERLIN_DEFAULT_Y -# define SIVPERLIN_DEFAULT_Y (0.12345) -# endif +#ifndef SIVPERLIN_DEFAULT_Y +#define SIVPERLIN_DEFAULT_Y (0.12345) +#endif // arbitrary value for increasing entropy -# ifndef SIVPERLIN_DEFAULT_Z -# define SIVPERLIN_DEFAULT_Z (0.34567) -# endif - +#ifndef SIVPERLIN_DEFAULT_Z +#define SIVPERLIN_DEFAULT_Z (0.34567) +#endif namespace siv { - template - class BasicPerlinNoise - { - public: + template + class BasicPerlinNoise + { + public: + static_assert(std::is_floating_point_v); + + /////////////////////////////////////// + // + // Typedefs + // + + using state_type = std::array; + + using value_type = Float; + + using default_random_engine = std::mt19937; - static_assert(std::is_floating_point_v); + using seed_type = typename default_random_engine::result_type; - /////////////////////////////////////// - // - // Typedefs - // + /////////////////////////////////////// + // + // Constructors + // - using state_type = std::array; + SIVPERLIN_NODISCARD_CXX20 + constexpr BasicPerlinNoise() noexcept; - using value_type = Float; + SIVPERLIN_NODISCARD_CXX20 + explicit BasicPerlinNoise(seed_type seed); - using default_random_engine = std::mt19937; + SIVPERLIN_CONCEPT_URBG + SIVPERLIN_NODISCARD_CXX20 + explicit BasicPerlinNoise(URBG &&urbg); - using seed_type = typename default_random_engine::result_type; + /////////////////////////////////////// + // + // Reseed + // - /////////////////////////////////////// - // - // Constructors - // + void reseed(seed_type seed); - SIVPERLIN_NODISCARD_CXX20 - constexpr BasicPerlinNoise() noexcept; + SIVPERLIN_CONCEPT_URBG + void reseed(URBG &&urbg); - SIVPERLIN_NODISCARD_CXX20 - explicit BasicPerlinNoise(seed_type seed); + /////////////////////////////////////// + // + // Serialization + // - SIVPERLIN_CONCEPT_URBG - SIVPERLIN_NODISCARD_CXX20 - explicit BasicPerlinNoise(URBG&& urbg); + [[nodiscard]] constexpr const state_type &serialize() const noexcept; - /////////////////////////////////////// - // - // Reseed - // + constexpr void deserialize(const state_type &state) noexcept; - void reseed(seed_type seed); + /////////////////////////////////////// + // + // Noise (The result is in the range [-1, 1]) + // - SIVPERLIN_CONCEPT_URBG - void reseed(URBG&& urbg); + [[nodiscard]] value_type noise1D(value_type x) const noexcept; - /////////////////////////////////////// - // - // Serialization - // + [[nodiscard]] value_type noise2D(value_type x, value_type y) const noexcept; - [[nodiscard]] - constexpr const state_type& serialize() const noexcept; + [[nodiscard]] value_type noise3D(value_type x, value_type y, value_type z) const noexcept; - constexpr void deserialize(const state_type& state) noexcept; + /////////////////////////////////////// + // + // Noise (The result is remapped to the range [0, 1]) + // - /////////////////////////////////////// - // - // Noise (The result is in the range [-1, 1]) - // + [[nodiscard]] value_type noise1D_01(value_type x) const noexcept; - [[nodiscard]] - value_type noise1D(value_type x) const noexcept; + [[nodiscard]] value_type noise2D_01(value_type x, value_type y) const noexcept; - [[nodiscard]] - value_type noise2D(value_type x, value_type y) const noexcept; + [[nodiscard]] value_type noise3D_01(value_type x, value_type y, value_type z) const noexcept; - [[nodiscard]] - value_type noise3D(value_type x, value_type y, value_type z) const noexcept; + /////////////////////////////////////// + // + // Octave noise (The result can be out of the range [-1, 1]) + // - /////////////////////////////////////// - // - // Noise (The result is remapped to the range [0, 1]) - // + [[nodiscard]] value_type octave1D(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; - [[nodiscard]] - value_type noise1D_01(value_type x) const noexcept; + [[nodiscard]] value_type octave2D(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; - [[nodiscard]] - value_type noise2D_01(value_type x, value_type y) const noexcept; + [[nodiscard]] value_type octave3D(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) + const noexcept; - [[nodiscard]] - value_type noise3D_01(value_type x, value_type y, value_type z) const noexcept; + /////////////////////////////////////// + // + // Octave noise (The result is clamped to the range [-1, 1]) + // - /////////////////////////////////////// - // - // Octave noise (The result can be out of the range [-1, 1]) - // + [[nodiscard]] value_type octave1D_11(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; - [[nodiscard]] - value_type octave1D(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + [[nodiscard]] value_type octave2D_11(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) + const noexcept; - [[nodiscard]] - value_type octave2D(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + [[nodiscard]] value_type octave3D_11(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) + const noexcept; - [[nodiscard]] - value_type octave3D(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + /////////////////////////////////////// + // + // Octave noise (The result is clamped and remapped to the range [0, 1]) + // - /////////////////////////////////////// - // - // Octave noise (The result is clamped to the range [-1, 1]) - // + [[nodiscard]] value_type octave1D_01(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; - [[nodiscard]] - value_type octave1D_11(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + [[nodiscard]] value_type octave2D_01(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) + const noexcept; - [[nodiscard]] - value_type octave2D_11(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + [[nodiscard]] value_type octave3D_01(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) + const noexcept; - [[nodiscard]] - value_type octave3D_11(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + /////////////////////////////////////// + // + // Octave noise (The result is normalized to the range [-1, 1]) + // - /////////////////////////////////////// - // - // Octave noise (The result is clamped and remapped to the range [0, 1]) - // + [[nodiscard]] value_type normalizedOctave1D(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + + [[nodiscard]] value_type normalizedOctave2D(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) + const noexcept; + + [[nodiscard]] value_type normalizedOctave3D( + value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5) + ) const noexcept; - [[nodiscard]] - value_type octave1D_01(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; - - [[nodiscard]] - value_type octave2D_01(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; - - [[nodiscard]] - value_type octave3D_01(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; - - /////////////////////////////////////// - // - // Octave noise (The result is normalized to the range [-1, 1]) - // - - [[nodiscard]] - value_type normalizedOctave1D(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; - - [[nodiscard]] - value_type normalizedOctave2D(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; - - [[nodiscard]] - value_type normalizedOctave3D(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; - - /////////////////////////////////////// - // - // Octave noise (The result is normalized and remapped to the range [0, 1]) - // - - [[nodiscard]] - value_type normalizedOctave1D_01(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; - - [[nodiscard]] - value_type normalizedOctave2D_01(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; - - [[nodiscard]] - value_type normalizedOctave3D_01(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; - - private: - - state_type m_permutation; - }; - - using PerlinNoise = BasicPerlinNoise; - - namespace perlin_detail - { - //////////////////////////////////////////////// - // - // These functions are provided for consistency. - // You may get different results from std::shuffle() with different standard library implementations. - // - SIVPERLIN_CONCEPT_URBG - [[nodiscard]] - inline std::uint64_t Random(const std::uint64_t max, URBG&& urbg) - { - return (urbg() % (max + 1)); - } - - template - inline void Shuffle(RandomIt first, RandomIt last, URBG&& urbg) - { - if (first == last) - { - return; - } - - using difference_type = typename std::iterator_traits::difference_type; - - for (RandomIt it = first + 1; it < last; ++it) - { - const std::uint64_t n = static_cast(it - first); - std::iter_swap(it, first + static_cast(Random(n, std::forward(urbg)))); - } - } - // - //////////////////////////////////////////////// - - template - [[nodiscard]] - inline constexpr Float Fade(const Float t) noexcept - { - return t * t * t * (t * (t * 6 - 15) + 10); - } - - template - [[nodiscard]] - inline constexpr Float Lerp(const Float a, const Float b, const Float t) noexcept - { - return (a + (b - a) * t); - } - - template - [[nodiscard]] - inline constexpr Float Grad(const std::uint8_t hash, const Float x, const Float y, const Float z) noexcept - { - const std::uint8_t h = hash & 15; - const Float u = h < 8 ? x : y; - const Float v = h < 4 ? y : h == 12 || h == 14 ? x : z; - return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); - } - - template - [[nodiscard]] - inline constexpr Float Remap_01(const Float x) noexcept - { - return (x * Float(0.5) + Float(0.5)); - } - - template - [[nodiscard]] - inline constexpr Float Clamp_11(const Float x) noexcept - { - return std::clamp(x, Float(-1.0), Float(1.0)); - } - - template - [[nodiscard]] - inline constexpr Float RemapClamp_01(const Float x) noexcept - { - if (x <= Float(-1.0)) - { - return Float(0.0); - } - else if (Float(1.0) <= x) - { - return Float(1.0); - } - - return (x * Float(0.5) + Float(0.5)); - } - - template - [[nodiscard]] - inline auto Octave1D(const Noise& noise, Float x, const std::int32_t octaves, const Float persistence) noexcept - { - using value_type = Float; - value_type result = 0; - value_type amplitude = 1; - - for (std::int32_t i = 0; i < octaves; ++i) - { - result += (noise.noise1D(x) * amplitude); - x *= 2; - amplitude *= persistence; - } - - return result; - } - - template - [[nodiscard]] - inline auto Octave2D(const Noise& noise, Float x, Float y, const std::int32_t octaves, const Float persistence) noexcept - { - using value_type = Float; - value_type result = 0; - value_type amplitude = 1; - - for (std::int32_t i = 0; i < octaves; ++i) - { - result += (noise.noise2D(x, y) * amplitude); - x *= 2; - y *= 2; - amplitude *= persistence; - } - - return result; - } - - template - [[nodiscard]] - inline auto Octave3D(const Noise& noise, Float x, Float y, Float z, const std::int32_t octaves, const Float persistence) noexcept - { - using value_type = Float; - value_type result = 0; - value_type amplitude = 1; - - for (std::int32_t i = 0; i < octaves; ++i) - { - result += (noise.noise3D(x, y, z) * amplitude); - x *= 2; - y *= 2; - z *= 2; - amplitude *= persistence; - } - - return result; - } - - template - [[nodiscard]] - inline constexpr Float MaxAmplitude(const std::int32_t octaves, const Float persistence) noexcept - { - using value_type = Float; - value_type result = 0; - value_type amplitude = 1; - - for (std::int32_t i = 0; i < octaves; ++i) - { - result += amplitude; - amplitude *= persistence; - } - - return result; - } - } - - /////////////////////////////////////// - - template - inline constexpr BasicPerlinNoise::BasicPerlinNoise() noexcept - : m_permutation{ 151,160,137,91,90,15, - 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, - 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, - 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, - 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, - 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, - 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, - 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, - 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, - 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, - 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, - 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, - 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 } {} - - template - inline BasicPerlinNoise::BasicPerlinNoise(const seed_type seed) - { - reseed(seed); - } - - template - SIVPERLIN_CONCEPT_URBG_ - inline BasicPerlinNoise::BasicPerlinNoise(URBG&& urbg) - { - reseed(std::forward(urbg)); - } - - /////////////////////////////////////// - - template - inline void BasicPerlinNoise::reseed(const seed_type seed) - { - reseed(default_random_engine{ seed }); - } - - template - SIVPERLIN_CONCEPT_URBG_ - inline void BasicPerlinNoise::reseed(URBG&& urbg) - { - std::iota(m_permutation.begin(), m_permutation.end(), uint8_t{ 0 }); - - perlin_detail::Shuffle(m_permutation.begin(), m_permutation.end(), std::forward(urbg)); - } - - /////////////////////////////////////// - - template - inline constexpr const typename BasicPerlinNoise::state_type& BasicPerlinNoise::serialize() const noexcept - { - return m_permutation; - } - - template - inline constexpr void BasicPerlinNoise::deserialize(const state_type& state) noexcept - { - m_permutation = state; - } - - /////////////////////////////////////// - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise1D(const value_type x) const noexcept - { - return noise3D(x, - static_cast(SIVPERLIN_DEFAULT_Y), - static_cast(SIVPERLIN_DEFAULT_Z)); - } - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise2D(const value_type x, const value_type y) const noexcept - { - return noise3D(x, - y, - static_cast(SIVPERLIN_DEFAULT_Z)); - } - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise3D(const value_type x, const value_type y, const value_type z) const noexcept - { - const value_type _x = std::floor(x); - const value_type _y = std::floor(y); - const value_type _z = std::floor(z); - - const std::int32_t ix = static_cast(_x) & 255; - const std::int32_t iy = static_cast(_y) & 255; - const std::int32_t iz = static_cast(_z) & 255; - - const value_type fx = (x - _x); - const value_type fy = (y - _y); - const value_type fz = (z - _z); - - const value_type u = perlin_detail::Fade(fx); - const value_type v = perlin_detail::Fade(fy); - const value_type w = perlin_detail::Fade(fz); - - const std::uint8_t A = (m_permutation[ix & 255] + iy) & 255; - const std::uint8_t B = (m_permutation[(ix + 1) & 255] + iy) & 255; - - const std::uint8_t AA = (m_permutation[A] + iz) & 255; - const std::uint8_t AB = (m_permutation[(A + 1) & 255] + iz) & 255; - - const std::uint8_t BA = (m_permutation[B] + iz) & 255; - const std::uint8_t BB = (m_permutation[(B + 1) & 255] + iz) & 255; - - const value_type p0 = perlin_detail::Grad(m_permutation[AA], fx, fy, fz); - const value_type p1 = perlin_detail::Grad(m_permutation[BA], fx - 1, fy, fz); - const value_type p2 = perlin_detail::Grad(m_permutation[AB], fx, fy - 1, fz); - const value_type p3 = perlin_detail::Grad(m_permutation[BB], fx - 1, fy - 1, fz); - const value_type p4 = perlin_detail::Grad(m_permutation[(AA + 1) & 255], fx, fy, fz - 1); - const value_type p5 = perlin_detail::Grad(m_permutation[(BA + 1) & 255], fx - 1, fy, fz - 1); - const value_type p6 = perlin_detail::Grad(m_permutation[(AB + 1) & 255], fx, fy - 1, fz - 1); - const value_type p7 = perlin_detail::Grad(m_permutation[(BB + 1) & 255], fx - 1, fy - 1, fz - 1); - - const value_type q0 = perlin_detail::Lerp(p0, p1, u); - const value_type q1 = perlin_detail::Lerp(p2, p3, u); - const value_type q2 = perlin_detail::Lerp(p4, p5, u); - const value_type q3 = perlin_detail::Lerp(p6, p7, u); - - const value_type r0 = perlin_detail::Lerp(q0, q1, v); - const value_type r1 = perlin_detail::Lerp(q2, q3, v); - - return perlin_detail::Lerp(r0, r1, w); - } - - /////////////////////////////////////// - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise1D_01(const value_type x) const noexcept - { - return perlin_detail::Remap_01(noise1D(x)); - } - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise2D_01(const value_type x, const value_type y) const noexcept - { - return perlin_detail::Remap_01(noise2D(x, y)); - } - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise3D_01(const value_type x, const value_type y, const value_type z) const noexcept - { - return perlin_detail::Remap_01(noise3D(x, y, z)); - } - - /////////////////////////////////////// - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave1D(const value_type x, const std::int32_t octaves, const value_type persistence) const noexcept - { - return perlin_detail::Octave1D(*this, x, octaves, persistence); - } - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave2D(const value_type x, const value_type y, const std::int32_t octaves, const value_type persistence) const noexcept - { - return perlin_detail::Octave2D(*this, x, y, octaves, persistence); - } - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave3D(const value_type x, const value_type y, const value_type z, const std::int32_t octaves, const value_type persistence) const noexcept - { - return perlin_detail::Octave3D(*this, x, y, z, octaves, persistence); - } - - /////////////////////////////////////// - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave1D_11(const value_type x, const std::int32_t octaves, const value_type persistence) const noexcept - { - return perlin_detail::Clamp_11(octave1D(x, octaves, persistence)); - } - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave2D_11(const value_type x, const value_type y, const std::int32_t octaves, const value_type persistence) const noexcept - { - return perlin_detail::Clamp_11(octave2D(x, y, octaves, persistence)); - } - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave3D_11(const value_type x, const value_type y, const value_type z, const std::int32_t octaves, const value_type persistence) const noexcept - { - return perlin_detail::Clamp_11(octave3D(x, y, z, octaves, persistence)); - } - - /////////////////////////////////////// - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave1D_01(const value_type x, const std::int32_t octaves, const value_type persistence) const noexcept - { - return perlin_detail::RemapClamp_01(octave1D(x, octaves, persistence)); - } - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave2D_01(const value_type x, const value_type y, const std::int32_t octaves, const value_type persistence) const noexcept - { - return perlin_detail::RemapClamp_01(octave2D(x, y, octaves, persistence)); - } - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave3D_01(const value_type x, const value_type y, const value_type z, const std::int32_t octaves, const value_type persistence) const noexcept - { - return perlin_detail::RemapClamp_01(octave3D(x, y, z, octaves, persistence)); - } - - /////////////////////////////////////// - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave1D(const value_type x, const std::int32_t octaves, const value_type persistence) const noexcept - { - return (octave1D(x, octaves, persistence) / perlin_detail::MaxAmplitude(octaves, persistence)); - } - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave2D(const value_type x, const value_type y, const std::int32_t octaves, const value_type persistence) const noexcept - { - return (octave2D(x, y, octaves, persistence) / perlin_detail::MaxAmplitude(octaves, persistence)); - } - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave3D(const value_type x, const value_type y, const value_type z, const std::int32_t octaves, const value_type persistence) const noexcept - { - return (octave3D(x, y, z, octaves, persistence) / perlin_detail::MaxAmplitude(octaves, persistence)); - } - - /////////////////////////////////////// - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave1D_01(const value_type x, const std::int32_t octaves, const value_type persistence) const noexcept - { - return perlin_detail::Remap_01(normalizedOctave1D(x, octaves, persistence)); - } - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave2D_01(const value_type x, const value_type y, const std::int32_t octaves, const value_type persistence) const noexcept - { - return perlin_detail::Remap_01(normalizedOctave2D(x, y, octaves, persistence)); - } - - template - inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave3D_01(const value_type x, const value_type y, const value_type z, const std::int32_t octaves, const value_type persistence) const noexcept - { - return perlin_detail::Remap_01(normalizedOctave3D(x, y, z, octaves, persistence)); - } -} - -# undef SIVPERLIN_NODISCARD_CXX20 -# undef SIVPERLIN_CONCEPT_URBG -# undef SIVPERLIN_CONCEPT_URBG_ + /////////////////////////////////////// + // + // Octave noise (The result is normalized and remapped to the range [0, 1]) + // + + [[nodiscard]] value_type normalizedOctave1D_01(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + + [[nodiscard]] value_type normalizedOctave2D_01(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) + const noexcept; + + [[nodiscard]] value_type normalizedOctave3D_01( + value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5) + ) const noexcept; + + private: + state_type m_permutation; + }; + + using PerlinNoise = BasicPerlinNoise; + + namespace perlin_detail + { + //////////////////////////////////////////////// + // + // These functions are provided for consistency. + // You may get different results from std::shuffle() with different standard library implementations. + // + SIVPERLIN_CONCEPT_URBG + [[nodiscard]] inline std::uint64_t Random(const std::uint64_t max, URBG &&urbg) + { + return (urbg() % (max + 1)); + } + + template + inline void Shuffle(RandomIt first, RandomIt last, URBG &&urbg) + { + if (first == last) + { + return; + } + + using difference_type = typename std::iterator_traits::difference_type; + + for (RandomIt it = first + 1; it < last; ++it) + { + const std::uint64_t n = static_cast(it - first); + std::iter_swap(it, first + static_cast(Random(n, std::forward(urbg)))); + } + } + // + //////////////////////////////////////////////// + + template + [[nodiscard]] inline constexpr Float Fade(const Float t) noexcept + { + return t * t * t * (t * (t * 6 - 15) + 10); + } + + template + [[nodiscard]] inline constexpr Float Lerp(const Float a, const Float b, const Float t) noexcept + { + return (a + (b - a) * t); + } + + template + [[nodiscard]] inline constexpr Float Grad(const std::uint8_t hash, const Float x, const Float y, const Float z) noexcept + { + const std::uint8_t h = hash & 15; + const Float u = h < 8 ? x : y; + const Float v = h < 4 ? y : h == 12 || h == 14 ? x : z; + return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); + } + + template + [[nodiscard]] inline constexpr Float Remap_01(const Float x) noexcept + { + return (x * Float(0.5) + Float(0.5)); + } + + template + [[nodiscard]] inline constexpr Float Clamp_11(const Float x) noexcept + { + return std::clamp(x, Float(-1.0), Float(1.0)); + } + + template + [[nodiscard]] inline constexpr Float RemapClamp_01(const Float x) noexcept + { + if (x <= Float(-1.0)) + { + return Float(0.0); + } + else if (Float(1.0) <= x) + { + return Float(1.0); + } + + return (x * Float(0.5) + Float(0.5)); + } + + template + [[nodiscard]] inline auto Octave1D(const Noise &noise, Float x, const std::int32_t octaves, const Float persistence) noexcept + { + using value_type = Float; + value_type result = 0; + value_type amplitude = 1; + + for (std::int32_t i = 0; i < octaves; ++i) + { + result += (noise.noise1D(x) * amplitude); + x *= 2; + amplitude *= persistence; + } + + return result; + } + + template + [[nodiscard]] inline auto Octave2D(const Noise &noise, Float x, Float y, const std::int32_t octaves, const Float persistence) noexcept + { + using value_type = Float; + value_type result = 0; + value_type amplitude = 1; + + for (std::int32_t i = 0; i < octaves; ++i) + { + result += (noise.noise2D(x, y) * amplitude); + x *= 2; + y *= 2; + amplitude *= persistence; + } + + return result; + } + + template + [[nodiscard]] inline auto Octave3D( + const Noise &noise, Float x, Float y, Float z, const std::int32_t octaves, const Float persistence + ) noexcept + { + using value_type = Float; + value_type result = 0; + value_type amplitude = 1; + + for (std::int32_t i = 0; i < octaves; ++i) + { + result += (noise.noise3D(x, y, z) * amplitude); + x *= 2; + y *= 2; + z *= 2; + amplitude *= persistence; + } + + return result; + } + + template + [[nodiscard]] inline constexpr Float MaxAmplitude(const std::int32_t octaves, const Float persistence) noexcept + { + using value_type = Float; + value_type result = 0; + value_type amplitude = 1; + + for (std::int32_t i = 0; i < octaves; ++i) + { + result += amplitude; + amplitude *= persistence; + } + + return result; + } + } // namespace perlin_detail + + /////////////////////////////////////// + + template + inline constexpr BasicPerlinNoise::BasicPerlinNoise() noexcept : + m_permutation{151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, + 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, + 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, + 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, + 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, + 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, + 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, + 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, + 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, + 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, + 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180} + { + } + + template + inline BasicPerlinNoise::BasicPerlinNoise(const seed_type seed) + { + reseed(seed); + } + + template + SIVPERLIN_CONCEPT_URBG_ inline BasicPerlinNoise::BasicPerlinNoise(URBG &&urbg) + { + reseed(std::forward(urbg)); + } + + /////////////////////////////////////// + + template + inline void BasicPerlinNoise::reseed(const seed_type seed) + { + reseed(default_random_engine{seed}); + } + + template + SIVPERLIN_CONCEPT_URBG_ inline void BasicPerlinNoise::reseed(URBG &&urbg) + { + std::iota(m_permutation.begin(), m_permutation.end(), uint8_t{0}); + + perlin_detail::Shuffle(m_permutation.begin(), m_permutation.end(), std::forward(urbg)); + } + + /////////////////////////////////////// + + template + inline constexpr const typename BasicPerlinNoise::state_type &BasicPerlinNoise::serialize() const noexcept + { + return m_permutation; + } + + template + inline constexpr void BasicPerlinNoise::deserialize(const state_type &state) noexcept + { + m_permutation = state; + } + + /////////////////////////////////////// + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise1D(const value_type x) const noexcept + { + return noise3D(x, static_cast(SIVPERLIN_DEFAULT_Y), static_cast(SIVPERLIN_DEFAULT_Z)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise2D(const value_type x, const value_type y) const noexcept + { + return noise3D(x, y, static_cast(SIVPERLIN_DEFAULT_Z)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise3D(const value_type x, const value_type y, const value_type z) + const noexcept + { + const value_type _x = std::floor(x); + const value_type _y = std::floor(y); + const value_type _z = std::floor(z); + + const std::int32_t ix = static_cast(_x) & 255; + const std::int32_t iy = static_cast(_y) & 255; + const std::int32_t iz = static_cast(_z) & 255; + + const value_type fx = (x - _x); + const value_type fy = (y - _y); + const value_type fz = (z - _z); + + const value_type u = perlin_detail::Fade(fx); + const value_type v = perlin_detail::Fade(fy); + const value_type w = perlin_detail::Fade(fz); + + const std::uint8_t A = (m_permutation[ix & 255] + iy) & 255; + const std::uint8_t B = (m_permutation[(ix + 1) & 255] + iy) & 255; + + const std::uint8_t AA = (m_permutation[A] + iz) & 255; + const std::uint8_t AB = (m_permutation[(A + 1) & 255] + iz) & 255; + + const std::uint8_t BA = (m_permutation[B] + iz) & 255; + const std::uint8_t BB = (m_permutation[(B + 1) & 255] + iz) & 255; + + const value_type p0 = perlin_detail::Grad(m_permutation[AA], fx, fy, fz); + const value_type p1 = perlin_detail::Grad(m_permutation[BA], fx - 1, fy, fz); + const value_type p2 = perlin_detail::Grad(m_permutation[AB], fx, fy - 1, fz); + const value_type p3 = perlin_detail::Grad(m_permutation[BB], fx - 1, fy - 1, fz); + const value_type p4 = perlin_detail::Grad(m_permutation[(AA + 1) & 255], fx, fy, fz - 1); + const value_type p5 = perlin_detail::Grad(m_permutation[(BA + 1) & 255], fx - 1, fy, fz - 1); + const value_type p6 = perlin_detail::Grad(m_permutation[(AB + 1) & 255], fx, fy - 1, fz - 1); + const value_type p7 = perlin_detail::Grad(m_permutation[(BB + 1) & 255], fx - 1, fy - 1, fz - 1); + + const value_type q0 = perlin_detail::Lerp(p0, p1, u); + const value_type q1 = perlin_detail::Lerp(p2, p3, u); + const value_type q2 = perlin_detail::Lerp(p4, p5, u); + const value_type q3 = perlin_detail::Lerp(p6, p7, u); + + const value_type r0 = perlin_detail::Lerp(q0, q1, v); + const value_type r1 = perlin_detail::Lerp(q2, q3, v); + + return perlin_detail::Lerp(r0, r1, w); + } + + /////////////////////////////////////// + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise1D_01(const value_type x) const noexcept + { + return perlin_detail::Remap_01(noise1D(x)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise2D_01(const value_type x, const value_type y) const noexcept + { + return perlin_detail::Remap_01(noise2D(x, y)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise3D_01( + const value_type x, const value_type y, const value_type z + ) const noexcept + { + return perlin_detail::Remap_01(noise3D(x, y, z)); + } + + /////////////////////////////////////// + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave1D( + const value_type x, const std::int32_t octaves, const value_type persistence + ) const noexcept + { + return perlin_detail::Octave1D(*this, x, octaves, persistence); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave2D( + const value_type x, const value_type y, const std::int32_t octaves, const value_type persistence + ) const noexcept + { + return perlin_detail::Octave2D(*this, x, y, octaves, persistence); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave3D( + const value_type x, const value_type y, const value_type z, const std::int32_t octaves, const value_type persistence + ) const noexcept + { + return perlin_detail::Octave3D(*this, x, y, z, octaves, persistence); + } + + /////////////////////////////////////// + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave1D_11( + const value_type x, const std::int32_t octaves, const value_type persistence + ) const noexcept + { + return perlin_detail::Clamp_11(octave1D(x, octaves, persistence)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave2D_11( + const value_type x, const value_type y, const std::int32_t octaves, const value_type persistence + ) const noexcept + { + return perlin_detail::Clamp_11(octave2D(x, y, octaves, persistence)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave3D_11( + const value_type x, const value_type y, const value_type z, const std::int32_t octaves, const value_type persistence + ) const noexcept + { + return perlin_detail::Clamp_11(octave3D(x, y, z, octaves, persistence)); + } + + /////////////////////////////////////// + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave1D_01( + const value_type x, const std::int32_t octaves, const value_type persistence + ) const noexcept + { + return perlin_detail::RemapClamp_01(octave1D(x, octaves, persistence)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave2D_01( + const value_type x, const value_type y, const std::int32_t octaves, const value_type persistence + ) const noexcept + { + return perlin_detail::RemapClamp_01(octave2D(x, y, octaves, persistence)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave3D_01( + const value_type x, const value_type y, const value_type z, const std::int32_t octaves, const value_type persistence + ) const noexcept + { + return perlin_detail::RemapClamp_01(octave3D(x, y, z, octaves, persistence)); + } + + /////////////////////////////////////// + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave1D( + const value_type x, const std::int32_t octaves, const value_type persistence + ) const noexcept + { + return (octave1D(x, octaves, persistence) / perlin_detail::MaxAmplitude(octaves, persistence)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave2D( + const value_type x, const value_type y, const std::int32_t octaves, const value_type persistence + ) const noexcept + { + return (octave2D(x, y, octaves, persistence) / perlin_detail::MaxAmplitude(octaves, persistence)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave3D( + const value_type x, const value_type y, const value_type z, const std::int32_t octaves, const value_type persistence + ) const noexcept + { + return (octave3D(x, y, z, octaves, persistence) / perlin_detail::MaxAmplitude(octaves, persistence)); + } + + /////////////////////////////////////// + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave1D_01( + const value_type x, const std::int32_t octaves, const value_type persistence + ) const noexcept + { + return perlin_detail::Remap_01(normalizedOctave1D(x, octaves, persistence)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave2D_01( + const value_type x, const value_type y, const std::int32_t octaves, const value_type persistence + ) const noexcept + { + return perlin_detail::Remap_01(normalizedOctave2D(x, y, octaves, persistence)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave3D_01( + const value_type x, const value_type y, const value_type z, const std::int32_t octaves, const value_type persistence + ) const noexcept + { + return perlin_detail::Remap_01(normalizedOctave3D(x, y, z, octaves, persistence)); + } +} // namespace siv + +#undef SIVPERLIN_NODISCARD_CXX20 +#undef SIVPERLIN_CONCEPT_URBG +#undef SIVPERLIN_CONCEPT_URBG_ diff --git a/src/util/SyncJobExecutor.cpp b/src/util/SyncJobExecutor.cpp index 6d03bd4..6ec39b1 100644 --- a/src/util/SyncJobExecutor.cpp +++ b/src/util/SyncJobExecutor.cpp @@ -4,34 +4,31 @@ using namespace explo; -SyncJobExecutor::SyncJobExecutor() -{ -} +SyncJobExecutor::SyncJobExecutor() {} size_t SyncJobExecutor::get_job_count() const { - std::lock_guard lock(m_mutex); + std::lock_guard lock(m_mutex); - return m_jobs.size(); + return m_jobs.size(); } -void SyncJobExecutor::enqueue_job(JobT const& job) +void SyncJobExecutor::enqueue_job(JobT const &job) { - std::lock_guard lock(m_mutex); + std::lock_guard lock(m_mutex); - m_jobs.push_back(job); + m_jobs.push_back(job); } void SyncJobExecutor::process() { - // Iterates for the jobs that were enqueued just before process() was called. This not to iterate jobs that could be - // enqueued by a job itself - std::deque jobs; - { - std::lock_guard lock(m_mutex); - jobs = std::move(m_jobs); - } - - for (JobT const& job : jobs) - job(); + // Iterates for the jobs that were enqueued just before process() was called. This not to iterate jobs that could be + // enqueued by a job itself + std::deque jobs; + { + std::lock_guard lock(m_mutex); + jobs = std::move(m_jobs); + } + + for (JobT const &job : jobs) job(); } diff --git a/src/util/SyncJobExecutor.hpp b/src/util/SyncJobExecutor.hpp index 81b79da..580028d 100644 --- a/src/util/SyncJobExecutor.hpp +++ b/src/util/SyncJobExecutor.hpp @@ -6,24 +6,24 @@ namespace explo { - class SyncJobExecutor - { - public: - using JobT = std::function; + class SyncJobExecutor + { + public: + using JobT = std::function; - private: - std::deque m_jobs; + private: + std::deque m_jobs; - mutable std::mutex m_mutex; + mutable std::mutex m_mutex; - public: - explicit SyncJobExecutor(); - ~SyncJobExecutor() = default; + public: + explicit SyncJobExecutor(); + ~SyncJobExecutor() = default; - size_t get_job_count() const; + size_t get_job_count() const; - void enqueue_job(JobT const& job); + void enqueue_job(JobT const &job); - void process(); - }; -} // namespace explo + void process(); + }; +} // namespace explo diff --git a/src/util/ThreadPool.cpp b/src/util/ThreadPool.cpp index 21afa1a..b997c9f 100644 --- a/src/util/ThreadPool.cpp +++ b/src/util/ThreadPool.cpp @@ -9,16 +9,23 @@ ThreadPool::ThreadPool(size_t num_threads) m_threads.reserve(num_threads); for (size_t thread_id = 0; thread_id < num_threads; thread_id++) - { - m_threads.emplace_back([this, thread_id] { thread_loop(thread_id); }); - } + { + m_threads.emplace_back( + [this, thread_id] + { + thread_loop(thread_id); + } + ); + } } ThreadPool::ThreadPool() : - ThreadPool(std::thread::hardware_concurrency() > 0 ? std::thread::hardware_concurrency() : 8) -{} + ThreadPool(std::thread::hardware_concurrency() > 0 ? std::thread::hardware_concurrency() : 8) +{ +} -ThreadPool::~ThreadPool() { +ThreadPool::~ThreadPool() +{ { std::lock_guard lock(m_mutex); m_should_terminate = true; @@ -26,84 +33,88 @@ ThreadPool::~ThreadPool() { m_condition_variable.notify_all(); - for (std::thread& thread: m_threads) - thread.join(); + for (std::thread &thread : m_threads) thread.join(); } size_t ThreadPool::get_thread_count() const { - return m_threads.size(); + return m_threads.size(); } bool ThreadPool::is_thread_working(size_t thread_id) { - // Here we use the same mutex used to synchronize the queue. We could improve it by locking on a different mutex - std::lock_guard lock(m_mutex); + // Here we use the same mutex used to synchronize the queue. We could improve it by locking on a different mutex + std::lock_guard lock(m_mutex); - return m_thread_working.test(thread_id); + return m_thread_working.test(thread_id); } size_t ThreadPool::get_job_count() { - std::lock_guard lock(m_mutex); + std::lock_guard lock(m_mutex); - return m_jobs.size(); + return m_jobs.size(); } -size_t ThreadPool::enqueue_job(std::function const& job) +size_t ThreadPool::enqueue_job(std::function const &job) { - std::lock_guard lock(m_mutex); + std::lock_guard lock(m_mutex); - size_t job_id = m_next_job_id++; - m_jobs.push_back(job); + size_t job_id = m_next_job_id++; + m_jobs.push_back(job); - m_condition_variable.notify_all(); + m_condition_variable.notify_all(); - return job_id; + return job_id; } void ThreadPool::drop_job(size_t job_id) { - std::lock_guard lock(m_mutex); - m_jobs.erase(m_jobs.begin() + job_id); + std::lock_guard lock(m_mutex); + m_jobs.erase(m_jobs.begin() + job_id); } void ThreadPool::drain() { std::unique_lock lock(m_mutex); - m_drained_condition.wait(lock, [this] { - return m_jobs.empty(); - }); + m_drained_condition.wait( + lock, + [this] + { + return m_jobs.empty(); + } + ); } void ThreadPool::thread_loop(size_t thread_id) { - while (true) - { - std::function job; - { - std::unique_lock lock(m_mutex); + while (true) + { + std::function job; + { + std::unique_lock lock(m_mutex); - m_thread_working[thread_id] = false; + m_thread_working[thread_id] = false; - // If after executing the job we find out that there are no jobs left, we signal that the jobs queue is drained - if (m_jobs.empty()) - m_drained_condition.notify_all(); + // If after executing the job we find out that there are no jobs left, we signal that the jobs queue is drained + if (m_jobs.empty()) m_drained_condition.notify_all(); - m_condition_variable.wait(lock, [this] { - return m_jobs.size() > 0; - }); + m_condition_variable.wait( + lock, + [this] + { + return m_jobs.size() > 0; + } + ); - if (m_should_terminate) - return; + if (m_should_terminate) return; - job = m_jobs.front(); - m_jobs.pop_front(); + job = m_jobs.front(); + m_jobs.pop_front(); - m_thread_working[thread_id] = true; - } + m_thread_working[thread_id] = true; + } - job(); - } + job(); + } } - diff --git a/src/util/ThreadPool.hpp b/src/util/ThreadPool.hpp index 2556851..2f67e23 100644 --- a/src/util/ThreadPool.hpp +++ b/src/util/ThreadPool.hpp @@ -1,49 +1,49 @@ #pragma once +#include +#include #include -#include #include -#include #include -#include -#include +#include +#include namespace explo { - class ThreadPool - { - public: - inline static constexpr size_t k_max_threads = 32; + class ThreadPool + { + public: + inline static constexpr size_t k_max_threads = 32; - private: - std::vector m_threads; - std::deque> m_jobs; - std::condition_variable m_condition_variable; - std::condition_variable m_drained_condition; + private: + std::vector m_threads; + std::deque> m_jobs; + std::condition_variable m_condition_variable; + std::condition_variable m_drained_condition; - std::mutex m_mutex; + std::mutex m_mutex; - bool m_should_terminate = false; + bool m_should_terminate = false; - size_t m_next_job_id = 0; + size_t m_next_job_id = 0; - std::bitset m_thread_working; + std::bitset m_thread_working; - public: - explicit ThreadPool(size_t num_threads); - explicit ThreadPool(); - ~ThreadPool(); + public: + explicit ThreadPool(size_t num_threads); + explicit ThreadPool(); + ~ThreadPool(); - size_t get_thread_count() const; - bool is_thread_working(size_t thread_id); + size_t get_thread_count() const; + bool is_thread_working(size_t thread_id); - size_t get_job_count(); - size_t enqueue_job(std::function const& job); - void drop_job(size_t job_id); + size_t get_job_count(); + size_t enqueue_job(std::function const &job); + void drop_job(size_t job_id); - void drain(); + void drain(); - private: - void thread_loop(size_t thread_id); - }; -} + private: + void thread_loop(size_t thread_id); + }; +} // namespace explo diff --git a/src/util/VirtualAllocator.cpp b/src/util/VirtualAllocator.cpp index 05c3167..bbbc7a1 100644 --- a/src/util/VirtualAllocator.cpp +++ b/src/util/VirtualAllocator.cpp @@ -6,93 +6,90 @@ using namespace explo; VirtualAllocator::VirtualAllocator(size_t size, size_t alignment, size_t min_page_size) : - m_alignment(alignment) + m_alignment(alignment) { - // Calc page size (ensure it's a multiple of alignment) - if (m_alignment > 0) - { - m_page_size = m_alignment; - while (m_page_size < min_page_size) - m_page_size *= 2; - } - else m_page_size = min_page_size; - - resize(size); + // Calc page size (ensure it's a multiple of alignment) + if (m_alignment > 0) + { + m_page_size = m_alignment; + while (m_page_size < min_page_size) m_page_size *= 2; + } + else + m_page_size = min_page_size; + + resize(size); } -VirtualAllocator::~VirtualAllocator() -{ -} +VirtualAllocator::~VirtualAllocator() {} void VirtualAllocator::resize(size_t new_size) { - size_t num_pages = std::ceil(double(new_size) / double(m_page_size)); - new_size = num_pages * m_page_size; + size_t num_pages = std::ceil(double(new_size) / double(m_page_size)); + new_size = num_pages * m_page_size; - m_size = new_size; - m_page_status.resize(num_pages); + m_size = new_size; + m_page_status.resize(num_pages); } -bool VirtualAllocator::allocate(size_t size, size_t& offset) +bool VirtualAllocator::allocate(size_t size, size_t &offset) { - int num_required_pages = std::ceil(double(size) / double(m_page_size)); - - int found_page_start_idx = -1; - int num_found_pages = 0; - bool found = false; - - for (int i = 0; i < m_page_status.size(); i++) - { - if (m_page_status.at(i) == 0) - { - if (found_page_start_idx < 0) found_page_start_idx = i; - - num_found_pages++; - if (num_found_pages == num_required_pages) - { - found = true; - break; - } - } - else - { - found_page_start_idx = -1; - num_found_pages = 0; - } - } - - if (found) - { - offset = found_page_start_idx * m_page_size; - - for (int i = 0; i < num_required_pages - 1; i++) - m_page_status[found_page_start_idx + i] = 0xFF; - m_page_status[found_page_start_idx + num_required_pages - 1] = 0x7F; // MSB not set because there's no continuation page - - m_num_allocated_pages += num_required_pages; - - assert(m_alignment > 0 && offset % m_alignment == 0); - - return true; - } - - return false; + int num_required_pages = std::ceil(double(size) / double(m_page_size)); + + int found_page_start_idx = -1; + int num_found_pages = 0; + bool found = false; + + for (int i = 0; i < m_page_status.size(); i++) + { + if (m_page_status.at(i) == 0) + { + if (found_page_start_idx < 0) found_page_start_idx = i; + + num_found_pages++; + if (num_found_pages == num_required_pages) + { + found = true; + break; + } + } + else + { + found_page_start_idx = -1; + num_found_pages = 0; + } + } + + if (found) + { + offset = found_page_start_idx * m_page_size; + + for (int i = 0; i < num_required_pages - 1; i++) m_page_status[found_page_start_idx + i] = 0xFF; + m_page_status[found_page_start_idx + num_required_pages - 1] = 0x7F; // MSB not set because there's no continuation page + + m_num_allocated_pages += num_required_pages; + + assert(m_alignment > 0 && offset % m_alignment == 0); + + return true; + } + + return false; } void VirtualAllocator::free(size_t offset) { - int start_page_idx = std::floor(double(offset) / double(m_page_size)); - assert((m_page_status.at(start_page_idx) & 0x7F) != 0); - assert(start_page_idx == 0 || (m_page_status.at(start_page_idx - 1) & 0x80) == 0); // The previous page shouldn't be a continuation page + int start_page_idx = std::floor(double(offset) / double(m_page_size)); + assert((m_page_status.at(start_page_idx) & 0x7F) != 0); + assert(start_page_idx == 0 || (m_page_status.at(start_page_idx - 1) & 0x80) == 0); // The previous page shouldn't be a continuation page - for (int i = start_page_idx; i < m_page_status.size(); i++) - { - uint8_t page_status = m_page_status.at(i); - assert((page_status & 0x7F) != 0); + for (int i = start_page_idx; i < m_page_status.size(); i++) + { + uint8_t page_status = m_page_status.at(i); + assert((page_status & 0x7F) != 0); - m_page_status[i] = 0; - m_num_allocated_pages--; + m_page_status[i] = 0; + m_num_allocated_pages--; - if ((page_status & 0x80) == 0) break; - } + if ((page_status & 0x80) == 0) break; + } } diff --git a/src/util/VirtualAllocator.hpp b/src/util/VirtualAllocator.hpp index 1b21c98..337fb79 100644 --- a/src/util/VirtualAllocator.hpp +++ b/src/util/VirtualAllocator.hpp @@ -7,40 +7,40 @@ namespace explo { - /// An allocator that allows resizing of the managed memory. - class VirtualAllocator - { - private: - size_t m_size; - size_t m_alignment; - - size_t m_page_size; - - // If the i-th entry isn't null, the i-th page is reserved. If the MSB is set it means the allocation also takes the - // next page - std::vector m_page_status; - - int m_num_allocated_pages = 0; - - public: - /// \param size The initial size of the allocable space. - /// \param alignment Every allocation offset will be a multiple of this number (0 means not set). - /// \param min_page_size The actual page size will be at least this value and a multiple of alignment. - explicit VirtualAllocator(size_t size, size_t alignment, size_t min_page_size); - ~VirtualAllocator(); - - size_t get_size() const { return m_size; } - int get_num_allocated_pages() const { return m_num_allocated_pages; } - - /// Resizes the managed memory to the given one. - void resize(size_t new_size); - - /// Allocates a block memory of the requested size and returns its offset. - /// Returns false if the allocation was unsuccessful. In this case offset has an undefined value. - bool allocate(size_t size, size_t& offset); - - /// Frees the block of memory corresponding to the given offset. - /// Does nothing if the block wasn't allocated. - void free(size_t offset); - }; -} // namespace explo \ No newline at end of file + /// An allocator that allows resizing of the managed memory. + class VirtualAllocator + { + private: + size_t m_size; + size_t m_alignment; + + size_t m_page_size; + + // If the i-th entry isn't null, the i-th page is reserved. If the MSB is set it means the allocation also takes the + // next page + std::vector m_page_status; + + int m_num_allocated_pages = 0; + + public: + /// \param size The initial size of the allocable space. + /// \param alignment Every allocation offset will be a multiple of this number (0 means not set). + /// \param min_page_size The actual page size will be at least this value and a multiple of alignment. + explicit VirtualAllocator(size_t size, size_t alignment, size_t min_page_size); + ~VirtualAllocator(); + + size_t get_size() const { return m_size; } + int get_num_allocated_pages() const { return m_num_allocated_pages; } + + /// Resizes the managed memory to the given one. + void resize(size_t new_size); + + /// Allocates a block memory of the requested size and returns its offset. + /// Returns false if the allocation was unsuccessful. In this case offset has an undefined value. + bool allocate(size_t size, size_t &offset); + + /// Frees the block of memory corresponding to the given offset. + /// Does nothing if the block wasn't allocated. + void free(size_t offset); + }; +} // namespace explo \ No newline at end of file diff --git a/src/util/camera.cpp b/src/util/camera.cpp index 0834846..b73e64d 100644 --- a/src/util/camera.cpp +++ b/src/util/camera.cpp @@ -2,9 +2,8 @@ glm::mat4 explo::build_orientation_mat(float yaw, float pitch) { - glm::mat4 m(1); - m = glm::rotate(m, yaw, glm::vec3(0, 1, 0)); - m = glm::rotate(m, -pitch, glm::vec3(1, 0, 0)); - return m; + glm::mat4 m(1); + m = glm::rotate(m, yaw, glm::vec3(0, 1, 0)); + m = glm::rotate(m, -pitch, glm::vec3(1, 0, 0)); + return m; } - diff --git a/src/util/camera.hpp b/src/util/camera.hpp index 09d22d7..70d5b1c 100644 --- a/src/util/camera.hpp +++ b/src/util/camera.hpp @@ -5,12 +5,21 @@ namespace explo { - using camera_t = vren::camera; + using camera_t = vren::camera; - glm::mat4 build_orientation_mat(float yaw, float pitch); + glm::mat4 build_orientation_mat(float yaw, float pitch); - inline glm::vec3 get_right_vec(glm::mat4 const& orientation_mat) { return orientation_mat[0]; } - inline glm::vec3 get_up_vec(glm::mat4 const& orientation_mat) { return orientation_mat[1]; } - inline glm::vec3 get_forward_vec(glm::mat4 const& orientation_mat) { return orientation_mat[2]; } + inline glm::vec3 get_right_vec(glm::mat4 const &orientation_mat) + { + return orientation_mat[0]; + } + inline glm::vec3 get_up_vec(glm::mat4 const &orientation_mat) + { + return orientation_mat[1]; + } + inline glm::vec3 get_forward_vec(glm::mat4 const &orientation_mat) + { + return orientation_mat[2]; + } -} // namespace explo \ No newline at end of file +} // namespace explo \ No newline at end of file diff --git a/src/util/misc.cpp b/src/util/misc.cpp index d9d4126..86bb103 100644 --- a/src/util/misc.cpp +++ b/src/util/misc.cpp @@ -1,31 +1,31 @@ #include "misc.hpp" -#include - #include +#include + std::string explo::stringify_byte_size(size_t byte_size) { - float v = float(byte_size); - size_t i = 0; + float v = float(byte_size); + size_t i = 0; - char const* units[]{"", "Kb", "Mb", "Gb", "Tb"}; + char const *units[]{"", "Kb", "Mb", "Gb", "Tb"}; - while (v > 1000) - { - v /= 1024; - i++; - } + while (v > 1000) + { + v /= 1024; + i++; + } - std::string out{}; - out += fmt::format("{:0.2f}", v); - out += units[i]; - return out; + std::string out{}; + out += fmt::format("{:0.2f}", v); + out += units[i]; + return out; } uint64_t explo::get_nanos_since_epoch() { - using namespace std::chrono; + using namespace std::chrono; - return duration_cast(system_clock::now().time_since_epoch()).count(); + return duration_cast(system_clock::now().time_since_epoch()).count(); } diff --git a/src/util/misc.hpp b/src/util/misc.hpp index 39da79a..8291219 100644 --- a/src/util/misc.hpp +++ b/src/util/misc.hpp @@ -1,63 +1,60 @@ #pragma once -#include #include - #include #include +#include namespace explo { - struct vec_hash - { - template - size_t operator()(glm::vec<_length, _t> const& vec) const - { - size_t result = 0; - for (glm::length_t i = 0; i < _length; i++) - result ^= vec[i]; - return result; - } - }; - - inline float normalize_angle(float a) // angle in radians - { - constexpr float pi = glm::pi(); - - a = glm::mod(a + pi, pi * 2); - if (a < 0) - a += pi * 2; - return a - pi; - } - - template - _T pmod(_T i, _T n) - { - return (i % n + n) % n; - } - - /// Converts the given byte size to a human readable string (e.g. 567296 -> 554Kb) - std::string stringify_byte_size(size_t byte_size); - - uint64_t get_nanos_since_epoch(); - - template - _IntT ceil_to_power_of_2(_IntT n) - { - n--; - n |= n >> 1; - n |= n >> 2; - n |= n >> 4; - n |= n >> 8; - n |= n >> 16; - n++; - return n; - } - - inline double current_ms() - { - using namespace std::chrono; - return duration_cast(system_clock::now().time_since_epoch()).count(); - } - -} // namespace explo \ No newline at end of file + struct vec_hash + { + template + size_t operator()(glm::vec<_length, _t> const &vec) const + { + size_t result = 0; + for (glm::length_t i = 0; i < _length; i++) result ^= vec[i]; + return result; + } + }; + + inline float normalize_angle(float a) // angle in radians + { + constexpr float pi = glm::pi(); + + a = glm::mod(a + pi, pi * 2); + if (a < 0) a += pi * 2; + return a - pi; + } + + template + _T pmod(_T i, _T n) + { + return (i % n + n) % n; + } + + /// Converts the given byte size to a human readable string (e.g. 567296 -> 554Kb) + std::string stringify_byte_size(size_t byte_size); + + uint64_t get_nanos_since_epoch(); + + template + _IntT ceil_to_power_of_2(_IntT n) + { + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n++; + return n; + } + + inline double current_ms() + { + using namespace std::chrono; + return duration_cast(system_clock::now().time_since_epoch()).count(); + } + +} // namespace explo \ No newline at end of file diff --git a/src/util/profile_stats.cpp b/src/util/profile_stats.cpp index d1c4484..f4bcf83 100644 --- a/src/util/profile_stats.cpp +++ b/src/util/profile_stats.cpp @@ -4,26 +4,26 @@ void explo::profile_stats::push_elapsed_time(uint64_t elapsed_time) { - m_min_elapsed_ns = std::min(elapsed_time, m_min_elapsed_ns); - m_max_elapsed_ns = std::max(elapsed_time, m_max_elapsed_ns); - m_last_elapsed_ns = elapsed_time; + m_min_elapsed_ns = std::min(elapsed_time, m_min_elapsed_ns); + m_max_elapsed_ns = std::max(elapsed_time, m_max_elapsed_ns); + m_last_elapsed_ns = elapsed_time; - if (m_sample_count == 0) - { - m_avg_elapsed_ns = double(elapsed_time); - } - else - { - // Update cumulative average (https://en.wikipedia.org/wiki/Moving_average) - m_avg_elapsed_ns += double(elapsed_time - m_avg_elapsed_ns) / double(m_sample_count + 1); - } + if (m_sample_count == 0) + { + m_avg_elapsed_ns = double(elapsed_time); + } + else + { + // Update cumulative average (https://en.wikipedia.org/wiki/Moving_average) + m_avg_elapsed_ns += double(elapsed_time - m_avg_elapsed_ns) / double(m_sample_count + 1); + } - m_sample_count++; + m_sample_count++; } void explo::monitor::push_elapsed_time(uint64_t elapsed_time) { - std::lock_guard lock(m_mutex); + std::lock_guard lock(m_mutex); - m_handle.push_elapsed_time(elapsed_time); + m_handle.push_elapsed_time(elapsed_time); } diff --git a/src/util/profile_stats.hpp b/src/util/profile_stats.hpp index 78407b1..40ce29e 100644 --- a/src/util/profile_stats.hpp +++ b/src/util/profile_stats.hpp @@ -2,87 +2,88 @@ #include #include -#include #include +#include namespace explo { - // ------------------------------------------------------------------------------------------------ - // monitor - // ------------------------------------------------------------------------------------------------ - - template - class base_monitor - { - protected: - _t m_handle; - mutable std::mutex m_mutex; - - public: - _t read() const - { - std::lock_guard lock(m_mutex); - return m_handle; - } - }; - - template - class monitor : public base_monitor<_t> {}; - - template - void access(_t const& object, std::function const& callback) - { - callback(object); - } - - template // template specialization for monitor<_t> - void access(monitor<_t> const& monitored_object, std::function const& callback) - { - _t t = monitored_object.read(); - callback(t); - } - - // ------------------------------------------------------------------------------------------------ - // profile_stats - // ------------------------------------------------------------------------------------------------ - - class profile_stats - { - uint64_t m_min_elapsed_ns = UINT64_MAX; - uint64_t m_max_elapsed_ns = 0; - uint64_t m_last_elapsed_ns = UINT64_MAX; - - double m_avg_elapsed_ns; - - size_t m_sample_count = 0; ///< The total number of samples pushed - - public: - explicit profile_stats() = default; - ~profile_stats() = default; - - uint64_t min_ns() const { return m_min_elapsed_ns; } - uint64_t max_ns() const { return m_max_elapsed_ns; } - uint64_t last_ns() const { return m_last_elapsed_ns; } - double avg_ns() const { return m_avg_elapsed_ns; } - - double min_ms() const { return m_min_elapsed_ns / 1'000'000.0; } - double max_ms() const { return m_max_elapsed_ns / 1'000'000.0; } - double avg_ms() const { return m_avg_elapsed_ns / 1'000'000.0; } - double last_ms() const { return m_last_elapsed_ns / 1'000'000.0; } - - void push_elapsed_time(uint64_t elapsed_time); - }; - - // ------------------------------------------------------------------------------------------------ - // monitor - // ------------------------------------------------------------------------------------------------ - - template<> - class monitor : public base_monitor - { - public: - void push_elapsed_time(uint64_t elapsed_time); - }; - - -} // namespace explo + // ------------------------------------------------------------------------------------------------ + // monitor + // ------------------------------------------------------------------------------------------------ + + template + class base_monitor + { + protected: + _t m_handle; + mutable std::mutex m_mutex; + + public: + _t read() const + { + std::lock_guard lock(m_mutex); + return m_handle; + } + }; + + template + class monitor : public base_monitor<_t> + { + }; + + template + void access(_t const &object, std::function const &callback) + { + callback(object); + } + + template // template specialization for monitor<_t> + void access(monitor<_t> const &monitored_object, std::function const &callback) + { + _t t = monitored_object.read(); + callback(t); + } + + // ------------------------------------------------------------------------------------------------ + // profile_stats + // ------------------------------------------------------------------------------------------------ + + class profile_stats + { + uint64_t m_min_elapsed_ns = UINT64_MAX; + uint64_t m_max_elapsed_ns = 0; + uint64_t m_last_elapsed_ns = UINT64_MAX; + + double m_avg_elapsed_ns; + + size_t m_sample_count = 0; ///< The total number of samples pushed + + public: + explicit profile_stats() = default; + ~profile_stats() = default; + + uint64_t min_ns() const { return m_min_elapsed_ns; } + uint64_t max_ns() const { return m_max_elapsed_ns; } + uint64_t last_ns() const { return m_last_elapsed_ns; } + double avg_ns() const { return m_avg_elapsed_ns; } + + double min_ms() const { return m_min_elapsed_ns / 1'000'000.0; } + double max_ms() const { return m_max_elapsed_ns / 1'000'000.0; } + double avg_ms() const { return m_avg_elapsed_ns / 1'000'000.0; } + double last_ms() const { return m_last_elapsed_ns / 1'000'000.0; } + + void push_elapsed_time(uint64_t elapsed_time); + }; + + // ------------------------------------------------------------------------------------------------ + // monitor + // ------------------------------------------------------------------------------------------------ + + template <> + class monitor : public base_monitor + { + public: + void push_elapsed_time(uint64_t elapsed_time); + }; + +} // namespace explo diff --git a/src/util/system.cpp b/src/util/system.cpp index bc412c9..b1d161b 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -1,7 +1,7 @@ #include "system.hpp" -#include #include +#include // https://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process @@ -9,45 +9,45 @@ MEMORYSTATUSEX get_memory_status() { - MEMORYSTATUSEX memory_info{}; - memory_info.dwLength = sizeof(MEMORYSTATUSEX); - GlobalMemoryStatusEx(&memory_info); - return memory_info; + MEMORYSTATUSEX memory_info{}; + memory_info.dwLength = sizeof(MEMORYSTATUSEX); + GlobalMemoryStatusEx(&memory_info); + return memory_info; } size_t explo::get_total_virtual_memory() { - return get_memory_status().ullTotalPageFile; + return get_memory_status().ullTotalPageFile; } size_t explo::get_used_virtual_memory() { - MEMORYSTATUSEX memory_info = get_memory_status(); - return memory_info.ullTotalPageFile - memory_info.ullAvailPageFile; + MEMORYSTATUSEX memory_info = get_memory_status(); + return memory_info.ullTotalPageFile - memory_info.ullAvailPageFile; } size_t explo::get_virtual_memory_used_by_current_process() { - PROCESS_MEMORY_COUNTERS_EX pmc; - GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*) &pmc, sizeof(pmc)); - return pmc.PrivateUsage; + PROCESS_MEMORY_COUNTERS_EX pmc; + GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS *)&pmc, sizeof(pmc)); + return pmc.PrivateUsage; } size_t explo::get_total_physical_memory() { - MEMORYSTATUSEX memory_info = get_memory_status(); - return memory_info.ullTotalPhys; + MEMORYSTATUSEX memory_info = get_memory_status(); + return memory_info.ullTotalPhys; } size_t explo::get_used_physical_memory() { - MEMORYSTATUSEX memory_info = get_memory_status(); - return memory_info.ullTotalPhys - memory_info.ullAvailPhys; + MEMORYSTATUSEX memory_info = get_memory_status(); + return memory_info.ullTotalPhys - memory_info.ullAvailPhys; } size_t explo::get_physical_memory_used_by_current_process() { - PROCESS_MEMORY_COUNTERS_EX pmc; - GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*) &pmc, sizeof(pmc)); - return pmc.WorkingSetSize; + PROCESS_MEMORY_COUNTERS_EX pmc; + GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS *)&pmc, sizeof(pmc)); + return pmc.WorkingSetSize; } diff --git a/src/util/system.hpp b/src/util/system.hpp index 994b93b..691300c 100644 --- a/src/util/system.hpp +++ b/src/util/system.hpp @@ -2,12 +2,12 @@ namespace explo { - size_t get_total_virtual_memory(); - size_t get_used_virtual_memory(); - size_t get_virtual_memory_used_by_current_process(); + size_t get_total_virtual_memory(); + size_t get_used_virtual_memory(); + size_t get_virtual_memory_used_by_current_process(); - size_t get_total_physical_memory(); - size_t get_used_physical_memory(); - size_t get_physical_memory_used_by_current_process(); + size_t get_total_physical_memory(); + size_t get_used_physical_memory(); + size_t get_physical_memory_used_by_current_process(); -} // namespace explo +} // namespace explo diff --git a/src/video/BakedWorldView.cpp b/src/video/BakedWorldView.cpp index ec046a8..84d5ff9 100644 --- a/src/video/BakedWorldView.cpp +++ b/src/video/BakedWorldView.cpp @@ -8,238 +8,202 @@ using namespace explo; // BakedWorldViewCircularGrid // -------------------------------------------------------------------------------------------------------------------------------- -BakedWorldViewCircularGrid::BakedWorldViewCircularGrid( - Renderer& renderer, - glm::ivec3 const& render_distance - ) : - m_renderer(renderer), - - m_render_distance(render_distance), - m_side(m_render_distance * 2 + 1) -{ - m_start = glm::ivec3(0); - - glm::ivec3 gpu_image_size( - m_side.x * 2, // The X axis is doubled in order to store the chunk information - m_side.y, - m_side.z - ); - m_gpu_image = std::make_unique(m_renderer, gpu_image_size, VK_FORMAT_R32G32B32A32_UINT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - m_cpu_image.resize(m_side.x * m_side.y * m_side.z); -} +BakedWorldViewCircularGrid::BakedWorldViewCircularGrid(Renderer &renderer, glm::ivec3 const &render_distance) : + m_renderer(renderer), -BakedWorldViewCircularGrid::~BakedWorldViewCircularGrid() + m_render_distance(render_distance), + m_side(m_render_distance * 2 + 1) { + m_start = glm::ivec3(0); + + glm::ivec3 gpu_image_size( + m_side.x * 2, // The X axis is doubled in order to store the chunk information + m_side.y, + m_side.z + ); + m_gpu_image = std::make_unique(m_renderer, gpu_image_size, VK_FORMAT_R32G32B32A32_UINT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + m_cpu_image.resize(m_side.x * m_side.y * m_side.z); } -glm::ivec3 BakedWorldViewCircularGrid::to_image_index(glm::ivec3 const& pos) const +BakedWorldViewCircularGrid::~BakedWorldViewCircularGrid() {} + +glm::ivec3 BakedWorldViewCircularGrid::to_image_index(glm::ivec3 const &pos) const { - return (m_start + pos) % m_side; + return (m_start + pos) % m_side; } -size_t BakedWorldViewCircularGrid::to_flatten_index(glm::ivec3 const& pos) const +size_t BakedWorldViewCircularGrid::to_flatten_index(glm::ivec3 const &pos) const { - glm::ivec3 mod_pos = to_image_index(pos); - return mod_pos.y * (m_side.x * m_side.z) + mod_pos.x * m_side.z + mod_pos.z; + glm::ivec3 mod_pos = to_image_index(pos); + return mod_pos.y * (m_side.x * m_side.z) + mod_pos.x * m_side.z + mod_pos.z; } -BakedWorldViewCircularGrid::Pixel& BakedWorldViewCircularGrid::read_pixel(glm::ivec3 const& pos) +BakedWorldViewCircularGrid::Pixel &BakedWorldViewCircularGrid::read_pixel(glm::ivec3 const &pos) { - assert( - pos.x >= 0 && pos.x < m_side.x && - pos.y >= 0 && pos.y < m_side.y && - pos.z >= 0 && pos.z < m_side.z - ); + assert(pos.x >= 0 && pos.x < m_side.x && pos.y >= 0 && pos.y < m_side.y && pos.z >= 0 && pos.z < m_side.z); - return m_cpu_image.at(to_flatten_index(pos)); + return m_cpu_image.at(to_flatten_index(pos)); } -void BakedWorldViewCircularGrid::write_pixel(glm::ivec3 const& pos, Pixel const& pixel) +void BakedWorldViewCircularGrid::write_pixel(glm::ivec3 const &pos, Pixel const &pixel) { - assert( - pos.x >= 0 && pos.x < m_side.x && - pos.y >= 0 && pos.y < m_side.y && - pos.z >= 0 && pos.z < m_side.z - ); - + assert(pos.x >= 0 && pos.x < m_side.x && pos.y >= 0 && pos.y < m_side.y && pos.z >= 0 && pos.z < m_side.z); - // We split the pixel that we have to write into two pixels that lie side-by-side, along the X axis, in the final image - glm::uvec4 p1 = {pixel.m_index_count, pixel.m_instance_count, pixel.m_first_index, pixel.m_vertex_offset}; - glm::uvec4 p2 = {pixel.m_first_instance, 1, 2, 3 /* Random numbers that can be useful for debug */}; + // We split the pixel that we have to write into two pixels that lie + // side-by-side, along the X axis, in the final image + glm::uvec4 p1 = {pixel.m_index_count, pixel.m_instance_count, pixel.m_first_index, pixel.m_vertex_offset}; + glm::uvec4 p2 = {pixel.m_first_instance, 1, 2, 3 /* Random numbers that can be useful for debug */}; - glm::ivec3 img_idx = to_image_index(pos); + glm::ivec3 img_idx = to_image_index(pos); - m_gpu_image->write_pixel(img_idx * glm::ivec3(2, 1, 1), &p1, sizeof(p1)); - m_gpu_image->write_pixel(img_idx * glm::ivec3(2, 1, 1) + glm::ivec3(1, 0, 0), &p2, sizeof(p2)); + m_gpu_image->write_pixel(img_idx * glm::ivec3(2, 1, 1), &p1, sizeof(p1)); + m_gpu_image->write_pixel(img_idx * glm::ivec3(2, 1, 1) + glm::ivec3(1, 0, 0), &p2, sizeof(p2)); - m_cpu_image[to_flatten_index(pos)] = pixel; + m_cpu_image[to_flatten_index(pos)] = pixel; } // -------------------------------------------------------------------------------------------------------------------------------- // BakedWorldView // -------------------------------------------------------------------------------------------------------------------------------- -BakedWorldView::BakedWorldView( - Renderer& renderer, - glm::ivec3 const& init_position, - glm::ivec3 const& render_distance - ) : - m_renderer(renderer), - - m_position(init_position), - m_render_distance(render_distance), - - m_vertex_buffer( - m_renderer, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - k_vertex_buffer_init_size - ), - m_vertex_buffer_allocator(k_vertex_buffer_init_size, sizeof(SurfaceVertex), 128 * 1024 /* 128KB */), - - m_index_buffer( - m_renderer, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, - k_index_buffer_init_size - ), - m_index_buffer_allocator(k_index_buffer_init_size, sizeof(SurfaceIndex), 128 * 1024 /* 128KB */), - - m_instance_buffer( - m_renderer, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - k_instance_buffer_init_size - ), - m_instance_buffer_allocator(k_instance_buffer_init_size, sizeof(SurfaceInstance), 1024 /* 1KB */), - - m_circular_grid(renderer, render_distance) +BakedWorldView::BakedWorldView(Renderer &renderer, glm::ivec3 const &init_position, glm::ivec3 const &render_distance) : + m_renderer(renderer), + + m_position(init_position), + m_render_distance(render_distance), + + m_vertex_buffer( + m_renderer, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + k_vertex_buffer_init_size + ), + m_vertex_buffer_allocator(k_vertex_buffer_init_size, sizeof(SurfaceVertex), 128 * 1024 /* 128KB */), + + m_index_buffer( + m_renderer, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + k_index_buffer_init_size + ), + m_index_buffer_allocator(k_index_buffer_init_size, sizeof(SurfaceIndex), 128 * 1024 /* 128KB */), + + m_instance_buffer( + m_renderer, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + k_instance_buffer_init_size + ), + m_instance_buffer_allocator(k_instance_buffer_init_size, sizeof(SurfaceInstance), 1024 /* 1KB */), + + m_circular_grid(renderer, render_distance) { } -BakedWorldView::~BakedWorldView() -{ -} +BakedWorldView::~BakedWorldView() {} -bool BakedWorldView::is_chunk_position_inside(glm::ivec3 const& chunk_pos) const +bool BakedWorldView::is_chunk_position_inside(glm::ivec3 const &chunk_pos) const { - glm::ivec3 side = m_render_distance * 2 + 1; - glm::ivec3 rel_pos = to_relative_chunk_position(chunk_pos); - return - rel_pos.x >= 0 && rel_pos.x < side.x && - rel_pos.y >= 0 && rel_pos.y < side.y && - rel_pos.z >= 0 && rel_pos.z < side.z; + glm::ivec3 side = m_render_distance * 2 + 1; + glm::ivec3 rel_pos = to_relative_chunk_position(chunk_pos); + return rel_pos.x >= 0 && rel_pos.x < side.x && rel_pos.y >= 0 && rel_pos.y < side.y && rel_pos.z >= 0 && rel_pos.z < side.z; } -glm::ivec3 BakedWorldView::to_relative_chunk_position(glm::ivec3 const& chunk_pos) const +glm::ivec3 BakedWorldView::to_relative_chunk_position(glm::ivec3 const &chunk_pos) const { - return chunk_pos - m_position + m_render_distance; + return chunk_pos - m_position + m_render_distance; } -size_t BakedWorldView::place_data(DeviceBuffer& buffer, VirtualAllocator& allocator, void* data, size_t data_size) +size_t BakedWorldView::place_data(DeviceBuffer &buffer, VirtualAllocator &allocator, void *data, size_t data_size) { - size_t alloc_offset; - while (true) - { - if (allocator.allocate(data_size, alloc_offset)) break; - - // If it can't allocate, try to slightly increase the buffer size - size_t buffer_size = glm::ceil(double(allocator.get_size()) * 1.7); - allocator.resize(buffer_size); - } - - // If a resize was done, make sure the buffer size matches the virtual allocator size - if (buffer.get_size() != allocator.get_size()) - buffer.resize(allocator.get_size()); - - buffer.write(data, data_size, alloc_offset); - return alloc_offset; + size_t alloc_offset; + while (true) + { + if (allocator.allocate(data_size, alloc_offset)) break; + + // If it can't allocate, try to slightly increase the buffer size + size_t buffer_size = glm::ceil(double(allocator.get_size()) * 1.7); + allocator.resize(buffer_size); + } + + // If a resize was done, make sure the buffer size matches the virtual + // allocator size + if (buffer.get_size() != allocator.get_size()) buffer.resize(allocator.get_size()); + + buffer.write(data, data_size, alloc_offset); + return alloc_offset; } -void BakedWorldView::set_position(glm::ivec3 const& new_position) +void BakedWorldView::set_position(glm::ivec3 const &new_position) { - glm::ivec3 offset = new_position - m_position; + glm::ivec3 offset = new_position - m_position; - m_circular_grid.m_start = pmod(m_circular_grid.m_start + offset, m_circular_grid.m_side); + m_circular_grid.m_start = pmod(m_circular_grid.m_start + offset, m_circular_grid.m_side); - m_position = new_position; + m_position = new_position; } -void BakedWorldView::upload_chunk(Chunk const& chunk) +void BakedWorldView::upload_chunk(Chunk const &chunk) { - // The user asked to upload a chunk that is outside the world view. This can happen frequently because the chunk construction - // is asynchronous and if the player is travelling through the world fast, chunks could be built when they're no longer inside - // the world view - glm::ivec3 chunk_pos = chunk.get_position(); - if (!is_chunk_position_inside(chunk_pos)) return; - - std::unique_ptr const& surface = chunk.m_surface; - - // The chunk doesn't have the surface! Instead of throwing, we silently ignore the uploading - if (!surface || - surface->m_vertices.empty() || - surface->m_indices.empty() || - surface->m_instances.empty() - ) return; - - size_t vertex_offset = place_data( - m_vertex_buffer, - m_vertex_buffer_allocator, - surface->m_vertices.data(), - surface->m_vertices.size() * sizeof(SurfaceVertex) - ); - - size_t index_offset = place_data( - m_index_buffer, - m_index_buffer_allocator, - surface->m_indices.data(), - surface->m_indices.size() * sizeof(SurfaceIndex) - ); - - size_t instance_offset = place_data( - m_instance_buffer, - m_instance_buffer_allocator, - surface->m_instances.data(), - surface->m_instances.size() * sizeof(SurfaceInstance) - ); - - // Set the chunk's draw call within the circular grid - assert(vertex_offset % sizeof(SurfaceVertex) == 0); - assert(index_offset % sizeof(SurfaceIndex) == 0); - assert(instance_offset % sizeof(SurfaceInstance) == 0); - - BakedWorldViewCircularGrid::Pixel pixel{}; - pixel.m_index_count = surface->m_indices.size(); - pixel.m_instance_count = surface->m_instances.size(); - pixel.m_first_index = index_offset / sizeof(SurfaceIndex); - pixel.m_vertex_offset = vertex_offset / sizeof(SurfaceVertex); - pixel.m_first_instance = instance_offset / sizeof(SurfaceInstance); - - m_circular_grid.write_pixel(to_relative_chunk_position(chunk_pos), pixel); + // The user asked to upload a chunk that is outside the world view. This can happen frequently because the chunk + // construction is asynchronous and if the player is travelling through the world fast, chunks could be built when they're + // no longer inside the world view + glm::ivec3 chunk_pos = chunk.get_position(); + if (!is_chunk_position_inside(chunk_pos)) return; + + std::unique_ptr const &surface = chunk.m_surface; + + // The chunk doesn't have the surface! Instead of throwing, we silently + // ignore the uploading + if (!surface || surface->m_vertices.empty() || surface->m_indices.empty() || surface->m_instances.empty()) return; + + size_t vertex_offset = + place_data(m_vertex_buffer, m_vertex_buffer_allocator, surface->m_vertices.data(), surface->m_vertices.size() * sizeof(SurfaceVertex)); + + size_t index_offset = + place_data(m_index_buffer, m_index_buffer_allocator, surface->m_indices.data(), surface->m_indices.size() * sizeof(SurfaceIndex)); + + size_t instance_offset = place_data( + m_instance_buffer, m_instance_buffer_allocator, surface->m_instances.data(), surface->m_instances.size() * sizeof(SurfaceInstance) + ); + + // Set the chunk's draw call within the circular grid + assert(vertex_offset % sizeof(SurfaceVertex) == 0); + assert(index_offset % sizeof(SurfaceIndex) == 0); + assert(instance_offset % sizeof(SurfaceInstance) == 0); + + BakedWorldViewCircularGrid::Pixel pixel{}; + pixel.m_index_count = surface->m_indices.size(); + pixel.m_instance_count = surface->m_instances.size(); + pixel.m_first_index = index_offset / sizeof(SurfaceIndex); + pixel.m_vertex_offset = vertex_offset / sizeof(SurfaceVertex); + pixel.m_first_instance = instance_offset / sizeof(SurfaceInstance); + + m_circular_grid.write_pixel(to_relative_chunk_position(chunk_pos), pixel); } -void BakedWorldView::destroy_chunk(glm::ivec3 const& chunk_pos) +void BakedWorldView::destroy_chunk(glm::ivec3 const &chunk_pos) { - // If the chunk asked to be destroyed isn't covered by the world view anymore, we have lost references to it (that are - // stored in the circular image): we can't free its geometry! - // IMPORTANT: The responsibility of destroying chunks that exit the world view is left to the user! + // If the chunk asked to be destroyed isn't covered by the world view anymore, we have lost references to it (that are stored in the circular + // image): we can't free its geometry! + // IMPORTANT: The responsibility of destroying chunks that exit the world view is left to the user! - if (!is_chunk_position_inside(chunk_pos)) return; + if (!is_chunk_position_inside(chunk_pos)) return; - glm::ivec3 rel_pos = to_relative_chunk_position(chunk_pos); - BakedWorldViewCircularGrid::Pixel& pixel = m_circular_grid.read_pixel(rel_pos); + glm::ivec3 rel_pos = to_relative_chunk_position(chunk_pos); + BakedWorldViewCircularGrid::Pixel &pixel = m_circular_grid.read_pixel(rel_pos); - if (pixel.m_index_count == 0) return; // The pixel was invalid + if (pixel.m_index_count == 0) return; // The pixel was invalid - size_t vertex_offset = pixel.m_vertex_offset * sizeof(SurfaceVertex); - size_t index_offset = pixel.m_first_index * sizeof(SurfaceIndex); - size_t instance_offset = pixel.m_first_instance * sizeof(SurfaceInstance); + size_t vertex_offset = pixel.m_vertex_offset * sizeof(SurfaceVertex); + size_t index_offset = pixel.m_first_index * sizeof(SurfaceIndex); + size_t instance_offset = pixel.m_first_instance * sizeof(SurfaceInstance); - m_vertex_buffer_allocator.free(vertex_offset); - m_index_buffer_allocator.free(index_offset); - m_instance_buffer_allocator.free(instance_offset); + m_vertex_buffer_allocator.free(vertex_offset); + m_index_buffer_allocator.free(index_offset); + m_instance_buffer_allocator.free(instance_offset); - pixel.m_index_count = 0; // Invalidate the pixel + pixel.m_index_count = 0; // Invalidate the pixel - m_circular_grid.write_pixel(rel_pos, pixel); + m_circular_grid.write_pixel(rel_pos, pixel); } diff --git a/src/video/BakedWorldView.hpp b/src/video/BakedWorldView.hpp index e0d868b..621a24f 100644 --- a/src/video/BakedWorldView.hpp +++ b/src/video/BakedWorldView.hpp @@ -1,123 +1,118 @@ #pragma once #include - #include -#include "world/Chunk.hpp" #include "DeviceBuffer.hpp" #include "DeviceImage3d.hpp" +#include "world/Chunk.hpp" namespace explo { - // Forward decl - class Renderer; - - // ------------------------------------------------------------------------------------------------ - // BakedWorldViewCircularGrid - // ------------------------------------------------------------------------------------------------ - - /// A circular 3d image (just like a circular buffer, but with 3 dimensions), where every pixel is a chunk and stores the - /// reference to its geometry (i.e. vertex, index and instance data). The advantage of using as circular structure, is for - /// shifting whilst the player is moving. - class BakedWorldViewCircularGrid - { - friend class Renderer; - friend class BakedWorldView; - friend class CullWorldView; - friend class DebugUi; - - public: - // The size of this struct is 8 x sizeof(uint32_t) = 8 x 4 = 32 bytes - // The required image format should be VK_FORMAT_R64G64B64A64_UINT (32 bytes), but it's not widely supported! - // Therefore a meaningful pixel is made to be a group blocks of 2x2x2 pixels of VK_FORMAT_R8G8B8A8_UINT - struct Pixel - { - uint32_t m_index_count; // If index_count == 0, then it's invalid - uint32_t m_instance_count; - uint32_t m_first_index; - uint32_t m_vertex_offset; - uint32_t m_first_instance; - }; - - private: - Renderer& m_renderer; - - glm::ivec3 m_render_distance; - glm::ivec3 m_side; - - glm::ivec3 m_start; - - std::unique_ptr m_gpu_image; - std::vector m_cpu_image; - - public: - explicit BakedWorldViewCircularGrid(Renderer& renderer, glm::ivec3 const& render_distance); - ~BakedWorldViewCircularGrid(); - - Pixel& read_pixel(glm::ivec3 const& pos); - void write_pixel(glm::ivec3 const& pos, Pixel const& pixel); - - private: - glm::ivec3 to_image_index(glm::ivec3 const& pos) const; - size_t to_flatten_index(glm::ivec3 const& pos) const; - }; - - // ------------------------------------------------------------------------------------------------ - // BakedWorldView - // ------------------------------------------------------------------------------------------------ - - class BakedWorldView - { - friend class Renderer; - friend class CullWorldView; - friend class DrawChunkList; - friend class DebugUi; - - public: - static constexpr size_t k_vertex_buffer_init_size = 64 * 1024 * 1024; // 64MB - static constexpr size_t k_index_buffer_init_size = 64 * 1024 * 1024; // 64MB - static constexpr size_t k_instance_buffer_init_size = 1024 * 1024; // 1MB - - private: - Renderer& m_renderer; - - glm::ivec3 m_position; - glm::ivec3 m_render_distance; - - DeviceBuffer m_vertex_buffer; - VirtualAllocator m_vertex_buffer_allocator; - - DeviceBuffer m_index_buffer; - VirtualAllocator m_index_buffer_allocator; - - DeviceBuffer m_instance_buffer; - VirtualAllocator m_instance_buffer_allocator; - - BakedWorldViewCircularGrid m_circular_grid; - - public: - explicit BakedWorldView( - Renderer& renderer, - glm::ivec3 const& init_position, - glm::ivec3 const& render_distance - ); - ~BakedWorldView(); - - bool is_chunk_position_inside(glm::ivec3 const& chunk_pos) const; - - /// Sets the position of the world view. As internally uses a CircularGrid, chunks that would be still in WorldView - /// after the movement will be preserved. However, the responsibility of destroying old chunks is left to the user. - /// NOTE: Old chunks should be destroyed using `destroy_chunk()` before calling this function. - void set_position(glm::ivec3 const& position); - void upload_chunk(Chunk const& chunk); - void destroy_chunk(glm::ivec3 const& chunk_pos); - - private: - /// Places the given data in the device buffer eventually re-allocating it if doesn't fit. - /// \return The offset, within the buffer, where the data is allocated. - size_t place_data(DeviceBuffer& buffer, VirtualAllocator& allocator, void* data, size_t data_size); - - glm::ivec3 to_relative_chunk_position(glm::ivec3 const& chunk_pos) const; - }; -} // namespace explo + // Forward decl + class Renderer; + + // ------------------------------------------------------------------------------------------------ + // BakedWorldViewCircularGrid + // ------------------------------------------------------------------------------------------------ + + /// A circular 3d image (just like a circular buffer, but with 3 dimensions), where every pixel is a chunk and stores the + /// reference to its geometry (i.e. vertex, index and instance data). The advantage of using as circular structure, is for + /// shifting whilst the player is moving. + class BakedWorldViewCircularGrid + { + friend class Renderer; + friend class BakedWorldView; + friend class CullWorldView; + friend class DebugUi; + + public: + // The size of this struct is 8 x sizeof(uint32_t) = 8 x 4 = 32 bytes + // The required image format should be VK_FORMAT_R64G64B64A64_UINT (32 bytes), but it's not widely supported! + // Therefore a meaningful pixel is made to be a group blocks of 2x2x2 pixels of VK_FORMAT_R8G8B8A8_UINT + struct Pixel + { + uint32_t m_index_count; // If index_count == 0, then it's invalid + uint32_t m_instance_count; + uint32_t m_first_index; + uint32_t m_vertex_offset; + uint32_t m_first_instance; + }; + + private: + Renderer &m_renderer; + + glm::ivec3 m_render_distance; + glm::ivec3 m_side; + + glm::ivec3 m_start; + + std::unique_ptr m_gpu_image; + std::vector m_cpu_image; + + public: + explicit BakedWorldViewCircularGrid(Renderer &renderer, glm::ivec3 const &render_distance); + ~BakedWorldViewCircularGrid(); + + Pixel &read_pixel(glm::ivec3 const &pos); + void write_pixel(glm::ivec3 const &pos, Pixel const &pixel); + + private: + glm::ivec3 to_image_index(glm::ivec3 const &pos) const; + size_t to_flatten_index(glm::ivec3 const &pos) const; + }; + + // ------------------------------------------------------------------------------------------------ + // BakedWorldView + // ------------------------------------------------------------------------------------------------ + + class BakedWorldView + { + friend class Renderer; + friend class CullWorldView; + friend class DrawChunkList; + friend class DebugUi; + + public: + static constexpr size_t k_vertex_buffer_init_size = 64 * 1024 * 1024; // 64MB + static constexpr size_t k_index_buffer_init_size = 64 * 1024 * 1024; // 64MB + static constexpr size_t k_instance_buffer_init_size = 1024 * 1024; // 1MB + + private: + Renderer &m_renderer; + + glm::ivec3 m_position; + glm::ivec3 m_render_distance; + + DeviceBuffer m_vertex_buffer; + VirtualAllocator m_vertex_buffer_allocator; + + DeviceBuffer m_index_buffer; + VirtualAllocator m_index_buffer_allocator; + + DeviceBuffer m_instance_buffer; + VirtualAllocator m_instance_buffer_allocator; + + BakedWorldViewCircularGrid m_circular_grid; + + public: + explicit BakedWorldView(Renderer &renderer, glm::ivec3 const &init_position, glm::ivec3 const &render_distance); + ~BakedWorldView(); + + bool is_chunk_position_inside(glm::ivec3 const &chunk_pos) const; + + /// Sets the position of the world view. As internally uses a CircularGrid, chunks that would be still in WorldView + /// after the movement will be preserved. However, the responsibility of destroying old chunks is left to the user. + /// NOTE: Old chunks should be destroyed using `destroy_chunk()` before calling this function. + void set_position(glm::ivec3 const &position); + void upload_chunk(Chunk const &chunk); + void destroy_chunk(glm::ivec3 const &chunk_pos); + + private: + /// Places the given data in the device buffer eventually re-allocating it if doesn't fit. + /// \return The offset, within the buffer, where the data is allocated. + size_t place_data(DeviceBuffer &buffer, VirtualAllocator &allocator, void *data, size_t data_size); + + glm::ivec3 to_relative_chunk_position(glm::ivec3 const &chunk_pos) const; + }; +} // namespace explo diff --git a/src/video/DebugUi.cpp b/src/video/DebugUi.cpp index f0a3766..55ac2d3 100644 --- a/src/video/DebugUi.cpp +++ b/src/video/DebugUi.cpp @@ -2,256 +2,246 @@ #include -#include "Renderer.hpp" #include "Game.hpp" +#include "Renderer.hpp" #include "util/system.hpp" using namespace explo; -DebugUi::DebugUi(Renderer& renderer) : - m_renderer(renderer) +DebugUi::DebugUi(Renderer &renderer) : + m_renderer(renderer) { } -DebugUi::~DebugUi() -{ -} +DebugUi::~DebugUi() {} void DebugUi::display_player_window() { - Entity& player = *explo::game().m_player; - - if (ImGui::Begin("Player")) - { - glm::vec3 const& pos = player.get_position(); - glm::ivec3 const& chunk_pos = player.get_chunk_position(); - glm::vec3 const& chunk_rel_pos = player.get_chunk_relative_position(); - - ImGui::Text("Position: (%.3f, %.3f, %.3f)", pos.x, pos.y, pos.z); - ImGui::Text("Chunk position: (%d, %d, %d)", chunk_pos.x, chunk_pos.y, chunk_pos.z); - ImGui::Text("Chunk-relative position: (%.3f, %.3f, %.3f)", chunk_rel_pos.x, chunk_rel_pos.y, chunk_rel_pos.z); - ImGui::Text("Yaw: %.3f", player.get_yaw()); - ImGui::Text("Pitch: %.3f", player.get_pitch()); - - glm::vec3 right = player.get_right(); - glm::vec3 up = player.get_up(); - glm::vec3 forward = player.get_forward(); - ImGui::Text("Right: (%.3f, %.3f, %.1f)", right.x, right.y, right.z); - ImGui::Text("Up: (%.3f, %.3f, %.3f)", up.x, up.y, up.z); - ImGui::Text("Forward: (%.3f, %.3f, %.3f)", forward.x, forward.y, forward.z); - } - - ImGui::End(); + Entity &player = *explo::game().m_player; + + if (ImGui::Begin("Player")) + { + glm::vec3 const &pos = player.get_position(); + glm::ivec3 const &chunk_pos = player.get_chunk_position(); + glm::vec3 const &chunk_rel_pos = player.get_chunk_relative_position(); + + ImGui::Text("Position: (%.3f, %.3f, %.3f)", pos.x, pos.y, pos.z); + ImGui::Text("Chunk position: (%d, %d, %d)", chunk_pos.x, chunk_pos.y, chunk_pos.z); + ImGui::Text("Chunk-relative position: (%.3f, %.3f, %.3f)", chunk_rel_pos.x, chunk_rel_pos.y, chunk_rel_pos.z); + ImGui::Text("Yaw: %.3f", player.get_yaw()); + ImGui::Text("Pitch: %.3f", player.get_pitch()); + + glm::vec3 right = player.get_right(); + glm::vec3 up = player.get_up(); + glm::vec3 forward = player.get_forward(); + ImGui::Text("Right: (%.3f, %.3f, %.1f)", right.x, right.y, right.z); + ImGui::Text("Up: (%.3f, %.3f, %.3f)", up.x, up.y, up.z); + ImGui::Text("Forward: (%.3f, %.3f, %.3f)", forward.x, forward.y, forward.z); + } + + ImGui::End(); } void DebugUi::display_world_view_window() { - Entity& player = *explo::game().m_player; - - if (!player.has_world_view()) return; - - World& world = player.get_world(); - WorldView& world_view = player.get_world_view(); - - glm::ivec3 render_distance = world_view.get_render_distance(); - glm::ivec3 world_view_side = world_view.get_side(); - - ImGui::SetNextWindowSize(ImVec2(300, 300), ImGuiCond_Once); - - if (ImGui::Begin("World view")) - { - // https://github.com/ocornut/imgui/issues/2342#issuecomment-462515297 - - // TODO reverse Y - - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - - ImVec2 win_pos = ImGui::GetWindowPos(); - ImVec2 win_reg = ImGui::GetWindowContentRegionMin(); - win_pos.x += win_reg.x; - win_pos.y += win_reg.y; - - ImVec2 rect_size = ImVec2(20, 20); - ImVec2 player_rect_size = ImVec2(2, 2); - ImVec2 rect_pad = ImVec2(4, 4); - - glm::ivec3 player_chunk_pos = player.get_chunk_position(); - glm::vec3 player_chunk_rel_pos = player.get_chunk_relative_position(); - - for (int x = 0; x < world_view_side.x; x++) - { - for (int z = 0; z < world_view_side.z; z++) - { - glm::ivec3 chunk_pos( - x - render_distance.x + player_chunk_pos.x, - player_chunk_pos.y, - z - render_distance.z + player_chunk_pos.z - ); - - ImU32 rect_color = 0xFF9E9E9E; // Grey, not loaded - if (world.is_chunk_loaded(chunk_pos)) - { - std::shared_ptr chunk = world.get_chunk(chunk_pos); - if (chunk->has_surface()) rect_color = 0xFF485579; // Brownish, with surface - //else if (chunk->has_volume()) rect_color = 0xFF00FFFF; // Yellow, with volume - else rect_color = 0xFF90FFCC; // Green, just loaded - } - - draw_list->AddRectFilled( - ImVec2( - win_pos.x + x * rect_size.x + rect_pad.x * x, - win_pos.y + z * rect_size.y + rect_pad.y * z - ), - ImVec2( - win_pos.x + x * rect_size.x + rect_size.x + rect_pad.x * x, - win_pos.y + z * rect_size.y + rect_size.y + rect_pad.y * z - ), - rect_color, - 0.0f, - ImDrawFlags_None - ); - } - } - - ImVec2 player_rect_pos = ImVec2( - win_pos.x + render_distance.x * rect_size.x + rect_pad.x * render_distance.x, - win_pos.y + render_distance.z * rect_size.y + rect_pad.y * render_distance.z - ); - - glm::vec3 n = player_chunk_rel_pos / Chunk::k_world_size; // Normalized chunk-relative position - player_rect_pos.x += n.x * rect_size.x; - player_rect_pos.y += n.z * rect_size.y; - - draw_list->AddRectFilled( - ImVec2( - player_rect_pos.x - player_rect_size.x / 2, - player_rect_pos.y - player_rect_size.y / 2 - ), - ImVec2( - player_rect_pos.x + player_rect_size.x / 2, - player_rect_pos.y + player_rect_size.y / 2 - ), - 0xFF0000FF, - 5.0f, - ImDrawFlags_None - ); - } - - ImGui::End(); + Entity &player = *explo::game().m_player; + + if (!player.has_world_view()) return; + + World &world = player.get_world(); + WorldView &world_view = player.get_world_view(); + + glm::ivec3 render_distance = world_view.get_render_distance(); + glm::ivec3 world_view_side = world_view.get_side(); + + ImGui::SetNextWindowSize(ImVec2(300, 300), ImGuiCond_Once); + + if (ImGui::Begin("World view")) + { + // https://github.com/ocornut/imgui/issues/2342#issuecomment-462515297 + + // TODO reverse Y + + ImDrawList *draw_list = ImGui::GetWindowDrawList(); + + ImVec2 win_pos = ImGui::GetWindowPos(); + ImVec2 win_reg = ImGui::GetWindowContentRegionMin(); + win_pos.x += win_reg.x; + win_pos.y += win_reg.y; + + ImVec2 rect_size = ImVec2(20, 20); + ImVec2 player_rect_size = ImVec2(2, 2); + ImVec2 rect_pad = ImVec2(4, 4); + + glm::ivec3 player_chunk_pos = player.get_chunk_position(); + glm::vec3 player_chunk_rel_pos = player.get_chunk_relative_position(); + + for (int x = 0; x < world_view_side.x; x++) + { + for (int z = 0; z < world_view_side.z; z++) + { + glm::ivec3 chunk_pos(x - render_distance.x + player_chunk_pos.x, player_chunk_pos.y, z - render_distance.z + player_chunk_pos.z); + + ImU32 rect_color = 0xFF9E9E9E; // Grey, not loaded + if (world.is_chunk_loaded(chunk_pos)) + { + std::shared_ptr chunk = world.get_chunk(chunk_pos); + if (chunk->has_surface()) rect_color = 0xFF485579; // Brownish, with surface + // else if (chunk->has_volume()) rect_color = 0xFF00FFFF; // Yellow, with volume + else + rect_color = 0xFF90FFCC; // Green, just loaded + } + + draw_list->AddRectFilled( + ImVec2(win_pos.x + x * rect_size.x + rect_pad.x * x, win_pos.y + z * rect_size.y + rect_pad.y * z), + ImVec2(win_pos.x + x * rect_size.x + rect_size.x + rect_pad.x * x, win_pos.y + z * rect_size.y + rect_size.y + rect_pad.y * z), + rect_color, + 0.0f, + ImDrawFlags_None + ); + } + } + + ImVec2 player_rect_pos = ImVec2( + win_pos.x + render_distance.x * rect_size.x + rect_pad.x * render_distance.x, + win_pos.y + render_distance.z * rect_size.y + rect_pad.y * render_distance.z + ); + + glm::vec3 n = player_chunk_rel_pos / Chunk::k_world_size; // Normalized chunk-relative position + player_rect_pos.x += n.x * rect_size.x; + player_rect_pos.y += n.z * rect_size.y; + + draw_list->AddRectFilled( + ImVec2(player_rect_pos.x - player_rect_size.x / 2, player_rect_pos.y - player_rect_size.y / 2), + ImVec2(player_rect_pos.x + player_rect_size.x / 2, player_rect_pos.y + player_rect_size.y / 2), + 0xFF0000FF, + 5.0f, + ImDrawFlags_None + ); + } + + ImGui::End(); } void DebugUi::display_baked_world_view_window() { - if (!m_renderer.has_world_view()) return; - - BakedWorldView& baked_world_view = m_renderer.get_world_view(); - - if (ImGui::Begin("Baked world view")) - { - ImGui::Text("Vertex buffer - Size: %zu, Allocated pages: %d", - baked_world_view.m_vertex_buffer.get_size(), - baked_world_view.m_vertex_buffer_allocator.get_num_allocated_pages() - ); - - ImGui::Text("Index buffer - Size: %zu, Allocated pages: %d", - baked_world_view.m_index_buffer.get_size(), - baked_world_view.m_index_buffer_allocator.get_num_allocated_pages() - ); - - ImGui::Text("Instance buffer - Size: %zu, Allocated pages: %d", - baked_world_view.m_instance_buffer.get_size(), - baked_world_view.m_instance_buffer_allocator.get_num_allocated_pages() - ); - - ImGui::Spacing(); ImGui::Spacing(); ImGui::Spacing(); - - auto& circular_grid = baked_world_view.m_circular_grid; - ImGui::Text("Render distance: %d", circular_grid.m_render_distance); - ImGui::Text("Circular grid start: (%d, %d, %d)", circular_grid.m_start.x, circular_grid.m_start.y, circular_grid.m_start.z); - } - - ImGui::End(); + if (!m_renderer.has_world_view()) return; + + BakedWorldView &baked_world_view = m_renderer.get_world_view(); + + if (ImGui::Begin("Baked world view")) + { + ImGui::Text( + "Vertex buffer - Size: %zu, Allocated pages: %d", + baked_world_view.m_vertex_buffer.get_size(), + baked_world_view.m_vertex_buffer_allocator.get_num_allocated_pages() + ); + + ImGui::Text( + "Index buffer - Size: %zu, Allocated pages: %d", + baked_world_view.m_index_buffer.get_size(), + baked_world_view.m_index_buffer_allocator.get_num_allocated_pages() + ); + + ImGui::Text( + "Instance buffer - Size: %zu, Allocated pages: %d", + baked_world_view.m_instance_buffer.get_size(), + baked_world_view.m_instance_buffer_allocator.get_num_allocated_pages() + ); + + ImGui::Spacing(); + ImGui::Spacing(); + ImGui::Spacing(); + + auto &circular_grid = baked_world_view.m_circular_grid; + ImGui::Text("Render distance: %d", circular_grid.m_render_distance); + ImGui::Text("Circular grid start: (%d, %d, %d)", circular_grid.m_start.x, circular_grid.m_start.y, circular_grid.m_start.z); + } + + ImGui::End(); } void DebugUi::display_renderer_window() { - if (ImGui::Begin("Renderer")) - { - ImGui::Text("Dt: %.3f", game().m_dt); - ImGui::Text("FPS: %d", game().m_fps); - } + if (ImGui::Begin("Renderer")) + { + ImGui::Text("Dt: %.3f", game().m_dt); + ImGui::Text("FPS: %d", game().m_fps); + } - ImGui::End(); + ImGui::End(); } void DebugUi::display_jobs_window() { - ThreadPool& thread_pool = game().m_thread_pool; + ThreadPool &thread_pool = game().m_thread_pool; - // Thread pool - if (ImGui::Begin("Thread pool")) - { - ImGui::Text("Main thread jobs: -1"); // todo + // Thread pool + if (ImGui::Begin("Thread pool")) + { + ImGui::Text("Main thread jobs: -1"); // todo - ImGui::Separator(); + ImGui::Separator(); - ImGui::Text("Threads count: %zu", thread_pool.get_thread_count()); - ImGui::Text("Jobs count: %zu", thread_pool.get_job_count()); + ImGui::Text("Threads count: %zu", thread_pool.get_thread_count()); + ImGui::Text("Jobs count: %zu", thread_pool.get_job_count()); - ImGui::Separator(); + ImGui::Separator(); - for (size_t thread_id = 0; thread_id < thread_pool.get_thread_count(); thread_id++) - ImGui::Text("Thread %03zu: %s", thread_id, thread_pool.is_thread_working(thread_id) ? "YES" : "NOO"); + for (size_t thread_id = 0; thread_id < thread_pool.get_thread_count(); thread_id++) + ImGui::Text("Thread %03zu: %s", thread_id, thread_pool.is_thread_working(thread_id) ? "YES" : "NOO"); - ImGui::Separator(); + ImGui::Separator(); - // Virtual memory - ImGui::Text("Virtual memory: %s/%s", - stringify_byte_size(get_virtual_memory_used_by_current_process()).c_str(), - stringify_byte_size(get_total_virtual_memory()).c_str() - ); + // Virtual memory + ImGui::Text( + "Virtual memory: %s/%s", + stringify_byte_size(get_virtual_memory_used_by_current_process()).c_str(), + stringify_byte_size(get_total_virtual_memory()).c_str() + ); - // Physical memory - ImGui::Text("Physical memory: %s/%s", - stringify_byte_size(get_physical_memory_used_by_current_process()).c_str(), - stringify_byte_size(get_total_physical_memory()).c_str() - ); - } + // Physical memory + ImGui::Text( + "Physical memory: %s/%s", + stringify_byte_size(get_physical_memory_used_by_current_process()).c_str(), + stringify_byte_size(get_total_physical_memory()).c_str() + ); + } - ImGui::End(); + ImGui::End(); } void DebugUi::display_vma_memory_statistics() { - if (ImGui::Begin("Vulkan memory statistics")) - { - VmaTotalStatistics stats{}; - vmaCalculateStatistics(m_renderer.m_context.m_vma_allocator, &stats); + if (ImGui::Begin("Vulkan memory statistics")) + { + VmaTotalStatistics stats{}; + vmaCalculateStatistics(m_renderer.m_context.m_vma_allocator, &stats); - VmaDetailedStatistics& detailed_stats = stats.total; + VmaDetailedStatistics &detailed_stats = stats.total; - ImGui::Text("blockCount: %d", detailed_stats.statistics.blockCount); - ImGui::Text("allocationCount: %d", detailed_stats.statistics.allocationCount); - ImGui::Text("blockBytes: %zu", detailed_stats.statistics.blockBytes); - ImGui::Text("allocationBytes: %zu", detailed_stats.statistics.allocationBytes); + ImGui::Text("blockCount: %d", detailed_stats.statistics.blockCount); + ImGui::Text("allocationCount: %d", detailed_stats.statistics.allocationCount); + ImGui::Text("blockBytes: %zu", detailed_stats.statistics.blockBytes); + ImGui::Text("allocationBytes: %zu", detailed_stats.statistics.allocationBytes); - ImGui::Separator(); + ImGui::Separator(); - ImGui::Text("unusedRangeCount: %d", detailed_stats.unusedRangeCount); - ImGui::Text("allocationSizeMin: %zu", detailed_stats.allocationSizeMin); - ImGui::Text("allocationSizeMax: %zu", detailed_stats.allocationSizeMax); - ImGui::Text("unusedRangeSizeMin: %zu", detailed_stats.unusedRangeSizeMin); - ImGui::Text("unusedRangeSizeMax: %zu", detailed_stats.unusedRangeSizeMax); - } + ImGui::Text("unusedRangeCount: %d", detailed_stats.unusedRangeCount); + ImGui::Text("allocationSizeMin: %zu", detailed_stats.allocationSizeMin); + ImGui::Text("allocationSizeMax: %zu", detailed_stats.allocationSizeMax); + ImGui::Text("unusedRangeSizeMin: %zu", detailed_stats.unusedRangeSizeMin); + ImGui::Text("unusedRangeSizeMax: %zu", detailed_stats.unusedRangeSizeMax); + } - ImGui::End(); + ImGui::End(); } void DebugUi::display() { - display_jobs_window(); - display_player_window(); - display_renderer_window(); - display_baked_world_view_window(); - display_vma_memory_statistics(); + display_jobs_window(); + display_player_window(); + display_renderer_window(); + display_baked_world_view_window(); + display_vma_memory_statistics(); } diff --git a/src/video/DebugUi.hpp b/src/video/DebugUi.hpp index 605ddf1..5fc3d14 100644 --- a/src/video/DebugUi.hpp +++ b/src/video/DebugUi.hpp @@ -2,27 +2,26 @@ namespace explo { - // Forward decl - class Renderer; + // Forward decl + class Renderer; - class DebugUi - { - private: - Renderer& m_renderer; + class DebugUi + { + private: + Renderer &m_renderer; - public: - explicit DebugUi(Renderer& renderer); - ~DebugUi(); + public: + explicit DebugUi(Renderer &renderer); + ~DebugUi(); - void display(); - - private: - void display_jobs_window(); - void display_player_window(); - void display_world_view_window(); - void display_baked_world_view_window(); - void display_renderer_window(); - void display_vma_memory_statistics(); - }; -} // namespace explo + void display(); + private: + void display_jobs_window(); + void display_player_window(); + void display_world_view_window(); + void display_baked_world_view_window(); + void display_renderer_window(); + void display_vma_memory_statistics(); + }; +} // namespace explo diff --git a/src/video/DeviceBuffer.cpp b/src/video/DeviceBuffer.cpp index 35a5cbd..063e942 100644 --- a/src/video/DeviceBuffer.cpp +++ b/src/video/DeviceBuffer.cpp @@ -4,139 +4,121 @@ using namespace explo; -DeviceBuffer::DeviceBuffer( - Renderer& renderer, - VkMemoryPropertyFlags memory_properties, - VkBufferUsageFlags buffer_usages, - size_t init_size - ) : - m_renderer(renderer), - m_memory_properties(memory_properties), - m_buffer_usages(buffer_usages), - m_size(init_size) +DeviceBuffer::DeviceBuffer(Renderer &renderer, VkMemoryPropertyFlags memory_properties, VkBufferUsageFlags buffer_usages, size_t init_size) : + m_renderer(renderer), + m_memory_properties(memory_properties), + m_buffer_usages(buffer_usages), + m_size(init_size) { - m_buffer = std::make_shared(create_buffer(init_size)); + m_buffer = std::make_shared(create_buffer(init_size)); } -DeviceBuffer::~DeviceBuffer() -{ -} +DeviceBuffer::~DeviceBuffer() {} -void DeviceBuffer::write(void* data, size_t data_size, size_t offset) +void DeviceBuffer::write(void *data, size_t data_size, size_t offset) { - // TODO IMPROVEMENT: Cache and re-use the staging buffers; avoid allocating each write - - std::shared_ptr staging_buffer = std::make_shared( - vren::vk_utils::alloc_host_visible_buffer( - m_renderer.m_context, - m_buffer_usages | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, - data_size, - false // Persistently mapped - ) - ); - - vren::vk_utils::set_name(m_renderer.m_context, *staging_buffer, "DeviceBuffer-Write-StagingBuffer"); - - void* staging_buffer_data; - vmaMapMemory(m_renderer.m_context.m_vma_allocator, staging_buffer->m_allocation.m_handle, &staging_buffer_data); - { - std::memcpy(staging_buffer_data, data, data_size); - } - vmaUnmapMemory(m_renderer.m_context.m_vma_allocator, staging_buffer->m_allocation.m_handle); - - // Enqueue a copy operation to copy data from the staging buffer to the actual device buffer - // TODO this is the same as registering the copy on a secondary command buffer; we have to review vren's command_pool/command_buffer API first - CopyOp copy_op{}; - copy_op.m_src_buffer = staging_buffer; - copy_op.m_dst_buffer = m_buffer; - copy_op.m_src_offset = 0; - copy_op.m_dst_offset = offset; - copy_op.m_size = data_size; - - m_operations.push_back(copy_op); + // TODO IMPROVEMENT: Cache and re-use the staging buffers; avoid allocating each write + + std::shared_ptr staging_buffer = std::make_shared(vren::vk_utils::alloc_host_visible_buffer( + m_renderer.m_context, + m_buffer_usages | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + data_size, + false // Persistently mapped + )); + + vren::vk_utils::set_name(m_renderer.m_context, *staging_buffer, "DeviceBuffer-Write-StagingBuffer"); + + void *staging_buffer_data; + vmaMapMemory(m_renderer.m_context.m_vma_allocator, staging_buffer->m_allocation.m_handle, &staging_buffer_data); + { + std::memcpy(staging_buffer_data, data, data_size); + } + vmaUnmapMemory(m_renderer.m_context.m_vma_allocator, staging_buffer->m_allocation.m_handle); + + // Enqueue a copy operation to copy data from the staging buffer to the actual device buffer + // TODO this is the same as registering the copy on a secondary command buffer; we have to review vren's command_pool/command_buffer API first + CopyOp copy_op{}; + copy_op.m_src_buffer = staging_buffer; + copy_op.m_dst_buffer = m_buffer; + copy_op.m_src_offset = 0; + copy_op.m_dst_offset = offset; + copy_op.m_size = data_size; + + m_operations.push_back(copy_op); } void DeviceBuffer::resize(size_t size) { - if (size <= m_size) return; + if (size <= m_size) return; - const size_t k_grow_factor = 4096; // TODO Change this in order to minimize resizes - size_t new_size = glm::ceil(double(size) / double(k_grow_factor)) * k_grow_factor; + const size_t k_grow_factor = 4096; // TODO Change this in order to minimize resizes + size_t new_size = glm::ceil(double(size) / double(k_grow_factor)) * k_grow_factor; - std::shared_ptr new_buffer = - std::make_shared(create_buffer(new_size)); + std::shared_ptr new_buffer = std::make_shared(create_buffer(new_size)); - // Enqueue a copy operation to copy data from the old buffer to the new buffer - CopyOp copy_op{}; - copy_op.m_src_buffer = m_buffer; - copy_op.m_dst_buffer = new_buffer; - copy_op.m_src_offset = 0; - copy_op.m_dst_offset = 0; - copy_op.m_size = std::min(m_size, new_size); + // Enqueue a copy operation to copy data from the old buffer to the new buffer + CopyOp copy_op{}; + copy_op.m_src_buffer = m_buffer; + copy_op.m_dst_buffer = new_buffer; + copy_op.m_src_offset = 0; + copy_op.m_dst_offset = 0; + copy_op.m_size = std::min(m_size, new_size); - m_operations.push_back(copy_op); + m_operations.push_back(copy_op); - // Update the state - m_buffer = new_buffer; - m_size = new_size; + // Update the state + m_buffer = new_buffer; + m_size = new_size; } -void DeviceBuffer::record(VkCommandBuffer command_buffer, vren::resource_container& resource_container) +void DeviceBuffer::record(VkCommandBuffer command_buffer, vren::resource_container &resource_container) { - // TODO IMPROVEMENT: this could be replaced with a compute shader that performs all the copies in parallel - // TODO we only have OP_WRITE, don't make an enum and simplify Op + // TODO IMPROVEMENT: this could be replaced with a compute shader that performs all the copies in parallel + // TODO we only have OP_WRITE, don't make an enum and simplify Op - for (CopyOp const& copy_op : m_operations) - perform_copy(command_buffer, resource_container, copy_op); + for (CopyOp const ©_op : m_operations) perform_copy(command_buffer, resource_container, copy_op); - m_operations.clear(); + m_operations.clear(); } -void DeviceBuffer::perform_copy( - VkCommandBuffer command_buffer, - vren::resource_container& resource_container, - CopyOp const& copy_op -) +void DeviceBuffer::perform_copy(VkCommandBuffer command_buffer, vren::resource_container &resource_container, CopyOp const ©_op) { - VkBufferCopy buffer_copy{}; - buffer_copy.srcOffset = copy_op.m_src_offset; - buffer_copy.dstOffset = copy_op.m_dst_offset; - buffer_copy.size = copy_op.m_size; - - vkCmdCopyBuffer( - command_buffer, - copy_op.m_src_buffer->m_buffer.m_handle, // srcBuffer - copy_op.m_dst_buffer->m_buffer.m_handle, // dstBuffer - 1, &buffer_copy - ); - - resource_container.add_resources( - copy_op.m_src_buffer, - copy_op.m_dst_buffer, - m_buffer - ); + VkBufferCopy buffer_copy{}; + buffer_copy.srcOffset = copy_op.m_src_offset; + buffer_copy.dstOffset = copy_op.m_dst_offset; + buffer_copy.size = copy_op.m_size; + + vkCmdCopyBuffer( + command_buffer, + copy_op.m_src_buffer->m_buffer.m_handle, // srcBuffer + copy_op.m_dst_buffer->m_buffer.m_handle, // dstBuffer + 1, + &buffer_copy + ); + + resource_container.add_resources(copy_op.m_src_buffer, copy_op.m_dst_buffer, m_buffer); } vren::vk_utils::buffer DeviceBuffer::create_buffer(size_t size) { - VkBufferCreateInfo buffer_info{}; - buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - buffer_info.size = size; - buffer_info.usage = m_buffer_usages | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; - buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - - VmaAllocationCreateInfo alloc_create_info{}; - alloc_create_info.requiredFlags = m_memory_properties; - - VkBuffer buffer_handle; - VmaAllocation alloc; - VmaAllocationInfo alloc_info; - vmaCreateBuffer(m_renderer.m_context.m_vma_allocator, &buffer_info, &alloc_create_info, &buffer_handle, &alloc, &alloc_info); - - vren::vk_utils::buffer buffer = vren::vk_utils::buffer{ - .m_buffer = vren::vk_buffer(m_renderer.m_context, buffer_handle), - .m_allocation = vren::vma_allocation (m_renderer.m_context, alloc), - .m_allocation_info = alloc_info, - }; - return std::move(buffer); + VkBufferCreateInfo buffer_info{}; + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.size = size; + buffer_info.usage = m_buffer_usages | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VmaAllocationCreateInfo alloc_create_info{}; + alloc_create_info.requiredFlags = m_memory_properties; + + VkBuffer buffer_handle; + VmaAllocation alloc; + VmaAllocationInfo alloc_info; + vmaCreateBuffer(m_renderer.m_context.m_vma_allocator, &buffer_info, &alloc_create_info, &buffer_handle, &alloc, &alloc_info); + + vren::vk_utils::buffer buffer = vren::vk_utils::buffer{ + .m_buffer = vren::vk_buffer(m_renderer.m_context, buffer_handle), + .m_allocation = vren::vma_allocation(m_renderer.m_context, alloc), + .m_allocation_info = alloc_info, + }; + return std::move(buffer); } diff --git a/src/video/DeviceBuffer.hpp b/src/video/DeviceBuffer.hpp index 1ca077c..4434d55 100644 --- a/src/video/DeviceBuffer.hpp +++ b/src/video/DeviceBuffer.hpp @@ -1,66 +1,56 @@ #pragma once #include - -#include #include +#include #include "util/VirtualAllocator.hpp" namespace explo { - // Forward decl - class Renderer; + // Forward decl + class Renderer; - class DeviceBuffer - { - public: - struct CopyOp - { - std::shared_ptr m_src_buffer; - std::shared_ptr m_dst_buffer; - uint64_t m_src_offset; - uint64_t m_dst_offset; - uint64_t m_size; - }; + class DeviceBuffer + { + public: + struct CopyOp + { + std::shared_ptr m_src_buffer; + std::shared_ptr m_dst_buffer; + uint64_t m_src_offset; + uint64_t m_dst_offset; + uint64_t m_size; + }; - private: - Renderer& m_renderer; + private: + Renderer &m_renderer; - VkMemoryPropertyFlags m_memory_properties; - VkBufferUsageFlags m_buffer_usages; + VkMemoryPropertyFlags m_memory_properties; + VkBufferUsageFlags m_buffer_usages; - std::shared_ptr m_buffer; - size_t m_size = 0; + std::shared_ptr m_buffer; + size_t m_size = 0; - std::vector m_operations; + std::vector m_operations; - public: - explicit DeviceBuffer( - Renderer& renderer, - VkMemoryPropertyFlags memory_properties, - VkBufferUsageFlags buffer_usages, - size_t init_size - ); - ~DeviceBuffer(); + public: + explicit DeviceBuffer(Renderer &renderer, VkMemoryPropertyFlags memory_properties, VkBufferUsageFlags buffer_usages, size_t init_size); + ~DeviceBuffer(); - std::shared_ptr get_buffer() const { return m_buffer; } - size_t get_size() const { return m_size; } - size_t get_pending_operations_count() const { return m_operations.size(); } + std::shared_ptr get_buffer() const { return m_buffer; } + size_t get_size() const { return m_size; } + size_t get_pending_operations_count() const { return m_operations.size(); } - void write(void* data, size_t data_size, size_t offset); - void resize(size_t init_size); + void write(void *data, size_t data_size, size_t offset); + void resize(size_t init_size); - void record(VkCommandBuffer command_buffer, vren::resource_container& resource_container); + void record(VkCommandBuffer command_buffer, vren::resource_container &resource_container); - private: - vren::vk_utils::buffer create_buffer(size_t size); + private: + vren::vk_utils::buffer create_buffer(size_t size); - void perform_copy( - VkCommandBuffer command_buffer, - vren::resource_container& resource_container, - CopyOp const& copy_op - ); - }; + void perform_copy(VkCommandBuffer command_buffer, vren::resource_container &resource_container, CopyOp const ©_op); + }; -} // namespace explo +} // namespace explo diff --git a/src/video/DeviceImage3d.cpp b/src/video/DeviceImage3d.cpp index 6004990..d14318f 100644 --- a/src/video/DeviceImage3d.cpp +++ b/src/video/DeviceImage3d.cpp @@ -4,177 +4,160 @@ using namespace explo; -DeviceImage3d::DeviceImage3d(Renderer& renderer, - glm::ivec3 const& size, - VkFormat format, - VkMemoryPropertyFlags memory_properties - ) : - m_renderer(renderer), - m_size(size), - m_format(format), - m_memory_properties(memory_properties) +DeviceImage3d::DeviceImage3d(Renderer &renderer, glm::ivec3 const &size, VkFormat format, VkMemoryPropertyFlags memory_properties) : + m_renderer(renderer), + m_size(size), + m_format(format), + m_memory_properties(memory_properties) { - m_image = std::make_shared(create_image()); + m_image = std::make_shared(create_image()); } -DeviceImage3d::~DeviceImage3d() -{ -} +DeviceImage3d::~DeviceImage3d() {} -void DeviceImage3d::write_region(glm::ivec3 const& region_position, glm::ivec3 const& region_size, void* data, size_t data_size) +void DeviceImage3d::write_region(glm::ivec3 const ®ion_position, glm::ivec3 const ®ion_size, void *data, size_t data_size) { - std::shared_ptr staging_buffer = std::make_shared( - vren::vk_utils::alloc_host_visible_buffer( - m_renderer.m_context, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - data_size, - true // Persistently mapped - ) - ); - - vren::vk_utils::update_host_visible_buffer(m_renderer.m_context, *staging_buffer, data, data_size, 0); - - Op op{}; - op.m_type = OpType::OP_WRITE_REGION; - - WriteRegionOp& write_region_op = op.m_write_region; - write_region_op.m_buffer = staging_buffer; - write_region_op.m_buffer_offset = 0; - write_region_op.m_region_position = region_position; - write_region_op.m_region_size = region_size; - - m_operations.push_back(op); + std::shared_ptr staging_buffer = std::make_shared(vren::vk_utils::alloc_host_visible_buffer( + m_renderer.m_context, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + data_size, + true // Persistently mapped + )); + + vren::vk_utils::update_host_visible_buffer(m_renderer.m_context, *staging_buffer, data, data_size, 0); + + Op op{}; + op.m_type = OpType::OP_WRITE_REGION; + + WriteRegionOp &write_region_op = op.m_write_region; + write_region_op.m_buffer = staging_buffer; + write_region_op.m_buffer_offset = 0; + write_region_op.m_region_position = region_position; + write_region_op.m_region_size = region_size; + + m_operations.push_back(op); } -void DeviceImage3d::write_pixel(glm::ivec3 const& position, void* data, size_t data_size) +void DeviceImage3d::write_pixel(glm::ivec3 const &position, void *data, size_t data_size) { - write_region(position, glm::ivec3{1, 1, 1}, data, data_size); + write_region(position, glm::ivec3{1, 1, 1}, data, data_size); } -void DeviceImage3d::record(VkCommandBuffer command_buffer, vren::resource_container& resource_container) +void DeviceImage3d::record(VkCommandBuffer command_buffer, vren::resource_container &resource_container) { - for (Op const& op : m_operations) - { - switch (op.m_type) - { - case OP_WRITE_REGION: - perform_write_region(command_buffer, resource_container, op.m_write_region); - break; - default: - throw std::runtime_error("Invalid operation type"); - } - } - - m_operations.clear(); + for (Op const &op : m_operations) + { + switch (op.m_type) + { + case OP_WRITE_REGION: + perform_write_region(command_buffer, resource_container, op.m_write_region); + break; + default: + throw std::runtime_error("Invalid operation type"); + } + } + + m_operations.clear(); } void DeviceImage3d::perform_write_region( - VkCommandBuffer command_buffer, - vren::resource_container& resource_container, - WriteRegionOp const& write_region_op - ) + VkCommandBuffer command_buffer, vren::resource_container &resource_container, WriteRegionOp const &write_region_op +) { - glm::ivec3 reg_pos = write_region_op.m_region_position; - glm::ivec3 reg_size = write_region_op.m_region_size; - - VkBufferImageCopy buffer_image_copy{}; - buffer_image_copy.bufferOffset = write_region_op.m_buffer_offset; - buffer_image_copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - buffer_image_copy.imageSubresource.mipLevel = 0; - buffer_image_copy.imageSubresource.baseArrayLayer = 0; - buffer_image_copy.imageSubresource.layerCount = 1; - buffer_image_copy.imageOffset = {reg_pos.x, reg_pos.y, reg_pos.z}; - buffer_image_copy.imageExtent = {uint32_t(reg_size.x), uint32_t(reg_size.y), uint32_t(reg_size.z)}; - - transit_image_layout(command_buffer, m_image->m_image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - vkCmdCopyBufferToImage( - command_buffer, - write_region_op.m_buffer->m_buffer.m_handle, - m_image->m_image.m_image.m_handle, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 1, - &buffer_image_copy - ); - - transit_image_layout(command_buffer, m_image->m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL); - - resource_container.add_resources(write_region_op.m_buffer, m_image); + glm::ivec3 reg_pos = write_region_op.m_region_position; + glm::ivec3 reg_size = write_region_op.m_region_size; + + VkBufferImageCopy buffer_image_copy{}; + buffer_image_copy.bufferOffset = write_region_op.m_buffer_offset; + buffer_image_copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + buffer_image_copy.imageSubresource.mipLevel = 0; + buffer_image_copy.imageSubresource.baseArrayLayer = 0; + buffer_image_copy.imageSubresource.layerCount = 1; + buffer_image_copy.imageOffset = {reg_pos.x, reg_pos.y, reg_pos.z}; + buffer_image_copy.imageExtent = {uint32_t(reg_size.x), uint32_t(reg_size.y), uint32_t(reg_size.z)}; + + transit_image_layout(command_buffer, m_image->m_image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + vkCmdCopyBufferToImage( + command_buffer, + write_region_op.m_buffer->m_buffer.m_handle, + m_image->m_image.m_image.m_handle, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + &buffer_image_copy + ); + + transit_image_layout(command_buffer, m_image->m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL); + + resource_container.add_resources(write_region_op.m_buffer, m_image); } vren::vk_utils::combined_image_view DeviceImage3d::create_image() { - // Creates the image - VkImageCreateInfo image_info{}; - image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - image_info.pNext = nullptr; - image_info.flags = NULL; - image_info.imageType = VK_IMAGE_TYPE_3D; - image_info.format = m_format; - image_info.extent = {uint32_t(m_size.x), uint32_t(m_size.y), uint32_t(m_size.z)}; - image_info.mipLevels = 1; - image_info.arrayLayers = 1; - image_info.samples = VK_SAMPLE_COUNT_1_BIT; - image_info.tiling = VK_IMAGE_TILING_OPTIMAL; - image_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT; - image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - VmaAllocationCreateInfo alloc_create_info{}; - alloc_create_info.requiredFlags = m_memory_properties; - - VkImage image_handle{}; - VmaAllocation alloc{}; - VmaAllocationInfo alloc_info{}; - vmaCreateImage( - m_renderer.m_context.m_vma_allocator, - &image_info, - &alloc_create_info, - &image_handle, - &alloc, - &alloc_info - ); - - vren::vk_utils::image image = - vren::vk_utils::image{ - .m_image = vren::vk_image(m_renderer.m_context, image_handle), - .m_allocation = vren::vma_allocation(m_renderer.m_context, alloc), - .m_allocation_info = alloc_info - }; - - // Immediately transits the image to VK_IMAGE_LAYOUT_GENERAL - vren::vk_utils::immediate_graphics_queue_submit(m_renderer.m_context, [&](VkCommandBuffer command_buffer, vren::resource_container& resource_container) - { - vren::vk_utils::transit_image_layout(command_buffer, image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); - }); - - // Creates the image view - VkImageViewCreateInfo image_view_info{}; - image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - image_view_info.pNext = nullptr; - image_view_info.flags = NULL; - image_view_info.image = image.m_image.m_handle; - image_view_info.viewType = VK_IMAGE_VIEW_TYPE_3D; - image_view_info.format = m_format; - image_view_info.components.r = VK_COMPONENT_SWIZZLE_R; - image_view_info.components.g = VK_COMPONENT_SWIZZLE_G; - image_view_info.components.b = VK_COMPONENT_SWIZZLE_B; - image_view_info.components.a = VK_COMPONENT_SWIZZLE_A; - image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - image_view_info.subresourceRange.baseMipLevel = 0; - image_view_info.subresourceRange.levelCount = 1; - image_view_info.subresourceRange.baseArrayLayer = 0; - image_view_info.subresourceRange.layerCount = 1; - - VkImageView image_view_handle{}; - vkCreateImageView(m_renderer.m_context.m_device, &image_view_info, nullptr, &image_view_handle); - - vren::vk_image_view image_view = - vren::vk_image_view(m_renderer.m_context, image_view_handle); - - // Combines them into an image/image_view struct - return { - .m_image = std::move(image), - .m_image_view = std::move(image_view), - }; + // Creates the image + VkImageCreateInfo image_info{}; + image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_info.pNext = nullptr; + image_info.flags = NULL; + image_info.imageType = VK_IMAGE_TYPE_3D; + image_info.format = m_format; + image_info.extent = {uint32_t(m_size.x), uint32_t(m_size.y), uint32_t(m_size.z)}; + image_info.mipLevels = 1; + image_info.arrayLayers = 1; + image_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_info.tiling = VK_IMAGE_TILING_OPTIMAL; + image_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT; + image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + VmaAllocationCreateInfo alloc_create_info{}; + alloc_create_info.requiredFlags = m_memory_properties; + + VkImage image_handle{}; + VmaAllocation alloc{}; + VmaAllocationInfo alloc_info{}; + vmaCreateImage(m_renderer.m_context.m_vma_allocator, &image_info, &alloc_create_info, &image_handle, &alloc, &alloc_info); + + vren::vk_utils::image image = vren::vk_utils::image{ + .m_image = vren::vk_image(m_renderer.m_context, image_handle), + .m_allocation = vren::vma_allocation(m_renderer.m_context, alloc), + .m_allocation_info = alloc_info}; + + // Immediately transits the image to VK_IMAGE_LAYOUT_GENERAL + vren::vk_utils::immediate_graphics_queue_submit( + m_renderer.m_context, + [&](VkCommandBuffer command_buffer, vren::resource_container &resource_container) + { + vren::vk_utils::transit_image_layout(command_buffer, image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); + } + ); + + // Creates the image view + VkImageViewCreateInfo image_view_info{}; + image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_info.pNext = nullptr; + image_view_info.flags = NULL; + image_view_info.image = image.m_image.m_handle; + image_view_info.viewType = VK_IMAGE_VIEW_TYPE_3D; + image_view_info.format = m_format; + image_view_info.components.r = VK_COMPONENT_SWIZZLE_R; + image_view_info.components.g = VK_COMPONENT_SWIZZLE_G; + image_view_info.components.b = VK_COMPONENT_SWIZZLE_B; + image_view_info.components.a = VK_COMPONENT_SWIZZLE_A; + image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_view_info.subresourceRange.baseMipLevel = 0; + image_view_info.subresourceRange.levelCount = 1; + image_view_info.subresourceRange.baseArrayLayer = 0; + image_view_info.subresourceRange.layerCount = 1; + + VkImageView image_view_handle{}; + vkCreateImageView(m_renderer.m_context.m_device, &image_view_info, nullptr, &image_view_handle); + + vren::vk_image_view image_view = vren::vk_image_view(m_renderer.m_context, image_view_handle); + + // Combines them into an image/image_view struct + return { + .m_image = std::move(image), + .m_image_view = std::move(image_view), + }; } diff --git a/src/video/DeviceImage3d.hpp b/src/video/DeviceImage3d.hpp index d39e06a..98cc4e5 100644 --- a/src/video/DeviceImage3d.hpp +++ b/src/video/DeviceImage3d.hpp @@ -1,74 +1,62 @@ #pragma once #include - #include #include namespace explo { - // Forward decl - class Renderer; - - class DeviceImage3d - { - public: - struct WriteRegionOp - { - std::shared_ptr m_buffer; - size_t m_buffer_offset; - glm::ivec3 m_region_position; - glm::ivec3 m_region_size; - }; - - enum OpType : uint32_t { OP_WRITE_REGION }; - - struct Op - { - OpType m_type; - - WriteRegionOp m_write_region; // OP_WRITE_REGION - }; - - private: - Renderer& m_renderer; - - std::shared_ptr m_image; - glm::ivec3 m_size; - VkFormat m_format; - VkMemoryPropertyFlags m_memory_properties; - - std::vector m_operations; - - public: - explicit DeviceImage3d(Renderer& renderer, - glm::ivec3 const& size, - VkFormat format, - VkMemoryPropertyFlags memory_properties - ); - ~DeviceImage3d(); - - std::shared_ptr get_image() const { return m_image; } - - void write_pixel(glm::ivec3 const& position, void* data, size_t data_size); - - void write_region( - glm::ivec3 const& region_position, - glm::ivec3 const& region_size, - void* data, - size_t data_size - ); - - void record(VkCommandBuffer command_buffer, vren::resource_container& resource_container); - - private: - void perform_write_region( - VkCommandBuffer command_buffer, - vren::resource_container& resource_container, - WriteRegionOp const& write_region_op - ); - - vren::vk_utils::combined_image_view create_image(); - }; -} + // Forward decl + class Renderer; + + class DeviceImage3d + { + public: + struct WriteRegionOp + { + std::shared_ptr m_buffer; + size_t m_buffer_offset; + glm::ivec3 m_region_position; + glm::ivec3 m_region_size; + }; + + enum OpType : uint32_t + { + OP_WRITE_REGION + }; + + struct Op + { + OpType m_type; + + WriteRegionOp m_write_region; // OP_WRITE_REGION + }; + + private: + Renderer &m_renderer; + + std::shared_ptr m_image; + glm::ivec3 m_size; + VkFormat m_format; + VkMemoryPropertyFlags m_memory_properties; + + std::vector m_operations; + + public: + explicit DeviceImage3d(Renderer &renderer, glm::ivec3 const &size, VkFormat format, VkMemoryPropertyFlags memory_properties); + ~DeviceImage3d(); + + std::shared_ptr get_image() const { return m_image; } + + void write_pixel(glm::ivec3 const &position, void *data, size_t data_size); + + void write_region(glm::ivec3 const ®ion_position, glm::ivec3 const ®ion_size, void *data, size_t data_size); + + void record(VkCommandBuffer command_buffer, vren::resource_container &resource_container); + + private: + void perform_write_region(VkCommandBuffer command_buffer, vren::resource_container &resource_container, WriteRegionOp const &write_region_op); + vren::vk_utils::combined_image_view create_image(); + }; +} // namespace explo diff --git a/src/video/RenderApi.cpp b/src/video/RenderApi.cpp index a291250..6032fa2 100644 --- a/src/video/RenderApi.cpp +++ b/src/video/RenderApi.cpp @@ -6,80 +6,80 @@ using namespace explo; std::unique_ptr s_renderer; -void RenderApi::init(GLFWwindow* window) +void RenderApi::init(GLFWwindow *window) { - s_renderer = std::make_unique(window); + s_renderer = std::make_unique(window); } void RenderApi::destroy() { - s_renderer.reset(); + s_renderer.reset(); } -Renderer& RenderApi::renderer() +Renderer &RenderApi::renderer() { - return *s_renderer; + return *s_renderer; } void RenderApi::window_resize(uint32_t width, uint32_t height) { - s_renderer->on_window_resize(width, height); + s_renderer->on_window_resize(width, height); } void RenderApi::render() { - s_renderer->render(); + s_renderer->render(); } /* Camera */ -void RenderApi::camera_set_position(glm::vec3 const& position) +void RenderApi::camera_set_position(glm::vec3 const &position) { - s_renderer->set_camera_position(position); + s_renderer->set_camera_position(position); } void RenderApi::camera_set_rotation(float yaw, float pitch) { - s_renderer->set_camera_rotation(yaw, pitch); + s_renderer->set_camera_rotation(yaw, pitch); } void RenderApi::camera_set_projection_params(float fov_y, float aspect_ratio, float near_plane, float far_plane) { - s_renderer->set_camera_projection_params(fov_y, aspect_ratio, near_plane, far_plane); + s_renderer->set_camera_projection_params(fov_y, aspect_ratio, near_plane, far_plane); } /* Block registry */ -void RenderApi::block_registry_upload(BlockRegistry const& block_registry) +void RenderApi::block_registry_upload(BlockRegistry const &block_registry) { - s_renderer->upload_block_registry(block_registry); + s_renderer->upload_block_registry(block_registry); } /* World view */ -void RenderApi::world_view_recreate(glm::ivec3 const& init_position, glm::ivec3 const& render_distance) +void RenderApi::world_view_recreate(glm::ivec3 const &init_position, glm::ivec3 const &render_distance) { - s_renderer->recreate_world_view(init_position, render_distance); + s_renderer->recreate_world_view(init_position, render_distance); } -void RenderApi::world_view_set_position(glm::ivec3 const& offset) +void RenderApi::world_view_set_position(glm::ivec3 const &offset) { - s_renderer->get_world_view().set_position(offset); + s_renderer->get_world_view().set_position(offset); } -void RenderApi::world_view_upload_chunk(Chunk const& chunk) +void RenderApi::world_view_upload_chunk(Chunk const &chunk) { - s_renderer->get_world_view().upload_chunk(chunk); + s_renderer->get_world_view().upload_chunk(chunk); } -void RenderApi::world_view_destroy_chunk(glm::ivec3 const& chunk_pos) +void RenderApi::world_view_destroy_chunk(glm::ivec3 const &chunk_pos) { - s_renderer->get_world_view().destroy_chunk(chunk_pos); + s_renderer->get_world_view().destroy_chunk(chunk_pos); } /* UI */ -void RenderApi::ui_draw(std::function const& callback) +void RenderApi::ui_draw(std::function const &callback) { - s_renderer->set_ui_setup_function(callback); + s_renderer->set_ui_setup_function(callback); } diff --git a/src/video/RenderApi.hpp b/src/video/RenderApi.hpp index 6b3eb71..4644cfd 100644 --- a/src/video/RenderApi.hpp +++ b/src/video/RenderApi.hpp @@ -1,50 +1,50 @@ #pragma once -#include -#include - #include + +#include #include +#include #include "world/BlockRegistry.hpp" #include "world/Chunk.hpp" namespace explo { - // Forward decl - class Renderer; + // Forward decl + class Renderer; - /// An API used from core to perform rendering actions. - namespace RenderApi - { - void init(GLFWwindow* window); - void destroy(); + /// An API used from core to perform rendering actions. + namespace RenderApi + { + void init(GLFWwindow *window); + void destroy(); - Renderer& renderer(); + Renderer &renderer(); - void window_resize(uint32_t width, uint32_t height); - void render(); + void window_resize(uint32_t width, uint32_t height); + void render(); - /* Camera */ + /* Camera */ - void camera_set_position(glm::vec3 const& position); - void camera_set_rotation(float yaw, float pitch); - void camera_set_projection_params(float fov_y, float aspect_ratio, float near_plane, float far_plane); + void camera_set_position(glm::vec3 const &position); + void camera_set_rotation(float yaw, float pitch); + void camera_set_projection_params(float fov_y, float aspect_ratio, float near_plane, float far_plane); - /* World view */ + /* World view */ - void world_view_recreate(glm::ivec3 const& init_chunk_pos, glm::ivec3 const& render_distance); - void world_view_set_position(glm::ivec3 const& chunk_pos); - void world_view_upload_chunk(Chunk const& chunk); - void world_view_destroy_chunk(glm::ivec3 const& chunk_pos); + void world_view_recreate(glm::ivec3 const &init_chunk_pos, glm::ivec3 const &render_distance); + void world_view_set_position(glm::ivec3 const &chunk_pos); + void world_view_upload_chunk(Chunk const &chunk); + void world_view_destroy_chunk(glm::ivec3 const &chunk_pos); - /* Block registry */ + /* Block registry */ - void block_registry_upload(BlockRegistry const& block_registry); + void block_registry_upload(BlockRegistry const &block_registry); - /* UI */ + /* UI */ - /// Sets a function in charge of displaying the UI using the ImGui API. - void ui_draw(std::function const& callback); - }; -} + /// Sets a function in charge of displaying the UI using the ImGui API. + void ui_draw(std::function const &callback); + }; // namespace RenderApi +} // namespace explo diff --git a/src/video/Renderer.cpp b/src/video/Renderer.cpp index 90a7d9c..754c909 100644 --- a/src/video/Renderer.cpp +++ b/src/video/Renderer.cpp @@ -1,119 +1,122 @@ #include "Renderer.hpp" -#include - #include #include +#include + #include "Game.hpp" #include "log.hpp" using namespace explo; -Renderer::Renderer(GLFWwindow* window) : - m_window(window), - - m_context([]() { - vren::context_info context_info{ - .m_app_name = "explo", - .m_app_version = VK_MAKE_VERSION(1, 0, 0), - .m_layers = {}, - .m_extensions = {}, - .m_device_extensions = {} - }; - - uint32_t glfw_extension_count = 0; - char const** glfw_extensions; - glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extension_count); - context_info.m_extensions.insert(context_info.m_extensions.end(), glfw_extensions, glfw_extensions + glfw_extension_count); - - return vren::context(context_info); - }()), - - m_surface([&]() { - VkSurfaceKHR surface_handle{}; - VREN_CHECK(glfwCreateWindowSurface(m_context.m_instance, m_window, nullptr, &surface_handle), &m_context); - return vren::vk_surface_khr(m_context, surface_handle); - }()), - - m_presenter(m_context, m_surface, [this](vren::swapchain const& swapchain) { - on_swapchain_change(swapchain); - }), +Renderer::Renderer(GLFWwindow *window) : + m_window(window), + + m_context( + []() + { + vren::context_info context_info{ + .m_app_name = "explo", .m_app_version = VK_MAKE_VERSION(1, 0, 0), .m_layers = {}, .m_extensions = {}, .m_device_extensions = {}}; + + uint32_t glfw_extension_count = 0; + char const **glfw_extensions; + glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extension_count); + context_info.m_extensions.insert(context_info.m_extensions.end(), glfw_extensions, glfw_extensions + glfw_extension_count); + + return vren::context(context_info); + }() + ), + + m_surface( + [&]() + { + VkSurfaceKHR surface_handle{}; + VREN_CHECK(glfwCreateWindowSurface(m_context.m_instance, m_window, nullptr, &surface_handle), &m_context); + return vren::vk_surface_khr(m_context, surface_handle); + }() + ), + + m_presenter( + m_context, + m_surface, + [this](vren::swapchain const &swapchain) + { + on_swapchain_change(swapchain); + } + ), m_render_graph_allocator(), m_cluster_and_shade(m_context), - m_imgui_renderer(m_context, vren::imgui_windowing_backend_hooks{ - .m_init_callback = [this]() { ImGui_ImplGlfw_InitForVulkan(m_window, true); }, - .m_new_frame_callback = []() { ImGui_ImplGlfw_NewFrame(); }, - .m_shutdown_callback = []() { ImGui_ImplGlfw_Shutdown(); } - }), - - m_debug_draw_buffer(m_context), - m_debug_renderer(m_context), + m_imgui_renderer( + m_context, + vren::imgui_windowing_backend_hooks{ + .m_init_callback = + [this]() + { + ImGui_ImplGlfw_InitForVulkan(m_window, true); + }, + .m_new_frame_callback = + []() + { + ImGui_ImplGlfw_NewFrame(); + }, + .m_shutdown_callback = + []() + { + ImGui_ImplGlfw_Shutdown(); + }} + ), + + m_debug_draw_buffer(m_context), + m_debug_renderer(m_context), m_light_array(m_context), m_material_buffer(m_context), - m_chunk_draw_list(vren::vk_utils::create_device_only_buffer( - m_context, - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, - nullptr, - k_chunk_draw_list_buffer_size - )), - m_chunk_draw_list_idx(vren::vk_utils::create_device_only_buffer( - m_context, - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, - nullptr, - sizeof(uint32_t) - )), - - m_cull_world_view(*this), - m_draw_chunk_list(*this) + m_chunk_draw_list(vren::vk_utils::create_device_only_buffer( + m_context, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, nullptr, k_chunk_draw_list_buffer_size + )), + m_chunk_draw_list_idx(vren::vk_utils::create_device_only_buffer( + m_context, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, nullptr, sizeof(uint32_t) + )), + + m_cull_world_view(*this), + m_draw_chunk_list(*this) { - vren::directional_light* directional_lights = - m_light_array.m_directional_light_buffer.get_mapped_pointer(); + vren::directional_light *directional_lights = m_light_array.m_directional_light_buffer.get_mapped_pointer(); - directional_lights[0].m_direction = glm::vec3(0.707f, -0.707f, 0.); - directional_lights[0].m_color = glm::vec3(255, 221, 48) / glm::vec3(255.0f); + directional_lights[0].m_direction = glm::vec3(0.707f, -0.707f, 0.); + directional_lights[0].m_color = glm::vec3(255, 221, 48) / glm::vec3(255.0f); - m_light_array.m_directional_light_count = 1; + m_light_array.m_directional_light_count = 1; } -Renderer::~Renderer() -{ -} +Renderer::~Renderer() {} glm::uvec2 Renderer::get_framebuffer_size() const { - return glm::uvec2( - m_presenter.get_swapchain()->m_image_width, - m_presenter.get_swapchain()->m_image_height - ); + return glm::uvec2(m_presenter.get_swapchain()->m_image_width, m_presenter.get_swapchain()->m_image_height); } -void Renderer::on_swapchain_change(vren::swapchain const& swapchain) +void Renderer::on_swapchain_change(vren::swapchain const &swapchain) { uint32_t width = swapchain.m_image_width; uint32_t height = swapchain.m_image_height; - m_color_buffer = std::make_shared( - vren::vk_utils::create_color_buffer( - m_context, - width, height, - VREN_COLOR_BUFFER_OUTPUT_FORMAT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT - ) - ); + m_color_buffer = std::make_shared(vren::vk_utils::create_color_buffer( + m_context, + width, + height, + VREN_COLOR_BUFFER_OUTPUT_FORMAT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT + )); - m_depth_buffer = std::make_shared( - vren::vk_utils::create_depth_buffer( - m_context, - width, height, - VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT - ) - ); + m_depth_buffer = std::make_shared(vren::vk_utils::create_depth_buffer( + m_context, width, height, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT + )); m_gbuffer = std::make_shared(m_context, width, height); @@ -122,18 +125,18 @@ void Renderer::on_swapchain_change(vren::swapchain const& swapchain) void Renderer::on_window_resize(int width, int height) { - LOG_D("render", "Window resized to ({}, {})", width, height); + LOG_D("render", "Window resized to ({}, {})", width, height); m_presenter.recreate_swapchain(width, height); } -void Renderer::flush_device_buffer_operations(VkCommandBuffer cmd_buf, vren::resource_container& res_container) +void Renderer::flush_device_buffer_operations(VkCommandBuffer cmd_buf, vren::resource_container &res_container) { - m_baked_world_view->m_vertex_buffer.record(cmd_buf, res_container); - m_baked_world_view->m_index_buffer.record(cmd_buf, res_container); - m_baked_world_view->m_instance_buffer.record(cmd_buf, res_container); + m_baked_world_view->m_vertex_buffer.record(cmd_buf, res_container); + m_baked_world_view->m_index_buffer.record(cmd_buf, res_container); + m_baked_world_view->m_instance_buffer.record(cmd_buf, res_container); - m_baked_world_view->m_circular_grid.m_gpu_image->record(cmd_buf, res_container); + m_baked_world_view->m_circular_grid.m_gpu_image->record(cmd_buf, res_container); } static bool g_render_graph_dump_taken = false; @@ -141,113 +144,99 @@ static bool g_render_graph_dump_taken = false; void Renderer::on_frame( uint32_t frame_idx, uint32_t swapchain_image_idx, - vren::swapchain const& swapchain, + vren::swapchain const &swapchain, VkCommandBuffer cmd_buf, - vren::resource_container& res_container + vren::resource_container &res_container ) { - if (!m_baked_world_view) return; + if (!m_baked_world_view) return; - // Barrier to ensure this frame's commands are executed only when ALL the previous commands have executed on GPU - vren::vk_utils::pipeline_barrier(cmd_buf); + // Barrier to ensure this frame's commands are executed only when ALL the previous commands have executed on GPU + vren::vk_utils::pipeline_barrier(cmd_buf); - // Records the host-conducted operations on device buffers; then place barriers to synchronize them with the render-graph - // execution. This can't be a render-graph node because operations can potentially change buffers (e.g. resize), and render-graph - // executor wouldn't be able to place barriers - flush_device_buffer_operations(cmd_buf, res_container); + // Records the host-conducted operations on device buffers; then place barriers to synchronize them with the render-graph + // execution. This can't be a render-graph node because operations can potentially change buffers (e.g. resize), and render-graph + // executor wouldn't be able to place barriers + flush_device_buffer_operations(cmd_buf, res_container); - vren::vk_utils::pipeline_barrier(cmd_buf, *m_baked_world_view->m_vertex_buffer.get_buffer()); - vren::vk_utils::pipeline_barrier(cmd_buf, *m_baked_world_view->m_index_buffer.get_buffer()); - vren::vk_utils::pipeline_barrier(cmd_buf, *m_baked_world_view->m_instance_buffer.get_buffer()); + vren::vk_utils::pipeline_barrier(cmd_buf, *m_baked_world_view->m_vertex_buffer.get_buffer()); + vren::vk_utils::pipeline_barrier(cmd_buf, *m_baked_world_view->m_index_buffer.get_buffer()); + vren::vk_utils::pipeline_barrier(cmd_buf, *m_baked_world_view->m_instance_buffer.get_buffer()); - vren::vk_utils::pipeline_barrier(cmd_buf, m_baked_world_view->m_circular_grid.m_gpu_image->get_image()->m_image); + vren::vk_utils::pipeline_barrier(cmd_buf, m_baked_world_view->m_circular_grid.m_gpu_image->get_image()->m_image); - // Begin render graph - uint64_t start_ns = get_nanos_since_epoch(); + // Begin render graph + uint64_t start_ns = get_nanos_since_epoch(); vren::render_graph_builder render_graph(m_render_graph_allocator); - auto render_target = - vren::render_target::cover(swapchain.m_image_width, swapchain.m_image_height, *m_color_buffer, *m_depth_buffer); + auto render_target = vren::render_target::cover(swapchain.m_image_width, swapchain.m_image_height, *m_color_buffer, *m_depth_buffer); - glm::uvec2 screen(swapchain.m_image_width, swapchain.m_image_height); + glm::uvec2 screen(swapchain.m_image_width, swapchain.m_image_height); VkImage output = swapchain.m_images.at(swapchain_image_idx); - res_container.add_resources(m_color_buffer, m_depth_buffer, m_gbuffer); + res_container.add_resources(m_color_buffer, m_depth_buffer, m_gbuffer); // Clears color buffer render_graph.concat(vren::clear_color_buffer(m_render_graph_allocator, m_color_buffer->get_image(), m_background_color)); // Clears depth buffer - render_graph.concat(vren::clear_depth_stencil_buffer(m_render_graph_allocator, m_depth_buffer->get_image(), { .depth = 1.0f })); + render_graph.concat(vren::clear_depth_stencil_buffer(m_render_graph_allocator, m_depth_buffer->get_image(), {.depth = 1.0f})); // Clears gbuffer render_graph.concat(vren::clear_gbuffer(m_render_graph_allocator, *m_gbuffer)); - // Filters only the world view chunks that are visible to the camera - render_graph.concat(m_cull_world_view.create_render_graph_node(m_render_graph_allocator)); - - // Draws the list of visible chunks - render_graph.concat(m_draw_chunk_list.create_render_graph_node(m_render_graph_allocator)); - - // Applies deferred shading - render_graph.concat( - m_cluster_and_shade( - m_render_graph_allocator, - screen, - m_camera, - *m_gbuffer, - *m_depth_buffer, - m_light_array, - m_material_buffer, - *m_color_buffer - ) - ); + // Filters only the world view chunks that are visible to the camera + render_graph.concat(m_cull_world_view.create_render_graph_node(m_render_graph_allocator)); + + // Draws the list of visible chunks + render_graph.concat(m_draw_chunk_list.create_render_graph_node(m_render_graph_allocator)); + + // Applies deferred shading + render_graph.concat(m_cluster_and_shade( + m_render_graph_allocator, screen, m_camera, *m_gbuffer, *m_depth_buffer, m_light_array, m_material_buffer, *m_color_buffer + )); + + /* + m_debug_draw_buffer.clear(); + + m_debug_draw_buffer.add_cube({ + .m_min = glm::vec3(m_baked_world_view->m_position - m_baked_world_view->m_render_distance) * Chunk::k_world_size, + .m_max = glm::vec3(m_baked_world_view->m_position + m_baked_world_view->m_render_distance + 1) * Chunk::k_world_size, + .m_color = 0xFFFF0000, + }); - /* - m_debug_draw_buffer.clear(); - - m_debug_draw_buffer.add_cube({ - .m_min = glm::vec3(m_baked_world_view->m_position - m_baked_world_view->m_render_distance) * Chunk::k_world_size, - .m_max = glm::vec3(m_baked_world_view->m_position + m_baked_world_view->m_render_distance + 1) * Chunk::k_world_size, - .m_color = 0xFFFF0000, - }); - - // Draw debug lines - render_graph.concat( - m_debug_renderer.render( - m_render_graph_allocator, - render_target, - vren::camera_data{ - .m_position = m_camera.m_position, - .m_view = m_camera.get_view(), - .m_projection = m_camera.get_projection(), - .m_z_near = m_camera.m_near_plane - }, - m_debug_draw_buffer, - true - )); - */ - - // Renders UI - if (m_ui_setup_function) - { - render_graph.concat(m_imgui_renderer.render(m_render_graph_allocator, render_target, m_ui_setup_function)); - } + // Draw debug lines + render_graph.concat(m_debug_renderer.render( + m_render_graph_allocator, + render_target, + vren::camera_data{ + .m_position = m_camera.m_position, + .m_view = m_camera.get_view(), + .m_projection = m_camera.get_projection(), + .m_z_near = m_camera.m_near_plane}, + m_debug_draw_buffer, + true + )); + */ + + // Renders UI + if (m_ui_setup_function) + { + render_graph.concat(m_imgui_renderer.render(m_render_graph_allocator, render_target, m_ui_setup_function)); + } // Blits the color buffer to the swapchain image for presentation - render_graph.concat( - vren::blit_color_buffer_to_swapchain_image( - m_render_graph_allocator, - *m_color_buffer, - swapchain.m_image_width, - swapchain.m_image_height, - output, - swapchain.m_image_width, - swapchain.m_image_height - ) - ); + render_graph.concat(vren::blit_color_buffer_to_swapchain_image( + m_render_graph_allocator, + *m_color_buffer, + swapchain.m_image_width, + swapchain.m_image_height, + output, + swapchain.m_image_width, + swapchain.m_image_height + )); // Transits swapchain image to present layout render_graph.concat(vren::transit_swapchain_image_to_present_layout(m_render_graph_allocator, output)); @@ -256,137 +245,130 @@ void Renderer::on_frame( vren::render_graph_executor executor(frame_idx, cmd_buf, res_container); executor.execute(m_render_graph_allocator, render_graph.get_head()); - if (!g_render_graph_dump_taken) - { - std::ofstream of("./render_graph.dot"); - vren::render_graph_dumper dumper(of); - dumper.dump(m_render_graph_allocator, render_graph.get_head()); + if (!g_render_graph_dump_taken) + { + std::ofstream of("./render_graph.dot"); + vren::render_graph_dumper dumper(of); + dumper.dump(m_render_graph_allocator, render_graph.get_head()); - g_render_graph_dump_taken = true; - } + g_render_graph_dump_taken = true; + } m_render_graph_allocator.clear(); - m_profile_stats.push_elapsed_time(get_nanos_since_epoch() - start_ns); + m_profile_stats.push_elapsed_time(get_nanos_since_epoch() - start_ns); } /* Camera */ -void Renderer::set_camera_position(glm::vec3 const& position) +void Renderer::set_camera_position(glm::vec3 const &position) { - m_camera.m_position = position; - rebuild_camera_view_matrix(); + m_camera.m_position = position; + rebuild_camera_view_matrix(); } void Renderer::set_camera_rotation(float yaw, float pitch) { - m_camera.m_yaw = yaw; - m_camera.m_pitch = pitch; - rebuild_camera_view_matrix(); + m_camera.m_yaw = yaw; + m_camera.m_pitch = pitch; + rebuild_camera_view_matrix(); } void Renderer::set_camera_projection_params(float fov_y, float aspect_ratio, float near_plane, float far_plane) { - m_camera.m_fov_y = fov_y; - m_camera.m_aspect_ratio = aspect_ratio; - m_camera.m_near_plane = near_plane; - m_camera.m_far_plane = far_plane; - rebuild_camera_projection_matrix(); + m_camera.m_fov_y = fov_y; + m_camera.m_aspect_ratio = aspect_ratio; + m_camera.m_near_plane = near_plane; + m_camera.m_far_plane = far_plane; + rebuild_camera_projection_matrix(); } void Renderer::rebuild_camera_view_matrix() { - m_view_matrix = m_camera.get_view(); + m_view_matrix = m_camera.get_view(); } void Renderer::rebuild_camera_projection_matrix() { - m_projection_matrix = m_camera.get_projection(); + m_projection_matrix = m_camera.get_projection(); } /* World view */ -void Renderer::recreate_world_view(glm::ivec3 const& init_position, glm::ivec3 const& render_distance) +void Renderer::recreate_world_view(glm::ivec3 const &init_position, glm::ivec3 const &render_distance) { - m_baked_world_view = std::make_unique(*this, init_position, render_distance); + m_baked_world_view = std::make_unique(*this, init_position, render_distance); } -BakedWorldView& Renderer::get_world_view() +BakedWorldView &Renderer::get_world_view() { - // todo check baked_world_view exists - return *m_baked_world_view; + // todo check baked_world_view exists + return *m_baked_world_view; } -void Renderer::upload_block_registry(BlockRegistry const& block_registry) +void Renderer::upload_block_registry(BlockRegistry const &block_registry) { - vren::texture_manager& texture_manager = m_context.m_toolbox->m_texture_manager; - - texture_manager.m_textures.clear(); - - if (block_registry.size() > 0) - { - std::vector image_data{}; - image_data.reserve(block_registry.size()); - - for (BlockData const& block_data : block_registry.get_block_data()) - image_data.push_back(block_data.m_color); - - vren::vk_utils::texture texture = vren::vk_utils::create_texture( - m_context, - block_registry.size(), - 1, - image_data.data(), - VK_FORMAT_R8G8B8A8_UNORM, - VK_FILTER_NEAREST, - VK_FILTER_NEAREST, - VK_SAMPLER_MIPMAP_MODE_NEAREST, - VK_SAMPLER_ADDRESS_MODE_REPEAT, - VK_SAMPLER_ADDRESS_MODE_REPEAT, - VK_SAMPLER_ADDRESS_MODE_REPEAT - ); - texture_manager.m_textures.push_back(std::move(texture)); - - LOG_D("upload_block_registry", "Texture uploaded and registered in texture manager"); - } - - vren::vk_utils::texture metallic_roughness_texture = - vren::vk_utils::create_color_texture(m_context, 255, /* roughness */ 128, /* metallic */ 255, 255); - texture_manager.m_textures.push_back(std::move(metallic_roughness_texture)); - - texture_manager.rewrite_descriptor_set(); - - // Update the material buffer - vren::material_buffer& material_buffer = m_material_buffer; - vren::material* material_buffer_ptr = material_buffer.m_buffer.get_mapped_pointer(); - - material_buffer_ptr[0] = vren::material{ - .m_base_color_texture_idx = 0, // Block atlas - .m_metallic_roughness_texture_idx = 1, // Metallic roughness texture - .m_metallic_factor = 1.0f, - .m_roughness_factor = 1.0f, - .m_base_color_factor = glm::vec4(1), - }; - material_buffer.m_material_count = 1; - - LOG_D("upload_block_registry", "Material buffer updated"); + vren::texture_manager &texture_manager = m_context.m_toolbox->m_texture_manager; + + texture_manager.m_textures.clear(); + + if (block_registry.size() > 0) + { + std::vector image_data{}; + image_data.reserve(block_registry.size()); + + for (BlockData const &block_data : block_registry.get_block_data()) image_data.push_back(block_data.m_color); + + vren::vk_utils::texture texture = vren::vk_utils::create_texture( + m_context, + block_registry.size(), + 1, + image_data.data(), + VK_FORMAT_R8G8B8A8_UNORM, + VK_FILTER_NEAREST, + VK_FILTER_NEAREST, + VK_SAMPLER_MIPMAP_MODE_NEAREST, + VK_SAMPLER_ADDRESS_MODE_REPEAT, + VK_SAMPLER_ADDRESS_MODE_REPEAT, + VK_SAMPLER_ADDRESS_MODE_REPEAT + ); + texture_manager.m_textures.push_back(std::move(texture)); + + LOG_D("upload_block_registry", "Texture uploaded and registered in texture manager"); + } + + vren::vk_utils::texture metallic_roughness_texture = + vren::vk_utils::create_color_texture(m_context, 255, /* roughness */ 128, /* metallic */ 255, 255); + texture_manager.m_textures.push_back(std::move(metallic_roughness_texture)); + + texture_manager.rewrite_descriptor_set(); + + // Update the material buffer + vren::material_buffer &material_buffer = m_material_buffer; + vren::material *material_buffer_ptr = material_buffer.m_buffer.get_mapped_pointer(); + + material_buffer_ptr[0] = vren::material{ + .m_base_color_texture_idx = 0, // Block atlas + .m_metallic_roughness_texture_idx = 1, // Metallic roughness texture + .m_metallic_factor = 1.0f, + .m_roughness_factor = 1.0f, + .m_base_color_factor = glm::vec4(1), + }; + material_buffer.m_material_count = 1; + + LOG_D("upload_block_registry", "Material buffer updated"); } void Renderer::render() { - m_presenter.present([&]( - uint32_t frame_idx, - uint32_t swapchain_image_idx, - vren::swapchain const& swapchain, - VkCommandBuffer command_buffer, - vren::resource_container& resource_container - ) - { - on_frame( - frame_idx, - swapchain_image_idx, - swapchain, - command_buffer, - resource_container - ); - }); + m_presenter.present( + [&](uint32_t frame_idx, + uint32_t swapchain_image_idx, + vren::swapchain const &swapchain, + VkCommandBuffer command_buffer, + vren::resource_container &resource_container) + { + on_frame(frame_idx, swapchain_image_idx, swapchain, command_buffer, resource_container); + } + ); } diff --git a/src/video/Renderer.hpp b/src/video/Renderer.hpp index 2a7c3bb..fbf5c9f 100644 --- a/src/video/Renderer.hpp +++ b/src/video/Renderer.hpp @@ -1,56 +1,55 @@ #pragma once -#include -#include - -#include #include -#include +#include +#include +#include +#include #include -#include #include -#include -#include #include +#include +#include +#include -#include "util/profile_stats.hpp" -#include "world/WorldView.hpp" -#include "world/Chunk.hpp" #include "BakedWorldView.hpp" #include "pipeline/CullWorldView.hpp" #include "pipeline/DrawChunkList.hpp" +#include "util/profile_stats.hpp" #include "world/BlockRegistry.hpp" +#include "world/Chunk.hpp" +#include "world/WorldView.hpp" namespace explo { class Renderer { - friend class BakedWorldView; - friend class BakedWorldViewCircularGrid; - friend class CullWorldView; - friend class DebugUi; - friend class DeviceBuffer; - friend class DeviceImage3d; - friend class DrawChunkList; + friend class BakedWorldView; + friend class BakedWorldViewCircularGrid; + friend class CullWorldView; + friend class DebugUi; + friend class DeviceBuffer; + friend class DeviceImage3d; + friend class DrawChunkList; - private: - GLFWwindow* m_window; + private: + GLFWwindow *m_window; - vren::context m_context; + vren::context m_context; - profile_stats m_profile_stats; + profile_stats m_profile_stats; - vren::vk_surface_khr m_surface; - vren::presenter m_presenter; + vren::vk_surface_khr m_surface; + vren::presenter m_presenter; - vren::render_graph_allocator m_render_graph_allocator; + vren::render_graph_allocator m_render_graph_allocator; vren::cluster_and_shade m_cluster_and_shade; - vren::imgui_renderer m_imgui_renderer; + vren::imgui_renderer m_imgui_renderer; - vren::debug_renderer_draw_buffer m_debug_draw_buffer; - vren::debug_renderer m_debug_renderer; + vren::debug_renderer_draw_buffer m_debug_draw_buffer; + vren::debug_renderer m_debug_renderer; std::shared_ptr m_color_buffer; std::shared_ptr m_depth_buffer; @@ -59,74 +58,71 @@ namespace explo vren::light_array m_light_array; vren::material_buffer m_material_buffer; - std::function m_ui_setup_function; + std::function m_ui_setup_function; - vren::camera m_camera; - glm::mat4 m_view_matrix; - glm::mat4 m_projection_matrix; + vren::camera m_camera; + glm::mat4 m_view_matrix; + glm::mat4 m_projection_matrix; - std::unique_ptr m_baked_world_view; + std::unique_ptr m_baked_world_view; - /// A list of draw calls for every visible chunk (format is VkDrawIndexedIndirectCommand). - /// Vertex, index, instance buffers are managed by BakedWorldView. - vren::vk_utils::buffer m_chunk_draw_list; + /// A list of draw calls for every visible chunk (format is VkDrawIndexedIndirectCommand). + /// Vertex, index, instance buffers are managed by BakedWorldView. + vren::vk_utils::buffer m_chunk_draw_list; - /// An atomic counter which is used to allocate the draw call within m_chunk_draw_list. - vren::vk_utils::buffer m_chunk_draw_list_idx; + /// An atomic counter which is used to allocate the draw call within m_chunk_draw_list. + vren::vk_utils::buffer m_chunk_draw_list_idx; - CullWorldView m_cull_world_view; - DrawChunkList m_draw_chunk_list; + CullWorldView m_cull_world_view; + DrawChunkList m_draw_chunk_list; - public: - static constexpr size_t k_chunk_draw_list_buffer_size = 8388608; // 8MB + public: + static constexpr size_t k_chunk_draw_list_buffer_size = 8388608; // 8MB - VkClearColorValue m_background_color = VkClearColorValue{0.32f, 0.933f, 1.0f, 0.0f}; + VkClearColorValue m_background_color = VkClearColorValue{0.32f, 0.933f, 1.0f, 0.0f}; - explicit Renderer(GLFWwindow* m_window); + explicit Renderer(GLFWwindow *m_window); ~Renderer(); - glm::uvec2 get_framebuffer_size() const; + glm::uvec2 get_framebuffer_size() const; - auto const& get_profile_stats() const { return m_profile_stats; } + auto const &get_profile_stats() const { return m_profile_stats; } - void on_window_resize(int width, int height); + void on_window_resize(int width, int height); - void set_ui_setup_function(std::function const& ui_setup_function) - { - m_ui_setup_function = ui_setup_function; - } + void set_ui_setup_function(std::function const &ui_setup_function) { m_ui_setup_function = ui_setup_function; } - /* Camera */ + /* Camera */ - void set_camera_position(glm::vec3 const& position); - void set_camera_rotation(float yaw, float pitch); - void set_camera_projection_params(float fov_y, float aspect_ratio, float near_plane, float far_plane); + void set_camera_position(glm::vec3 const &position); + void set_camera_rotation(float yaw, float pitch); + void set_camera_projection_params(float fov_y, float aspect_ratio, float near_plane, float far_plane); - void rebuild_camera_view_matrix(); - void rebuild_camera_projection_matrix(); + void rebuild_camera_view_matrix(); + void rebuild_camera_projection_matrix(); - /* World view */ + /* World view */ - bool has_world_view() const { return bool(m_baked_world_view); } - void recreate_world_view(glm::ivec3 const& init_position, glm::ivec3 const& render_distance); - BakedWorldView& get_world_view(); + bool has_world_view() const { return bool(m_baked_world_view); } + void recreate_world_view(glm::ivec3 const &init_position, glm::ivec3 const &render_distance); + BakedWorldView &get_world_view(); - void upload_block_registry(BlockRegistry const& block_registry); + void upload_block_registry(BlockRegistry const &block_registry); - void render(); + void render(); - private: - void on_swapchain_change(vren::swapchain const& swapchain); + private: + void on_swapchain_change(vren::swapchain const &swapchain); - /// Flushes the host operations pending on the device buffers (e.g. vertex buffer, index buffer, ...). - void flush_device_buffer_operations(VkCommandBuffer cmd_buf, vren::resource_container& res_container); + /// Flushes the host operations pending on the device buffers (e.g. vertex buffer, index buffer, ...). + void flush_device_buffer_operations(VkCommandBuffer cmd_buf, vren::resource_container &res_container); void on_frame( uint32_t frame_idx, uint32_t swapchain_image_idx, - vren::swapchain const& swapchain, + vren::swapchain const &swapchain, VkCommandBuffer command_buffer, - vren::resource_container& resource_container + vren::resource_container &resource_container ); }; -} +} // namespace explo diff --git a/src/video/pipeline/CullWorldView.cpp b/src/video/pipeline/CullWorldView.cpp index ad9fc1c..4eed0e8 100644 --- a/src/video/pipeline/CullWorldView.cpp +++ b/src/video/pipeline/CullWorldView.cpp @@ -4,105 +4,135 @@ using namespace explo; -CullWorldView::CullWorldView(Renderer& renderer) : - m_renderer(renderer), - m_pipeline(create_pipeline()) +CullWorldView::CullWorldView(Renderer &renderer) : + m_renderer(renderer), + m_pipeline(create_pipeline()) { } -CullWorldView::~CullWorldView() -{ -} +CullWorldView::~CullWorldView() {} -void CullWorldView::record(VkCommandBuffer cmd_buf, vren::resource_container& res_container) +void CullWorldView::record(VkCommandBuffer cmd_buf, vren::resource_container &res_container) { - // Clear the chunk draw list index (atomic counter) - vkCmdFillBuffer(cmd_buf, m_renderer.m_chunk_draw_list_idx.m_buffer.m_handle, 0, sizeof(uint32_t), 0 /* data */); - - vren::vk_utils::pipeline_barrier(cmd_buf, m_renderer.m_chunk_draw_list_idx); - - // Run cull world view - m_pipeline.bind(cmd_buf); - - vren::context& context = m_renderer.m_context; - BakedWorldView& baked_world_view = *m_renderer.m_baked_world_view; - BakedWorldViewCircularGrid& circular_grid = baked_world_view.m_circular_grid; - - // Push constants - m_pipeline.push_constants(cmd_buf, VK_SHADER_STAGE_COMPUTE_BIT, &m_renderer.m_camera.m_position, sizeof(m_renderer.m_camera.m_position), offsetof(PushConstants, m_camera)); - m_pipeline.push_constants(cmd_buf, VK_SHADER_STAGE_COMPUTE_BIT, &circular_grid.m_start, sizeof(circular_grid.m_start), offsetof(PushConstants, m_world_view_start)); - m_pipeline.push_constants(cmd_buf, VK_SHADER_STAGE_COMPUTE_BIT, &circular_grid.m_render_distance, sizeof(circular_grid.m_render_distance), offsetof(PushConstants, m_render_distance)); - - m_pipeline.acquire_and_bind_descriptor_set(context, cmd_buf, res_container, 0, [&](VkDescriptorSet descriptor_set) - { - // Baked world view 3d image - vren::vk_utils::write_storage_image_descriptor( - context, - descriptor_set, - 0, // binding - circular_grid.m_gpu_image->get_image()->get_image_view(), - VK_IMAGE_LAYOUT_GENERAL - ); - - // Chunk draw list (output buffer) - vren::vk_utils::write_buffer_descriptor( - context, - descriptor_set, - 1, // binding - m_renderer.m_chunk_draw_list.m_buffer.m_handle, - Renderer::k_chunk_draw_list_buffer_size, 0 - ); - - // Chunk draw list index (atomic counter) - vren::vk_utils::write_buffer_descriptor( - context, - descriptor_set, - 2, // binding - m_renderer.m_chunk_draw_list_idx.m_buffer.m_handle, - sizeof(uint32_t), 0 - ); - }); - - m_pipeline.dispatch(cmd_buf, 8, 8, 8); // todo workgroups count dynamical based on world view size - - res_container.add_resources( - circular_grid.m_gpu_image->get_image() - ); + // Clear the chunk draw list index (atomic counter) + vkCmdFillBuffer(cmd_buf, m_renderer.m_chunk_draw_list_idx.m_buffer.m_handle, 0, sizeof(uint32_t), 0 /* data */); + + vren::vk_utils::pipeline_barrier(cmd_buf, m_renderer.m_chunk_draw_list_idx); + + // Run cull world view + m_pipeline.bind(cmd_buf); + + vren::context &context = m_renderer.m_context; + BakedWorldView &baked_world_view = *m_renderer.m_baked_world_view; + BakedWorldViewCircularGrid &circular_grid = baked_world_view.m_circular_grid; + + // Push constants + m_pipeline.push_constants( + cmd_buf, + VK_SHADER_STAGE_COMPUTE_BIT, + &m_renderer.m_camera.m_position, + sizeof(m_renderer.m_camera.m_position), + offsetof(PushConstants, m_camera) + ); + m_pipeline.push_constants( + cmd_buf, VK_SHADER_STAGE_COMPUTE_BIT, &circular_grid.m_start, sizeof(circular_grid.m_start), offsetof(PushConstants, m_world_view_start) + ); + m_pipeline.push_constants( + cmd_buf, + VK_SHADER_STAGE_COMPUTE_BIT, + &circular_grid.m_render_distance, + sizeof(circular_grid.m_render_distance), + offsetof(PushConstants, m_render_distance) + ); + + m_pipeline.acquire_and_bind_descriptor_set( + context, + cmd_buf, + res_container, + 0, + [&](VkDescriptorSet descriptor_set) + { + // Baked world view 3d image + vren::vk_utils::write_storage_image_descriptor( + context, + descriptor_set, + 0, // binding + circular_grid.m_gpu_image->get_image()->get_image_view(), + VK_IMAGE_LAYOUT_GENERAL + ); + + // Chunk draw list (output buffer) + vren::vk_utils::write_buffer_descriptor( + context, + descriptor_set, + 1, // binding + m_renderer.m_chunk_draw_list.m_buffer.m_handle, + Renderer::k_chunk_draw_list_buffer_size, + 0 + ); + + // Chunk draw list index (atomic counter) + vren::vk_utils::write_buffer_descriptor( + context, + descriptor_set, + 2, // binding + m_renderer.m_chunk_draw_list_idx.m_buffer.m_handle, + sizeof(uint32_t), + 0 + ); + } + ); + + m_pipeline.dispatch(cmd_buf, 8, 8, 8); // todo workgroups count dynamical based on world view size + + res_container.add_resources(circular_grid.m_gpu_image->get_image()); } -vren::render_graph_t CullWorldView::create_render_graph_node(vren::render_graph_allocator& allocator) +vren::render_graph_t CullWorldView::create_render_graph_node(vren::render_graph_allocator &allocator) { - vren::render_graph_node* node = allocator.allocate(); - node->set_name("CullWorldView"); - node->set_src_stage(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); - node->set_dst_stage(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); - - node->add_buffer({ - .m_name = "chunk_draw_list", - .m_buffer = m_renderer.m_chunk_draw_list.m_buffer.m_handle, - }, VK_ACCESS_SHADER_WRITE_BIT); - node->add_buffer({ - .m_name = "chunk_draw_list_idx", - .m_buffer = m_renderer.m_chunk_draw_list_idx.m_buffer.m_handle, - }, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT); - - node->add_image({ - .m_name = "baked_world_view-circular_grid", - .m_image = m_renderer.m_baked_world_view->m_circular_grid.m_gpu_image->get_image()->get_image(), - .m_image_aspect = VK_IMAGE_ASPECT_COLOR_BIT, - .m_mip_level = 0, - .m_layer = 0, - }, VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_SHADER_READ_BIT); - - node->set_callback([this](uint32_t frame_idx, VkCommandBuffer cmd_buf, vren::resource_container& res_container) - { - record(cmd_buf, res_container); - }); - return vren::render_graph_gather(node); + vren::render_graph_node *node = allocator.allocate(); + node->set_name("CullWorldView"); + node->set_src_stage(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); + node->set_dst_stage(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); + + node->add_buffer( + { + .m_name = "chunk_draw_list", + .m_buffer = m_renderer.m_chunk_draw_list.m_buffer.m_handle, + }, + VK_ACCESS_SHADER_WRITE_BIT + ); + node->add_buffer( + { + .m_name = "chunk_draw_list_idx", + .m_buffer = m_renderer.m_chunk_draw_list_idx.m_buffer.m_handle, + }, + VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT + ); + + node->add_image( + { + .m_name = "baked_world_view-circular_grid", + .m_image = m_renderer.m_baked_world_view->m_circular_grid.m_gpu_image->get_image()->get_image(), + .m_image_aspect = VK_IMAGE_ASPECT_COLOR_BIT, + .m_mip_level = 0, + .m_layer = 0, + }, + VK_IMAGE_LAYOUT_GENERAL, + VK_ACCESS_SHADER_READ_BIT + ); + + node->set_callback( + [this](uint32_t frame_idx, VkCommandBuffer cmd_buf, vren::resource_container &res_container) + { + record(cmd_buf, res_container); + } + ); + return vren::render_graph_gather(node); } vren::pipeline CullWorldView::create_pipeline() { - vren::shader_module shader = vren::load_shader_module_from_file(m_renderer.m_context, "./resources/shaders/cull_world_view.comp.spv"); - return vren::create_compute_pipeline(m_renderer.m_context, vren::specialized_shader(shader)); + vren::shader_module shader = vren::load_shader_module_from_file(m_renderer.m_context, "./resources/shaders/cull_world_view.comp.spv"); + return vren::create_compute_pipeline(m_renderer.m_context, vren::specialized_shader(shader)); } diff --git a/src/video/pipeline/CullWorldView.hpp b/src/video/pipeline/CullWorldView.hpp index d5366a5..61bfa26 100644 --- a/src/video/pipeline/CullWorldView.hpp +++ b/src/video/pipeline/CullWorldView.hpp @@ -1,45 +1,48 @@ #pragma once +#include #include #include -#include #include "util/camera.hpp" namespace explo { - // Forward decl - class Renderer; - - /// A shader program that takes the circular-grid of the world view and the camera and filters out the chunks that are - /// visible. The output of this program is a list of draw-call of the visible chunks. - class CullWorldView - { - struct PushConstants - { - struct - { - glm::vec3 m_position; float _pad; - } m_camera; - glm::ivec3 m_world_view_start; float _pad1; - glm::ivec3 m_render_distance; float _pad2; - }; - - private: - Renderer& m_renderer; - - vren::pipeline m_pipeline; - - public: - explicit CullWorldView(Renderer& renderer); - ~CullWorldView(); - - void record(VkCommandBuffer cmd_buf, vren::resource_container& res_container); - - vren::render_graph_t create_render_graph_node(vren::render_graph_allocator& allocator); - - private: - vren::pipeline create_pipeline(); - }; - -} // namespace explo + // Forward decl + class Renderer; + + /// A shader program that takes the circular-grid of the world view and the camera and filters out the chunks that are + /// visible. The output of this program is a list of draw-call of the visible chunks. + class CullWorldView + { + struct PushConstants + { + struct + { + glm::vec3 m_position; + float _pad; + } m_camera; + glm::ivec3 m_world_view_start; + float _pad1; + glm::ivec3 m_render_distance; + float _pad2; + }; + + private: + Renderer &m_renderer; + + vren::pipeline m_pipeline; + + public: + explicit CullWorldView(Renderer &renderer); + ~CullWorldView(); + + void record(VkCommandBuffer cmd_buf, vren::resource_container &res_container); + + vren::render_graph_t create_render_graph_node(vren::render_graph_allocator &allocator); + + private: + vren::pipeline create_pipeline(); + }; + +} // namespace explo diff --git a/src/video/pipeline/DrawChunkList.cpp b/src/video/pipeline/DrawChunkList.cpp index 48c90d0..e6e7aa3 100644 --- a/src/video/pipeline/DrawChunkList.cpp +++ b/src/video/pipeline/DrawChunkList.cpp @@ -4,96 +4,120 @@ using namespace explo; -DrawChunkList::DrawChunkList(Renderer& renderer) : - m_renderer(renderer), - m_basic_renderer(m_renderer.m_context) +DrawChunkList::DrawChunkList(Renderer &renderer) : + m_renderer(renderer), + m_basic_renderer(m_renderer.m_context) { } -DrawChunkList::~DrawChunkList() -{ -} +DrawChunkList::~DrawChunkList() {} -void DrawChunkList::record(VkCommandBuffer cmd_buf, vren::resource_container& resource_container) +void DrawChunkList::record(VkCommandBuffer cmd_buf, vren::resource_container &resource_container) { - auto vertex_buffer = m_renderer.m_baked_world_view->m_vertex_buffer.get_buffer(); - auto index_buffer = m_renderer.m_baked_world_view->m_index_buffer.get_buffer(); - auto instance_buffer = m_renderer.m_baked_world_view->m_instance_buffer.get_buffer(); + auto vertex_buffer = m_renderer.m_baked_world_view->m_vertex_buffer.get_buffer(); + auto index_buffer = m_renderer.m_baked_world_view->m_index_buffer.get_buffer(); + auto instance_buffer = m_renderer.m_baked_world_view->m_instance_buffer.get_buffer(); - glm::uvec2 fb_size = m_renderer.get_framebuffer_size(); - m_basic_renderer.make_rendering_scope(cmd_buf, fb_size, *m_renderer.m_gbuffer, *m_renderer.m_depth_buffer, [&]() - { - m_basic_renderer.set_viewport(cmd_buf, fb_size.x, fb_size.y); - m_basic_renderer.set_scissor(cmd_buf, fb_size.x, fb_size.y); + glm::uvec2 fb_size = m_renderer.get_framebuffer_size(); + m_basic_renderer.make_rendering_scope( + cmd_buf, + fb_size, + *m_renderer.m_gbuffer, + *m_renderer.m_depth_buffer, + [&]() + { + m_basic_renderer.set_viewport(cmd_buf, fb_size.x, fb_size.y); + m_basic_renderer.set_scissor(cmd_buf, fb_size.x, fb_size.y); - m_basic_renderer.set_vertex_buffer(cmd_buf, *vertex_buffer); - m_basic_renderer.set_index_buffer(cmd_buf, *index_buffer); - m_basic_renderer.set_instance_buffer(cmd_buf, *instance_buffer); + m_basic_renderer.set_vertex_buffer(cmd_buf, *vertex_buffer); + m_basic_renderer.set_index_buffer(cmd_buf, *index_buffer); + m_basic_renderer.set_instance_buffer(cmd_buf, *instance_buffer); - m_basic_renderer.set_push_constants(cmd_buf, vren::basic_renderer::push_constants{ - .m_camera_view = m_renderer.m_view_matrix, - .m_camera_projection = m_renderer.m_projection_matrix, - .m_material_index = 0, - }); + m_basic_renderer.set_push_constants( + cmd_buf, + vren::basic_renderer::push_constants{ + .m_camera_view = m_renderer.m_view_matrix, + .m_camera_projection = m_renderer.m_projection_matrix, + .m_material_index = 0, + } + ); - vkCmdDrawIndexedIndirectCount( - cmd_buf, - m_renderer.m_chunk_draw_list.m_buffer.m_handle, 0, // Buffer - m_renderer.m_chunk_draw_list_idx.m_buffer.m_handle, 0, // Count buffer - 274625 /* 65^3, derived from max render distance */, - sizeof(VkDrawIndexedIndirectCommand) // stride - ); - }); + vkCmdDrawIndexedIndirectCount( + cmd_buf, + m_renderer.m_chunk_draw_list.m_buffer.m_handle, + 0, // Buffer + m_renderer.m_chunk_draw_list_idx.m_buffer.m_handle, + 0, // Count buffer + 274625 /* 65^3, derived from max render distance */, + sizeof(VkDrawIndexedIndirectCommand) // stride + ); + } + ); - resource_container.add_resources( - m_renderer.m_gbuffer, - m_renderer.m_depth_buffer, - vertex_buffer, - index_buffer, - instance_buffer - ); + resource_container.add_resources(m_renderer.m_gbuffer, m_renderer.m_depth_buffer, vertex_buffer, index_buffer, instance_buffer); } -vren::render_graph_t DrawChunkList::create_render_graph_node(vren::render_graph_allocator& allocator) +vren::render_graph_t DrawChunkList::create_render_graph_node(vren::render_graph_allocator &allocator) { - BakedWorldView& baked_world_view = *m_renderer.m_baked_world_view; + BakedWorldView &baked_world_view = *m_renderer.m_baked_world_view; - vren::render_graph_node* node = allocator.allocate(); - node->set_name("DrawChunkList"); - node->set_src_stage(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); - node->set_dst_stage(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); + vren::render_graph_node *node = allocator.allocate(); + node->set_name("DrawChunkList"); + node->set_src_stage(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); + node->set_dst_stage(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); - node->add_buffer({ - .m_name = "chunk_draw_list", - .m_buffer = m_renderer.m_chunk_draw_list.m_buffer.m_handle, - }, VK_ACCESS_INDIRECT_COMMAND_READ_BIT); - node->add_buffer({ - .m_name = "chunk_draw_list_idx", - .m_buffer = m_renderer.m_chunk_draw_list_idx.m_buffer.m_handle, - }, VK_ACCESS_INDIRECT_COMMAND_READ_BIT); - node->add_buffer({ - .m_name = "vertex_buffer", - .m_buffer = baked_world_view.m_vertex_buffer.get_buffer()->m_buffer.m_handle, - }, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT); - node->add_buffer({ - .m_name = "instance_buffer", - .m_buffer = baked_world_view.m_instance_buffer.get_buffer()->m_buffer.m_handle, - }, VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT); - node->add_buffer({ - .m_name = "index_buffer", - .m_buffer = baked_world_view.m_index_buffer.get_buffer()->m_buffer.m_handle, - }, VK_ACCESS_INDEX_READ_BIT); - m_renderer.m_gbuffer->add_render_graph_node_resources(*node, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + node->add_buffer( + { + .m_name = "chunk_draw_list", + .m_buffer = m_renderer.m_chunk_draw_list.m_buffer.m_handle, + }, + VK_ACCESS_INDIRECT_COMMAND_READ_BIT + ); + node->add_buffer( + { + .m_name = "chunk_draw_list_idx", + .m_buffer = m_renderer.m_chunk_draw_list_idx.m_buffer.m_handle, + }, + VK_ACCESS_INDIRECT_COMMAND_READ_BIT + ); + node->add_buffer( + { + .m_name = "vertex_buffer", + .m_buffer = baked_world_view.m_vertex_buffer.get_buffer()->m_buffer.m_handle, + }, + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT + ); + node->add_buffer( + { + .m_name = "instance_buffer", + .m_buffer = baked_world_view.m_instance_buffer.get_buffer()->m_buffer.m_handle, + }, + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT + ); + node->add_buffer( + { + .m_name = "index_buffer", + .m_buffer = baked_world_view.m_index_buffer.get_buffer()->m_buffer.m_handle, + }, + VK_ACCESS_INDEX_READ_BIT + ); + m_renderer.m_gbuffer->add_render_graph_node_resources(*node, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); - node->add_image({ - .m_name = "depth_buffer", - .m_image = m_renderer.m_depth_buffer->get_image(), - .m_image_aspect = VK_IMAGE_ASPECT_DEPTH_BIT, - }, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); + node->add_image( + { + .m_name = "depth_buffer", + .m_image = m_renderer.m_depth_buffer->get_image(), + .m_image_aspect = VK_IMAGE_ASPECT_DEPTH_BIT, + }, + VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT + ); - node->set_callback([this](uint32_t frame_idx, VkCommandBuffer cmd_buf, vren::resource_container& resource_container) - { - record(cmd_buf, resource_container); - }); - return vren::render_graph_gather(node); + node->set_callback( + [this](uint32_t frame_idx, VkCommandBuffer cmd_buf, vren::resource_container &resource_container) + { + record(cmd_buf, resource_container); + } + ); + return vren::render_graph_gather(node); } diff --git a/src/video/pipeline/DrawChunkList.hpp b/src/video/pipeline/DrawChunkList.hpp index 0fdba0d..0a7c258 100644 --- a/src/video/pipeline/DrawChunkList.hpp +++ b/src/video/pipeline/DrawChunkList.hpp @@ -7,24 +7,24 @@ namespace explo { - // Forward decl - class Renderer; + // Forward decl + class Renderer; - /// A shader program which takes the chunk draw list from the Renderer and renders it to a g-buffer, using vren's basic_renderer module. - /// The draw is indirect since the draw calls (i.e. which chunk to render) are filled dynamically by CullWorldView. - class DrawChunkList - { - private: - Renderer& m_renderer; + /// A shader program which takes the chunk draw list from the Renderer and renders it to a g-buffer, using vren's basic_renderer module. + /// The draw is indirect since the draw calls (i.e. which chunk to render) are filled dynamically by CullWorldView. + class DrawChunkList + { + private: + Renderer &m_renderer; - vren::basic_renderer m_basic_renderer; + vren::basic_renderer m_basic_renderer; - public: - explicit DrawChunkList(Renderer& renderer); - ~DrawChunkList(); + public: + explicit DrawChunkList(Renderer &renderer); + ~DrawChunkList(); - void record(VkCommandBuffer cmd_buf, vren::resource_container& resource_container); + void record(VkCommandBuffer cmd_buf, vren::resource_container &resource_container); - vren::render_graph_t create_render_graph_node(vren::render_graph_allocator& allocator); - }; -} + vren::render_graph_t create_render_graph_node(vren::render_graph_allocator &allocator); + }; +} // namespace explo diff --git a/src/world/BlockData.hpp b/src/world/BlockData.hpp index 8cf52ee..42d3452 100644 --- a/src/world/BlockData.hpp +++ b/src/world/BlockData.hpp @@ -4,8 +4,8 @@ namespace explo { - struct BlockData - { - uint32_t m_color; // R8G8B8A8 - }; -} // namespace explo \ No newline at end of file + struct BlockData + { + uint32_t m_color; // R8G8B8A8 + }; +} // namespace explo \ No newline at end of file diff --git a/src/world/BlockRegistry.cpp b/src/world/BlockRegistry.cpp index d810430..7b67823 100644 --- a/src/world/BlockRegistry.cpp +++ b/src/world/BlockRegistry.cpp @@ -6,17 +6,17 @@ using namespace explo; BlockRegistry::BlockRegistry() { - // TODO Format is ABGR (wtf?) + // TODO Format is ABGR (wtf?) - // Grass (rgb): 3ac94d - // Dirt (rgb): 966f2a - // Stone (rgb): 8c857a + // Grass (rgb): 3ac94d + // Dirt (rgb): 966f2a + // Stone (rgb): 8c857a - m_block_data.push_back({ .m_color = 0x00000000 }); // 0 = Air - m_block_data.push_back({ .m_color = 0xff4dc93a }); // 1 = Grass - m_block_data.push_back({ .m_color = 0xff2a6f96 }); // 2 = Dirt - m_block_data.push_back({ .m_color = 0xff7a858c }); // 3 = Stone - m_block_data.push_back({ .m_color = 0xffffffff }); // 4 = Snow + m_block_data.push_back({.m_color = 0x00000000}); // 0 = Air + m_block_data.push_back({.m_color = 0xff4dc93a}); // 1 = Grass + m_block_data.push_back({.m_color = 0xff2a6f96}); // 2 = Dirt + m_block_data.push_back({.m_color = 0xff7a858c}); // 3 = Stone + m_block_data.push_back({.m_color = 0xffffffff}); // 4 = Snow - RenderApi::block_registry_upload(*this); + RenderApi::block_registry_upload(*this); } diff --git a/src/world/BlockRegistry.hpp b/src/world/BlockRegistry.hpp index a7542e6..fc57581 100644 --- a/src/world/BlockRegistry.hpp +++ b/src/world/BlockRegistry.hpp @@ -1,23 +1,22 @@ #pragma once #include - #include #include "BlockData.hpp" namespace explo { - class BlockRegistry - { - private: - std::vector m_block_data; + class BlockRegistry + { + private: + std::vector m_block_data; - public: - explicit BlockRegistry(); - ~BlockRegistry() = default; + public: + explicit BlockRegistry(); + ~BlockRegistry() = default; - std::vector const& get_block_data() const { return m_block_data; }; - size_t size() const { return m_block_data.size(); } - }; -} // namespace explo + std::vector const &get_block_data() const { return m_block_data; }; + size_t size() const { return m_block_data.size(); } + }; +} // namespace explo diff --git a/src/world/Chunk.cpp b/src/world/Chunk.cpp index 7b78d43..0bfaa04 100644 --- a/src/world/Chunk.cpp +++ b/src/world/Chunk.cpp @@ -7,72 +7,67 @@ using namespace explo; -Chunk::Chunk(World& world, glm::ivec3 const& position) : - m_world(world), - m_position(position) +Chunk::Chunk(World &world, glm::ivec3 const &position) : + m_world(world), + m_position(position) { - uint32_t max_side = std::max(std::max(Chunk::k_grid_size.x, Chunk::k_grid_size.y), Chunk::k_grid_size.z); - uint32_t octree_depth = glm::log2(ceil_to_power_of_2(max_side)); - m_octree = std::make_unique(octree_depth); + uint32_t max_side = std::max(std::max(Chunk::k_grid_size.x, Chunk::k_grid_size.y), Chunk::k_grid_size.z); + uint32_t octree_depth = glm::log2(ceil_to_power_of_2(max_side)); + m_octree = std::make_unique(octree_depth); } -Chunk::~Chunk() -{ -} +Chunk::~Chunk() {} -Octree& Chunk::octree() const +Octree &Chunk::octree() const { - return *m_octree; + return *m_octree; } -glm::ivec3 Chunk::to_world_block_position(glm::ivec3 const& chunk_block_pos) const +glm::ivec3 Chunk::to_world_block_position(glm::ivec3 const &chunk_block_pos) const { - return m_position * Chunk::k_grid_size + chunk_block_pos; + return m_position * Chunk::k_grid_size + chunk_block_pos; } -bool Chunk::test_block_position(glm::ivec3 const& block_pos) +bool Chunk::test_block_position(glm::ivec3 const &block_pos) { - glm::ivec3 chunk_pos = Chunk::get_position(block_pos); - return chunk_pos == m_position; + glm::ivec3 chunk_pos = Chunk::get_position(block_pos); + return chunk_pos == m_position; } -glm::vec3 Chunk::to_world_position(glm::vec3 const& chunk_pos) const +glm::vec3 Chunk::to_world_position(glm::vec3 const &chunk_pos) const { - return glm::vec3(m_position) * Chunk::k_world_size + chunk_pos; + return glm::vec3(m_position) * Chunk::k_world_size + chunk_pos; } -glm::ivec3 Chunk::to_chunk_block_position(glm::ivec3 const& block_pos) +glm::ivec3 Chunk::to_chunk_block_position(glm::ivec3 const &block_pos) { - return glm::floor(glm::vec3(block_pos) / glm::vec3(Chunk::k_grid_size)); + return glm::floor(glm::vec3(block_pos) / glm::vec3(Chunk::k_grid_size)); } -glm::vec3 Chunk::to_chunk_position(glm::vec3 const& world_pos) +glm::vec3 Chunk::to_chunk_position(glm::vec3 const &world_pos) { - return glm::mod(world_pos, Chunk::k_world_size); + return glm::mod(world_pos, Chunk::k_world_size); } -uint8_t Chunk::get_block_type_at(glm::ivec3 const& block_pos) const +uint8_t Chunk::get_block_type_at(glm::ivec3 const &block_pos) const { - if (Chunk::test_chunk_block_position(block_pos)) - return 0; // TODO if the block coord is not inside the chunk, throw an error instead! - return m_octree->get_voxel_at(Octree::to_morton_code(block_pos)); + if (Chunk::test_chunk_block_position(block_pos)) return 0; // TODO if the block coord is not inside the chunk, throw an error instead! + return m_octree->get_voxel_at(Octree::to_morton_code(block_pos)); } -void Chunk::set_block_type_at(glm::ivec3 const& block_pos, uint8_t block_type) +void Chunk::set_block_type_at(glm::ivec3 const &block_pos, uint8_t block_type) { - assert(Chunk::test_chunk_block_position(block_pos)); - return m_octree->set_voxel_at(Octree::to_morton_code(block_pos), block_type); + assert(Chunk::test_chunk_block_position(block_pos)); + return m_octree->set_voxel_at(Octree::to_morton_code(block_pos), block_type); } -bool Chunk::test_chunk_block_position(glm::ivec3 const& chunk_block_pos) +bool Chunk::test_chunk_block_position(glm::ivec3 const &chunk_block_pos) { - return - chunk_block_pos.x >= 0 && chunk_block_pos.x < k_grid_size.x && - chunk_block_pos.y >= 0 && chunk_block_pos.y < k_grid_size.y && - chunk_block_pos.z >= 0 && chunk_block_pos.z < k_grid_size.z; + return chunk_block_pos.x >= 0 && chunk_block_pos.x < k_grid_size.x && chunk_block_pos.y >= 0 && chunk_block_pos.y < k_grid_size.y && + chunk_block_pos.z >= 0 && chunk_block_pos.z < k_grid_size.z; } -glm::ivec3 Chunk::get_position(glm::ivec3 const& block_pos) +glm::ivec3 Chunk::get_position(glm::ivec3 const &block_pos) { - return glm::floor(glm::vec3(block_pos) / glm::vec3(Chunk::k_grid_size)); + return glm::floor(glm::vec3(block_pos) / glm::vec3(Chunk::k_grid_size)); } diff --git a/src/world/Chunk.hpp b/src/world/Chunk.hpp index 4f3c5c5..6324b5d 100644 --- a/src/world/Chunk.hpp +++ b/src/world/Chunk.hpp @@ -1,13 +1,11 @@ #pragma once -#include #include #include -#include -#include - #include - +#include +#include +#include #include #include "world/surface/SurfaceGenerator.hpp" @@ -16,68 +14,69 @@ namespace explo { - // Forward decl - class World; - class WorldView; + // Forward decl + class World; + class WorldView; - class BakedWorldView; + class BakedWorldView; - // ------------------------------------------------------------------------------------------------ + // ------------------------------------------------------------------------------------------------ - /* Surface */ - using chunk_surface_t = vren::model; + /* Surface */ + using chunk_surface_t = vren::model; - /* Block */ - using block_t = uint8_t; + /* Block */ + using block_t = uint8_t; - class Chunk : public std::enable_shared_from_this - { - friend class World; - friend class WorldView; - friend class BakedWorldView; - friend class VolumeGenerator; - friend class SurfaceGenerator; + class Chunk : public std::enable_shared_from_this + { + friend class World; + friend class WorldView; + friend class BakedWorldView; + friend class VolumeGenerator; + friend class SurfaceGenerator; - public: - static constexpr glm::ivec3 k_grid_size = glm::ivec3(16, 256, 16); ///< How many blocks does a chunk contain - static constexpr glm::vec3 k_world_size = glm::vec3(16.0f, 256.0f, 16.0f); ///< The size of the chunk in world space (commonly equal to the grid size) + public: + static constexpr glm::ivec3 k_grid_size = glm::ivec3(16, 256, 16); ///< How many blocks does a chunk contain + static constexpr glm::vec3 k_world_size = + glm::vec3(16.0f, 256.0f, 16.0f); ///< The size of the chunk in world space (commonly equal to the grid size) - private: - World& m_world; - glm::ivec3 m_position; + private: + World &m_world; + glm::ivec3 m_position; - mutable std::mutex m_volume_mutex; - std::unique_ptr m_octree; + mutable std::mutex m_volume_mutex; + std::unique_ptr m_octree; - std::unique_ptr m_surface; + std::unique_ptr m_surface; - public: - explicit Chunk(World& world, glm::ivec3 const& position); - ~Chunk(); + public: + explicit Chunk(World &world, glm::ivec3 const &position); + ~Chunk(); - World& get_world() const { return m_world; } - glm::ivec3 const& get_position() const { return m_position; } + World &get_world() const { return m_world; } + glm::ivec3 const &get_position() const { return m_position; } - Octree& octree() const; + Octree &octree() const; - uint8_t get_block_type_at(glm::ivec3 const& block_pos) const; - void set_block_type_at(glm::ivec3 const& block_pos, uint8_t block_type); + uint8_t get_block_type_at(glm::ivec3 const &block_pos) const; + void set_block_type_at(glm::ivec3 const &block_pos, uint8_t block_type); - bool has_surface() const { return bool(m_surface); }; - std::unique_ptr const& get_surface() const { return m_surface; } + bool has_surface() const { return bool(m_surface); }; + std::unique_ptr const &get_surface() const { return m_surface; } - glm::uvec3 const& get_grid_size() const { return k_grid_size; } + glm::uvec3 const &get_grid_size() const { return k_grid_size; } - bool test_block_position(glm::ivec3 const& block_pos); - glm::ivec3 to_world_block_position(glm::ivec3 const& chunk_block_pos) const; - glm::vec3 to_world_position(glm::vec3 const& chunk_pos) const; + bool test_block_position(glm::ivec3 const &block_pos); + glm::ivec3 to_world_block_position(glm::ivec3 const &chunk_block_pos) const; + glm::vec3 to_world_position(glm::vec3 const &chunk_pos) const; - static glm::ivec3 to_chunk_block_position(glm::ivec3 const& block_pos); - static glm::vec3 to_chunk_position(glm::vec3 const& world_pos); + static glm::ivec3 to_chunk_block_position(glm::ivec3 const &block_pos); + static glm::vec3 to_chunk_position(glm::vec3 const &world_pos); - /// Checks whether the relative block position is inside the chunk. - static bool test_chunk_block_position(glm::ivec3 const& chunk_block_pos); + /// Checks whether the relative block position is inside the chunk. + static bool test_chunk_block_position(glm::ivec3 const &chunk_block_pos); - static glm::ivec3 get_position(glm::ivec3 const& block_pos); - }; -} + static glm::ivec3 get_position(glm::ivec3 const &block_pos); + }; +} // namespace explo diff --git a/src/world/DeltaChunkIterator.cpp b/src/world/DeltaChunkIterator.cpp index e2a3987..524c3d9 100644 --- a/src/world/DeltaChunkIterator.cpp +++ b/src/world/DeltaChunkIterator.cpp @@ -6,61 +6,55 @@ using namespace explo; DeltaChunkIterator::DeltaChunkIterator( - glm::ivec3 const& old_center, - glm::ivec3 const& new_center, - glm::ivec3 const& render_distance, - CallbackT const& callback + glm::ivec3 const &old_center, glm::ivec3 const &new_center, glm::ivec3 const &render_distance, CallbackT const &callback ) : - m_old_center(old_center), - m_new_center(new_center), - m_render_distance(render_distance), - m_callback(callback) + m_old_center(old_center), + m_new_center(new_center), + m_render_distance(render_distance), + m_callback(callback) { } -DeltaChunkIterator::~DeltaChunkIterator() -{ -} +DeltaChunkIterator::~DeltaChunkIterator() {} void DeltaChunkIterator::iterate_along_axis(int axis) { - assert(axis >= 0 && axis < 3); - assert(m_new_center[axis] != m_old_center[axis]); - - int ax0 = axis; // The axis of movement - int ax1 = (axis + 1) % 3; - int ax2 = (axis + 2) % 3; - - int axis_delta = m_new_center[axis] - m_old_center[axis]; - int axis_from = glm::max(glm::abs(axis_delta) - m_render_distance[ax0], m_render_distance[ax0] + 1); - int axis_to = glm::abs(axis_delta) + m_render_distance[ax0] + 1; - - for (int c = axis_from; c < axis_to; c++) - { - for (int a = -m_render_distance[ax1]; a <= m_render_distance[ax1]; a++) - { - for (int b = -m_render_distance[ax2]; b <= m_render_distance[ax2]; b++) - { - glm::ivec3 chunk_pos{}; - chunk_pos[ax0] = m_old_center[ax0] + c * glm::sign(axis_delta); - chunk_pos[ax1] = m_new_center[ax1] + a; - chunk_pos[ax2] = m_new_center[ax2] + b; - - if (!m_visited_chunks.contains(chunk_pos)) - { - m_callback(chunk_pos); - m_visited_chunks.emplace(chunk_pos); - } - } - } - } + assert(axis >= 0 && axis < 3); + assert(m_new_center[axis] != m_old_center[axis]); + + int ax0 = axis; // The axis of movement + int ax1 = (axis + 1) % 3; + int ax2 = (axis + 2) % 3; + + int axis_delta = m_new_center[axis] - m_old_center[axis]; + int axis_from = glm::max(glm::abs(axis_delta) - m_render_distance[ax0], m_render_distance[ax0] + 1); + int axis_to = glm::abs(axis_delta) + m_render_distance[ax0] + 1; + + for (int c = axis_from; c < axis_to; c++) + { + for (int a = -m_render_distance[ax1]; a <= m_render_distance[ax1]; a++) + { + for (int b = -m_render_distance[ax2]; b <= m_render_distance[ax2]; b++) + { + glm::ivec3 chunk_pos{}; + chunk_pos[ax0] = m_old_center[ax0] + c * glm::sign(axis_delta); + chunk_pos[ax1] = m_new_center[ax1] + a; + chunk_pos[ax2] = m_new_center[ax2] + b; + + if (!m_visited_chunks.contains(chunk_pos)) + { + m_callback(chunk_pos); + m_visited_chunks.emplace(chunk_pos); + } + } + } + } } void DeltaChunkIterator::iterate() { - for (int axis = 0; axis < 3; axis++) - { - if (m_new_center[axis] != m_old_center[axis]) - iterate_along_axis(axis); - } + for (int axis = 0; axis < 3; axis++) + { + if (m_new_center[axis] != m_old_center[axis]) iterate_along_axis(axis); + } } diff --git a/src/world/DeltaChunkIterator.hpp b/src/world/DeltaChunkIterator.hpp index 135f575..4bbaadc 100644 --- a/src/world/DeltaChunkIterator.hpp +++ b/src/world/DeltaChunkIterator.hpp @@ -1,42 +1,38 @@ #pragma once #include -#include - #include +#include #include "util/misc.hpp" namespace explo { - class DeltaChunkIterator - { - public: - using CallbackT = std::function; - - private: - glm::ivec3 m_old_center; - glm::ivec3 m_new_center; - glm::ivec3 m_render_distance; - CallbackT m_callback; - - /// A set holding the chunks for which the callback was called (very simple and stupid approach). - std::unordered_set m_visited_chunks; - - public: - explicit DeltaChunkIterator( - glm::ivec3 const& old_center, - glm::ivec3 const& new_center, - glm::ivec3 const& render_distance, - CallbackT const& callback - ); - ~DeltaChunkIterator(); - - /// Iterates over the new chunks according to the shift given by world view's old center and new center. - void iterate(); - - private: - void iterate_along_axis(int axis); - }; - -} // namespace explo \ No newline at end of file + class DeltaChunkIterator + { + public: + using CallbackT = std::function; + + private: + glm::ivec3 m_old_center; + glm::ivec3 m_new_center; + glm::ivec3 m_render_distance; + CallbackT m_callback; + + /// A set holding the chunks for which the callback was called (very simple and stupid approach). + std::unordered_set m_visited_chunks; + + public: + explicit DeltaChunkIterator( + glm::ivec3 const &old_center, glm::ivec3 const &new_center, glm::ivec3 const &render_distance, CallbackT const &callback + ); + ~DeltaChunkIterator(); + + /// Iterates over the new chunks according to the shift given by world view's old center and new center. + void iterate(); + + private: + void iterate_along_axis(int axis); + }; + +} // namespace explo \ No newline at end of file diff --git a/src/world/Entity.cpp b/src/world/Entity.cpp index 3701961..146866f 100644 --- a/src/world/Entity.cpp +++ b/src/world/Entity.cpp @@ -4,104 +4,99 @@ using namespace explo; -Entity::Entity(World& world, glm::vec3 const& init_position) : - m_world(&world), - m_position(init_position), - m_yaw(0.0f), - m_pitch(0.0f) +Entity::Entity(World &world, glm::vec3 const &init_position) : + m_world(&world), + m_position(init_position), + m_yaw(0.0f), + m_pitch(0.0f) { - update_orientation_matrix(); + update_orientation_matrix(); } -Entity::~Entity() -{ -} +Entity::~Entity() {} -void Entity::set_world(World& world, glm::vec3 const& position) +void Entity::set_world(World &world, glm::vec3 const &position) { - m_world = &world; - m_position = position; + m_world = &world; + m_position = position; - if (m_world_view) // If the entity already had a WorldView we recreate it with the new World - { - recreate_world_view(m_world_view->get_render_distance()); - } + if (m_world_view) // If the entity already had a WorldView we recreate it with the new World + { + recreate_world_view(m_world_view->get_render_distance()); + } } -void Entity::set_position(glm::vec3 const& position) +void Entity::set_position(glm::vec3 const &position) { - if (position == m_position) - return; - - glm::ivec3 old_chunk_position = get_chunk_position(); - m_position = position; - - if (m_world_view) - { - glm::ivec3 new_chunk_position = get_chunk_position(); - if (old_chunk_position != new_chunk_position) - m_world_view->set_position(new_chunk_position); - } + if (position == m_position) return; + + glm::ivec3 old_chunk_position = get_chunk_position(); + m_position = position; + + if (m_world_view) + { + glm::ivec3 new_chunk_position = get_chunk_position(); + if (old_chunk_position != new_chunk_position) m_world_view->set_position(new_chunk_position); + } } glm::vec3 Entity::get_chunk_relative_position() const { - return glm::mod(m_position, Chunk::k_world_size); + return glm::mod(m_position, Chunk::k_world_size); } glm::ivec3 Entity::get_chunk_position() const { - return glm::floor(m_position / Chunk::k_world_size); + return glm::floor(m_position / Chunk::k_world_size); } void Entity::set_rotation(float yaw, float pitch) { - if (yaw == m_yaw && pitch == m_pitch) - return; + if (yaw == m_yaw && pitch == m_pitch) return; - m_yaw = yaw; - m_pitch = pitch; + m_yaw = yaw; + m_pitch = pitch; - update_orientation_matrix(); + update_orientation_matrix(); } void Entity::update_orientation_matrix() { - m_orientation_matrix = build_orientation_mat(m_yaw, m_pitch); + m_orientation_matrix = build_orientation_mat(m_yaw, m_pitch); } glm::vec3 Entity::get_right() const { - return m_orientation_matrix[0]; + return m_orientation_matrix[0]; } glm::vec3 Entity::get_up() const { - return m_orientation_matrix[1]; + return m_orientation_matrix[1]; } glm::vec3 Entity::get_forward() const { - return m_orientation_matrix[2]; + return m_orientation_matrix[2]; } bool Entity::has_world_view() const { - return bool(m_world_view); + return bool(m_world_view); } -WorldView& Entity::recreate_world_view(glm::ivec3 const& render_distance) +WorldView &Entity::recreate_world_view(glm::ivec3 const &render_distance) { - glm::ivec3 pos = get_chunk_position(); + glm::ivec3 pos = get_chunk_position(); - RenderApi::world_view_recreate(pos, render_distance); - m_world_view = std::make_unique(*m_world, pos, render_distance); + RenderApi::world_view_recreate(pos, render_distance); + m_world_view = std::make_unique(*m_world, pos, render_distance); - return *m_world_view; + return *m_world_view; } -WorldView& Entity::get_world_view() +WorldView &Entity::get_world_view() { - // TODO check m_world_view != null - return *m_world_view; + // TODO check m_world_view != null + return *m_world_view; } diff --git a/src/world/Entity.hpp b/src/world/Entity.hpp index 4e70680..dfe0cdb 100644 --- a/src/world/Entity.hpp +++ b/src/world/Entity.hpp @@ -1,8 +1,7 @@ #pragma once -#include - #include +#include #include "util/camera.hpp" #include "world/World.hpp" @@ -10,49 +9,49 @@ namespace explo { - class Entity : public std::enable_shared_from_this - { - private: - World* m_world; + class Entity : public std::enable_shared_from_this + { + private: + World *m_world; - glm::vec3 m_position; + glm::vec3 m_position; - float m_yaw; - float m_pitch; - glm::mat4 m_orientation_matrix; + float m_yaw; + float m_pitch; + glm::mat4 m_orientation_matrix; - std::unique_ptr m_world_view; + std::unique_ptr m_world_view; - public: - inline static glm::vec3 k_camera_offset = glm::vec3(0, 2 /* Entity's height */, 0); + public: + inline static glm::vec3 k_camera_offset = glm::vec3(0, 2 /* Entity's height */, 0); - explicit Entity(World& world, glm::vec3 const& init_position = glm::vec3(0)); - ~Entity(); + explicit Entity(World &world, glm::vec3 const &init_position = glm::vec3(0)); + ~Entity(); - World& get_world() { return *m_world; }; - void set_world(World& world, glm::vec3 const& position = glm::vec3(0)); + World &get_world() { return *m_world; }; + void set_world(World &world, glm::vec3 const &position = glm::vec3(0)); - glm::vec3 const& get_position() const { return m_position; }; - void set_position(glm::vec3 const& position); + glm::vec3 const &get_position() const { return m_position; }; + void set_position(glm::vec3 const &position); - /// Gets the position relative to the chunk the player is in. - glm::vec3 get_chunk_relative_position() const; + /// Gets the position relative to the chunk the player is in. + glm::vec3 get_chunk_relative_position() const; - /// Gets the position of the chunk where the player is at. - glm::ivec3 get_chunk_position() const; + /// Gets the position of the chunk where the player is at. + glm::ivec3 get_chunk_position() const; - float get_yaw() const { return m_yaw; }; - float get_pitch() const { return m_pitch; }; - void set_rotation(float yaw, float pitch); + float get_yaw() const { return m_yaw; }; + float get_pitch() const { return m_pitch; }; + void set_rotation(float yaw, float pitch); - void update_orientation_matrix(); + void update_orientation_matrix(); - glm::vec3 get_right() const; - glm::vec3 get_up() const; - glm::vec3 get_forward() const; + glm::vec3 get_right() const; + glm::vec3 get_up() const; + glm::vec3 get_forward() const; - bool has_world_view() const; - WorldView& recreate_world_view(glm::ivec3 const& render_distance); - WorldView& get_world_view(); - }; -} + bool has_world_view() const; + WorldView &recreate_world_view(glm::ivec3 const &render_distance); + WorldView &get_world_view(); + }; +} // namespace explo diff --git a/src/world/World.cpp b/src/world/World.cpp index 73df5ac..ef5f96c 100644 --- a/src/world/World.cpp +++ b/src/world/World.cpp @@ -8,103 +8,99 @@ using namespace explo; -World::World(VolumeGenerator& volume_generator, SurfaceGenerator& surface_generator) : - m_volume_generator(volume_generator), - m_surface_generator(surface_generator) +World::World(VolumeGenerator &volume_generator, SurfaceGenerator &surface_generator) : + m_volume_generator(volume_generator), + m_surface_generator(surface_generator) { } -World::~World() -{ -} +World::~World() {} -std::pair World::load_chunk_async(glm::ivec3 const& chunk_pos, ChunkLoadedCallbackT const& callback) +std::pair World::load_chunk_async(glm::ivec3 const &chunk_pos, ChunkLoadedCallbackT const &callback) { - std::shared_ptr chunk = std::make_shared(*this, chunk_pos); - auto [iterator, inserted] = m_chunks.emplace(chunk_pos, chunk); + std::shared_ptr chunk = std::make_shared(*this, chunk_pos); + auto [iterator, inserted] = m_chunks.emplace(chunk_pos, chunk); - if (!inserted) - return {*iterator->second.get(), false}; // Chunk already loaded + if (!inserted) return {*iterator->second.get(), false}; // Chunk already loaded - generate_chunk_async(chunk, callback); + generate_chunk_async(chunk, callback); - return {*chunk, true}; + return {*chunk, true}; } -bool World::unload_chunk(glm::ivec3 const& chunk_pos) +bool World::unload_chunk(glm::ivec3 const &chunk_pos) { - auto chunk_it = m_chunks.find(chunk_pos); - if (chunk_it == m_chunks.end()) - return false; // Chunk wasn't loaded + auto chunk_it = m_chunks.find(chunk_pos); + if (chunk_it == m_chunks.end()) return false; // Chunk wasn't loaded - std::shared_ptr chunk = chunk_it->second; - return m_chunks.erase(chunk_pos) == 1; + std::shared_ptr chunk = chunk_it->second; + return m_chunks.erase(chunk_pos) == 1; } -void World::generate_chunk_surface(Chunk& chunk) +void World::generate_chunk_surface(Chunk &chunk) { - // TODO Volume generation shall not take place while the surface is being generated + // TODO Volume generation shall not take place while the surface is being generated - uint64_t started_at = current_ms(); + uint64_t started_at = current_ms(); - Surface surface; - SurfaceWriter surface_writer(surface); + Surface surface; + SurfaceWriter surface_writer(surface); - m_surface_generator.generate(chunk, surface_writer); + m_surface_generator.generate(chunk, surface_writer); - chunk.m_surface = std::make_unique(std::move(surface)); + chunk.m_surface = std::make_unique(std::move(surface)); - glm::ivec3 chunk_pos = chunk.get_position(); + glm::ivec3 chunk_pos = chunk.get_position(); } -void World::generate_chunk_async(std::shared_ptr const& chunk, ChunkLoadedCallbackT const& callback) +void World::generate_chunk_async(std::shared_ptr const &chunk, ChunkLoadedCallbackT const &callback) { - JobChain job_chain{}; - job_chain - // Generate the volume - .then([ weak_world = weak_from_this(), weak_chunk = std::weak_ptr(chunk) ]() - { - std::shared_ptr world = weak_world.lock(); - std::shared_ptr chunk = weak_chunk.lock(); - - if (!world || !chunk) return; - - uint64_t started_at = current_ms(); - - world->m_volume_generator.generate_volume(*chunk); - - glm::ivec3 chunk_pos = chunk->get_position(); - LOG_D("World", "Volume generated; Chunk: ({}, {}, {}), dt: {}", - chunk_pos.x, chunk_pos.y, chunk_pos.z, - current_ms() - started_at - ); - }) - // Generate the surface - .then([ weak_world = weak_from_this(), weak_chunk = std::weak_ptr(chunk) ]() - { - std::shared_ptr world = weak_world.lock(); - std::shared_ptr chunk = weak_chunk.lock(); - - if (!world || !chunk) return; - - uint64_t started_at = current_ms(); - - world->generate_chunk_surface(*chunk); - - glm::ivec3 chunk_pos = chunk->get_position(); - LOG_D("World", "Surface generated; Chunk: ({}, {}, {}), dt: {}", - chunk_pos.x, chunk_pos.y, chunk_pos.z, - current_ms() - started_at - ); - }) - // Call the user provided callback - .then([ weak_chunk = std::weak_ptr(chunk), callback ]() - { - std::shared_ptr chunk = weak_chunk.lock(); - - if (!chunk) return; - - callback(chunk); - }); - job_chain.dispatch(game().m_thread_pool); + JobChain job_chain{}; + job_chain + // Generate the volume + .then( + [weak_world = weak_from_this(), weak_chunk = std::weak_ptr(chunk)]() + { + std::shared_ptr world = weak_world.lock(); + std::shared_ptr chunk = weak_chunk.lock(); + + if (!world || !chunk) return; + + uint64_t started_at = current_ms(); + + world->m_volume_generator.generate_volume(*chunk); + + glm::ivec3 chunk_pos = chunk->get_position(); + LOG_D("World", "Volume generated; Chunk: ({}, {}, {}), dt: {}", chunk_pos.x, chunk_pos.y, chunk_pos.z, current_ms() - started_at); + } + ) + // Generate the surface + .then( + [weak_world = weak_from_this(), weak_chunk = std::weak_ptr(chunk)]() + { + std::shared_ptr world = weak_world.lock(); + std::shared_ptr chunk = weak_chunk.lock(); + + if (!world || !chunk) return; + + uint64_t started_at = current_ms(); + + world->generate_chunk_surface(*chunk); + + glm::ivec3 chunk_pos = chunk->get_position(); + LOG_D("World", "Surface generated; Chunk: ({}, {}, {}), dt: {}", chunk_pos.x, chunk_pos.y, chunk_pos.z, current_ms() - started_at); + } + ) + // Call the user provided callback + .then( + [weak_chunk = std::weak_ptr(chunk), callback]() + { + std::shared_ptr chunk = weak_chunk.lock(); + + if (!chunk) return; + + callback(chunk); + } + ); + job_chain.dispatch(game().m_thread_pool); } diff --git a/src/world/World.hpp b/src/world/World.hpp index 8225c59..32deb4c 100644 --- a/src/world/World.hpp +++ b/src/world/World.hpp @@ -1,9 +1,8 @@ #pragma once -#include -#include - #include +#include +#include #include "Chunk.hpp" #include "util/misc.hpp" @@ -11,39 +10,39 @@ namespace explo { - class World : public std::enable_shared_from_this - { - friend class Chunk; - friend class Entity; + class World : public std::enable_shared_from_this + { + friend class Chunk; + friend class Entity; - public: - using ChunkLoadedCallbackT = std::function const&)>; + public: + using ChunkLoadedCallbackT = std::function const &)>; - private: - VolumeGenerator& m_volume_generator; - SurfaceGenerator& m_surface_generator; + private: + VolumeGenerator &m_volume_generator; + SurfaceGenerator &m_surface_generator; - std::unordered_map, vec_hash> m_chunks; + std::unordered_map, vec_hash> m_chunks; - public: - explicit World(VolumeGenerator& volume_generator, SurfaceGenerator& surface_generator); - ~World(); + public: + explicit World(VolumeGenerator &volume_generator, SurfaceGenerator &surface_generator); + ~World(); - bool is_chunk_loaded(glm::ivec3 const& chunk_pos) const { return m_chunks.contains(chunk_pos); } - size_t get_loaded_chunk_count() const { return m_chunks.size(); } + bool is_chunk_loaded(glm::ivec3 const &chunk_pos) const { return m_chunks.contains(chunk_pos); } + size_t get_loaded_chunk_count() const { return m_chunks.size(); } - /// Gets the chunk loaded at the given position. The returned chunk shares the ownership with the World, which means the - /// requester could become the only owner of the chunk (e.g. in case the chunk is unloaded). - std::shared_ptr get_chunk(glm::ivec3 const& chunk_pos) { return m_chunks.at(chunk_pos); }; + /// Gets the chunk loaded at the given position. The returned chunk shares the ownership with the World, which means the + /// requester could become the only owner of the chunk (e.g. in case the chunk is unloaded). + std::shared_ptr get_chunk(glm::ivec3 const &chunk_pos) { return m_chunks.at(chunk_pos); }; - VolumeGenerator& get_volume_generator() const { return m_volume_generator; } - SurfaceGenerator& get_surface_generator() const { return m_surface_generator; } + VolumeGenerator &get_volume_generator() const { return m_volume_generator; } + SurfaceGenerator &get_surface_generator() const { return m_surface_generator; } - std::pair load_chunk_async(glm::ivec3 const& chunk_pos, ChunkLoadedCallbackT const& callback); - bool unload_chunk(glm::ivec3 const& chunk_pos); + std::pair load_chunk_async(glm::ivec3 const &chunk_pos, ChunkLoadedCallbackT const &callback); + bool unload_chunk(glm::ivec3 const &chunk_pos); - private: - void generate_chunk_surface(Chunk& chunk); - void generate_chunk_async(std::shared_ptr const& chunk, ChunkLoadedCallbackT const& callback); - }; -} + private: + void generate_chunk_surface(Chunk &chunk); + void generate_chunk_async(std::shared_ptr const &chunk, ChunkLoadedCallbackT const &callback); + }; +} // namespace explo diff --git a/src/world/WorldView.cpp b/src/world/WorldView.cpp index 8d75af0..8d15f2c 100644 --- a/src/world/WorldView.cpp +++ b/src/world/WorldView.cpp @@ -1,135 +1,137 @@ #include "WorldView.hpp" #include "DeltaChunkIterator.hpp" -#include "util/JobChain.hpp" #include "Game.hpp" #include "log.hpp" +#include "util/JobChain.hpp" using namespace explo; #define CALL_RENDER_API #ifdef CALL_RENDER_API -# include "video/RenderApi.hpp" +#include "video/RenderApi.hpp" #endif -WorldView::WorldView(World& world, glm::ivec3 const& init_position, glm::ivec3 const& render_distance) : - m_world(world), - m_render_distance(render_distance) +WorldView::WorldView(World &world, glm::ivec3 const &init_position, glm::ivec3 const &render_distance) : + m_world(world), + m_render_distance(render_distance) { - // Use an old position such that DeltaChunkIterator will iterate over all the chunks (as the world has changed) - m_position = init_position + glm::ivec3(m_render_distance * 2 + 1); - set_position(init_position); + // Use an old position such that DeltaChunkIterator will iterate over all the chunks (as the world has changed) + m_position = init_position + glm::ivec3(m_render_distance * 2 + 1); + set_position(init_position); } -WorldView::~WorldView() -{ -} +WorldView::~WorldView() {} -glm::ivec3 WorldView::get_relative_chunk_position(glm::ivec3 const& chunk_pos) const +glm::ivec3 WorldView::get_relative_chunk_position(glm::ivec3 const &chunk_pos) const { - return chunk_pos - m_position + glm::ivec3(m_render_distance); + return chunk_pos - m_position + glm::ivec3(m_render_distance); } -bool WorldView::is_relative_position_inside(glm::ivec3 const& rel_chunk_pos) const +bool WorldView::is_relative_position_inside(glm::ivec3 const &rel_chunk_pos) const { - glm::ivec3 side = get_side(); + glm::ivec3 side = get_side(); - bool is_in = true; - is_in &= rel_chunk_pos.x >= 0 && rel_chunk_pos.x < side.x; - is_in &= rel_chunk_pos.y >= 0 && rel_chunk_pos.y < side.y; - is_in &= rel_chunk_pos.z >= 0 && rel_chunk_pos.z < side.z; - return is_in; + bool is_in = true; + is_in &= rel_chunk_pos.x >= 0 && rel_chunk_pos.x < side.x; + is_in &= rel_chunk_pos.y >= 0 && rel_chunk_pos.y < side.y; + is_in &= rel_chunk_pos.z >= 0 && rel_chunk_pos.z < side.z; + return is_in; } -bool WorldView::is_position_inside(glm::ivec3 const& chunk_pos) const +bool WorldView::is_position_inside(glm::ivec3 const &chunk_pos) const { - return is_relative_position_inside(get_relative_chunk_position(chunk_pos)); + return is_relative_position_inside(get_relative_chunk_position(chunk_pos)); } -void WorldView::offset_position(glm::ivec3 const& offset) +void WorldView::offset_position(glm::ivec3 const &offset) { - if (offset.x == 0 && offset.y == 0 && offset.z == 0) return; + if (offset.x == 0 && offset.y == 0 && offset.z == 0) return; - glm::ivec3 old_position = m_position; - m_position += offset; + glm::ivec3 old_position = m_position; + m_position += offset; - // Destroy the chunks that went out of the world view - DeltaChunkIterator old_chunks_iterator(m_position, old_position, m_render_distance, [&](glm::ivec3 const& chunk_pos) - { - m_world.unload_chunk(chunk_pos); + // Destroy the chunks that went out of the world view + DeltaChunkIterator old_chunks_iterator( + m_position, + old_position, + m_render_distance, + [&](glm::ivec3 const &chunk_pos) + { + m_world.unload_chunk(chunk_pos); #ifdef CALL_RENDER_API - RenderApi::world_view_destroy_chunk(chunk_pos); + RenderApi::world_view_destroy_chunk(chunk_pos); #endif - }); - old_chunks_iterator.iterate(); + } + ); + old_chunks_iterator.iterate(); #ifdef CALL_RENDER_API - // Set the world view new position for the renderer, this has to be done *after* destroying old chunks. If the position were - // set before destroying the chunks, old chunk references would go missing and leak memory - RenderApi::world_view_set_position(m_position); + // Set the world view new position for the renderer, this has to be done *after* destroying old chunks. If the position were + // set before destroying the chunks, old chunk references would go missing and leak memory + RenderApi::world_view_set_position(m_position); #endif - // Iterate the new chunks, generate and upload them for rendering - DeltaChunkIterator new_chunks_iterator(old_position, m_position, m_render_distance, [&](glm::ivec3 const& chunk_pos) - { - glm::ivec3 rel_pos = chunk_pos - m_position + m_render_distance; - - m_world.load_chunk_async(chunk_pos, [](std::shared_ptr const& chunk) - { + // Iterate the new chunks, generate and upload them for rendering + DeltaChunkIterator new_chunks_iterator( + old_position, + m_position, + m_render_distance, + [&](glm::ivec3 const &chunk_pos) + { + glm::ivec3 rel_pos = chunk_pos - m_position + m_render_distance; + + m_world.load_chunk_async( + chunk_pos, + [](std::shared_ptr const &chunk) + { #ifdef CALL_RENDER_API - explo::run_on_main_thread([ weak_chunk = std::weak_ptr(chunk) ]() - { - std::shared_ptr chunk = weak_chunk.lock(); - if (!chunk) return; - - // Try to upload the chunk for rendering. Since the generation is asynchronous, we could be asking the renderer - // to upload a chunk that is now outside the world view (e.g. the player moved very fast). In this case the - // renderer will silently ignore the uploading - RenderApi::world_view_upload_chunk(*chunk); - }); + explo::run_on_main_thread( + [weak_chunk = std::weak_ptr(chunk)]() + { + std::shared_ptr chunk = weak_chunk.lock(); + if (!chunk) return; + + // Try to upload the chunk for rendering. Since the generation is asynchronous, we could be asking the renderer + // to upload a chunk that is now outside the world view (e.g. the player moved very fast). In this case the + // renderer will silently ignore the uploading + RenderApi::world_view_upload_chunk(*chunk); + } + ); #endif - }); - }); - new_chunks_iterator.iterate(); + } + ); + } + ); + new_chunks_iterator.iterate(); } -void WorldView::set_position(glm::ivec3 const& chunk_pos) +void WorldView::set_position(glm::ivec3 const &chunk_pos) { - offset_position(chunk_pos - m_position); + offset_position(chunk_pos - m_position); } -bool WorldView::is_chunk_position_inside( - glm::ivec3 const& world_view_pos, - glm::ivec3 const& render_distance, - glm::ivec3 const& chunk_pos - ) +bool WorldView::is_chunk_position_inside(glm::ivec3 const &world_view_pos, glm::ivec3 const &render_distance, glm::ivec3 const &chunk_pos) { - glm::ivec3 side = render_distance * 2 + 1; - glm::ivec3 rel_pos = chunk_pos - world_view_pos + render_distance; - return - rel_pos.x >= 0 && rel_pos.x < side.x && - rel_pos.y >= 0 && rel_pos.y < side.y && - rel_pos.z >= 0 && rel_pos.z < side.z; + glm::ivec3 side = render_distance * 2 + 1; + glm::ivec3 rel_pos = chunk_pos - world_view_pos + render_distance; + return rel_pos.x >= 0 && rel_pos.x < side.x && rel_pos.y >= 0 && rel_pos.y < side.y && rel_pos.z >= 0 && rel_pos.z < side.z; } -void WorldView::iterate_chunks( - glm::ivec3 const& position, - glm::ivec3 const& render_distance, - std::function const& callback -) +void WorldView::iterate_chunks(glm::ivec3 const &position, glm::ivec3 const &render_distance, std::function const &callback) { - glm::ivec3 offset{}; - for (offset.x = -render_distance.x; offset.x <= render_distance.x; offset.x++) - { - for (offset.y = -render_distance.y; offset.y <= render_distance.y; offset.y++) - { - for (offset.z = -render_distance.z; offset.z <= render_distance.z; offset.z++) - { - glm::ivec3 chunk_pos = position + offset; - callback(chunk_pos); - } - } - } + glm::ivec3 offset{}; + for (offset.x = -render_distance.x; offset.x <= render_distance.x; offset.x++) + { + for (offset.y = -render_distance.y; offset.y <= render_distance.y; offset.y++) + { + for (offset.z = -render_distance.z; offset.z <= render_distance.z; offset.z++) + { + glm::ivec3 chunk_pos = position + offset; + callback(chunk_pos); + } + } + } } diff --git a/src/world/WorldView.hpp b/src/world/WorldView.hpp index 8628713..a239b48 100644 --- a/src/world/WorldView.hpp +++ b/src/world/WorldView.hpp @@ -1,64 +1,57 @@ #pragma once -#include - #include +#include #include "World.hpp" -#include "util/Image3d.hpp" #include "util/CircularImage3d.hpp" +#include "util/Image3d.hpp" #include "util/VirtualAllocator.hpp" namespace explo { - /// This class handles the loading/unloading of chunks surrounding a particular chunk position (the center), with a radius (the render distance). - /// While it's mainly used to render the world for the player, it could also be used to load the chunks around an entity for physics computation. - class WorldView : public std::enable_shared_from_this - { - static constexpr size_t k_max_render_distance = 32; - static constexpr size_t k_max_side = k_max_render_distance * 2 + 1; - static constexpr size_t k_max_size = k_max_side * k_max_side * k_max_side; + /// This class handles the loading/unloading of chunks surrounding a particular chunk position (the center), with a radius (the render distance). + /// While it's mainly used to render the world for the player, it could also be used to load the chunks around an entity for physics computation. + class WorldView : public std::enable_shared_from_this + { + static constexpr size_t k_max_render_distance = 32; + static constexpr size_t k_max_side = k_max_render_distance * 2 + 1; + static constexpr size_t k_max_size = k_max_side * k_max_side * k_max_side; - private: - World& m_world; - glm::ivec3 m_position; - glm::ivec3 m_render_distance; + private: + World &m_world; + glm::ivec3 m_position; + glm::ivec3 m_render_distance; - public: - explicit WorldView(World& world, glm::ivec3 const& init_position, glm::ivec3 const& render_distance); - ~WorldView(); + public: + explicit WorldView(World &world, glm::ivec3 const &init_position, glm::ivec3 const &render_distance); + ~WorldView(); - glm::ivec3 get_render_distance() const { return m_render_distance; } - glm::ivec3 get_side() const { return m_render_distance * 2 + 1; } - size_t get_size() const { return m_render_distance.x * m_render_distance.y * m_render_distance.z; } + glm::ivec3 get_render_distance() const { return m_render_distance; } + glm::ivec3 get_side() const { return m_render_distance * 2 + 1; } + size_t get_size() const { return m_render_distance.x * m_render_distance.y * m_render_distance.z; } - /// Returns the chunk position relative to the world view. Such that (0,0,0) is left-bottom-back. - glm::ivec3 get_relative_chunk_position(glm::ivec3 const& chunk_pos) const; + /// Returns the chunk position relative to the world view. Such that (0,0,0) is left-bottom-back. + glm::ivec3 get_relative_chunk_position(glm::ivec3 const &chunk_pos) const; - /// Checks whether the given chunk position, relative to the world view, is inside the world view. - bool is_relative_position_inside(glm::ivec3 const& rel_chunk_pos) const; + /// Checks whether the given chunk position, relative to the world view, is inside the world view. + bool is_relative_position_inside(glm::ivec3 const &rel_chunk_pos) const; - /// Checks whether the given chunk position is inside the world view. - bool is_position_inside(glm::ivec3 const& chunk_pos) const; + /// Checks whether the given chunk position is inside the world view. + bool is_position_inside(glm::ivec3 const &chunk_pos) const; - glm::ivec3 get_position() const { return m_position; } - void offset_position(glm::ivec3 const& offset); - void set_position(glm::ivec3 const& chunk_pos); + glm::ivec3 get_position() const { return m_position; } + void offset_position(glm::ivec3 const &offset); + void set_position(glm::ivec3 const &chunk_pos); - // ------------------------------------------------------------------------------------------------ Static methods + // ------------------------------------------------------------------------------------------------ Static methods - static bool is_chunk_position_inside( - glm::ivec3 const& world_view_pos, - glm::ivec3 const& render_distance, - glm::ivec3 const& chunk_pos - ); + static bool is_chunk_position_inside(glm::ivec3 const &world_view_pos, glm::ivec3 const &render_distance, glm::ivec3 const &chunk_pos); - /// Iterates the chunks of the world view defined by the given position and render_distance. Calls callback for every occurrence. - static void iterate_chunks( - glm::ivec3 const& world_view_pos, - glm::ivec3 const& render_distance, - std::function const& callback - ); - }; + /// Iterates the chunks of the world view defined by the given position and render_distance. Calls callback for every occurrence. + static void iterate_chunks( + glm::ivec3 const &world_view_pos, glm::ivec3 const &render_distance, std::function const &callback + ); + }; -} // namespace explo +} // namespace explo diff --git a/src/world/surface/BlockySurfaceGenerator.cpp b/src/world/surface/BlockySurfaceGenerator.cpp index bdad26d..0306eeb 100644 --- a/src/world/surface/BlockySurfaceGenerator.cpp +++ b/src/world/surface/BlockySurfaceGenerator.cpp @@ -2,109 +2,98 @@ #include -#include "world/Chunk.hpp" #include "Game.hpp" +#include "world/Chunk.hpp" using namespace explo; -void BlockySurfaceGenerator::write_block_geometry( - Chunk& chunk, - glm::ivec3 const& block, - uint8_t block_type, - SurfaceWriter& surface_writer - ) +void BlockySurfaceGenerator::write_block_geometry(Chunk &chunk, glm::ivec3 const &block, uint8_t block_type, SurfaceWriter &surface_writer) { - glm::vec2 texcoord = glm::vec2( - (float(block_type) + 0.5f) / float(game().m_block_registry.size()), - 0.5f - ); + glm::vec2 texcoord = glm::vec2((float(block_type) + 0.5f) / float(game().m_block_registry.size()), 0.5f); - glm::vec3 f = chunk.to_world_position(block); // Block from (world space) - glm::vec3 t = f + (glm::vec3(Chunk::k_world_size) / glm::vec3(Chunk::k_grid_size)); // Block to (world space) + glm::vec3 f = chunk.to_world_position(block); // Block from (world space) + glm::vec3 t = f + (glm::vec3(Chunk::k_world_size) / glm::vec3(Chunk::k_grid_size)); // Block to (world space) - // Left face - if (block.x == 0 || chunk.get_block_type_at(block + glm::ivec3(-1, 0, 0)) == 0) // TODO check if the offset block is still inside the chunk - { - surface_writer.add_quad( - SurfaceVertex{.m_position = glm::vec3(f.x, f.y, t.z), .m_normal = glm::vec3(-1, 0, 0), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(f.x, f.y, f.z), .m_normal = glm::vec3(-1, 0, 0), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(f.x, t.y, f.z), .m_normal = glm::vec3(-1, 0, 0), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(f.x, t.y, t.z), .m_normal = glm::vec3(-1, 0, 0), .m_texcoords = texcoord } - ); - } + // Left face + if (block.x == 0 || chunk.get_block_type_at(block + glm::ivec3(-1, 0, 0)) == 0) // TODO check if the offset block is still inside the chunk + { + surface_writer.add_quad( + SurfaceVertex{.m_position = glm::vec3(f.x, f.y, t.z), .m_normal = glm::vec3(-1, 0, 0), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(f.x, f.y, f.z), .m_normal = glm::vec3(-1, 0, 0), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(f.x, t.y, f.z), .m_normal = glm::vec3(-1, 0, 0), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(f.x, t.y, t.z), .m_normal = glm::vec3(-1, 0, 0), .m_texcoords = texcoord} + ); + } - // Right face - if (block.x == Chunk::k_grid_size.x - 1 || chunk.get_block_type_at(block + glm::ivec3(1, 0, 0)) == 0) - { - surface_writer.add_quad( - SurfaceVertex{.m_position = glm::vec3(t.x, f.y, f.z), .m_normal = glm::vec3(-1, 0, 0), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(t.x, f.y, t.z), .m_normal = glm::vec3(-1, 0, 0), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(t.x, t.y, t.z), .m_normal = glm::vec3(-1, 0, 0), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(t.x, t.y, f.z), .m_normal = glm::vec3(-1, 0, 0), .m_texcoords = texcoord } - ); - } + // Right face + if (block.x == Chunk::k_grid_size.x - 1 || chunk.get_block_type_at(block + glm::ivec3(1, 0, 0)) == 0) + { + surface_writer.add_quad( + SurfaceVertex{.m_position = glm::vec3(t.x, f.y, f.z), .m_normal = glm::vec3(-1, 0, 0), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(t.x, f.y, t.z), .m_normal = glm::vec3(-1, 0, 0), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(t.x, t.y, t.z), .m_normal = glm::vec3(-1, 0, 0), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(t.x, t.y, f.z), .m_normal = glm::vec3(-1, 0, 0), .m_texcoords = texcoord} + ); + } - // Bottom face - if (block.z == 0 || chunk.get_block_type_at(block - glm::ivec3(0, 1, 0)) == 0) - { - surface_writer.add_quad( - SurfaceVertex{.m_position = glm::vec3(f.x, f.y, f.z), .m_normal = glm::vec3(0, -1, 0), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(f.x, f.y, t.z), .m_normal = glm::vec3(0, -1, 0), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(t.x, f.y, t.z), .m_normal = glm::vec3(0, -1, 0), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(t.x, f.y, f.z), .m_normal = glm::vec3(0, -1, 0), .m_texcoords = texcoord } - ); - } + // Bottom face + if (block.z == 0 || chunk.get_block_type_at(block - glm::ivec3(0, 1, 0)) == 0) + { + surface_writer.add_quad( + SurfaceVertex{.m_position = glm::vec3(f.x, f.y, f.z), .m_normal = glm::vec3(0, -1, 0), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(f.x, f.y, t.z), .m_normal = glm::vec3(0, -1, 0), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(t.x, f.y, t.z), .m_normal = glm::vec3(0, -1, 0), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(t.x, f.y, f.z), .m_normal = glm::vec3(0, -1, 0), .m_texcoords = texcoord} + ); + } - // Top face - if (block.z == Chunk::k_grid_size.y - 1 || chunk.get_block_type_at(block + glm::ivec3(0, 1, 0)) == 0) - { - surface_writer.add_quad( - SurfaceVertex{.m_position = glm::vec3(f.x, t.y, f.z), .m_normal = glm::vec3(0, 1, 0), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(f.x, t.y, t.z), .m_normal = glm::vec3(0, 1, 0), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(t.x, t.y, t.z), .m_normal = glm::vec3(0, 1, 0), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(t.x, t.y, f.z), .m_normal = glm::vec3(0, 1, 0), .m_texcoords = texcoord } - ); - } + // Top face + if (block.z == Chunk::k_grid_size.y - 1 || chunk.get_block_type_at(block + glm::ivec3(0, 1, 0)) == 0) + { + surface_writer.add_quad( + SurfaceVertex{.m_position = glm::vec3(f.x, t.y, f.z), .m_normal = glm::vec3(0, 1, 0), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(f.x, t.y, t.z), .m_normal = glm::vec3(0, 1, 0), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(t.x, t.y, t.z), .m_normal = glm::vec3(0, 1, 0), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(t.x, t.y, f.z), .m_normal = glm::vec3(0, 1, 0), .m_texcoords = texcoord} + ); + } - // Back face - if (block.z == 0 || chunk.get_block_type_at(block + glm::ivec3(0, 0, -1)) == 0) - { - surface_writer.add_quad( - SurfaceVertex{.m_position = glm::vec3(f.x, f.y, f.z), .m_normal = glm::vec3(0, 0, -1), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(f.x, t.y, f.z), .m_normal = glm::vec3(0, 0, -1), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(t.x, t.y, f.z), .m_normal = glm::vec3(0, 0, -1), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(t.x, f.y, f.z), .m_normal = glm::vec3(0, 0, -1), .m_texcoords = texcoord } - ); - } + // Back face + if (block.z == 0 || chunk.get_block_type_at(block + glm::ivec3(0, 0, -1)) == 0) + { + surface_writer.add_quad( + SurfaceVertex{.m_position = glm::vec3(f.x, f.y, f.z), .m_normal = glm::vec3(0, 0, -1), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(f.x, t.y, f.z), .m_normal = glm::vec3(0, 0, -1), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(t.x, t.y, f.z), .m_normal = glm::vec3(0, 0, -1), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(t.x, f.y, f.z), .m_normal = glm::vec3(0, 0, -1), .m_texcoords = texcoord} + ); + } - // Front face - if (block.z == Chunk::k_grid_size.z - 1 || chunk.get_block_type_at(block + glm::ivec3(0, 0, 1)) == 0) - { - surface_writer.add_quad( - SurfaceVertex{.m_position = glm::vec3(f.x, f.y, t.z), .m_normal = glm::vec3(0, 0, 1), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(f.x, t.y, t.z), .m_normal = glm::vec3(0, 0, 1), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(t.x, t.y, t.z), .m_normal = glm::vec3(0, 0, 1), .m_texcoords = texcoord }, - SurfaceVertex{.m_position = glm::vec3(t.x, f.y, t.z), .m_normal = glm::vec3(0, 0, 1), .m_texcoords = texcoord } - ); - } + // Front face + if (block.z == Chunk::k_grid_size.z - 1 || chunk.get_block_type_at(block + glm::ivec3(0, 0, 1)) == 0) + { + surface_writer.add_quad( + SurfaceVertex{.m_position = glm::vec3(f.x, f.y, t.z), .m_normal = glm::vec3(0, 0, 1), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(f.x, t.y, t.z), .m_normal = glm::vec3(0, 0, 1), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(t.x, t.y, t.z), .m_normal = glm::vec3(0, 0, 1), .m_texcoords = texcoord}, + SurfaceVertex{.m_position = glm::vec3(t.x, f.y, t.z), .m_normal = glm::vec3(0, 0, 1), .m_texcoords = texcoord} + ); + } } -void BlockySurfaceGenerator::generate(Chunk& chunk, SurfaceWriter& surface_writer) +void BlockySurfaceGenerator::generate(Chunk &chunk, SurfaceWriter &surface_writer) { - chunk.octree().traverse([&](uint32_t block_type, uint32_t level, uint32_t morton_code) - { - if (block_type > 0) // TODO check if it's a visible block or not using the BlockRegistry? - { - write_block_geometry( - chunk, - Octree::to_voxel_position(morton_code), - block_type, - surface_writer - ); - } - }); + chunk.octree().traverse( + [&](uint32_t block_type, uint32_t level, uint32_t morton_code) + { + if (block_type > 0) // TODO check if it's a visible block or not using the BlockRegistry? + { + write_block_geometry(chunk, Octree::to_voxel_position(morton_code), block_type, surface_writer); + } + } + ); - surface_writer.add_instance(SurfaceInstance{ - .m_transform = glm::identity(), - }); - } + surface_writer.add_instance(SurfaceInstance{ + .m_transform = glm::identity(), + }); +} diff --git a/src/world/surface/BlockySurfaceGenerator.hpp b/src/world/surface/BlockySurfaceGenerator.hpp index 2b9e0fd..9b105d0 100644 --- a/src/world/surface/BlockySurfaceGenerator.hpp +++ b/src/world/surface/BlockySurfaceGenerator.hpp @@ -1,27 +1,21 @@ #pragma once -#include - #include +#include #include "SurfaceGenerator.hpp" namespace explo { - class BlockySurfaceGenerator : public SurfaceGenerator - { - public: - explicit BlockySurfaceGenerator() = default; - ~BlockySurfaceGenerator() = default; + class BlockySurfaceGenerator : public SurfaceGenerator + { + public: + explicit BlockySurfaceGenerator() = default; + ~BlockySurfaceGenerator() = default; - void generate(Chunk& chunk, SurfaceWriter& surface_writer) override; // TODO no SurfaceWriter in function prototype + void generate(Chunk &chunk, SurfaceWriter &surface_writer) override; // TODO no SurfaceWriter in function prototype - protected: - void write_block_geometry( - Chunk& chunk, - glm::ivec3 const& block, - uint8_t block_type, - SurfaceWriter& surface_writer - ); - }; -} // namespace explo + protected: + void write_block_geometry(Chunk &chunk, glm::ivec3 const &block, uint8_t block_type, SurfaceWriter &surface_writer); + }; +} // namespace explo diff --git a/src/world/surface/Surface.hpp b/src/world/surface/Surface.hpp index ebcfbfc..e4c7f33 100644 --- a/src/world/surface/Surface.hpp +++ b/src/world/surface/Surface.hpp @@ -1,21 +1,20 @@ #pragma once #include - #include namespace explo { - using SurfaceVertex = vren::vertex; + using SurfaceVertex = vren::vertex; - using SurfaceIndex = uint32_t; + using SurfaceIndex = uint32_t; - using SurfaceInstance = vren::mesh_instance; + using SurfaceInstance = vren::mesh_instance; - struct Surface - { - std::vector m_vertices; - std::vector m_indices; - std::vector m_instances; - }; -} // namespace explo + struct Surface + { + std::vector m_vertices; + std::vector m_indices; + std::vector m_instances; + }; +} // namespace explo diff --git a/src/world/surface/SurfaceGenerator.hpp b/src/world/surface/SurfaceGenerator.hpp index a308bae..7ea29e4 100644 --- a/src/world/surface/SurfaceGenerator.hpp +++ b/src/world/surface/SurfaceGenerator.hpp @@ -4,12 +4,12 @@ namespace explo { - // Forward decl - class Chunk; + // Forward decl + class Chunk; - class SurfaceGenerator - { - public: - virtual void generate(Chunk& chunk, SurfaceWriter& surface_writer) = 0; - }; -} // namespace explo + class SurfaceGenerator + { + public: + virtual void generate(Chunk &chunk, SurfaceWriter &surface_writer) = 0; + }; +} // namespace explo diff --git a/src/world/surface/SurfaceWriter.cpp b/src/world/surface/SurfaceWriter.cpp index 71f3200..40d0e41 100644 --- a/src/world/surface/SurfaceWriter.cpp +++ b/src/world/surface/SurfaceWriter.cpp @@ -2,44 +2,50 @@ using namespace explo; -SurfaceWriter::SurfaceWriter(Surface& surface) : - m_surface(surface) +SurfaceWriter::SurfaceWriter(Surface &surface) : + m_surface(surface) { } -uint32_t SurfaceWriter::add_vertex(SurfaceVertex const& v) +uint32_t SurfaceWriter::add_vertex(SurfaceVertex const &v) { - uint32_t i = m_surface.m_vertices.size(); - m_surface.m_vertices.push_back(v); - return i; + uint32_t i = m_surface.m_vertices.size(); + m_surface.m_vertices.push_back(v); + return i; } -void SurfaceWriter::add_triangle(SurfaceVertex const& v0, SurfaceVertex const& v1, SurfaceVertex const& v2) +void SurfaceWriter::add_triangle(SurfaceVertex const &v0, SurfaceVertex const &v1, SurfaceVertex const &v2) { - uint32_t i0 = add_vertex(v0); - uint32_t i1 = add_vertex(v1); - uint32_t i2 = add_vertex(v2); + uint32_t i0 = add_vertex(v0); + uint32_t i1 = add_vertex(v1); + uint32_t i2 = add_vertex(v2); - add_index(i0); add_index(i1); add_index(i2); + add_index(i0); + add_index(i1); + add_index(i2); } -void SurfaceWriter::add_quad(SurfaceVertex const& v0, SurfaceVertex const& v1, SurfaceVertex const& v2, SurfaceVertex const& v3) +void SurfaceWriter::add_quad(SurfaceVertex const &v0, SurfaceVertex const &v1, SurfaceVertex const &v2, SurfaceVertex const &v3) { - uint32_t i0 = add_vertex(v0); - uint32_t i1 = add_vertex(v1); - uint32_t i2 = add_vertex(v2); - uint32_t i3 = add_vertex(v3); - - add_index(i0); add_index(i1); add_index(i2); - add_index(i0); add_index(i2); add_index(i3); + uint32_t i0 = add_vertex(v0); + uint32_t i1 = add_vertex(v1); + uint32_t i2 = add_vertex(v2); + uint32_t i3 = add_vertex(v3); + + add_index(i0); + add_index(i1); + add_index(i2); + add_index(i0); + add_index(i2); + add_index(i3); } void SurfaceWriter::add_index(uint32_t index) { - m_surface.m_indices.push_back(index); + m_surface.m_indices.push_back(index); } -void SurfaceWriter::add_instance(SurfaceInstance const& instance) +void SurfaceWriter::add_instance(SurfaceInstance const &instance) { - m_surface.m_instances.push_back(instance); + m_surface.m_instances.push_back(instance); } diff --git a/src/world/surface/SurfaceWriter.hpp b/src/world/surface/SurfaceWriter.hpp index 86d7e38..4e8791f 100644 --- a/src/world/surface/SurfaceWriter.hpp +++ b/src/world/surface/SurfaceWriter.hpp @@ -6,27 +6,27 @@ namespace explo { - /// A helper class intended to aid the generation of a Surface. - class SurfaceWriter - { - private: - Surface& m_surface; + /// A helper class intended to aid the generation of a Surface. + class SurfaceWriter + { + private: + Surface &m_surface; - public: - explicit SurfaceWriter(Surface& surface); - ~SurfaceWriter() = default; + public: + explicit SurfaceWriter(Surface &surface); + ~SurfaceWriter() = default; - Surface& surface() { return m_surface; } + Surface &surface() { return m_surface; } - uint32_t add_vertex(SurfaceVertex const& v); - void add_triangle(SurfaceVertex const& v0, SurfaceVertex const& v1, SurfaceVertex const& v2); + uint32_t add_vertex(SurfaceVertex const &v); + void add_triangle(SurfaceVertex const &v0, SurfaceVertex const &v1, SurfaceVertex const &v2); - /// Adds two triangles forming a quad; the triangles generated are (v0, v1, v2) and (v2, v1, v3). - /// Vertex ordering (clockwise or counter-clockwise) is up to the user. - void add_quad(SurfaceVertex const& v0, SurfaceVertex const& v1, SurfaceVertex const& v2, SurfaceVertex const& v3); + /// Adds two triangles forming a quad; the triangles generated are (v0, v1, v2) and (v2, v1, v3). + /// Vertex ordering (clockwise or counter-clockwise) is up to the user. + void add_quad(SurfaceVertex const &v0, SurfaceVertex const &v1, SurfaceVertex const &v2, SurfaceVertex const &v3); - void add_index(SurfaceIndex index); + void add_index(SurfaceIndex index); - void add_instance(SurfaceInstance const& instance); - }; -} // namespace explo + void add_instance(SurfaceInstance const &instance); + }; +} // namespace explo diff --git a/src/world/volume/Octree.cpp b/src/world/volume/Octree.cpp index 8c614c8..cd6e089 100644 --- a/src/world/volume/Octree.cpp +++ b/src/world/volume/Octree.cpp @@ -3,138 +3,119 @@ using namespace explo; Octree::Octree(uint32_t depth) : - m_depth(depth) + m_depth(depth) { } -Octree::~Octree() -{ -} +Octree::~Octree() {} uint32_t Octree::get_voxel_at(uint32_t morton_code) const { - uint32_t node_idx = 0; // Root - for (int level = 0; level < m_depth; level++) - { - uint32_t child_idx = (morton_code >> ((m_depth - level - 1) * 3)) & 0x7; - if ((node_idx + child_idx) >= m_data.size()) - return 0; - - uint32_t child_val = m_data[node_idx + child_idx]; - if ((child_val & 0x80000000) != 0) // Parent node - node_idx = child_val & 0x7FFFFFFF; - else // Leaf node - return child_val; - } - return 0; + uint32_t node_idx = 0; // Root + for (int level = 0; level < m_depth; level++) + { + uint32_t child_idx = (morton_code >> ((m_depth - level - 1) * 3)) & 0x7; + if ((node_idx + child_idx) >= m_data.size()) return 0; + + uint32_t child_val = m_data[node_idx + child_idx]; + if ((child_val & 0x80000000) != 0) // Parent node + node_idx = child_val & 0x7FFFFFFF; + else // Leaf node + return child_val; + } + return 0; } void Octree::set_voxel_at(uint32_t morton_code, uint32_t value) { - uint32_t node_idx = 0; - for (int level = 0; level < m_depth; level++) - { - uint32_t child_idx = (morton_code >> ((m_depth - level - 1) * 3)) & 0x7; - if ((node_idx + child_idx) >= m_data.size()) - m_data.resize(m_data.size() + k_grow_size); // Will keep current data and zero new data - - uint32_t child_val = m_data[node_idx + child_idx]; - if ((child_val & 0x80000000) != 0) // Parent node - { - node_idx = child_val & 0x7FFFFFFF; - } - else // Leaf node - { - if (child_val == value) - { // The reached node is a leaf node and already has the value - return; - } - else if (level == m_depth - 1) - { // I've reached the max resolution, I can just set the leaf node value and return - m_data[node_idx + child_idx] = value; - } - else - { // The leaf node becomes a parent node, and we allocate its children - m_data[node_idx + child_idx] = m_next_alloc_index | 0x80000000; - node_idx = m_next_alloc_index; - m_next_alloc_index += 8; - } - } - } + uint32_t node_idx = 0; + for (int level = 0; level < m_depth; level++) + { + uint32_t child_idx = (morton_code >> ((m_depth - level - 1) * 3)) & 0x7; + if ((node_idx + child_idx) >= m_data.size()) m_data.resize(m_data.size() + k_grow_size); // Will keep current data and zero new data + + uint32_t child_val = m_data[node_idx + child_idx]; + if ((child_val & 0x80000000) != 0) // Parent node + { + node_idx = child_val & 0x7FFFFFFF; + } + else // Leaf node + { + if (child_val == value) + { // The reached node is a leaf node and already has the value + return; + } + else if (level == m_depth - 1) + { // I've reached the max resolution, I can just set the leaf node value and return + m_data[node_idx + child_idx] = value; + } + else + { // The leaf node becomes a parent node, and we allocate its children + m_data[node_idx + child_idx] = m_next_alloc_index | 0x80000000; + node_idx = m_next_alloc_index; + m_next_alloc_index += 8; + } + } + } } -void Octree::traverse_r( - uint32_t node_idx, - uint32_t level, - uint32_t morton_code, - TraversalCallbackT const& callback - ) const +void Octree::traverse_r(uint32_t node_idx, uint32_t level, uint32_t morton_code, TraversalCallbackT const &callback) const { - for (int child_idx = 0; child_idx < 8; child_idx++) - { - if ((node_idx + child_idx) >= m_data.size()) - return; - - uint32_t child_morton_code = morton_code | (child_idx << ((m_depth - level - 1) * 3)); - uint32_t child_val = m_data[node_idx + child_idx]; - if ((child_val & 0x80000000) != 0) // Parent node - { - traverse_r( - child_val & 0x7FFFFFFF, - level + 1, - child_morton_code, - callback - ); - } - else if (child_val > 0) // Leaf node - { - callback( - child_val, - level, - child_morton_code - ); - } - } + for (int child_idx = 0; child_idx < 8; child_idx++) + { + if ((node_idx + child_idx) >= m_data.size()) return; + + uint32_t child_morton_code = morton_code | (child_idx << ((m_depth - level - 1) * 3)); + uint32_t child_val = m_data[node_idx + child_idx]; + if ((child_val & 0x80000000) != 0) // Parent node + { + traverse_r(child_val & 0x7FFFFFFF, level + 1, child_morton_code, callback); + } + else if (child_val > 0) // Leaf node + { + callback(child_val, level, child_morton_code); + } + } } -void Octree::traverse(TraversalCallbackT const& callback) const +void Octree::traverse(TraversalCallbackT const &callback) const { - traverse_r(0, 0, 0, callback); + traverse_r(0, 0, 0, callback); } -uint32_t Octree::to_morton_code(glm::ivec3 const& voxel_pos) +uint32_t Octree::to_morton_code(glm::ivec3 const &voxel_pos) { - uint32_t x = voxel_pos.x; - uint32_t y = voxel_pos.y; - uint32_t z = voxel_pos.z; - - uint32_t morton_code = 0; - int i = 0; - while (x || y || z) - { - morton_code |= ((x & 1) | ((y & 1) << 1) | ((z & 1) << 2)) << (i * 3); - - x >>= 1; - y >>= 1; - z >>= 1; - - i++; - } - return morton_code; + uint32_t x = voxel_pos.x; + uint32_t y = voxel_pos.y; + uint32_t z = voxel_pos.z; + + uint32_t morton_code = 0; + int i = 0; + while (x || y || z) + { + morton_code |= ((x & 1) | ((y & 1) << 1) | ((z & 1) << 2)) << (i * 3); + + x >>= 1; + y >>= 1; + z >>= 1; + + i++; + } + return morton_code; } glm::ivec3 Octree::to_voxel_position(uint32_t morton_code) { - glm::ivec3 voxel_pos{}; - int i = 0; - while (morton_code > 0) - { - voxel_pos.x |= (morton_code & 1) << i; - voxel_pos.y |= ((morton_code & 0b10) >> 1) << i; - voxel_pos.z |= ((morton_code & 0b100) >> 2) << i; - - morton_code >>= 3; - i++; - } - return voxel_pos; + glm::ivec3 voxel_pos{}; + int i = 0; + while (morton_code > 0) + { + voxel_pos.x |= (morton_code & 1) << i; + voxel_pos.y |= ((morton_code & 0b10) >> 1) << i; + voxel_pos.z |= ((morton_code & 0b100) >> 2) << i; + + morton_code >>= 3; + i++; + } + return voxel_pos; } diff --git a/src/world/volume/Octree.hpp b/src/world/volume/Octree.hpp index a2c63f1..89e6a9d 100644 --- a/src/world/volume/Octree.hpp +++ b/src/world/volume/Octree.hpp @@ -1,50 +1,40 @@ #pragma once -#include #include - #include +#include namespace explo { - class Octree - { - static constexpr size_t k_grow_size = 1024; - - using TraversalCallbackT = std::function; - - private: - std::vector m_data; - uint32_t m_depth; - uint32_t m_next_alloc_index = 8; - - public: - explicit Octree(uint32_t depth); - ~Octree(); - - void const* data() const { return m_data.data(); } - size_t size() const { return m_data.size(); } - - uint32_t get_voxel_at(uint32_t morton_code) const; - void set_voxel_at(uint32_t morton_code, uint32_t value); - - // TODO Add a function to compact the octree: group 2x2x2 nodes with identical value into one - - void traverse(TraversalCallbackT const& callback) const; - - static uint32_t to_morton_code(glm::ivec3 const& voxel_pos); - static glm::ivec3 to_voxel_position(uint32_t morton_code); - - private: - void traverse_r( - uint32_t node_idx, - uint32_t depth, - uint32_t morton_code, - TraversalCallbackT const& callback - ) const; - }; -} // namespace explo + class Octree + { + static constexpr size_t k_grow_size = 1024; + + using TraversalCallbackT = std::function; + + private: + std::vector m_data; + uint32_t m_depth; + uint32_t m_next_alloc_index = 8; + + public: + explicit Octree(uint32_t depth); + ~Octree(); + + void const *data() const { return m_data.data(); } + size_t size() const { return m_data.size(); } + + uint32_t get_voxel_at(uint32_t morton_code) const; + void set_voxel_at(uint32_t morton_code, uint32_t value); + + // TODO Add a function to compact the octree: group 2x2x2 nodes with identical value into one + + void traverse(TraversalCallbackT const &callback) const; + + static uint32_t to_morton_code(glm::ivec3 const &voxel_pos); + static glm::ivec3 to_voxel_position(uint32_t morton_code); + + private: + void traverse_r(uint32_t node_idx, uint32_t depth, uint32_t morton_code, TraversalCallbackT const &callback) const; + }; +} // namespace explo diff --git a/src/world/volume/PerlinNoiseGenerator.cpp b/src/world/volume/PerlinNoiseGenerator.cpp index c7c7392..e3298d9 100644 --- a/src/world/volume/PerlinNoiseGenerator.cpp +++ b/src/world/volume/PerlinNoiseGenerator.cpp @@ -1,65 +1,62 @@ #include "PerlinNoiseGenerator.hpp" - using namespace explo; PerlinNoiseGenerator::PerlinNoiseGenerator() : - m_perlin_noise(1693894559) + m_perlin_noise(1693894559) { } -PerlinNoiseGenerator::~PerlinNoiseGenerator() -{ -} +PerlinNoiseGenerator::~PerlinNoiseGenerator() {} int PerlinNoiseGenerator::get_height_at(int x, int z) { - const double k_frequency = 0.023; - double val = m_perlin_noise.noise2D(double(x) * k_frequency, double(z) * k_frequency); - return ((val + 1.0) / 2.0) * k_max_world_height; + const double k_frequency = 0.023; + double val = m_perlin_noise.noise2D(double(x) * k_frequency, double(z) * k_frequency); + return ((val + 1.0) / 2.0) * k_max_world_height; } -void PerlinNoiseGenerator::generate_volume(Chunk& chunk) +void PerlinNoiseGenerator::generate_volume(Chunk &chunk) { - for (int x = 0; x < 16; x++) - { - for (int z = 0; z < 16; z++) - { - glm::ivec3 block_pos = chunk.to_world_block_position(glm::ivec3{x, 0, z}); - - int base_y = get_height_at(block_pos.x, block_pos.z); - block_pos.y = base_y; - - if (chunk.test_block_position(block_pos)) - { - int min_neighbor_y = block_pos.y; - min_neighbor_y = glm::min(get_height_at(block_pos.x - 1, block_pos.z), min_neighbor_y); - min_neighbor_y = glm::min(get_height_at(block_pos.x, block_pos.z - 1), min_neighbor_y); - min_neighbor_y = glm::min(get_height_at(block_pos.x + 1, block_pos.z), min_neighbor_y); - min_neighbor_y = glm::min(get_height_at(block_pos.x, block_pos.z + 1), min_neighbor_y); - min_neighbor_y = glm::min(get_height_at(block_pos.x + 1, block_pos.z + 1), min_neighbor_y); - min_neighbor_y = glm::min(get_height_at(block_pos.x + 1, block_pos.z - 1), min_neighbor_y); - min_neighbor_y = glm::min(get_height_at(block_pos.x - 1, block_pos.z + 1), min_neighbor_y); - min_neighbor_y = glm::min(get_height_at(block_pos.x - 1, block_pos.z - 1), min_neighbor_y); - - // TODO pick block types from BlockRegistry (e.g. as enums) - chunk.set_block_type_at(chunk.to_chunk_position(block_pos), 1); // Grass - block_pos.y--; - - int dirt_height = (int) (m_perlin_noise.noise2D(x ^ 3508739221, z ^ 2024663696) + 2.0); - - for (int i = 0; block_pos.y > min_neighbor_y && i < dirt_height; block_pos.y--, i++) - { - if (!chunk.test_block_position(block_pos)) break; - chunk.set_block_type_at(chunk.to_chunk_position(block_pos), 2); // Dirt - } - - for (; block_pos.y > min_neighbor_y; block_pos.y--) - { - if (!chunk.test_block_position(block_pos)) break; - chunk.set_block_type_at(chunk.to_chunk_position(block_pos), 3); // Stone - } - } - } - } + for (int x = 0; x < 16; x++) + { + for (int z = 0; z < 16; z++) + { + glm::ivec3 block_pos = chunk.to_world_block_position(glm::ivec3{x, 0, z}); + + int base_y = get_height_at(block_pos.x, block_pos.z); + block_pos.y = base_y; + + if (chunk.test_block_position(block_pos)) + { + int min_neighbor_y = block_pos.y; + min_neighbor_y = glm::min(get_height_at(block_pos.x - 1, block_pos.z), min_neighbor_y); + min_neighbor_y = glm::min(get_height_at(block_pos.x, block_pos.z - 1), min_neighbor_y); + min_neighbor_y = glm::min(get_height_at(block_pos.x + 1, block_pos.z), min_neighbor_y); + min_neighbor_y = glm::min(get_height_at(block_pos.x, block_pos.z + 1), min_neighbor_y); + min_neighbor_y = glm::min(get_height_at(block_pos.x + 1, block_pos.z + 1), min_neighbor_y); + min_neighbor_y = glm::min(get_height_at(block_pos.x + 1, block_pos.z - 1), min_neighbor_y); + min_neighbor_y = glm::min(get_height_at(block_pos.x - 1, block_pos.z + 1), min_neighbor_y); + min_neighbor_y = glm::min(get_height_at(block_pos.x - 1, block_pos.z - 1), min_neighbor_y); + + // TODO pick block types from BlockRegistry (e.g. as enums) + chunk.set_block_type_at(chunk.to_chunk_position(block_pos), 1); // Grass + block_pos.y--; + + int dirt_height = (int)(m_perlin_noise.noise2D(x ^ 3508739221, z ^ 2024663696) + 2.0); + + for (int i = 0; block_pos.y > min_neighbor_y && i < dirt_height; block_pos.y--, i++) + { + if (!chunk.test_block_position(block_pos)) break; + chunk.set_block_type_at(chunk.to_chunk_position(block_pos), 2); // Dirt + } + + for (; block_pos.y > min_neighbor_y; block_pos.y--) + { + if (!chunk.test_block_position(block_pos)) break; + chunk.set_block_type_at(chunk.to_chunk_position(block_pos), 3); // Stone + } + } + } + } } diff --git a/src/world/volume/PerlinNoiseGenerator.hpp b/src/world/volume/PerlinNoiseGenerator.hpp index 84fa261..46aaf0d 100644 --- a/src/world/volume/PerlinNoiseGenerator.hpp +++ b/src/world/volume/PerlinNoiseGenerator.hpp @@ -1,26 +1,25 @@ #pragma once #include "VolumeGenerator.hpp" - #include "util/PerlinNoise.hpp" namespace explo { - class PerlinNoiseGenerator : public VolumeGenerator - { - public: - static constexpr uint32_t k_max_world_height = 100; + class PerlinNoiseGenerator : public VolumeGenerator + { + public: + static constexpr uint32_t k_max_world_height = 100; - private: - siv::PerlinNoise m_perlin_noise; + private: + siv::PerlinNoise m_perlin_noise; - public: - explicit PerlinNoiseGenerator(); - ~PerlinNoiseGenerator(); + public: + explicit PerlinNoiseGenerator(); + ~PerlinNoiseGenerator(); - void generate_volume(Chunk& chunk); + void generate_volume(Chunk &chunk); - private: - int get_height_at(int x, int z); - }; -} // namespace explo + private: + int get_height_at(int x, int z); + }; +} // namespace explo diff --git a/src/world/volume/SinCosVolumeGenerator.cpp b/src/world/volume/SinCosVolumeGenerator.cpp index 0657377..4d5af94 100644 --- a/src/world/volume/SinCosVolumeGenerator.cpp +++ b/src/world/volume/SinCosVolumeGenerator.cpp @@ -4,23 +4,18 @@ using namespace explo; -void SinCosVolumeGenerator::generate_volume(Chunk& chunk) +void SinCosVolumeGenerator::generate_volume(Chunk &chunk) { - if (chunk.get_position().y != 0 && chunk.get_position().y != 1) return; + if (chunk.get_position().y != 0 && chunk.get_position().y != 1) return; - for (int x = 0; x < Chunk::k_grid_size.x; x++) - { - for (int z = 0; z < Chunk::k_grid_size.z; z++) - { - glm::ivec3 block_pos = chunk.to_world_block_position(glm::ivec3{x, 0, z}); - block_pos.y = glm::floor( - ((glm::sin(block_pos.x * 0.09f) + 1.0f) / 2.0f) * - ((glm::cos(block_pos.z * 0.09f) + 1.0f) / 2.0f) * - 32.0f - ); + for (int x = 0; x < Chunk::k_grid_size.x; x++) + { + for (int z = 0; z < Chunk::k_grid_size.z; z++) + { + glm::ivec3 block_pos = chunk.to_world_block_position(glm::ivec3{x, 0, z}); + block_pos.y = glm::floor(((glm::sin(block_pos.x * 0.09f) + 1.0f) / 2.0f) * ((glm::cos(block_pos.z * 0.09f) + 1.0f) / 2.0f) * 32.0f); - if (chunk.test_block_position(block_pos)) - chunk.set_block_type_at(chunk.to_chunk_position(block_pos), 1); - } - } + if (chunk.test_block_position(block_pos)) chunk.set_block_type_at(chunk.to_chunk_position(block_pos), 1); + } + } } diff --git a/src/world/volume/SinCosVolumeGenerator.hpp b/src/world/volume/SinCosVolumeGenerator.hpp index d3d19ac..fae7a21 100644 --- a/src/world/volume/SinCosVolumeGenerator.hpp +++ b/src/world/volume/SinCosVolumeGenerator.hpp @@ -4,13 +4,14 @@ namespace explo { - class SinCosVolumeGenerator : public VolumeGenerator - { - public: - explicit SinCosVolumeGenerator() = default; - ~SinCosVolumeGenerator() = default; - void generate_volume(Chunk& chunk) override; - }; + class SinCosVolumeGenerator : public VolumeGenerator + { + public: + explicit SinCosVolumeGenerator() = default; + ~SinCosVolumeGenerator() = default; -} // namespace explo + void generate_volume(Chunk &chunk) override; + }; + +} // namespace explo diff --git a/src/world/volume/VolumeGenerator.hpp b/src/world/volume/VolumeGenerator.hpp index ebbc546..7d6ffac 100644 --- a/src/world/volume/VolumeGenerator.hpp +++ b/src/world/volume/VolumeGenerator.hpp @@ -4,12 +4,12 @@ namespace explo { - class VolumeGenerator - { - public: - explicit VolumeGenerator() = default; - ~VolumeGenerator() = default; + class VolumeGenerator + { + public: + explicit VolumeGenerator() = default; + ~VolumeGenerator() = default; - virtual void generate_volume(Chunk& chunk) = 0; - }; -} // namespace explo + virtual void generate_volume(Chunk &chunk) = 0; + }; +} // namespace explo diff --git a/test/DeltaChunkIteratorTest.cpp b/test/DeltaChunkIteratorTest.cpp index 9bd7a7b..2a1f882 100644 --- a/test/DeltaChunkIteratorTest.cpp +++ b/test/DeltaChunkIteratorTest.cpp @@ -10,164 +10,187 @@ using namespace explo; TEST_CASE("DeltaChunkIterator-basic") { - constexpr glm::ivec3 k_render_distance(1); - constexpr glm::ivec3 k_side = k_render_distance * 2 + 1; - - glm::ivec3 old_chunk_pos{}; - glm::ivec3 new_chunk_pos{}; - std::vector cover_map{}; - - std::tie(old_chunk_pos, new_chunk_pos, cover_map) = - GENERATE( - Catch::Generators::table>( - { // 1 - std::make_tuple( - glm::ivec3(0, 0, 0), - glm::ivec3(-1, 0, 0), - std::vector{ - 0, 1, 1, - 0, 1, 1, - 0, 1, 1, } - ), // 2 - std::make_tuple( - glm::ivec3(0, 0, 0), - glm::ivec3(0, -1, 0), - std::vector{ - 1, 1, 1, - 1, 1, 1, - 0, 0, 0, } - ), // 3 - std::make_tuple( - glm::ivec3(0, 0, 0), - glm::ivec3(1, 0, 0), - std::vector{ - 1, 1, 0, - 1, 1, 0, - 1, 1, 0, } - ), // 4 - std::make_tuple( - glm::ivec3(0, 0, 0), - glm::ivec3(0, 1, 0), - std::vector{ - 0, 0, 0, - 1, 1, 1, - 1, 1, 1, } - ), // 5 - std::make_tuple( - glm::ivec3(0, 0, 0), - glm::ivec3(1, 1, 0), - std::vector{ - 0, 0, 0, - 1, 1, 0, - 1, 1, 0, } - ), // 6 - std::make_tuple( - glm::ivec3(0, 0, 0), - glm::ivec3(2, 2, 0), - std::vector{ - 0, 0, 0, - 0, 0, 0, - 1, 0, 0, } - ), // 7 - std::make_tuple( - glm::ivec3(0, 0, 0), - glm::ivec3(0, 3, 0), - std::vector{ - 0, 0, 0, - 0, 0, 0, - 0, 0, 0, } - ), // 8 - std::make_tuple( - glm::ivec3(0, 0, 0), - glm::ivec3(0, -2, 0), - std::vector{ - 1, 1, 1, - 0, 0, 0, - 0, 0, 0, } - ) - } - )); - - DeltaChunkIterator iterator(old_chunk_pos, new_chunk_pos, k_render_distance, [&](glm::ivec3 const& chunk_pos) - { - glm::ivec3 rel_pos = chunk_pos - new_chunk_pos + k_render_distance; - if (rel_pos.z != 0) return; - int i = (k_side.y - rel_pos.y - 1) * k_side.x + rel_pos.x; - cover_map[i]++; - }); - iterator.iterate(); - - for (int i : cover_map) REQUIRE(i == 1); // Make sure every chunk is visited once + constexpr glm::ivec3 k_render_distance(1); + constexpr glm::ivec3 k_side = k_render_distance * 2 + 1; + + glm::ivec3 old_chunk_pos{}; + glm::ivec3 new_chunk_pos{}; + std::vector cover_map{}; + + // clang-format off + std::tie(old_chunk_pos, new_chunk_pos, cover_map) = GENERATE( + Catch::Generators::table>( + { + std::make_tuple( // 1 + glm::ivec3(0, 0, 0), + glm::ivec3(-1, 0, 0), + std::vector{ + 0, 1, 1, + 0, 1, 1, + 0, 1, 1, } + ), + std::make_tuple( // 2 + glm::ivec3(0, 0, 0), + glm::ivec3(0, -1, 0), + std::vector{ + 1, 1, 1, + 1, 1, 1, + 0, 0, 0, } + ), + std::make_tuple( // 3 + glm::ivec3(0, 0, 0), + glm::ivec3(1, 0, 0), + std::vector{ + 1, 1, 0, + 1, 1, 0, + 1, 1, 0, } + ), + std::make_tuple( // 4 + glm::ivec3(0, 0, 0), + glm::ivec3(0, 1, 0), + std::vector{ + 0, 0, 0, + 1, 1, 1, + 1, 1, 1, } + ), + std::make_tuple( // 5 + glm::ivec3(0, 0, 0), + glm::ivec3(1, 1, 0), + std::vector{ + 0, 0, 0, + 1, 1, 0, + 1, 1, 0, } + ), + std::make_tuple( // 6 + glm::ivec3(0, 0, 0), + glm::ivec3(2, 2, 0), + std::vector{ + 0, 0, 0, + 0, 0, 0, + 1, 0, 0, } + ), + std::make_tuple( // 7 + glm::ivec3(0, 0, 0), + glm::ivec3(0, 3, 0), + std::vector{ + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, } + ), + std::make_tuple( // 8 + glm::ivec3(0, 0, 0), + glm::ivec3(0, -2, 0), + std::vector{ + 1, 1, 1, + 0, 0, 0, + 0, 0, 0, } + ) + } + )); + // clang-format on + + DeltaChunkIterator iterator( + old_chunk_pos, + new_chunk_pos, + k_render_distance, + [&](glm::ivec3 const &chunk_pos) + { + glm::ivec3 rel_pos = chunk_pos - new_chunk_pos + k_render_distance; + if (rel_pos.z != 0) return; + int i = (k_side.y - rel_pos.y - 1) * k_side.x + rel_pos.x; + cover_map[i]++; + } + ); + iterator.iterate(); + + for (int i : cover_map) REQUIRE(i == 1); // Make sure every chunk is visited once } TEST_CASE("DeltaChunkIterator-checkLoadUnloadOnOffset") { - // This test checks what's done by the world view when the player moves. The DeltaChunkIterator class is used to iterate - // and load the new chunks that enter the WorldView, but it's also used to iterate and unload the old chunks that have - // exited the world view - - glm::ivec3 render_distance = GENERATE( - glm::ivec3(1), - glm::ivec3(2), - glm::ivec3(0), - glm::ivec3(0, 1, 2), - glm::ivec3(1, 1, 1), - glm::ivec3(2, 1, 2), - glm::ivec3(3, 3, 3), - glm::ivec3(3, 0, 3) - ); - - glm::ivec3 side = render_distance * 2 + 1; - size_t size = side.x * side.y * side.z; - - glm::ivec3 old_chunk_pos{}; - glm::ivec3 new_chunk_pos{}; - std::tie(old_chunk_pos, new_chunk_pos) = - GENERATE( - Catch::Generators::table( - { - std::make_tuple(glm::ivec3(0, 0, 0), glm::ivec3(1, 0, 0)), - std::make_tuple(glm::ivec3(0, 0, 0), glm::ivec3(0, 1, 0)), - std::make_tuple(glm::ivec3(0, 0, 0), glm::ivec3(1, 0, 1)), - std::make_tuple(glm::ivec3(0, 0, 0), glm::ivec3(1, 1, 1)), - std::make_tuple(glm::ivec3(0, 0, 0), glm::ivec3(2, 2, 1)), - std::make_tuple(glm::ivec3(0, 0, 0), glm::ivec3(1, 2, 2)), - std::make_tuple(glm::ivec3(0, 0, 0), glm::ivec3(2, 2, 2)), - std::make_tuple(glm::ivec3(0, 0, 0), glm::ivec3(5, 5, 2)), - } - )); - - std::unordered_set loaded_chunks{}; - - WorldView::iterate_chunks(old_chunk_pos, render_distance, [&](glm::ivec3 const& chunk_pos) - { - loaded_chunks.emplace(chunk_pos); - }); - - CHECK(loaded_chunks.size() == size); - - DeltaChunkIterator old_chunks_iterator(new_chunk_pos, old_chunk_pos, render_distance, [&](glm::ivec3 const& chunk_pos) - { - loaded_chunks.erase(chunk_pos); - }); - old_chunks_iterator.iterate(); - - DeltaChunkIterator new_chunks_iterator(old_chunk_pos, new_chunk_pos, render_distance, [&](glm::ivec3 const& chunk_pos) - { - loaded_chunks.emplace(chunk_pos); - }); - new_chunks_iterator.iterate(); - - WorldView::iterate_chunks(new_chunk_pos, render_distance, [&](glm::ivec3 const& chunk_pos) - { - REQUIRE(loaded_chunks.contains(chunk_pos)); - }); - - WorldView::iterate_chunks(old_chunk_pos, render_distance, [&](glm::ivec3 const& chunk_pos) - { - if (!WorldView::is_chunk_position_inside(new_chunk_pos, render_distance, chunk_pos)) - REQUIRE(!loaded_chunks.contains(chunk_pos)); - }); - - CHECK(loaded_chunks.size() == size); + // This test checks what's done by the world view when the player moves. The DeltaChunkIterator class is used to iterate + // and load the new chunks that enter the WorldView, but it's also used to iterate and unload the old chunks that have + // exited the world view + + glm::ivec3 render_distance = GENERATE( + glm::ivec3(1), + glm::ivec3(2), + glm::ivec3(0), + glm::ivec3(0, 1, 2), + glm::ivec3(1, 1, 1), + glm::ivec3(2, 1, 2), + glm::ivec3(3, 3, 3), + glm::ivec3(3, 0, 3) + ); + + glm::ivec3 side = render_distance * 2 + 1; + size_t size = side.x * side.y * side.z; + + glm::ivec3 old_chunk_pos{}; + glm::ivec3 new_chunk_pos{}; + std::tie(old_chunk_pos, new_chunk_pos) = GENERATE(Catch::Generators::table({ + std::make_tuple(glm::ivec3(0, 0, 0), glm::ivec3(1, 0, 0)), + std::make_tuple(glm::ivec3(0, 0, 0), glm::ivec3(0, 1, 0)), + std::make_tuple(glm::ivec3(0, 0, 0), glm::ivec3(1, 0, 1)), + std::make_tuple(glm::ivec3(0, 0, 0), glm::ivec3(1, 1, 1)), + std::make_tuple(glm::ivec3(0, 0, 0), glm::ivec3(2, 2, 1)), + std::make_tuple(glm::ivec3(0, 0, 0), glm::ivec3(1, 2, 2)), + std::make_tuple(glm::ivec3(0, 0, 0), glm::ivec3(2, 2, 2)), + std::make_tuple(glm::ivec3(0, 0, 0), glm::ivec3(5, 5, 2)), + })); + + std::unordered_set loaded_chunks{}; + + WorldView::iterate_chunks( + old_chunk_pos, + render_distance, + [&](glm::ivec3 const &chunk_pos) + { + loaded_chunks.emplace(chunk_pos); + } + ); + + CHECK(loaded_chunks.size() == size); + + DeltaChunkIterator old_chunks_iterator( + new_chunk_pos, + old_chunk_pos, + render_distance, + [&](glm::ivec3 const &chunk_pos) + { + loaded_chunks.erase(chunk_pos); + } + ); + old_chunks_iterator.iterate(); + + DeltaChunkIterator new_chunks_iterator( + old_chunk_pos, + new_chunk_pos, + render_distance, + [&](glm::ivec3 const &chunk_pos) + { + loaded_chunks.emplace(chunk_pos); + } + ); + new_chunks_iterator.iterate(); + + WorldView::iterate_chunks( + new_chunk_pos, + render_distance, + [&](glm::ivec3 const &chunk_pos) + { + REQUIRE(loaded_chunks.contains(chunk_pos)); + } + ); + + WorldView::iterate_chunks( + old_chunk_pos, + render_distance, + [&](glm::ivec3 const &chunk_pos) + { + if (!WorldView::is_chunk_position_inside(new_chunk_pos, render_distance, chunk_pos)) REQUIRE(!loaded_chunks.contains(chunk_pos)); + } + ); + + CHECK(loaded_chunks.size() == size); } diff --git a/test/MiscTest.cpp b/test/MiscTest.cpp index 7de37b2..35490f0 100644 --- a/test/MiscTest.cpp +++ b/test/MiscTest.cpp @@ -6,14 +6,14 @@ using namespace explo; TEST_CASE("misc-pmod") { - int n = 3; - int v = 0; + int n = 3; + int v = 0; - REQUIRE(pmod(v - 3, n) == 0); - REQUIRE(pmod(v - 2, n) == 1); - REQUIRE(pmod(v - 1, n) == 2); - REQUIRE(pmod(v, n) == 0); - REQUIRE(pmod(v + 1, n) == 1); - REQUIRE(pmod(v + 2, n) == 2); - REQUIRE(pmod(v + 3, n) == 0); + REQUIRE(pmod(v - 3, n) == 0); + REQUIRE(pmod(v - 2, n) == 1); + REQUIRE(pmod(v - 1, n) == 2); + REQUIRE(pmod(v, n) == 0); + REQUIRE(pmod(v + 1, n) == 1); + REQUIRE(pmod(v + 2, n) == 2); + REQUIRE(pmod(v + 3, n) == 0); } \ No newline at end of file diff --git a/test/OctreeTest.cpp b/test/OctreeTest.cpp index a2c9ea5..d092fa0 100644 --- a/test/OctreeTest.cpp +++ b/test/OctreeTest.cpp @@ -1,8 +1,6 @@ #include - -#include - #include +#include #include "world/volume/Octree.hpp" @@ -10,45 +8,47 @@ using namespace explo; TEST_CASE("OctreeVolumeStorage-MortonCode") { - srand(NULL); + srand(NULL); - glm::ivec3 pos1(23, 10, 2); - REQUIRE(Octree::to_morton_code(pos1) == 0x1479); + glm::ivec3 pos1(23, 10, 2); + REQUIRE(Octree::to_morton_code(pos1) == 0x1479); - glm::ivec3 pos2(81, 99, 7); - REQUIRE(Octree::to_morton_code(pos2) == 0xd1137); + glm::ivec3 pos2(81, 99, 7); + REQUIRE(Octree::to_morton_code(pos2) == 0xd1137); - glm::ivec3 pos3(67, 2, 11); - REQUIRE(Octree::to_morton_code(pos3) == 0x4083d); + glm::ivec3 pos3(67, 2, 11); + REQUIRE(Octree::to_morton_code(pos3) == 0x4083d); - for (int i = 0; i < 16; i++) - { - glm::ivec3 pos{}; - pos.x = rand() % 50 + 50; - pos.y = rand() % 50 + 50; - pos.z = rand() % 50 + 50; + for (int i = 0; i < 16; i++) + { + glm::ivec3 pos{}; + pos.x = rand() % 50 + 50; + pos.y = rand() % 50 + 50; + pos.z = rand() % 50 + 50; - REQUIRE(Octree::to_voxel_position(Octree::to_morton_code(pos)) == pos); - } + REQUIRE(Octree::to_voxel_position(Octree::to_morton_code(pos)) == pos); + } } TEST_CASE("OctreeVolumeStorage-SetGet-Traverse") { - Octree octree(4); // Depth: 4, Octree: 16x16x16 - - glm::ivec3 my_voxel_pos = glm::ivec3(13, 8, 2); - uint64_t my_morton_code = Octree::to_morton_code(my_voxel_pos); - octree.set_voxel_at(my_morton_code, 23); - - REQUIRE(octree.get_voxel_at(my_morton_code) == 23); - - bool found = false; - octree.traverse([&](uint32_t value, uint32_t level, uint32_t morton_code) - { - REQUIRE(value == 23); - REQUIRE(my_morton_code == morton_code); - REQUIRE(my_voxel_pos == Octree::to_voxel_position(morton_code)); - found = true; - }); - REQUIRE(found); + Octree octree(4); // Depth: 4, Octree: 16x16x16 + + glm::ivec3 my_voxel_pos = glm::ivec3(13, 8, 2); + uint64_t my_morton_code = Octree::to_morton_code(my_voxel_pos); + octree.set_voxel_at(my_morton_code, 23); + + REQUIRE(octree.get_voxel_at(my_morton_code) == 23); + + bool found = false; + octree.traverse( + [&](uint32_t value, uint32_t level, uint32_t morton_code) + { + REQUIRE(value == 23); + REQUIRE(my_morton_code == morton_code); + REQUIRE(my_voxel_pos == Octree::to_voxel_position(morton_code)); + found = true; + } + ); + REQUIRE(found); } diff --git a/test/main.cpp b/test/main.cpp index 13e6cda..0770ae7 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -1,11 +1,10 @@ -#include - #include +#include -int main(int argc, char* argv[]) +int main(int argc, char *argv[]) { - setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); - int result = Catch::Session().run(argc, argv); - return result; + int result = Catch::Session().run(argc, argv); + return result; }