From 403884fcec17e0711c5e2e00eff28130de638b05 Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 21:36:05 +0300 Subject: [PATCH 01/27] refactor: create class to control meshes The mesh related code has been moved. Methods of new classes are used inside the controller. In order to test the code, we had to change the logic of the engine startup. --- demo/main.cpp | 21 +- include/IController.h | 24 +- src/core/CMakeLists.txt | 2 +- src/core/Controller.cpp | 4 +- src/core/ControllerImpl.cpp | 104 +- src/core/Mesh.cpp | 48 - src/core/MeshController.cpp | 29 + src/core/ModelImpl.cpp | 85 +- src/core/View.cpp | 7 +- src/core/ViewImpl.cpp | 110 +- src/core/createInstance.cpp | 5 +- src/graphics/CMakeLists.txt | 6 +- src/graphics/vulkan/RenderableGLTF.cpp | 7 + src/graphics/vulkan/vk_engine.cpp | 1045 +++++++++++++----- src/graphics/vulkan/vk_loader.cpp | 1014 +++++++++-------- src/include/core/Controller.h | 3 +- src/include/core/ControllerImpl.h | 15 +- src/include/core/Mesh.h | 25 - src/include/core/MeshController.h | 22 + src/include/core/ModelImpl.h | 22 +- src/include/core/View.h | 2 +- src/include/core/ViewImpl.h | 5 +- src/include/graphics/vulkan/RenderableGLTF.h | 7 + src/include/graphics/vulkan/vk_engine.h | 177 +-- src/include/graphics/vulkan/vk_loader.h | 108 +- src/include/graphics/vulkan/vk_types.h | 5 +- src/include/interfaces/IModel.h | 33 +- src/include/interfaces/IView.h | 4 +- src/include/scene/LoaderGLTF.h | 8 + src/include/scene/Mesh.h | 59 + src/scene/CMakeLists.txt | 1 + src/scene/LoaderGLTF.cpp | 458 ++++++++ 32 files changed, 2265 insertions(+), 1200 deletions(-) delete mode 100644 src/core/Mesh.cpp create mode 100644 src/core/MeshController.cpp create mode 100644 src/graphics/vulkan/RenderableGLTF.cpp delete mode 100644 src/include/core/Mesh.h create mode 100644 src/include/core/MeshController.h create mode 100644 src/include/graphics/vulkan/RenderableGLTF.h create mode 100644 src/include/scene/LoaderGLTF.h create mode 100644 src/include/scene/Mesh.h create mode 100644 src/scene/LoaderGLTF.cpp diff --git a/demo/main.cpp b/demo/main.cpp index 4585d801..99434856 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -1,16 +1,17 @@ #include -#include -#include #include "IController.h" -int main([[maybe_unused]] int argc, [[maybe_unused]] char* args[]) { - try { - const auto controller = createInstance(); - controller->init(); - } catch (const std::runtime_error& e) { - std::cerr << "Unhandled exception: " << e.what() << '\n'; - } +int main([[maybe_unused]] int argc, [[maybe_unused]] char* args[]) +{ + try + { + auto controller = createInstance(); + controller->init(); + } catch (std::runtime_error const& e) + { + std::cerr << "Unhandled exception: " << e.what() << '\n'; + } - return 0; + return 0; } \ No newline at end of file diff --git a/include/IController.h b/include/IController.h index c469b8bb..3eb14461 100644 --- a/include/IController.h +++ b/include/IController.h @@ -6,6 +6,7 @@ #include #include "SDL2/SDL.h" +#include "core/MeshController.h" /*! \brief * Interface for interacting with the chip that allows changing keyboard @@ -29,24 +30,7 @@ class IController { * * This method initializes the necessary components for the controller. */ - virtual void init() const = 0; - - /*! - * \brief Updates the controller status. - * - * This method updates the internal state or status of the controller. - */ - virtual void update() const = 0; - - /*! - * \brief Processes incoming events. - * - * \param e Event to be processed. - * - * This method processes the incoming SDL event and performs necessary - * actions. - */ - virtual void processEvent(SDL_Event &e) const = 0; + virtual void init() = 0; /*! * \brief Sends data to change the brightness of the backlight. @@ -69,11 +53,11 @@ class IController { * 0 to 255. */ // virtual void setColor(Color color) const = 0; - + [[nodiscard]] virtual MeshController& getMeshController() = 0; /*! * \brief Shared pointer type for IController. */ - using Ptr = std::shared_ptr; + using Ptr = std::shared_ptr; }; IController::Ptr createInstance(); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 987b4319..df62f0c3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -2,7 +2,7 @@ target_sources(${PROJECT_NAME} PRIVATE Controller.cpp ControllerImpl.cpp - Mesh.cpp + MeshController.cpp Model.cpp ModelImpl.cpp View.cpp diff --git a/src/core/Controller.cpp b/src/core/Controller.cpp index cfabd7e7..afbc8a0b 100644 --- a/src/core/Controller.cpp +++ b/src/core/Controller.cpp @@ -5,6 +5,6 @@ #include "core/ControllerImpl.h" -IController::Ptr createController(IModel::Ptr ptr) { - return std::make_shared(std::move(ptr)); +IController::Ptr createController(IModel::Ptr model, IView::Ptr view) { + return std::make_shared(std::move(model), std::move(view)); } \ No newline at end of file diff --git a/src/core/ControllerImpl.cpp b/src/core/ControllerImpl.cpp index d7ee3a5f..2caa6a2b 100644 --- a/src/core/ControllerImpl.cpp +++ b/src/core/ControllerImpl.cpp @@ -1,63 +1,109 @@ #include "core/ControllerImpl.h" +#include #include -#include -#include +#include #include #define GLM_ENABLE_EXPERIMENTAL +#include #include +#include +#include -#include "core/View.h" -#include "scene/Camera.h" - -ControllerImpl::ControllerImpl(IModel::Ptr model) : _model(std::move(model)) {} +ControllerImpl::ControllerImpl(IModel::Ptr model, IView::Ptr view) + : _model(std::move(model)), _view(std::move(view)), _mesh_controller(_model) {} double getCurrentGlobalTime() { // Get the current time point - const auto now = std::chrono::system_clock::now(); + auto now = std::chrono::system_clock::now(); // Cast to a time duration since the epoch - const auto durationSinceEpoch = now.time_since_epoch(); + auto durationSinceEpoch = now.time_since_epoch(); // Convert to seconds in double precision - const std::chrono::duration seconds = durationSinceEpoch; + std::chrono::duration seconds = durationSinceEpoch; // Return the double value return seconds.count(); } -void updateCube(const std::shared_ptr &_model, int name) { - const double sinValue = std::sin(getCurrentGlobalTime() + name) * 5.0; +void updateCube(const MeshController& mesh_controller, Mesh::rid_t rid, int8_t i) { + const double sinValue = std::sin(getCurrentGlobalTime() + static_cast(i)) * 5.0f; - const glm::mat4 scale = glm::scale(glm::vec3{0.2f}); - const glm::mat4 translation = glm::translate( - glm::vec3{static_cast(name) - 2.5f, sinValue, 0}); + const glm::mat4 scale = glm::scale(glm::vec3{0.2}); + const glm::mat4 translation = glm::translate(glm::vec3{static_cast(i) - 2.5f, sinValue, 0}); - _model->setMeshTransform("cube" + std::to_string(name), - scale * translation); + mesh_controller.set_transform(rid, scale * translation); } -void updateCubes(const std::shared_ptr &_model) { - for (int i = 0; i < 5; i++) { - updateCube(_model, i); +void updateCubes(const IModel::Ptr& model, const MeshController& mesh_controller) { + auto meshes = model->get_meshes(); + for (int8_t i {}; const auto& [key, value] : meshes) { + updateCube(mesh_controller, key, i); + ++i; } } -void ControllerImpl::update() const { - _model->updateVulkan(); +void update(const IModel::Ptr& model, const MeshController& mesh_controller) { + model->get_engine().update(model); - _model->getCamera()->update(); + model->getCamera()->update(); + + updateCubes(model, mesh_controller); +} - updateCubes(_model); +// void ControllerImpl::processEvent(SDL_Event &e) const { +// _model->getCamera()->processSDLEvent(e); +// } +// +void createCubes(const MeshController& mesh_controller) { + for (int i = 0; i < 5; i++) { + mesh_controller.create_mesh("/basicmesh.glb"); + } } -void ControllerImpl::processEvent(SDL_Event &e) const { - _model->getCamera()->processSDLEvent(e); +void ControllerImpl::init() { + const auto mesh_controller = this->getMeshController(); + createCubes(mesh_controller); + + SDL_Event e; + bool bQuit = false; + bool stop_rendering = false; + + // main loop + while (!bQuit) { + // Handle events on queue + while (SDL_PollEvent(&e) != 0) { + // close the window when user alt-f4s or clicks the X button + if (e.type == SDL_QUIT) bQuit = true; + + if (e.type == SDL_WINDOWEVENT) { + if (e.window.event == SDL_WINDOWEVENT_MINIMIZED) { + stop_rendering = true; + } + if (e.window.event == SDL_WINDOWEVENT_RESTORED) { + stop_rendering = false; + } + } + + _view->process_event(e); + } + + // do not draw if we are minimized + if (stop_rendering) { + // throttle the speed to avoid the endless spinning + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + continue; + } + + _view->run(); + + update(_model, mesh_controller); + } } -void ControllerImpl::init() const { - const auto controller = shared_from_this(); - const auto view = createView(controller, _model); - view->run(); + +MeshController& ControllerImpl::getMeshController() { + return _mesh_controller; } diff --git a/src/core/Mesh.cpp b/src/core/Mesh.cpp deleted file mode 100644 index f496c69d..00000000 --- a/src/core/Mesh.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// Mesh.cpp -#include "core/Mesh.h" - -Mesh::Mesh(const std::string &filePath) : _currentModelPath(filePath) { - VulkanEngine &engine = VulkanEngine::Get(); - _rid = engine.registerMesh(filePath); -} - -Mesh::~Mesh() { - remove_model(); -} - -void Mesh::set_model(const std::string &filePath) { - VulkanEngine &engine = VulkanEngine::Get(); - - // Unregister current model if exists - if (!_currentModelPath.empty()) { - engine.unregisterMesh(_rid); - } - - // Register new model - _currentModelPath = filePath; - _rid = engine.registerMesh(filePath); - - // Reapply transform to new model - engine.setMeshTransform(_rid, _transform); -} - -void Mesh::remove_model() { - if (!_currentModelPath.empty()) { - VulkanEngine &engine = VulkanEngine::Get(); - engine.unregisterMesh(_rid); - _currentModelPath.clear(); - } -} - - -void Mesh::set_transform(glm::mat4 t) { - VulkanEngine &engine = VulkanEngine::Get(); - - _transform = t; - - engine.setMeshTransform(_rid, t); -} - -glm::mat4 Mesh::get_transform() { - return _transform; -} diff --git a/src/core/MeshController.cpp b/src/core/MeshController.cpp new file mode 100644 index 00000000..e699f9f2 --- /dev/null +++ b/src/core/MeshController.cpp @@ -0,0 +1,29 @@ +#include "core/MeshController.h" + +#include + +#include "scene/Mesh.h" + +MeshController::MeshController(IModel::Ptr model) : _model(std::move(model)) {} + +void MeshController::create_mesh(std::string_view file_path) const { + //TODO: посмотреть, можно ли _engine засунуть в private + VulkanEngine& engine = _model->get_engine(); + + _model->createMesh(engine, file_path); + + // return rid; +} + +void MeshController::delete_mesh(Mesh::rid_t id) const { + _model->delete_mesh(id); +} + +void MeshController::set_transform(Mesh::rid_t id, glm::mat4 t) const { + _model->setMeshTransform(id, t); +} + +glm::mat4 MeshController::get_transform(Mesh::rid_t id) const { + const auto transform = _model->get_mesh_transform(id); + return transform; +} diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index f8e28caa..7e4b1812 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -1,36 +1,93 @@ #include "core/ModelImpl.h" -#include - -#include "core/Mesh.h" - +#include +#include +#include +#define GLM_ENABLE_EXPERIMENTAL +#include ModelImpl::~ModelImpl() { _engine.cleanup(); } ModelImpl::ModelImpl() = default; -void ModelImpl::registerWindow(SDL_Window *window) { +void ModelImpl::registerWindow(struct SDL_Window* window) { _engine.mainCamera = &_camera; _engine.init(window); } -void ModelImpl::updateVulkan() { - _engine.update(); +VulkanEngine& ModelImpl::get_engine() { + assert(_engine._isInitialized); + return _engine; } -Camera *ModelImpl::getCamera() { +Camera* ModelImpl::getCamera() { return &_camera; } -void ModelImpl::createMesh(std::string name) { - const auto mesh = std::make_shared("/basicmesh.glb"); +#include "core/config.h" +#include "scene/LoaderGLTF.h" + +Mesh::rid_t registerMesh( + VulkanEngine& engine, + ModelImpl::MeshMap& meshes, + std::string_view filePath) { + std::random_device rd; + + // Use the Mersenne Twister engine for high-quality random numbers + std::mt19937_64 generator(rd()); + + // Create a uniform distribution for int64_t + std::uniform_int_distribution distribution; + + // Generate and print a random int64_t value + const Mesh::rid_t random_rid_t = distribution(generator); + + std::string structurePath = {std::string(ASSETS_DIR) + + std::string(filePath)}; + auto structureFile = LoaderGLTF::loadGLTF(engine, structurePath); + + assert(structureFile.has_value()); + + engine.loadedScenes["structure"] = *structureFile; + + meshes[random_rid_t] = {structureFile.value(), glm::mat4(1.)}; + + return random_rid_t; +} + +void ModelImpl::createMesh(VulkanEngine& engine, std::string_view file_path) { + assert(_engine._isInitialized); + Mesh::rid_t rid = registerMesh(engine, _meshes, file_path); + + // auto mesh = std::make_shared("/basicmesh.glb"); + + constexpr glm::mat4 transform{1.}; + _meshes[rid].transform = transform; + // engine.setMeshTransform(rid, transform); + + // mesh->set_transform(glm::mat4(1.0f)); + + // Mesh::rid_t rid = registerMesh(file_path); + + // _meshes[name] = mesh; + + // return rid; +} + +void ModelImpl::setMeshTransform(Mesh::rid_t rid, glm::mat4x4 transform) { + _meshes[rid].transform = transform; +} - mesh->set_transform(glm::mat4(1.0f)); +glm::mat4 ModelImpl::get_mesh_transform(Mesh::rid_t rid) { + return _meshes[rid].transform; +} - _meshes[name] = mesh; +void ModelImpl::delete_mesh(Mesh::rid_t rid) { + _meshes.erase(rid); } -void ModelImpl::setMeshTransform(std::string name, glm::mat4x4 transform) { - _meshes[name]->set_transform(transform); +const ModelImpl::MeshMap& ModelImpl::get_meshes() { + return _meshes; } + diff --git a/src/core/View.cpp b/src/core/View.cpp index 8751d9d1..4ed5a02d 100644 --- a/src/core/View.cpp +++ b/src/core/View.cpp @@ -1,10 +1,7 @@ #include "core/View.h" -#include -#include - #include "core/ViewImpl.h" -IView::Ptr createView(IController::Ptr controller, IModel::Ptr model) { - return std::make_unique(std::move(controller), std::move(model)); +IView::Ptr createView(IModel::Ptr m_ptr) { + return std::make_unique(std::move(m_ptr)); } \ No newline at end of file diff --git a/src/core/ViewImpl.cpp b/src/core/ViewImpl.cpp index 45fc8b76..349ddf87 100644 --- a/src/core/ViewImpl.cpp +++ b/src/core/ViewImpl.cpp @@ -1,100 +1,54 @@ #include "core/ViewImpl.h" #include -#include -#include - -#ifdef __GNUC__ -#include -#else +#include +#include #include -#endif - -#include -#include +#include +#include +#include +#include #include #include +#include #include "graphics/vulkan/vk_engine.h" #include "imgui.h" #include "imgui_impl_sdl2.h" #include "imgui_impl_vulkan.h" -namespace { -constexpr auto kWindowFlags = - static_cast(SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE); -} - -ViewImpl::ViewImpl(IController::Ptr controller, IModel::Ptr model) - : _controller(std::move(controller)), _model(std::move(model)) { +ViewImpl::ViewImpl(IModel::Ptr model) + : _model(std::move(model)) { SDL_Init(SDL_INIT_VIDEO); - window = SDL_CreateWindow("engine", SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, 1700, 900, kWindowFlags); -} -void createCubes(const std::shared_ptr &_model) { - for (int i = 0; i < 5; i++) { - _model->createMesh("cube" + std::to_string(i)); - } -} + auto window_flags = + (SDL_WindowFlags)(SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE); -void ViewImpl::run() const { + window = SDL_CreateWindow("engine", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, 1700, 900, window_flags); _model->registerWindow(window); +} - createCubes(_model); - - SDL_Event e; - bool bQuit = false; - - bool stop_rendering = false; - - // main loop - while (!bQuit) { - // Handle events on queue - while (SDL_PollEvent(&e) != 0) { - // close the window when user alt-f4s or clicks the X button - if (e.type == SDL_QUIT) bQuit = true; - - if (e.type == SDL_WINDOWEVENT) { - if (e.window.event == SDL_WINDOWEVENT_MINIMIZED) { - stop_rendering = true; - } - if (e.window.event == SDL_WINDOWEVENT_RESTORED) { - stop_rendering = false; - } - } - - _controller->processEvent(e); - ImGui_ImplSDL2_ProcessEvent(&e); - } - - // do not draw if we are minimized - if (stop_rendering) { - // throttle the speed to avoid the endless spinning - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - continue; - } - - // imgui new frame - ImGui_ImplVulkan_NewFrame(); - ImGui_ImplSDL2_NewFrame(); - ImGui::NewFrame(); - - if (ImGui::Begin("background")) { - VulkanEngine &engine = VulkanEngine::Get(); - ImGui::SliderFloat("Render Scale", &engine.renderScale, 0.3f, 1.f); - // other code - } - ImGui::End(); - - // make imgui calculate internal draw structures - ImGui::Render(); +void ViewImpl::run() const { + // imgui new frame + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + ImGui::NewFrame(); + + if (ImGui::Begin("background")) { + VulkanEngine &engine = VulkanEngine::Get(); + ImGui::SliderFloat("Render Scale", &engine.renderScale, 0.3f, 1.f); + // other code + } + ImGui::End(); - _controller->update(); - //_model->updateVulkan(); + // make imgui calculate internal draw structures + ImGui::Render(); +} - // engine.update(); - } +void ViewImpl::process_event(const SDL_Event& e) { + _model->getCamera()->processSDLEvent(e); + ImGui_ImplSDL2_ProcessEvent(&e); } ViewImpl::~ViewImpl() { diff --git a/src/core/createInstance.cpp b/src/core/createInstance.cpp index fe1eb2bb..bbc3ef70 100644 --- a/src/core/createInstance.cpp +++ b/src/core/createInstance.cpp @@ -1,9 +1,12 @@ #include "IController.h" #include "core/Controller.h" #include "core/Model.h" +#include "core/View.h" +#include "interfaces/IModel.h" IController::Ptr createInstance() { const auto model = createModel(); - const auto controller = createController(model); + const auto view = createView(model); + const auto controller = createController(model, view); return controller; } diff --git a/src/graphics/CMakeLists.txt b/src/graphics/CMakeLists.txt index e117fd76..e74974ac 100644 --- a/src/graphics/CMakeLists.txt +++ b/src/graphics/CMakeLists.txt @@ -1,15 +1,11 @@ target_sources(${PROJECT_NAME} PRIVATE - vulkan/vk_command_buffers.cpp - vulkan/vk_command_buffers_container.cpp + vulkan/RenderableGLTF.cpp vulkan/vk_descriptors.cpp vulkan/vk_engine.cpp vulkan/vk_images.cpp vulkan/vk_initializers.cpp vulkan/vk_loader.cpp vulkan/vk_pipelines.cpp - vulkan/pipelines.cpp - vulkan/ComputePipeline.cpp - vulkan/GraphicsPipeline.cpp Graphics.cpp ) \ No newline at end of file diff --git a/src/graphics/vulkan/RenderableGLTF.cpp b/src/graphics/vulkan/RenderableGLTF.cpp new file mode 100644 index 00000000..90a47eba --- /dev/null +++ b/src/graphics/vulkan/RenderableGLTF.cpp @@ -0,0 +1,7 @@ +#include "graphics/vulkan/RenderableGLTF.h" +#include "graphics/vulkan/vk_loader.h" + +IRenderable::Ptr createRenderableGLTF(RenderableGLTF::LoadedGltfPtr ptr) +{ + return std::make_shared(std::move(ptr)); +} \ No newline at end of file diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index 99b94292..b01dfeb1 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -1,41 +1,25 @@ -#include "graphics/vulkan/vk_engine.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "core/Logging.h" +#include "graphics/vulkan/vk_engine.h" + #include "core/config.h" -#include "graphics/vulkan/vk_descriptors.h" -#include "scene/Camera.h" #define VMA_IMPLEMENTATION -#include -#include -#include -#include -#include -#include - -#define GLM_ENABLE_EXPERIMENTAL -#include - +#include "SDL_vulkan.h" +#include "VkBootstrap.h" +#include +#include "graphics/vulkan/RenderableGLTF.h" #include "graphics/vulkan/vk_images.h" #include "graphics/vulkan/vk_initializers.h" #include "graphics/vulkan/vk_loader.h" #include "graphics/vulkan/vk_pipelines.h" #include "graphics/vulkan/vk_types.h" -#include "graphics/vulkan/vk_command_buffers.h" +#include "imgui.h" +#include "imgui_impl_sdl2.h" +#include "imgui_impl_vulkan.h" +#include "interfaces/IModel.h" +#include "vk_mem_alloc.h" + +#define GLM_ENABLE_EXPERIMENTAL +#include "glm/gtx/transform.hpp" VulkanEngine* loadedEngine = nullptr; @@ -53,7 +37,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, - [[maybe_unused]] void* pUserData) { + void* pUserData) { std::string type; switch (messageType) { @@ -71,11 +55,6 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( type = "Performance"; break; - case VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT: - type = "Modified set of GPU-visible virtual addresses"; - break; - default: - type = "Unknown"; } std::string message = "(" + type + ")" + pCallbackData->pMessage; @@ -91,13 +70,158 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( } else if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { LOGE("{}", message) - } else { - LOGE("{}", message) } return VK_FALSE; } +#include +#include +#include + +// std::optional>> loadGltfMeshes( +// VulkanEngine* engine, std::filesystem::path filePath) { +// if (!std::filesystem::exists(filePath)) { +// std::cout << "Failed to find " << filePath << '\n'; +// return {}; +// } +// +// std::cout << "Loading " << filePath << '\n'; +// +// fastgltf::Asset gltf; +// +// // Parse the glTF file and get the constructed asset +// +// static constexpr auto supportedExtensions = +// fastgltf::Extensions::KHR_mesh_quantization | +// fastgltf::Extensions::KHR_texture_transform | +// fastgltf::Extensions::KHR_materials_variants; +// +// fastgltf::Parser parser(supportedExtensions); +// +// auto path = std::filesystem::path{filePath}; +// +// constexpr auto gltfOptions = +// fastgltf::Options::DontRequireValidAssetMember | +// fastgltf::Options::AllowDouble | fastgltf::Options::LoadGLBBuffers | +// fastgltf::Options::LoadExternalBuffers | +// fastgltf::Options::LoadExternalImages | +// fastgltf::Options::GenerateMeshIndices; +// +// fastgltf::GltfDataBuffer data; +// data.loadFromFile(path); +// +// auto asset = parser.loadGltf(&data, path.parent_path(), gltfOptions); +// +// if (asset) { +// gltf = std::move(asset.get()); +// } else { +// fmt::print("Failed to load glTF: {} \n", +// fastgltf::to_underlying(asset.error())); +// return {}; +// } +// +// std::vector> meshes; +// +// // use the same vectors for all meshes so that the memory doesnt reallocate +// // as often +// std::vector indices; +// std::vector vertices; +// for (fastgltf::Mesh& mesh : gltf.meshes) { +// Mesh::GLTF::MeshAsset newmesh; +// +// newmesh.name = mesh.name; +// +// // clear the mesh arrays each mesh, we dont want to merge them by error +// indices.clear(); +// vertices.clear(); +// +// for (auto&& p : mesh.primitives) { +// Mesh::GLTF::GeoSurface newSurface; +// newSurface.startIndex = (uint32_t)indices.size(); +// newSurface.count = +// (uint32_t)gltf.accessors[p.indicesAccessor.value()].count; +// +// size_t initial_vtx = vertices.size(); +// +// // load indexes +// { +// fastgltf::Accessor& indexaccessor = +// gltf.accessors[p.indicesAccessor.value()]; +// indices.reserve(indices.size() + indexaccessor.count); +// +// fastgltf::iterateAccessor( +// gltf, indexaccessor, [&](std::uint32_t idx) { +// indices.push_back(idx + initial_vtx); +// }); +// } +// +// // load vertex positions +// { +// fastgltf::Accessor& posAccessor = +// gltf.accessors[p.findAttribute("POSITION")->second]; +// vertices.resize(vertices.size() + posAccessor.count); +// +// fastgltf::iterateAccessorWithIndex( +// gltf, posAccessor, [&](glm::vec3 v, size_t index) { +// Vertex newvtx; +// newvtx.position = v; +// newvtx.normal = {1, 0, 0}; +// newvtx.color = glm::vec4{1.f}; +// newvtx.uv_x = 0; +// newvtx.uv_y = 0; +// vertices[initial_vtx + index] = newvtx; +// }); +// } +// +// // load vertex normals +// auto normals = p.findAttribute("NORMAL"); +// if (normals != p.attributes.end()) { +// fastgltf::iterateAccessorWithIndex( +// gltf, gltf.accessors[(*normals).second], +// [&](glm::vec3 v, size_t index) { +// vertices[initial_vtx + index].normal = v; +// }); +// } +// +// // load UVs +// auto uv = p.findAttribute("TEXCOORD_0"); +// if (uv != p.attributes.end()) { +// fastgltf::iterateAccessorWithIndex( +// gltf, gltf.accessors[(*uv).second], +// [&](glm::vec2 v, size_t index) { +// vertices[initial_vtx + index].uv_x = v.x; +// vertices[initial_vtx + index].uv_y = v.y; +// }); +// } +// +// // load vertex colors +// auto colors = p.findAttribute("COLOR_0"); +// if (colors != p.attributes.end()) { +// fastgltf::iterateAccessorWithIndex( +// gltf, gltf.accessors[(*colors).second], +// [&](glm::vec4 v, size_t index) { +// vertices[initial_vtx + index].color = v; +// }); +// } +// newmesh.surfaces.push_back(newSurface); +// } +// +// // display the vertex normals +// constexpr bool OverrideColors = true; +// if (OverrideColors) { +// for (Vertex& vtx : vertices) { +// vtx.color = glm::vec4(vtx.normal, 1.f); +// } +// } +// newmesh.meshBuffers = engine->uploadMesh(indices, vertices); +// +// meshes.emplace_back(std::make_shared(std::move(newmesh))); +// } +// +// return meshes; +// } + void VulkanEngine::init_default_data() { std::array rect_vertices{}; @@ -117,36 +241,36 @@ void VulkanEngine::init_default_data() { rect_indices[1] = 1; rect_indices[2] = 2; - const auto path_to_assets = std::string(ASSETS_DIR) + "/basicmesh.glb"; - testMeshes = loadGltfMeshes(this, path_to_assets).value(); + auto path_to_assets = std::string(ASSETS_DIR) + "/basicmesh.glb"; + // testMeshes = loadGltfMeshes(this, path_to_assets).value(); // 3 default textures, white, grey, black. 1 pixel each - const uint32_t white = glm::packUnorm4x8(glm::vec4(1, 1, 1, 1)); - AllocatedImage whiteImageData = create_image(&white, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, - VK_IMAGE_USAGE_SAMPLED_BIT); - _whiteImage = std::make_unique(_allocator, _device, whiteImageData); + uint32_t white = glm::packUnorm4x8(glm::vec4(1, 1, 1, 1)); + _whiteImage = + create_image((void*)&white, VkExtent3D{1, 1, 1}, + VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - const uint32_t grey = glm::packUnorm4x8(glm::vec4(0.66f, 0.66f, 0.66f, 1)); - AllocatedImage greyImageData = create_image(&grey, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, - VK_IMAGE_USAGE_SAMPLED_BIT); - _greyImage = std::make_unique(_allocator, _device, greyImageData); + uint32_t grey = glm::packUnorm4x8(glm::vec4(0.66f, 0.66f, 0.66f, 1)); + _greyImage = + create_image((void*)&grey, VkExtent3D{1, 1, 1}, + VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - const uint32_t black = glm::packUnorm4x8(glm::vec4(0, 0, 0, 0)); - AllocatedImage blackImageData = create_image(&black, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, - VK_IMAGE_USAGE_SAMPLED_BIT); - _blackImage = std::make_unique(_allocator, _device, blackImageData); + uint32_t black = glm::packUnorm4x8(glm::vec4(0, 0, 0, 0)); + _blackImage = + create_image((void*)&black, VkExtent3D{1, 1, 1}, + VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); // checkerboard image - const uint32_t magenta = glm::packUnorm4x8(glm::vec4(1, 0, 1, 1)); + uint32_t magenta = glm::packUnorm4x8(glm::vec4(1, 0, 1, 1)); std::array pixels{}; // for 16x16 checkerboard texture - for (size_t x = 0; x < 16; x++) { - for (size_t y = 0; y < 16; y++) { + for (int x = 0; x < 16; x++) { + for (int y = 0; y < 16; y++) { pixels[y * 16 + x] = ((x % 2) ^ (y % 2)) ? magenta : black; } } - AllocatedImage errorImageData = create_image(pixels.data(), VkExtent3D{16, 16, 1}, + _errorCheckerboardImage = + create_image(pixels.data(), VkExtent3D{16, 16, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - _errorCheckerboardImage = std::make_unique(_allocator, _device, errorImageData); VkSamplerCreateInfo sampl = {.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; @@ -162,13 +286,13 @@ void VulkanEngine::init_default_data() { GLTFMetallic_Roughness::MaterialResources materialResources{}; // default the material textures - materialResources.colorImage = _whiteImage->get(); + materialResources.colorImage = _whiteImage; materialResources.colorSampler = _defaultSamplerLinear; - materialResources.metalRoughImage = _whiteImage->get(); + materialResources.metalRoughImage = _whiteImage; materialResources.metalRoughSampler = _defaultSamplerLinear; // set the uniform buffer for the material data - const AllocatedBuffer materialConstants = create_buffer( + AllocatedBuffer materialConstants = create_buffer( sizeof(GLTFMetallic_Roughness::MaterialConstants), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); @@ -179,8 +303,8 @@ void VulkanEngine::init_default_data() { sceneUniformData->colorFactors = glm::vec4{1, 1, 1, 1}; sceneUniformData->metal_rough_factors = glm::vec4{1, 0.5, 0, 0}; - // Store material constants buffer in managed buffers for automatic cleanup - _managedBuffers.push_back(std::make_unique(_allocator, materialConstants)); + _mainDeletionQueue.push_function( + [=, this]() { destroy_buffer(materialConstants); }); materialResources.dataBuffer = materialConstants.buffer; materialResources.dataBufferOffset = 0; @@ -190,11 +314,140 @@ void VulkanEngine::init_default_data() { globalDescriptorAllocator); } +void VulkanEngine::init_mesh_pipeline() { + VkShaderModule triangleFragShader; + if (!vkutil::load_shader_module("./shaders/tex_image.frag.spv", _device, + &triangleFragShader)) { + fmt::println("Error when building the triangle fragment shader module"); + } else { + fmt::println("Triangle fragment shader successfully loaded"); + } + + VkShaderModule triangleVertexShader; + if (!vkutil::load_shader_module("./shaders/colored_triangle_mesh.vert.spv", + _device, &triangleVertexShader)) { + fmt::println("Error when building the triangle vertex shader module"); + } else { + fmt::println("Triangle vertex shader successfully loaded"); + } + + VkPushConstantRange bufferRange{}; + bufferRange.offset = 0; + bufferRange.size = sizeof(GPUDrawPushConstants); + bufferRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + + VkPipelineLayoutCreateInfo pipeline_layout_info = + vkinit::pipeline_layout_create_info(); + pipeline_layout_info.pPushConstantRanges = &bufferRange; + pipeline_layout_info.pushConstantRangeCount = 1; + pipeline_layout_info.pSetLayouts = &_singleImageDescriptorLayout; + pipeline_layout_info.setLayoutCount = 1; + VK_CHECK(vkCreatePipelineLayout(_device, &pipeline_layout_info, nullptr, + &_meshPipelineLayout)); + + PipelineBuilder pipelineBuilder; + + // use the triangle layout we created + pipelineBuilder._pipelineLayout = _meshPipelineLayout; + // connecting the vertex and pixel shaders to the pipeline + pipelineBuilder.set_shaders(triangleVertexShader, triangleFragShader); + // it will draw triangles + pipelineBuilder.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); + // filled triangles + pipelineBuilder.set_polygon_mode(VK_POLYGON_MODE_FILL); + // no backface culling + pipelineBuilder.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE); + // no multisampling + pipelineBuilder.set_multisampling_none(); + // no blending + pipelineBuilder.disable_blending(); + + // pipelineBuilder.disable_depthtest(); + pipelineBuilder.enable_depthtest(true, VK_COMPARE_OP_GREATER); + + // connect the image format we will draw into, from draw image + pipelineBuilder.set_color_attachment_format(_drawImage.imageFormat); + pipelineBuilder.set_depth_format(VK_FORMAT_UNDEFINED); + + // finally build the pipeline + _meshPipeline = pipelineBuilder.build_pipeline(_device); + + // clean structures + vkDestroyShaderModule(_device, triangleFragShader, nullptr); + vkDestroyShaderModule(_device, triangleVertexShader, nullptr); + + _mainDeletionQueue.push_function([&]() { + vkDestroyPipelineLayout(_device, _meshPipelineLayout, nullptr); + vkDestroyPipeline(_device, _meshPipeline, nullptr); + }); +} + +void VulkanEngine::init_triangle_pipeline() { + VkShaderModule triangleFragShader; + if (!vkutil::load_shader_module("./shaders/colored_triangle.frag.spv", + _device, &triangleFragShader)) { + fmt::println("Error when building the triangle fragment shader module"); + } else { + fmt::println("Triangle fragment shader successfully loaded"); + } + + VkShaderModule triangleVertexShader; + if (!vkutil::load_shader_module("./shaders/colored_triangle.vert.spv", + _device, &triangleVertexShader)) { + fmt::println("Error when building the triangle vertex shader module"); + } else { + fmt::println("Triangle vertex shader successfully loaded"); + } + + // build the pipeline layout that controls the inputs/outputs of the shader + // we are not using descriptor sets or other systems yet, so no need to use + // anything other than empty default + VkPipelineLayoutCreateInfo pipeline_layout_info = + vkinit::pipeline_layout_create_info(); + VK_CHECK(vkCreatePipelineLayout(_device, &pipeline_layout_info, nullptr, + &_trianglePipelineLayout)); + + PipelineBuilder pipelineBuilder; + + // use the triangle layout we created + pipelineBuilder._pipelineLayout = _trianglePipelineLayout; + // connecting the vertex and pixel shaders to the pipeline + pipelineBuilder.set_shaders(triangleVertexShader, triangleFragShader); + // it will draw triangles + pipelineBuilder.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); + // filled triangles + pipelineBuilder.set_polygon_mode(VK_POLYGON_MODE_FILL); + // no backface culling + pipelineBuilder.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE); + // no multisampling + pipelineBuilder.set_multisampling_none(); + // no blending + pipelineBuilder.disable_blending(); + // no depth testing + pipelineBuilder.disable_depthtest(); + + // connect the image format we will draw into, from draw image + pipelineBuilder.set_color_attachment_format(_drawImage.imageFormat); + pipelineBuilder.set_depth_format(VK_FORMAT_UNDEFINED); + + // finally build the pipeline + _trianglePipeline = pipelineBuilder.build_pipeline(_device); + + // clean structures + vkDestroyShaderModule(_device, triangleFragShader, nullptr); + vkDestroyShaderModule(_device, triangleVertexShader, nullptr); + + _mainDeletionQueue.push_function([&]() { + vkDestroyPipelineLayout(_device, _trianglePipelineLayout, nullptr); + vkDestroyPipeline(_device, _trianglePipeline, nullptr); + }); +} + void VulkanEngine::init_imgui() { // 1: create descriptor pool for IMGUI // the size of the pool is very oversize, but it's copied from imgui demo // itself. - const VkDescriptorPoolSize pool_sizes[] = { + VkDescriptorPoolSize pool_sizes[] = { {VK_DESCRIPTOR_TYPE_SAMPLER, 1000}, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000}, {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000}, @@ -249,9 +502,11 @@ void VulkanEngine::init_imgui() { ImGui_ImplVulkan_CreateFontsTexture(); - // Store ImGui cleanup info - will be automatically handled when engine destructs - // Note: ImGui cleanup is now handled by storing the descriptor pool - // that will be automatically destroyed when the device is destroyed + // add destroy the imgui created structures + _mainDeletionQueue.push_function([=, this]() { + ImGui_ImplVulkan_Shutdown(); + vkDestroyDescriptorPool(_device, imguiPool, nullptr); + }); } void VulkanEngine::init_descriptors() { @@ -290,13 +545,13 @@ void VulkanEngine::init_descriptors() { _device, _drawImageDescriptorLayout); DescriptorWriter writer; - writer.write_image(0, _drawImage->imageView(), VK_NULL_HANDLE, + writer.write_image(0, _drawImage.imageView, VK_NULL_HANDLE, VK_IMAGE_LAYOUT_GENERAL, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); writer.update_set(_device, _drawImageDescriptors); - for (auto& _frame : command_buffers_container._frames) { + for (int i = 0; i < FRAME_OVERLAP; i++) { // create a descriptor pool std::vector frame_sizes = { {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 3}, @@ -305,31 +560,77 @@ void VulkanEngine::init_descriptors() { {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 4}, }; - _frame._frameDescriptors = DescriptorAllocatorGrowable{}; - _frame._frameDescriptors.init(_device, 1000, frame_sizes); + _frames[i]._frameDescriptors = DescriptorAllocatorGrowable{}; + _frames[i]._frameDescriptors.init(_device, 1000, frame_sizes); - // No need for deletion queue - frame descriptors will be cleaned up in cleanup() + _mainDeletionQueue.push_function([&, i]() { + _frames[i]._frameDescriptors.destroy_pools(_device); + }); } } +void VulkanEngine::init_background_pipelines() { + VkPipelineLayoutCreateInfo computeLayout{}; + computeLayout.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + computeLayout.pNext = nullptr; + computeLayout.pSetLayouts = &_drawImageDescriptorLayout; + computeLayout.setLayoutCount = 1; + + VK_CHECK(vkCreatePipelineLayout(_device, &computeLayout, nullptr, + &_gradientPipelineLayout)); + + VkShaderModule computeDrawShader; + if (!vkutil::load_shader_module("./shaders/gradient.comp.spv", _device, + &computeDrawShader)) { + fmt::println("Error when building the compute shader \n"); + } + + VkPipelineShaderStageCreateInfo stageinfo{}; + stageinfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stageinfo.pNext = nullptr; + stageinfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; + stageinfo.module = computeDrawShader; + stageinfo.pName = "main"; + + VkComputePipelineCreateInfo computePipelineCreateInfo{}; + computePipelineCreateInfo.sType = + VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + computePipelineCreateInfo.pNext = nullptr; + computePipelineCreateInfo.layout = _gradientPipelineLayout; + computePipelineCreateInfo.stage = stageinfo; + + VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, + &computePipelineCreateInfo, nullptr, + &_gradientPipeline)); + + vkDestroyShaderModule(_device, computeDrawShader, nullptr); + + _mainDeletionQueue.push_function([&]() { + vkDestroyPipelineLayout(_device, _gradientPipelineLayout, nullptr); + vkDestroyPipeline(_device, _gradientPipeline, nullptr); + }); +} + void VulkanEngine::init_pipelines() { - pipelines.init(_device, _singleImageDescriptorLayout, _drawImageDescriptorLayout, _drawImage->get()); - // Pipeline cleanup is handled automatically by the Pipelines object + init_background_pipelines(); + + init_triangle_pipeline(); + init_mesh_pipeline(); + metalRoughMaterial.build_pipelines(this); } -void VulkanEngine::init(SDL_Window* window) { +void VulkanEngine::init(struct SDL_Window* window) { _window = window; // only one engine initialization is allowed with the application. assert(loadedEngine == nullptr); loadedEngine = this; + init_vulkan(); init_swapchain(); - - command_buffers.init_commands(this); - - command_buffers_container.init_sync_structures(this); + init_commands(); + init_sync_structures(); init_descriptors(); init_pipelines(); init_imgui(); @@ -341,12 +642,12 @@ void VulkanEngine::init(SDL_Window* window) { mainCamera->pitch = 0; mainCamera->yaw = 0; - const std::string structurePath = {std::string(ASSETS_DIR) + - "/basicmesh.glb"}; - const auto structureFile = loadGltf(this, structurePath); - - assert(structureFile.has_value()); - loadedScenes["structure"] = *structureFile; + // TODO: возможно, здесь вылезет проблема + // std::string structurePath = {std::string(ASSETS_DIR) + "/basicmesh.glb"}; + // auto structureFile = loadGltf(this, structurePath); + // + // assert(structureFile.has_value()); + // loadedScenes["structure"] = *structureFile; _isInitialized = true; } @@ -368,8 +669,8 @@ void VulkanEngine::init_vulkan() { LOGI("Available extensions:") - for (auto& [extensionName, _] : system_info.available_extensions) { - LOGI(extensionName); + for (auto& extension : system_info.available_extensions) { + LOGI(extension.extensionName); } vkb::InstanceBuilder builder; @@ -423,7 +724,7 @@ void VulkanEngine::init_vulkan() { physical_device_ret.error().message()); } - const vkb::PhysicalDevice& physicalDevice = physical_device_ret.value(); + vkb::PhysicalDevice physicalDevice = physical_device_ret.value(); vkb::DeviceBuilder deviceBuilder{physicalDevice}; @@ -433,7 +734,7 @@ void VulkanEngine::init_vulkan() { dev_ret.error().message()); } - const vkb::Device& vkbDevice = dev_ret.value(); + vkb::Device vkbDevice = dev_ret.value(); // Get the VkDevice handle used in the rest of a vulkan application _device = vkbDevice.device; @@ -463,9 +764,65 @@ void VulkanEngine::init_vulkan() { allocatorInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; vmaCreateAllocator(&allocatorInfo, &_allocator); - // VMA allocator will be destroyed in cleanup() - no need for deletion queue + _mainDeletionQueue.push_function( + [&]() { vmaDestroyAllocator(_allocator); }); +} + +void VulkanEngine::init_commands() { + // create a command pool for commands submitted to the graphics queue. + // we also want the pool to allow for resetting of individual command + // buffers + VkCommandPoolCreateInfo commandPoolInfo = vkinit::command_pool_create_info( + _graphicsQueueFamily, + VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); + + for (int i = 0; i < FRAME_OVERLAP; i++) { + VK_CHECK(vkCreateCommandPool(_device, &commandPoolInfo, nullptr, + &_frames[i]._commandPool)); + + // allocate the default command buffer that we will use for rendering + VkCommandBufferAllocateInfo cmdAllocInfo = + vkinit::command_buffer_allocate_info(_frames[i]._commandPool, + 1); + + VK_CHECK(vkAllocateCommandBuffers(_device, &cmdAllocInfo, + &_frames[i]._mainCommandBuffer)); + } + + VK_CHECK(vkCreateCommandPool(_device, &commandPoolInfo, nullptr, + &_immCommandPool)); + + // allocate the command buffer for immediate submits + VkCommandBufferAllocateInfo cmdAllocInfo = + vkinit::command_buffer_allocate_info(_immCommandPool, 1); + + VK_CHECK(vkAllocateCommandBuffers(_device, &cmdAllocInfo, + &_immCommandBuffer)); + + _mainDeletionQueue.push_function([=, this]() { + vkDestroyCommandPool(_device, _immCommandPool, nullptr); + }); } +void VulkanEngine::init_sync_structures() { + VkFenceCreateInfo fenceCreateInfo = + vkinit::fence_create_info(VK_FENCE_CREATE_SIGNALED_BIT); + VkSemaphoreCreateInfo semaphoreCreateInfo = vkinit::semaphore_create_info(); + + for (int i = 0; i < FRAME_OVERLAP; i++) { + VK_CHECK(vkCreateFence(_device, &fenceCreateInfo, nullptr, + &_frames[i]._renderFence)); + + VK_CHECK(vkCreateSemaphore(_device, &semaphoreCreateInfo, nullptr, + &_frames[i]._swapchainSemaphore)); + VK_CHECK(vkCreateSemaphore(_device, &semaphoreCreateInfo, nullptr, + &_frames[i]._renderSemaphore)); + } + + VK_CHECK(vkCreateFence(_device, &fenceCreateInfo, nullptr, &_immFence)); + _mainDeletionQueue.push_function( + [=, this]() { vkDestroyFence(_device, _immFence, nullptr); }); +} void VulkanEngine::create_swapchain(uint32_t width, uint32_t height) { vkb::SwapchainBuilder swapchainBuilder{_chosenGPU, _device, _surface}; @@ -502,11 +859,11 @@ void VulkanEngine::init_swapchain() { create_swapchain(_windowExtent.width, _windowExtent.height); // draw image size will match the window - const VkExtent3D drawImageExtent = {_windowExtent.width, - _windowExtent.height, 1}; + VkExtent3D drawImageExtent = {_windowExtent.width, _windowExtent.height, 1}; // hardcoding the draw format to 32-bit float - VkFormat drawImageFormat = VK_FORMAT_R16G16B16A16_SFLOAT; + _drawImage.imageFormat = VK_FORMAT_R16G16B16A16_SFLOAT; + _drawImage.imageExtent = drawImageExtent; VkImageUsageFlags drawImageUsages{}; drawImageUsages |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; @@ -514,78 +871,40 @@ void VulkanEngine::init_swapchain() { drawImageUsages |= VK_IMAGE_USAGE_STORAGE_BIT; drawImageUsages |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - const VkImageCreateInfo rimg_info = vkinit::image_create_info( - drawImageFormat, drawImageUsages, drawImageExtent); + VkImageCreateInfo rimg_info = vkinit::image_create_info( + _drawImage.imageFormat, drawImageUsages, drawImageExtent); // for the draw image, we want to allocate it from gpu local memory VmaAllocationCreateInfo rimg_allocinfo = {}; rimg_allocinfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - rimg_allocinfo.requiredFlags = static_cast( - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - - AllocatedImage drawImageData{}; - drawImageData.imageFormat = drawImageFormat; - drawImageData.imageExtent = drawImageExtent; + rimg_allocinfo.requiredFlags = + VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); // allocate and create the image - vmaCreateImage(_allocator, &rimg_info, &rimg_allocinfo, &drawImageData.image, - &drawImageData.allocation, nullptr); + vmaCreateImage(_allocator, &rimg_info, &rimg_allocinfo, &_drawImage.image, + &_drawImage.allocation, nullptr); // build an image-view for the draw image to use for rendering - const VkImageViewCreateInfo rview_info = vkinit::imageview_create_info( - drawImageFormat, drawImageData.image, + VkImageViewCreateInfo rview_info = vkinit::imageview_create_info( + _drawImage.imageFormat, _drawImage.image, VK_IMAGE_ASPECT_COLOR_BIT); VK_CHECK(vkCreateImageView(_device, &rview_info, nullptr, - &drawImageData.imageView)); - - // Create smart pointer for automatic cleanup - _drawImage = std::make_unique(_allocator, _device, drawImageData); - - // Create depth image - VkExtent3D depthImageExtent = { - _windowExtent.width, - _windowExtent.height, - 1 - }; - - VkFormat depthFormat = VK_FORMAT_D32_SFLOAT; - - VkImageCreateInfo dimg_info = vkinit::image_create_info( - depthFormat, - VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, - depthImageExtent); + &_drawImage.imageView)); - VmaAllocationCreateInfo dimg_allocinfo = {}; - dimg_allocinfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - dimg_allocinfo.requiredFlags = static_cast( - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - - AllocatedImage depthImageData{}; - depthImageData.imageFormat = depthFormat; - depthImageData.imageExtent = depthImageExtent; - - // allocate and create the depth image - vmaCreateImage(_allocator, &dimg_info, &dimg_allocinfo, &depthImageData.image, - &depthImageData.allocation, nullptr); - - // build an image-view for the depth image - VkImageViewCreateInfo dview_info = vkinit::imageview_create_info( - depthFormat, depthImageData.image, VK_IMAGE_ASPECT_DEPTH_BIT); - - VK_CHECK(vkCreateImageView(_device, &dview_info, nullptr, - &depthImageData.imageView)); - - // Create smart pointer for automatic cleanup - _depthImage = std::make_unique(_allocator, _device, depthImageData); + // add to deletion queues + _mainDeletionQueue.push_function([=, this]() { + vkDestroyImageView(_device, _drawImage.imageView, nullptr); + vmaDestroyImage(_allocator, _drawImage.image, _drawImage.allocation); + }); } void VulkanEngine::destroy_swapchain() { vkDestroySwapchainKHR(_device, _swapchain, nullptr); // destroy swapchain resources - for (const auto& _swapchainImageView : _swapchainImageViews) { - vkDestroyImageView(_device, _swapchainImageView, nullptr); + for (int i = 0; i < _swapchainImageViews.size(); i++) { + vkDestroyImageView(_device, _swapchainImageViews[i], nullptr); } } @@ -596,17 +915,17 @@ void VulkanEngine::cleanup() { loadedScenes.clear(); - // Smart pointers will automatically clean up resources + _mainDeletionQueue.flush(); - for (auto& _frame : command_buffers_container._frames) { - // Smart pointers automatically clean up sync objects - // Manual cleanup only for command pools and command buffers - if (_frame._commandPool) { - vkDestroyCommandPool(_device, _frame._commandPool->get(), nullptr); - } + for (int i = 0; i < FRAME_OVERLAP; i++) { + // already written from before + vkDestroyCommandPool(_device, _frames[i]._commandPool, nullptr); - // Destroy frame descriptors manually - _frame._frameDescriptors.destroy_pools(_device); + // destroy sync objects + vkDestroyFence(_device, _frames[i]._renderFence, nullptr); + vkDestroySemaphore(_device, _frames[i]._renderSemaphore, nullptr); + vkDestroySemaphore(_device, _frames[i]._swapchainSemaphore, + nullptr); } destroy_swapchain(); @@ -616,9 +935,6 @@ void VulkanEngine::cleanup() { vkb::destroy_debug_utils_messenger(_instance, _debug_messenger); vkDestroyInstance(_instance, nullptr); - - // VMA allocator cleanup - vmaDestroyAllocator(_allocator); } // clear engine pointer @@ -669,7 +985,7 @@ GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, VMA_MEMORY_USAGE_GPU_ONLY); // find the address of the vertex buffer - const VkBufferDeviceAddressInfo deviceAddressInfo{ + VkBufferDeviceAddressInfo deviceAddressInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, .buffer = newSurface.vertexBuffer.buffer}; newSurface.vertexBufferAddress = @@ -681,9 +997,9 @@ GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_GPU_ONLY); - const AllocatedBuffer staging = create_buffer( - vertexBufferSize + indexBufferSize, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY); + AllocatedBuffer staging = create_buffer(vertexBufferSize + indexBufferSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VMA_MEMORY_USAGE_CPU_ONLY); void* data = staging.allocation->GetMappedData(); @@ -692,7 +1008,7 @@ GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, // copy index buffer memcpy((char*)data + vertexBufferSize, indices.data(), indexBufferSize); - command_buffers.immediate_submit([&](VkCommandBuffer cmd) { + immediate_submit([&](VkCommandBuffer cmd) { VkBufferCopy vertexCopy{0}; vertexCopy.dstOffset = 0; vertexCopy.srcOffset = 0; @@ -708,37 +1024,34 @@ GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, vkCmdCopyBuffer(cmd, staging.buffer, newSurface.indexBuffer.buffer, 1, &indexCopy); - }, - this); + }); - // Store mesh buffers in managed collections for automatic cleanup - _managedBuffers.push_back(std::make_unique(_allocator, newSurface.vertexBuffer)); - _managedBuffers.push_back(std::make_unique(_allocator, newSurface.indexBuffer)); destroy_buffer(staging); return newSurface; } -void VulkanEngine::draw_background(VkCommandBuffer cmd) const { +void VulkanEngine::draw_background(VkCommandBuffer cmd) { // bind the gradient drawing compute pipeline - - pipelines.gradientPipeline->bind(cmd); - - // bind descriptor sets - pipelines.gradientPipeline->bindDescriptorSets(cmd, &_drawImageDescriptors, 1); - - // dispatch the compute shader - pipelines.gradientPipeline->dispatch(cmd, - static_cast(std::ceil(_drawExtent.width / 16.0)), - static_cast(std::ceil(_drawExtent.height / 16.0)), - 1); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, _gradientPipeline); + + // bind the descriptor set containing the draw image for the compute + // pipeline + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, + _gradientPipelineLayout, 0, 1, + &_drawImageDescriptors, 0, nullptr); + + // execute the compute pipeline dispatch. We are using 16x16 workgroup size, + // so we need to divide by it + vkCmdDispatch(cmd, std::ceil(_drawExtent.width / 16.0), + std::ceil(_drawExtent.height / 16.0), 1); } void VulkanEngine::draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView) const { - const VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info( + VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info( targetImageView, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - const VkRenderingInfo renderInfo = + VkRenderingInfo renderInfo = vkinit::rendering_info(_swapchainExtent, &colorAttachment, nullptr); vkCmdBeginRendering(cmd, &renderInfo); @@ -754,9 +1067,10 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { sizeof(GPUSceneData), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); - // add it to the current frame's managed buffers for automatic cleanup - get_current_frame()._frameBuffers.push_back( - std::make_unique(_allocator, gpuSceneDataBuffer)); + // add it to the deletion queue of this frame, so it gets deleted once it's + // been used + get_current_frame()._deletionQueue.push_function( + [=, this]() { destroy_buffer(gpuSceneDataBuffer); }); // write the buffer auto* sceneUniformData = @@ -774,20 +1088,20 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { writer.update_set(_device, globalDescriptor); VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info( - _drawImage->imageView(), nullptr, VK_IMAGE_LAYOUT_GENERAL); + _drawImage.imageView, nullptr, VK_IMAGE_LAYOUT_GENERAL); VkRenderingInfo renderInfo = vkinit::rendering_info(_drawExtent, &colorAttachment, nullptr); vkCmdBeginRendering(cmd, &renderInfo); - pipelines.trianglePipeline->bind(cmd); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _trianglePipeline); // set dynamic viewport and scissor VkViewport viewport = {}; viewport.x = 0; viewport.y = 0; - viewport.width = static_cast(_drawExtent.width); - viewport.height = static_cast(_drawExtent.height); + viewport.width = _drawExtent.width; + viewport.height = _drawExtent.height; viewport.minDepth = 0.f; viewport.maxDepth = 1.f; @@ -804,68 +1118,69 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { // bind a texture VkDescriptorSet imageSet = get_current_frame()._frameDescriptors.allocate( _device, _singleImageDescriptorLayout); - DescriptorWriter single_image_writer; - single_image_writer.write_image(0, _errorCheckerboardImage->imageView(), - _defaultSamplerNearest, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); - single_image_writer.update_set(_device, imageSet); + { + DescriptorWriter writer; + writer.write_image(0, _errorCheckerboardImage.imageView, + _defaultSamplerNearest, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + + writer.update_set(_device, imageSet); + } - pipelines.meshPipeline->bindDescriptorSets(cmd, &imageSet, 1); + vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + _meshPipelineLayout, 0, 1, &imageSet, 0, nullptr); - for (const auto& [indexCount, firstIndex, indexBuffer, material, transform, - vertexBufferAddress] : mainDrawContext.OpaqueSurfaces) { + for (const RenderObject& draw : mainDrawContext.OpaqueSurfaces) { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, - material->pipeline->pipeline); + draw.material->pipeline->pipeline); vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, - material->pipeline->layout, 0, 1, + draw.material->pipeline->layout, 0, 1, &globalDescriptor, 0, nullptr); vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, - material->pipeline->layout, 1, 1, - &material->materialSet, 0, nullptr); + draw.material->pipeline->layout, 1, 1, + &draw.material->materialSet, 0, nullptr); - vkCmdBindIndexBuffer(cmd, indexBuffer, 0, VK_INDEX_TYPE_UINT32); + vkCmdBindIndexBuffer(cmd, draw.indexBuffer, 0, VK_INDEX_TYPE_UINT32); GPUDrawPushConstants pushConstants{}; - pushConstants.vertexBuffer = vertexBufferAddress; - pushConstants.worldMatrix = transform; - vkCmdPushConstants(cmd, material->pipeline->layout, + pushConstants.vertexBuffer = draw.vertexBufferAddress; + pushConstants.worldMatrix = draw.transform; + vkCmdPushConstants(cmd, draw.material->pipeline->layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(GPUDrawPushConstants), &pushConstants); - vkCmdDrawIndexed(cmd, indexCount, 1, firstIndex, 0, 0); + vkCmdDrawIndexed(cmd, draw.indexCount, 1, draw.firstIndex, 0, 0); } vkCmdEndRendering(cmd); } -void VulkanEngine::draw() { - update_scene(); +void VulkanEngine::draw(const IModel::Ptr model) { + update_scene(model); // wait until the gpu has finished rendering the last frame. Timeout of 1 // second - VK_CHECK(vkWaitForFences(_device, 1, get_current_frame()._renderFence->getPtr(), + VK_CHECK(vkWaitForFences(_device, 1, &get_current_frame()._renderFence, true, 1000000000)); - // Clear frame buffers instead of flushing deletion queue - get_current_frame()._frameBuffers.clear(); + get_current_frame()._deletionQueue.flush(); get_current_frame()._frameDescriptors.clear_pools(_device); - VK_CHECK(vkResetFences(_device, 1, get_current_frame()._renderFence->getPtr())); + VK_CHECK(vkResetFences(_device, 1, &get_current_frame()._renderFence)); // request image from the swapchain uint32_t swapchainImageIndex; - const VkResult e = - vkAcquireNextImageKHR(_device, _swapchain, 1000000000, - get_current_frame()._swapchainSemaphore->get(), - nullptr, &swapchainImageIndex); + VkResult e = vkAcquireNextImageKHR(_device, _swapchain, 1000000000, + get_current_frame()._swapchainSemaphore, + nullptr, &swapchainImageIndex); if (e == VK_ERROR_OUT_OF_DATE_KHR) { resize_requested = true; return; } // naming it cmd for shorter writing - const VkCommandBuffer cmd = get_current_frame()._mainCommandBuffer; + VkCommandBuffer cmd = get_current_frame()._mainCommandBuffer; // now that we are sure that the commands finished executing, we can safely // reset the command buffer to begin recording again. @@ -873,37 +1188,34 @@ void VulkanEngine::draw() { // begin the command buffer recording. We will use this command buffer // exactly once, so we want to let vulkan know that - const VkCommandBufferBeginInfo cmdBeginInfo = - vkinit::command_buffer_begin_info( - VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); - - _drawExtent.height = static_cast( - (float)std::min(_swapchainExtent.height, - _drawImage->get().imageExtent.height) * - renderScale); - _drawExtent.width = static_cast( - (float)std::min(_swapchainExtent.width, - _drawImage->get().imageExtent.width) * - renderScale); + VkCommandBufferBeginInfo cmdBeginInfo = vkinit::command_buffer_begin_info( + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + + _drawExtent.height = + std::min(_swapchainExtent.height, _drawImage.imageExtent.height) * + renderScale; + _drawExtent.width = + std::min(_swapchainExtent.width, _drawImage.imageExtent.width) * + renderScale; VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo)); // transition our main draw image into general layout, so we can write into // it, we will overwrite it all, so we don't care about what was the older // layout - vkutil::transition_image(cmd, _drawImage->image(), VK_IMAGE_LAYOUT_UNDEFINED, + vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); draw_background(cmd); - vkutil::transition_image(cmd, _drawImage->image(), VK_IMAGE_LAYOUT_GENERAL, + vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); draw_geometry(cmd); // transition the draw image and the swapchain image into their correct // transfer layouts - vkutil::transition_image(cmd, _drawImage->image(), + vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], @@ -911,7 +1223,7 @@ void VulkanEngine::draw() { VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); // execute a copy from the draw image into the swapchain - vkutil::copy_image_to_image(cmd, _drawImage->image(), + vkutil::copy_image_to_image(cmd, _drawImage.image, _swapchainImages[swapchainImageIndex], _drawExtent, _swapchainExtent); @@ -937,23 +1249,22 @@ void VulkanEngine::draw() { // when the swapchain is ready we will signal the _renderSemaphore, to // signal that rendering has finished - const VkCommandBufferSubmitInfo cmdinfo = - vkinit::command_buffer_submit_info(cmd); + VkCommandBufferSubmitInfo cmdinfo = vkinit::command_buffer_submit_info(cmd); - const VkSemaphoreSubmitInfo waitInfo = vkinit::semaphore_submit_info( + VkSemaphoreSubmitInfo waitInfo = vkinit::semaphore_submit_info( VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR, - get_current_frame()._swapchainSemaphore->get()); - const VkSemaphoreSubmitInfo signalInfo = + get_current_frame()._swapchainSemaphore); + VkSemaphoreSubmitInfo signalInfo = vkinit::semaphore_submit_info(VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT, - get_current_frame()._renderSemaphore->get()); + get_current_frame()._renderSemaphore); - const VkSubmitInfo2 submit = + VkSubmitInfo2 submit = vkinit::submit_info(&cmdinfo, &signalInfo, &waitInfo); // submit command buffer to the queue and execute it. // _renderFence will now block until the graphic commands finish execution VK_CHECK(vkQueueSubmit2(_graphicsQueue, 1, &submit, - get_current_frame()._renderFence->get())); + get_current_frame()._renderFence)); // prepare present // this will put the image we just rendered to into the visible window. @@ -966,7 +1277,7 @@ void VulkanEngine::draw() { presentInfo.pSwapchains = &_swapchain; presentInfo.swapchainCount = 1; - presentInfo.pWaitSemaphores = get_current_frame()._renderSemaphore->getPtr(); + presentInfo.pWaitSemaphores = &get_current_frame()._renderSemaphore; presentInfo.waitSemaphoreCount = 1; presentInfo.pImageIndices = &swapchainImageIndex; @@ -987,20 +1298,46 @@ void VulkanEngine::resize_swapchain() { int w, h; SDL_GetWindowSize(_window, &w, &h); - _windowExtent.width = static_cast(w); - _windowExtent.height = static_cast(h); + _windowExtent.width = w; + _windowExtent.height = h; create_swapchain(_windowExtent.width, _windowExtent.height); resize_requested = false; } -void VulkanEngine::update() { +void VulkanEngine::immediate_submit( + std::function&& function) { + VK_CHECK(vkResetFences(_device, 1, &_immFence)); + VK_CHECK(vkResetCommandBuffer(_immCommandBuffer, 0)); + + VkCommandBuffer cmd = _immCommandBuffer; + + VkCommandBufferBeginInfo cmdBeginInfo = vkinit::command_buffer_begin_info( + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + + VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo)); + + function(cmd); + + VK_CHECK(vkEndCommandBuffer(cmd)); + + VkCommandBufferSubmitInfo cmdinfo = vkinit::command_buffer_submit_info(cmd); + VkSubmitInfo2 submit = vkinit::submit_info(&cmdinfo, nullptr, nullptr); + + // submit command buffer to the queue and execute it. + // _renderFence will now block until the graphic commands finish execution + VK_CHECK(vkQueueSubmit2(_graphicsQueue, 1, &submit, _immFence)); + + VK_CHECK(vkWaitForFences(_device, 1, &_immFence, true, 9999999999)); +} + +void VulkanEngine::update(const IModel::Ptr model) { if (resize_requested) { resize_swapchain(); } - draw(); + draw(model); } AllocatedImage VulkanEngine::create_image(VkExtent3D size, VkFormat format, @@ -1020,8 +1357,8 @@ AllocatedImage VulkanEngine::create_image(VkExtent3D size, VkFormat format, // always allocate images on dedicated GPU memory VmaAllocationCreateInfo allocinfo = {}; allocinfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - allocinfo.requiredFlags = static_cast( - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + allocinfo.requiredFlags = + VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); // allocate and create the image VK_CHECK(vmaCreateImage(_allocator, &img_info, &allocinfo, &newImage.image, @@ -1045,24 +1382,24 @@ AllocatedImage VulkanEngine::create_image(VkExtent3D size, VkFormat format, return newImage; } -AllocatedImage VulkanEngine::create_image(const void* data, VkExtent3D size, +AllocatedImage VulkanEngine::create_image(void* data, VkExtent3D size, VkFormat format, VkImageUsageFlags usage, - bool mipmapped) const { - const size_t data_size = size.depth * size.width * size.height * 4; - const AllocatedBuffer uploadbuffer = + bool mipmapped) { + size_t data_size = size.depth * size.width * size.height * 4; + AllocatedBuffer uploadbuffer = create_buffer(data_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); memcpy(uploadbuffer.info.pMappedData, data, data_size); - const AllocatedImage new_image = + AllocatedImage new_image = create_image(size, format, usage | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, mipmapped); - command_buffers.immediate_submit([&](VkCommandBuffer cmd) { + immediate_submit([&](VkCommandBuffer cmd) { vkutil::transition_image(cmd, new_image.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); @@ -1086,8 +1423,7 @@ AllocatedImage VulkanEngine::create_image(const void* data, VkExtent3D size, vkutil::transition_image(cmd, new_image.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - }, - (VulkanEngine*)this); + }); destroy_buffer(uploadbuffer); @@ -1099,15 +1435,124 @@ void VulkanEngine::destroy_image(const AllocatedImage& img) const { vmaDestroyImage(_allocator, img.image, img.allocation); } +void GLTFMetallic_Roughness::build_pipelines(VulkanEngine* engine) { + VkShaderModule meshFragShader; + if (!vkutil::load_shader_module("./shaders/mesh.frag.spv", engine->_device, + &meshFragShader)) { + fmt::println("Error when building the triangle fragment shader module"); + } + + VkShaderModule meshVertexShader; + if (!vkutil::load_shader_module("./shaders/mesh.vert.spv", engine->_device, + &meshVertexShader)) { + fmt::println("Error when building the triangle vertex shader module"); + } + + VkPushConstantRange matrixRange{}; + matrixRange.offset = 0; + matrixRange.size = sizeof(GPUDrawPushConstants); + matrixRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + + DescriptorLayoutBuilder layoutBuilder; + layoutBuilder.add_binding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); + layoutBuilder.add_binding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + layoutBuilder.add_binding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + + materialLayout = layoutBuilder.build( + engine->_device, + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT); + + VkDescriptorSetLayout layouts[] = {engine->_gpuSceneDataDescriptorLayout, + materialLayout}; + + VkPipelineLayoutCreateInfo mesh_layout_info = + vkinit::pipeline_layout_create_info(); + mesh_layout_info.setLayoutCount = 2; + mesh_layout_info.pSetLayouts = layouts; + mesh_layout_info.pPushConstantRanges = &matrixRange; + mesh_layout_info.pushConstantRangeCount = 1; + + VkPipelineLayout newLayout; + VK_CHECK(vkCreatePipelineLayout(engine->_device, &mesh_layout_info, nullptr, + &newLayout)); + + opaquePipeline.layout = newLayout; + transparentPipeline.layout = newLayout; + + // build the stage-create-info for both vertex and fragment stages. This + // lets the pipeline know the shader modules per stage + PipelineBuilder pipelineBuilder; + pipelineBuilder.set_shaders(meshVertexShader, meshFragShader); + pipelineBuilder.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); + pipelineBuilder.set_polygon_mode(VK_POLYGON_MODE_FILL); + pipelineBuilder.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE); + pipelineBuilder.set_multisampling_none(); + pipelineBuilder.disable_blending(); + pipelineBuilder.enable_depthtest(true, VK_COMPARE_OP_GREATER_OR_EQUAL); + + // render format + pipelineBuilder.set_color_attachment_format(engine->_drawImage.imageFormat); + pipelineBuilder.set_depth_format(engine->_depthImage.imageFormat); + + // use the triangle layout we created + pipelineBuilder._pipelineLayout = newLayout; + + // finally build the pipeline + opaquePipeline.pipeline = pipelineBuilder.build_pipeline(engine->_device); + + // create the transparent variant + pipelineBuilder.enable_blending_additive(); + + pipelineBuilder.enable_depthtest(false, VK_COMPARE_OP_GREATER_OR_EQUAL); + + transparentPipeline.pipeline = + pipelineBuilder.build_pipeline(engine->_device); + + vkDestroyShaderModule(engine->_device, meshFragShader, nullptr); + vkDestroyShaderModule(engine->_device, meshVertexShader, nullptr); +} + +MaterialInstance GLTFMetallic_Roughness::write_material( + VkDevice device, MaterialPass pass, const MaterialResources& resources, + DescriptorAllocatorGrowable& descriptorAllocator) { + MaterialInstance matData{}; + matData.passType = pass; + if (pass == MaterialPass::Transparent) { + matData.pipeline = &transparentPipeline; + } else { + matData.pipeline = &opaquePipeline; + } + + matData.materialSet = descriptorAllocator.allocate(device, materialLayout); + + writer.clear(); + writer.write_buffer(0, resources.dataBuffer, sizeof(MaterialConstants), + resources.dataBufferOffset, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); + writer.write_image(1, resources.colorImage.imageView, + resources.colorSampler, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + writer.write_image(2, resources.metalRoughImage.imageView, + resources.metalRoughSampler, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + + writer.update_set(device, matData.materialSet); + writer.update_set(device, matData.materialSet); + + return matData; +} + void MeshNode::Draw(const glm::mat4& topMatrix, DrawContext& ctx) { - const glm::mat4 nodeMatrix = topMatrix * worldTransform; + glm::mat4 nodeMatrix = topMatrix * worldTransform; - for (auto& [startIndex, count, material] : mesh->surfaces) { + for (auto& s : mesh->surfaces) { RenderObject def{}; - def.indexCount = count; - def.firstIndex = startIndex; + def.indexCount = s.count; + def.firstIndex = s.startIndex; def.indexBuffer = mesh->meshBuffers.indexBuffer.buffer; - def.material = &material->data; + def.material = &s.material->data; def.transform = nodeMatrix; def.vertexBufferAddress = mesh->meshBuffers.vertexBufferAddress; @@ -1118,10 +1563,10 @@ void MeshNode::Draw(const glm::mat4& topMatrix, DrawContext& ctx) { ENode::Draw(topMatrix, ctx); } -void VulkanEngine::update_scene() { +void VulkanEngine::update_scene(const IModel::Ptr& model) { mainCamera->update(); - const glm::mat4 view = mainCamera->getViewMatrix(); + glm::mat4 view = mainCamera->getViewMatrix(); glm::mat4 projection = glm::perspective( glm::radians(70.f), @@ -1141,42 +1586,42 @@ void VulkanEngine::update_scene() { sceneData.sunlightColor = glm::vec4(1.f); sceneData.sunlightDirection = glm::vec4(0, 1, 0.5, 1.f); - for (const auto& [key, mesh] : meshes) { - const std::shared_ptr loadedMesh = mesh; - loadedMesh->Draw(transforms[key], mainDrawContext); + auto meshes = model->get_meshes(); + for (const auto& [id, mesh_info] : meshes) { + const auto renderable_gltf = createRenderableGLTF(mesh_info.ptr); + // const std::shared_ptr loadedMesh = mesh_info.ptr; + renderable_gltf->Draw(mesh_info.transform, mainDrawContext); } } -int64_t VulkanEngine::registerMesh(const std::string& filePath) { - std::random_device rd; - - // Use the Mersenne Twister engine for high-quality random numbers - std::mt19937_64 generator(rd()); - - // Create a uniform distribution for int64_t - std::uniform_int_distribution distribution; - - // Generate and print a random int64_t value - const int64_t random_int64 = distribution(generator); - - const std::string structurePath = {std::string(ASSETS_DIR) + filePath}; - const auto structureFile = loadGltf(this, structurePath); - - assert(structureFile.has_value()); - - meshes[random_int64] = *structureFile; - transforms[random_int64] = glm::mat4(1.0f); - - return random_int64; -} - -void VulkanEngine::unregisterMesh(int64_t id) { - if (meshes.find(id) != meshes.end()) { - meshes.erase(id); - transforms.erase(id); - } -} - -void VulkanEngine::setMeshTransform(int64_t id, glm::mat4 mat) { - transforms[id] = mat; -} +// Mesh::rid_t VulkanEngine::registerMesh(std::string_view filePath) { +// std::random_device rd; +// +// // Use the Mersenne Twister engine for high-quality random numbers +// std::mt19937_64 generator(rd()); +// +// // Create a uniform distribution for int64_t +// std::uniform_int_distribution distribution; +// +// // Generate and print a random int64_t value +// Mesh::rid_t random_rid_t = distribution(generator); +// +// std::string structurePath = {std::string(ASSETS_DIR) + std::string(filePath)}; +// auto structureFile = loadGltf(this, structurePath); +// +// assert(structureFile.has_value()); +// +// meshes[random_rid_t] = *structureFile; +// transforms[random_rid_t] = glm::mat4(1.0f); +// +// return random_rid_t; +// } + +// void VulkanEngine::unregisterMesh(int64_t id) { +// meshes.erase(id); +// transforms.erase(id); +// } + +// void VulkanEngine::setMeshTransform(int64_t id, glm::mat4 mat) { +// transforms[id] = mat; +// } diff --git a/src/graphics/vulkan/vk_loader.cpp b/src/graphics/vulkan/vk_loader.cpp index 5c00e7be..0a571845 100644 --- a/src/graphics/vulkan/vk_loader.cpp +++ b/src/graphics/vulkan/vk_loader.cpp @@ -1,526 +1,514 @@ -#include "graphics/vulkan/vk_loader.h" +#include "graphics/vulkan/vk_loader.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#define GLM_ENABLE_EXPERIMENTAL -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include "core/Logging.h" -#include "graphics/vulkan/vk_descriptors.h" #include "graphics/vulkan/vk_engine.h" #include "graphics/vulkan/vk_types.h" +#include "stb_image.h" -std::optional>> loadGltfMeshes( - VulkanEngine* engine, const std::filesystem::path& filePath) { - if (!std::filesystem::exists(filePath)) { - LOGW("Failed to find file: {}", filePath.string()); - return {}; - } - LOGI("Loading: {}", filePath.string()); - - fastgltf::Asset gltf; - - // Parse the glTF file and get the constructed asset - - static constexpr auto supportedExtensions = - fastgltf::Extensions::KHR_mesh_quantization | - fastgltf::Extensions::KHR_texture_transform | - fastgltf::Extensions::KHR_materials_variants; - - fastgltf::Parser parser(supportedExtensions); - - auto path = std::filesystem::path{filePath}; - - constexpr auto gltfOptions = - fastgltf::Options::DontRequireValidAssetMember | - fastgltf::Options::AllowDouble | fastgltf::Options::LoadGLBBuffers | - fastgltf::Options::LoadExternalBuffers | - fastgltf::Options::LoadExternalImages | - fastgltf::Options::GenerateMeshIndices; - - if (!std::filesystem::exists(path)) { - LOGW("Failed to find file: {}", path.string()); - return {}; - } - - fastgltf::GltfDataBuffer data; - data.loadFromFile(path); - - auto asset = parser.loadGltf(&data, path.parent_path(), gltfOptions); - - if (asset) { - gltf = std::move(asset.get()); - } else { - LOGE("Failed to load glTF: {}", - fastgltf::to_underlying(asset.error())); - return {}; - } - - std::vector> meshes; - - // use the same vectors for all meshes so that the memory doesnt reallocate - // as often - std::vector indices; - std::vector vertices; - for (auto& [primitives, _, name] : gltf.meshes) { - MeshAsset newmesh; - - newmesh.name = name; - - // clear the mesh arrays each mesh, we dont want to merge them by error - indices.clear(); - vertices.clear(); - - for (auto&& p : primitives) { - GeoSurface newSurface; - newSurface.startIndex = (uint32_t)indices.size(); - newSurface.count = - (uint32_t)gltf.accessors[p.indicesAccessor.value()].count; - - auto initial_vtx = static_cast(vertices.size()); - - // load indexes - { - fastgltf::Accessor& indexaccessor = - gltf.accessors[p.indicesAccessor.value()]; - indices.reserve(indices.size() + indexaccessor.count); - - fastgltf::iterateAccessor( - gltf, indexaccessor, [&](std::uint32_t idx) { - indices.push_back(idx + initial_vtx); - }); - } - - // load vertex positions - { - fastgltf::Accessor& posAccessor = - gltf.accessors[p.findAttribute("POSITION")->second]; - vertices.resize(vertices.size() + posAccessor.count); - - fastgltf::iterateAccessorWithIndex( - gltf, posAccessor, [&](glm::vec3 v, size_t index) { - Vertex newvtx; - newvtx.position = v; - newvtx.normal = {1, 0, 0}; - newvtx.color = glm::vec4{1.f}; - newvtx.uv_x = 0; - newvtx.uv_y = 0; - vertices[initial_vtx + index] = newvtx; - }); - } - - // load vertex normals - auto normals = p.findAttribute("NORMAL"); - if (normals != p.attributes.end()) { - fastgltf::iterateAccessorWithIndex( - gltf, gltf.accessors[normals->second], - [&](glm::vec3 v, size_t index) { - vertices[initial_vtx + index].normal = v; - }); - } - - // load UVs - auto uv = p.findAttribute("TEXCOORD_0"); - if (uv != p.attributes.end()) { - fastgltf::iterateAccessorWithIndex( - gltf, gltf.accessors[uv->second], - [&](glm::vec2 v, size_t index) { - vertices[initial_vtx + index].uv_x = v.x; - vertices[initial_vtx + index].uv_y = v.y; - }); - } - - // load vertex colors - auto colors = p.findAttribute("COLOR_0"); - if (colors != p.attributes.end()) { - fastgltf::iterateAccessorWithIndex( - gltf, gltf.accessors[colors->second], - [&](glm::vec4 v, size_t index) { - vertices[initial_vtx + index].color = v; - }); - } - newmesh.surfaces.push_back(newSurface); - } - - // display the vertex normals - constexpr bool OverrideColors = true; - if (OverrideColors) { - for (Vertex& vtx : vertices) { - vtx.color = glm::vec4(vtx.normal, 1.f); - } - } - newmesh.meshBuffers = engine->uploadMesh(indices, vertices); - - meshes.emplace_back(std::make_shared(std::move(newmesh))); - } - - return meshes; -} - -VkFilter extract_filter(fastgltf::Filter filter) { - switch (filter) { - // nearest samplers - case fastgltf::Filter::Nearest: - case fastgltf::Filter::NearestMipMapNearest: - case fastgltf::Filter::NearestMipMapLinear: - return VK_FILTER_NEAREST; - - // linear samplers - case fastgltf::Filter::Linear: - case fastgltf::Filter::LinearMipMapNearest: - case fastgltf::Filter::LinearMipMapLinear: - default: - return VK_FILTER_LINEAR; - } -} - -VkSamplerMipmapMode extract_mipmap_mode(fastgltf::Filter filter) { - switch (filter) { - case fastgltf::Filter::NearestMipMapNearest: - case fastgltf::Filter::LinearMipMapNearest: - return VK_SAMPLER_MIPMAP_MODE_NEAREST; - - case fastgltf::Filter::NearestMipMapLinear: - case fastgltf::Filter::LinearMipMapLinear: - default: - return VK_SAMPLER_MIPMAP_MODE_LINEAR; - } -} - -std::optional> loadGltf(VulkanEngine* engine, - std::string_view filePath) { - LOGI("Loading GLTF: {}", filePath);if (!std::filesystem::exists(filePath)) { - LOGW("File does not exist: {}", filePath); - } - auto scene = std::make_shared(); - scene->creator = engine; - LoadedGLTF& file = *scene; - fastgltf::Parser parser{}; - constexpr auto gltfOptions = - fastgltf::Options::DontRequireValidAssetMember | - fastgltf::Options::AllowDouble | fastgltf::Options::LoadGLBBuffers | - fastgltf::Options::LoadExternalBuffers; - fastgltf::GltfDataBuffer data; - data.loadFromFile(filePath); - fastgltf::Asset gltf; - std::filesystem::path path = filePath; - auto type = fastgltf::determineGltfFileType(&data); - if (type == fastgltf::GltfType::glTF) { - auto load = parser.loadGltf(&data, path.parent_path(), gltfOptions); - if (load) { - gltf = std::move(load.get()); - } else { - LOGE("Failed to load glTF: {} ", fastgltf::to_underlying(load.error())); - return {}; - } - } else if (type == fastgltf::GltfType::GLB) { - auto load = - parser.loadGltfBinary(&data, path.parent_path(), gltfOptions); - if (load) { - gltf = std::move(load.get()); - } else { - LOGE("Failed to load glTF: {} ", fastgltf::to_underlying(load.error())); - return {}; - } - } else { - LOGE("Failed to determine glTF container"); - return {}; - } - - // Estimate descriptor pool size based on materials - std::vector sizes = { - {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3}, - {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3}, - {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}}; - file.descriptorPool.init( - engine->_device, - static_cast(std::max(gltf.materials.size(), size_t(1))), - sizes); - - // Load samplers - for (fastgltf::Sampler& sampler : gltf.samplers) { - VkSamplerCreateInfo sampl = { - .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .pNext = nullptr}; - sampl.maxLod = VK_LOD_CLAMP_NONE; - sampl.minLod = 0; - sampl.magFilter = extract_filter( - sampler.magFilter.value_or(fastgltf::Filter::Nearest)); - sampl.minFilter = extract_filter( - sampler.minFilter.value_or(fastgltf::Filter::Nearest)); - sampl.mipmapMode = extract_mipmap_mode( - sampler.minFilter.value_or(fastgltf::Filter::Nearest)); - VkSampler newSampler; - vkCreateSampler(engine->_device, &sampl, nullptr, &newSampler); - file.samplers.push_back(newSampler); - } - - std::vector> meshes; - std::vector> nodes; - std::vector images; - std::vector> materials; - - // Load all textures - images.reserve(gltf.images.size()); - for (size_t i = 0; i < gltf.images.size(); i++) { - images.push_back(engine->_errorCheckerboardImage->get()); - } - - // Create buffer to hold the material data - size_t materialCount = gltf.materials.size() ? gltf.materials.size() : 1; - file.materialDataBuffer = engine->create_buffer( - sizeof(GLTFMetallic_Roughness::MaterialConstants) * materialCount, - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); - - uint32_t data_index = 0; - auto* sceneMaterialConstants = - (GLTFMetallic_Roughness::MaterialConstants*) - file.materialDataBuffer.info.pMappedData; - - // Process all materials from the GLTF - for (fastgltf::Material& mat : gltf.materials) { - auto newMat = std::make_shared(); - materials.push_back(newMat); - file.materials[mat.name.c_str()] = newMat; - - GLTFMetallic_Roughness::MaterialConstants constants; - constants.colorFactors.x = mat.pbrData.baseColorFactor[0]; - constants.colorFactors.y = mat.pbrData.baseColorFactor[1]; - constants.colorFactors.z = mat.pbrData.baseColorFactor[2]; - constants.colorFactors.w = mat.pbrData.baseColorFactor[3]; - constants.metal_rough_factors.x = mat.pbrData.metallicFactor; - constants.metal_rough_factors.y = mat.pbrData.roughnessFactor; - - sceneMaterialConstants[data_index] = constants; - - auto passType = MaterialPass::MainColor; - if (mat.alphaMode == fastgltf::AlphaMode::Blend) { - passType = MaterialPass::Transparent; - } - - GLTFMetallic_Roughness::MaterialResources materialResources; - - materialResources.colorImage = engine->_whiteImage->get(); - materialResources.colorSampler = engine->_defaultSamplerLinear; - materialResources.metalRoughImage = engine->_whiteImage->get(); - materialResources.metalRoughSampler = engine->_defaultSamplerLinear; - materialResources.dataBuffer = file.materialDataBuffer.buffer; - materialResources.dataBufferOffset = - data_index * sizeof(GLTFMetallic_Roughness::MaterialConstants); - - if (mat.pbrData.baseColorTexture.has_value()) { - size_t img = gltf.textures[mat.pbrData.baseColorTexture.value() - .textureIndex] - .imageIndex.value(); - size_t sampler = gltf.textures[mat.pbrData.baseColorTexture.value() - .textureIndex] - .samplerIndex.value(); - materialResources.colorImage = images[img]; - materialResources.colorSampler = file.samplers[sampler]; - } - - newMat->data = engine->metalRoughMaterial.write_material( - engine->_device, passType, materialResources, - file.descriptorPool); - data_index++; - } - - // Add a fallback material if no materials were defined in the GLTF - if (materials.empty()) { - auto defaultMat = std::make_shared(); - materials.push_back(defaultMat); - - GLTFMetallic_Roughness::MaterialConstants constants = {}; - constants.colorFactors = glm::vec4(1.0f); // White base color - constants.metal_rough_factors = glm::vec4(0.0f); // Non-metallic, smooth - - sceneMaterialConstants[0] = constants; - - GLTFMetallic_Roughness::MaterialResources resources; - resources.colorImage = engine->_whiteImage->get(); - resources.colorSampler = engine->_defaultSamplerLinear; - resources.metalRoughImage = engine->_whiteImage->get(); - resources.metalRoughSampler = engine->_defaultSamplerLinear; - resources.dataBuffer = file.materialDataBuffer.buffer; - resources.dataBufferOffset = 0; - - defaultMat->data = engine->metalRoughMaterial.write_material( - engine->_device, MaterialPass::MainColor, resources, - file.descriptorPool); - } - - std::vector indices; - std::vector vertices; - - for (auto& [primitives, _, name] : gltf.meshes) { - auto newmesh = std::make_shared(); - meshes.push_back(newmesh); - file.meshes[name.c_str()] = newmesh; - newmesh->name = name; - indices.clear(); - vertices.clear(); - - for (auto&& p : primitives) { - GeoSurface newSurface; - newSurface.startIndex = (uint32_t)indices.size(); - newSurface.count = - (uint32_t)gltf.accessors[p.indicesAccessor.value()].count; - auto initial_vtx = static_cast(vertices.size()); - - // Load indices - { - fastgltf::Accessor& indexaccessor = - gltf.accessors[p.indicesAccessor.value()]; - indices.reserve(indices.size() + indexaccessor.count); - fastgltf::iterateAccessor( - gltf, indexaccessor, [&](std::uint32_t idx) { - indices.push_back(idx + initial_vtx); - }); - } - - // Load vertex positions - { - fastgltf::Accessor& posAccessor = - gltf.accessors[p.findAttribute("POSITION")->second]; - vertices.resize(vertices.size() + posAccessor.count); - fastgltf::iterateAccessorWithIndex( - gltf, posAccessor, [&](glm::vec3 v, size_t index) { - Vertex newvtx; - newvtx.position = v; - newvtx.normal = {1, 0, 0}; - newvtx.color = glm::vec4{1.f}; - newvtx.uv_x = 0; - newvtx.uv_y = 0; - vertices[initial_vtx + index] = newvtx; - }); - } - - // Load vertex normals - auto normals = p.findAttribute("NORMAL"); - if (normals != p.attributes.end()) { - fastgltf::iterateAccessorWithIndex( - gltf, gltf.accessors[normals->second], - [&](glm::vec3 v, size_t index) { - vertices[initial_vtx + index].normal = v; - }); - } - - // Load UVs - auto uv = p.findAttribute("TEXCOORD_0"); - if (uv != p.attributes.end()) { - fastgltf::iterateAccessorWithIndex( - gltf, gltf.accessors[uv->second], - [&](glm::vec2 v, size_t index) { - vertices[initial_vtx + index].uv_x = v.x; - vertices[initial_vtx + index].uv_y = v.y; - }); - } - - // Load vertex colors - auto colors = p.findAttribute("COLOR_0"); - if (colors != p.attributes.end()) { - fastgltf::iterateAccessorWithIndex( - gltf, gltf.accessors[colors->second], - [&](glm::vec4 v, size_t index) { - vertices[initial_vtx + index].color = v; - }); - } - - // Assign material safely - if (p.materialIndex.has_value()) { - newSurface.material = materials[p.materialIndex.value()]; - } else { - newSurface.material = materials[0]; // Always valid now - } - - newmesh->surfaces.push_back(newSurface); - } - newmesh->meshBuffers = engine->uploadMesh(indices, vertices); - } - - // Load all nodes and their meshes - for (fastgltf::Node& node : gltf.nodes) { - std::shared_ptr newNode; - if (node.meshIndex.has_value()) { - newNode = std::make_shared(); - dynamic_cast(newNode.get())->mesh = - meshes[*node.meshIndex]; - } else { - newNode = std::make_shared(); - } - nodes.push_back(newNode); - file.nodes[node.name.c_str()]; - - std::visit(fastgltf::visitor{ - [&](const fastgltf::Node::TransformMatrix& matrix) { - memcpy(&newNode->localTransform, matrix.data(), - sizeof(matrix)); - }, - [&](const fastgltf::TRS& transform) { - const glm::vec3 tl(transform.translation[0], - transform.translation[1], - transform.translation[2]); - const glm::quat rot(transform.rotation[3], - transform.rotation[0], - transform.rotation[1], - transform.rotation[2]); - const glm::vec3 sc(transform.scale[0], - transform.scale[1], - transform.scale[2]); - const glm::mat4 tm = - glm::translate(glm::mat4(1.f), tl); - const glm::mat4 rm = glm::toMat4(rot); - const glm::mat4 sm = - glm::scale(glm::mat4(1.f), sc); - newNode->localTransform = tm * rm * sm; - }}, - node.transform); - } - - // Setup transform hierarchy - for (size_t i = 0; i < gltf.nodes.size(); i++) { - fastgltf::Node& node = gltf.nodes[i]; - std::shared_ptr& sceneNode = nodes[i]; - for (auto& c : node.children) { - sceneNode->children.push_back(nodes[c]); - nodes[c]->parent = sceneNode; - } - } - - // Find top-level nodes - for (auto& n : nodes) { - if (n->parent.lock() == nullptr) { - file.topNodes.push_back(n); - n->refreshTransform(glm::mat4{1.f}); - } - } +#define GLM_ENABLE_EXPERIMENTAL - return scene; -} +#include +#include +#include +#include +#include -void LoadedGLTF::Draw(const glm::mat4& topMatrix, DrawContext& ctx) { +// std::optional>> loadGltfMeshes( +// VulkanEngine* engine, std::filesystem::path filePath) { +// if (!std::filesystem::exists(filePath)) { +// std::cout << "Failed to find " << filePath << '\n'; +// return {}; +// } +// +// std::cout << "Loading " << filePath << '\n'; +// +// fastgltf::Asset gltf; +// +// // Parse the glTF file and get the constructed asset +// +// static constexpr auto supportedExtensions = +// fastgltf::Extensions::KHR_mesh_quantization | +// fastgltf::Extensions::KHR_texture_transform | +// fastgltf::Extensions::KHR_materials_variants; +// +// fastgltf::Parser parser(supportedExtensions); +// +// auto path = std::filesystem::path{filePath}; +// +// constexpr auto gltfOptions = +// fastgltf::Options::DontRequireValidAssetMember | +// fastgltf::Options::AllowDouble | fastgltf::Options::LoadGLBBuffers | +// fastgltf::Options::LoadExternalBuffers | +// fastgltf::Options::LoadExternalImages | +// fastgltf::Options::GenerateMeshIndices; +// +// fastgltf::GltfDataBuffer data; +// data.loadFromFile(path); +// +// auto asset = parser.loadGltf(&data, path.parent_path(), gltfOptions); +// +// if (asset) { +// gltf = std::move(asset.get()); +// } else { +// fmt::print("Failed to load glTF: {} \n", +// fastgltf::to_underlying(asset.error())); +// return {}; +// } +// +// std::vector> meshes; +// +// // use the same vectors for all meshes so that the memory doesnt reallocate +// // as often +// std::vector indices; +// std::vector vertices; +// for (fastgltf::Mesh& mesh : gltf.meshes) { +// MeshAsset newmesh; +// +// newmesh.name = mesh.name; +// +// // clear the mesh arrays each mesh, we dont want to merge them by error +// indices.clear(); +// vertices.clear(); +// +// for (auto&& p : mesh.primitives) { +// GeoSurface newSurface; +// newSurface.startIndex = (uint32_t)indices.size(); +// newSurface.count = +// (uint32_t)gltf.accessors[p.indicesAccessor.value()].count; +// +// size_t initial_vtx = vertices.size(); +// +// // load indexes +// { +// fastgltf::Accessor& indexaccessor = +// gltf.accessors[p.indicesAccessor.value()]; +// indices.reserve(indices.size() + indexaccessor.count); +// +// fastgltf::iterateAccessor( +// gltf, indexaccessor, [&](std::uint32_t idx) { +// indices.push_back(idx + initial_vtx); +// }); +// } +// +// // load vertex positions +// { +// fastgltf::Accessor& posAccessor = +// gltf.accessors[p.findAttribute("POSITION")->second]; +// vertices.resize(vertices.size() + posAccessor.count); +// +// fastgltf::iterateAccessorWithIndex( +// gltf, posAccessor, [&](glm::vec3 v, size_t index) { +// Vertex newvtx; +// newvtx.position = v; +// newvtx.normal = {1, 0, 0}; +// newvtx.color = glm::vec4{1.f}; +// newvtx.uv_x = 0; +// newvtx.uv_y = 0; +// vertices[initial_vtx + index] = newvtx; +// }); +// } +// +// // load vertex normals +// auto normals = p.findAttribute("NORMAL"); +// if (normals != p.attributes.end()) { +// fastgltf::iterateAccessorWithIndex( +// gltf, gltf.accessors[(*normals).second], +// [&](glm::vec3 v, size_t index) { +// vertices[initial_vtx + index].normal = v; +// }); +// } +// +// // load UVs +// auto uv = p.findAttribute("TEXCOORD_0"); +// if (uv != p.attributes.end()) { +// fastgltf::iterateAccessorWithIndex( +// gltf, gltf.accessors[(*uv).second], +// [&](glm::vec2 v, size_t index) { +// vertices[initial_vtx + index].uv_x = v.x; +// vertices[initial_vtx + index].uv_y = v.y; +// }); +// } +// +// // load vertex colors +// auto colors = p.findAttribute("COLOR_0"); +// if (colors != p.attributes.end()) { +// fastgltf::iterateAccessorWithIndex( +// gltf, gltf.accessors[(*colors).second], +// [&](glm::vec4 v, size_t index) { +// vertices[initial_vtx + index].color = v; +// }); +// } +// newmesh.surfaces.push_back(newSurface); +// } +// +// // display the vertex normals +// constexpr bool OverrideColors = true; +// if (OverrideColors) { +// for (Vertex& vtx : vertices) { +// vtx.color = glm::vec4(vtx.normal, 1.f); +// } +// } +// newmesh.meshBuffers = engine->uploadMesh(indices, vertices); +// +// meshes.emplace_back(std::make_shared(std::move(newmesh))); +// } +// +// return meshes; +// } + +// см. ModelImpl.cpp +// VkFilter extract_filter(fastgltf::Filter filter) { +// switch (filter) { +// // nearest samplers +// case fastgltf::Filter::Nearest: +// case fastgltf::Filter::NearestMipMapNearest: +// case fastgltf::Filter::NearestMipMapLinear: +// return VK_FILTER_NEAREST; +// +// // linear samplers +// case fastgltf::Filter::Linear: +// case fastgltf::Filter::LinearMipMapNearest: +// case fastgltf::Filter::LinearMipMapLinear: +// default: +// return VK_FILTER_LINEAR; +// } +// } + +// VkSamplerMipmapMode extract_mipmap_mode(fastgltf::Filter filter) { +// switch (filter) { +// case fastgltf::Filter::NearestMipMapNearest: +// case fastgltf::Filter::LinearMipMapNearest: +// return VK_SAMPLER_MIPMAP_MODE_NEAREST; +// +// case fastgltf::Filter::NearestMipMapLinear: +// case fastgltf::Filter::LinearMipMapLinear: +// default: +// return VK_SAMPLER_MIPMAP_MODE_LINEAR; +// } +// } + +// см. ModelImpl.cpp +// std::optional> loadGltf(VulkanEngine* engine, +// std::string_view filePath) { +// fmt::print("Loading GLTF: {}", filePath); +// +// std::shared_ptr scene = std::make_shared(); +// scene->creator = engine; +// LoadedGLTF& file = *scene.get(); +// +// fastgltf::Parser parser{}; +// +// constexpr auto gltfOptions = +// fastgltf::Options::DontRequireValidAssetMember | +// fastgltf::Options::AllowDouble | fastgltf::Options::LoadGLBBuffers | +// fastgltf::Options::LoadExternalBuffers; +// // fastgltf::Options::LoadExternalImages; +// +// fastgltf::GltfDataBuffer data; +// data.loadFromFile(filePath); +// +// fastgltf::Asset gltf; +// +// std::filesystem::path path = filePath; +// +// auto type = fastgltf::determineGltfFileType(&data); +// if (type == fastgltf::GltfType::glTF) { +// auto load = parser.loadGltf(&data, path.parent_path(), gltfOptions); +// if (load) { +// gltf = std::move(load.get()); +// } else { +// std::cerr << "Failed to load glTF: " +// << fastgltf::to_underlying(load.error()) << std::endl; +// return {}; +// } +// } else if (type == fastgltf::GltfType::GLB) { +// auto load = +// parser.loadGltfBinary(&data, path.parent_path(), gltfOptions); +// if (load) { +// gltf = std::move(load.get()); +// } else { +// std::cerr << "Failed to load glTF: " +// << fastgltf::to_underlying(load.error()) << std::endl; +// return {}; +// } +// } else { +// std::cerr << "Failed to determine glTF container" << std::endl; +// return {}; +// } +// +// // we can stimate the descriptors we will need accurately +// std::vector sizes = { +// {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3}, +// {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3}, +// {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}}; +// +// file.descriptorPool.init(engine->_device, gltf.materials.size(), sizes); +// +// // load samplers +// for (fastgltf::Sampler& sampler : gltf.samplers) { +// VkSamplerCreateInfo sampl = { +// .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, +// .pNext = nullptr}; +// sampl.maxLod = VK_LOD_CLAMP_NONE; +// sampl.minLod = 0; +// +// sampl.magFilter = extract_filter( +// sampler.magFilter.value_or(fastgltf::Filter::Nearest)); +// sampl.minFilter = extract_filter( +// sampler.minFilter.value_or(fastgltf::Filter::Nearest)); +// +// sampl.mipmapMode = extract_mipmap_mode( +// sampler.minFilter.value_or(fastgltf::Filter::Nearest)); +// +// VkSampler newSampler; +// vkCreateSampler(engine->_device, &sampl, nullptr, &newSampler); +// +// file.samplers.push_back(newSampler); +// } +// +// std::vector> meshes; +// std::vector> nodes; +// std::vector images; +// std::vector> materials; +// +// // load all textures +// for (fastgltf::Image& image : gltf.images) { +// images.push_back(engine->_errorCheckerboardImage); +// } +// +// // create buffer to hold the material data +// file.materialDataBuffer = engine->create_buffer( +// sizeof(GLTFMetallic_Roughness::MaterialConstants) * +// gltf.materials.size(), +// VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); +// int data_index = 0; +// GLTFMetallic_Roughness::MaterialConstants* sceneMaterialConstants = +// (GLTFMetallic_Roughness::MaterialConstants*) +// file.materialDataBuffer.info.pMappedData; +// +// for (fastgltf::Material& mat : gltf.materials) { +// std::shared_ptr newMat = std::make_shared(); +// materials.push_back(newMat); +// file.materials[mat.name.c_str()] = newMat; +// +// GLTFMetallic_Roughness::MaterialConstants constants; +// constants.colorFactors.x = mat.pbrData.baseColorFactor[0]; +// constants.colorFactors.y = mat.pbrData.baseColorFactor[1]; +// constants.colorFactors.z = mat.pbrData.baseColorFactor[2]; +// constants.colorFactors.w = mat.pbrData.baseColorFactor[3]; +// +// constants.metal_rough_factors.x = mat.pbrData.metallicFactor; +// constants.metal_rough_factors.y = mat.pbrData.roughnessFactor; +// // write material parameters to buffer +// sceneMaterialConstants[data_index] = constants; +// +// MaterialPass passType = MaterialPass::MainColor; +// if (mat.alphaMode == fastgltf::AlphaMode::Blend) { +// passType = MaterialPass::Transparent; +// } +// +// GLTFMetallic_Roughness::MaterialResources materialResources; +// // default the material textures +// materialResources.colorImage = engine->_whiteImage; +// materialResources.colorSampler = engine->_defaultSamplerLinear; +// materialResources.metalRoughImage = engine->_whiteImage; +// materialResources.metalRoughSampler = engine->_defaultSamplerLinear; +// +// // set the uniform buffer for the material data +// materialResources.dataBuffer = file.materialDataBuffer.buffer; +// materialResources.dataBufferOffset = +// data_index * sizeof(GLTFMetallic_Roughness::MaterialConstants); +// // grab textures from gltf file +// if (mat.pbrData.baseColorTexture.has_value()) { +// size_t img = gltf.textures[mat.pbrData.baseColorTexture.value() +// .textureIndex] +// .imageIndex.value(); +// size_t sampler = gltf.textures[mat.pbrData.baseColorTexture.value() +// .textureIndex] +// .samplerIndex.value(); +// +// materialResources.colorImage = images[img]; +// materialResources.colorSampler = file.samplers[sampler]; +// } +// // build material +// newMat->data = engine->metalRoughMaterial.write_material( +// engine->_device, passType, materialResources, +// file.descriptorPool); +// +// data_index++; +// } +// +// // use the same vectors for all meshes so that the memory doesnt reallocate +// // as +// // often +// std::vector indices; +// std::vector vertices; +// +// for (fastgltf::Mesh& mesh : gltf.meshes) { +// std::shared_ptr newmesh = std::make_shared(); +// meshes.push_back(newmesh); +// file.meshes[mesh.name.c_str()] = newmesh; +// newmesh->name = mesh.name; +// +// // clear the mesh arrays each mesh, we dont want to merge them by error +// indices.clear(); +// vertices.clear(); +// +// for (auto&& p : mesh.primitives) { +// GeoSurface newSurface; +// newSurface.startIndex = (uint32_t)indices.size(); +// newSurface.count = +// (uint32_t)gltf.accessors[p.indicesAccessor.value()].count; +// +// size_t initial_vtx = vertices.size(); +// +// // load indexes +// { +// fastgltf::Accessor& indexaccessor = +// gltf.accessors[p.indicesAccessor.value()]; +// indices.reserve(indices.size() + indexaccessor.count); +// +// fastgltf::iterateAccessor( +// gltf, indexaccessor, [&](std::uint32_t idx) { +// indices.push_back(idx + initial_vtx); +// }); +// } +// +// // load vertex positions +// { +// fastgltf::Accessor& posAccessor = +// gltf.accessors[p.findAttribute("POSITION")->second]; +// vertices.resize(vertices.size() + posAccessor.count); +// +// fastgltf::iterateAccessorWithIndex( +// gltf, posAccessor, [&](glm::vec3 v, size_t index) { +// Vertex newvtx; +// newvtx.position = v; +// newvtx.normal = {1, 0, 0}; +// newvtx.color = glm::vec4{1.f}; +// newvtx.uv_x = 0; +// newvtx.uv_y = 0; +// vertices[initial_vtx + index] = newvtx; +// }); +// } +// +// // load vertex normals +// auto normals = p.findAttribute("NORMAL"); +// if (normals != p.attributes.end()) { +// fastgltf::iterateAccessorWithIndex( +// gltf, gltf.accessors[(*normals).second], +// [&](glm::vec3 v, size_t index) { +// vertices[initial_vtx + index].normal = v; +// }); +// } +// +// // load UVs +// auto uv = p.findAttribute("TEXCOORD_0"); +// if (uv != p.attributes.end()) { +// fastgltf::iterateAccessorWithIndex( +// gltf, gltf.accessors[(*uv).second], +// [&](glm::vec2 v, size_t index) { +// vertices[initial_vtx + index].uv_x = v.x; +// vertices[initial_vtx + index].uv_y = v.y; +// }); +// } +// +// // load vertex colors +// auto colors = p.findAttribute("COLOR_0"); +// if (colors != p.attributes.end()) { +// fastgltf::iterateAccessorWithIndex( +// gltf, gltf.accessors[(*colors).second], +// [&](glm::vec4 v, size_t index) { +// vertices[initial_vtx + index].color = v; +// }); +// } +// +// if (p.materialIndex.has_value()) { +// newSurface.material = materials[p.materialIndex.value()]; +// } else { +// newSurface.material = materials[0]; +// } +// +// newmesh->surfaces.push_back(newSurface); +// } +// +// newmesh->meshBuffers = engine->uploadMesh(indices, vertices); +// } +// +// // load all nodes and their meshes +// for (fastgltf::Node& node : gltf.nodes) { +// std::shared_ptr newNode; +// +// // find if the node has a mesh, and if it does hook it to the mesh +// // pointer and allocate it with the meshnode class +// if (node.meshIndex.has_value()) { +// newNode = std::make_shared(); +// static_cast(newNode.get())->mesh = +// meshes[*node.meshIndex]; +// } else { +// newNode = std::make_shared(); +// } +// +// nodes.push_back(newNode); +// file.nodes[node.name.c_str()]; +// +// std::visit( +// fastgltf::visitor{ +// [&](fastgltf::Node::TransformMatrix matrix) { +// memcpy(&newNode->localTransform, matrix.data(), +// sizeof(matrix)); +// }, +// [&](fastgltf::TRS transform) { +// glm::vec3 tl(transform.translation[0], +// transform.translation[1], +// transform.translation[2]); +// glm::quat rot(transform.rotation[3], +// transform.rotation[0], +// transform.rotation[1], +// transform.rotation[2]); +// glm::vec3 sc(transform.scale[0], transform.scale[1], +// transform.scale[2]); +// +// glm::mat4 tm = glm::translate(glm::mat4(1.f), tl); +// glm::mat4 rm = glm::toMat4(rot); +// glm::mat4 sm = glm::scale(glm::mat4(1.f), sc); +// +// newNode->localTransform = tm * rm * sm; +// }}, +// node.transform); +// } +// +// // run loop again to setup transform hierarchy +// for (int i = 0; i < gltf.nodes.size(); i++) { +// fastgltf::Node& node = gltf.nodes[i]; +// std::shared_ptr& sceneNode = nodes[i]; +// +// for (auto& c : node.children) { +// sceneNode->children.push_back(nodes[c]); +// nodes[c]->parent = sceneNode; +// } +// } +// +// // find the top nodes, with no parents +// for (auto& node : nodes) { +// if (node->parent.lock() == nullptr) { +// file.topNodes.push_back(node); +// node->refreshTransform(glm::mat4{1.f}); +// } +// } +// return scene; +// } + +// RenderableGLTF::RenderableGLTF(std::shared_ptr gltf) : _gltf{std::move(gltf)} {} + +RenderableGLTF::RenderableGLTF(LoadedGltfPtr gltf) : _gltf(std::move(gltf)) {} + +void RenderableGLTF::Draw(const glm::mat4& topMatrix, DrawContext& ctx) { // create renderables from the scenenodes - for (const auto& n : topNodes) { + for (auto& n : _gltf->topNodes) { n->Draw(topMatrix, ctx); } } -void LoadedGLTF::clearAll() {} +void RenderableGLTF::clearAll() {} diff --git a/src/include/core/Controller.h b/src/include/core/Controller.h index ed2e98c9..4c439d2b 100644 --- a/src/include/core/Controller.h +++ b/src/include/core/Controller.h @@ -2,5 +2,6 @@ #include "IController.h" #include "interfaces/IModel.h" +#include "interfaces/IView.h" -IController::Ptr createController(IModel::Ptr ptr); +IController::Ptr createController(IModel::Ptr model, IView::Ptr view); diff --git a/src/include/core/ControllerImpl.h b/src/include/core/ControllerImpl.h index f7025dd8..29b97f56 100644 --- a/src/include/core/ControllerImpl.h +++ b/src/include/core/ControllerImpl.h @@ -5,16 +5,17 @@ #include "IController.h" #include "interfaces/IModel.h" +#include "interfaces/IView.h" -class ControllerImpl : public IController, - public std::enable_shared_from_this { +class ControllerImpl : public IController { public: - explicit ControllerImpl(IModel::Ptr model); + explicit ControllerImpl(IModel::Ptr model, IView::Ptr view); - void init() const override; - void update() const override; - void processEvent(SDL_Event& e) const override; + void init() override; + MeshController& getMeshController() final; private: - std::shared_ptr _model; + IModel::Ptr _model; + IView::Ptr _view; + MeshController _mesh_controller; }; diff --git a/src/include/core/Mesh.h b/src/include/core/Mesh.h deleted file mode 100644 index a8e22857..00000000 --- a/src/include/core/Mesh.h +++ /dev/null @@ -1,25 +0,0 @@ -// Mesh.h -#pragma once - -#include - -#include "glm/detail/type_mat4x4.hpp" -#include "graphics/vulkan/vk_engine.h" - -class Mesh { -public: - Mesh(const std::string& filePath); - ~Mesh(); - - // Add model change functionality - void set_model(const std::string& filePath); - void remove_model(); - - void set_transform(glm::mat4 t); - glm::mat4 get_transform(); - -private: - glm::mat4 _transform; - std::string _currentModelPath; // Track current model path - int64_t _rid; -}; \ No newline at end of file diff --git a/src/include/core/MeshController.h b/src/include/core/MeshController.h new file mode 100644 index 00000000..b4684f43 --- /dev/null +++ b/src/include/core/MeshController.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +#include "scene/Mesh.h" +#include "interfaces/IModel.h" + +class MeshController { +public: + MeshController() = delete; + explicit MeshController(IModel::Ptr model); + ~MeshController() = default; + + void create_mesh(std::string_view file_path) const; + void delete_mesh(Mesh::rid_t id) const; + + void set_transform(Mesh::rid_t id, glm::mat4 t) const; + [[nodiscard]] glm::mat4 get_transform(Mesh::rid_t id) const; +private: + IModel::Ptr _model; +}; \ No newline at end of file diff --git a/src/include/core/ModelImpl.h b/src/include/core/ModelImpl.h index 2f2b6dc9..42e6308a 100644 --- a/src/include/core/ModelImpl.h +++ b/src/include/core/ModelImpl.h @@ -8,11 +8,15 @@ #include "graphics/vulkan/vk_engine.h" #include "interfaces/IModel.h" #include "scene/Camera.h" +#include "scene/Mesh.h" class Mesh; class ModelImpl : public IModel { public: + + + using MeshMap = std::unordered_map; /*! \brief * throws std::runtime_error() */ @@ -23,15 +27,25 @@ class ModelImpl : public IModel { ModelImpl &operator=(const ModelImpl &) = delete; void registerWindow(struct SDL_Window *window) override; - void updateVulkan() override; - void createMesh(std::string name) override; - void setMeshTransform(std::string name, glm::mat4x4 transform) override; + // Mesh::rid_t registerMesh(std::string_view file_path); + void createMesh(VulkanEngine& engine, std::string_view file_path) final; + void delete_mesh(Mesh::rid_t rid) final; + + void setMeshTransform(Mesh::rid_t rid, glm::mat4x4 transform) final; + glm::mat4 get_mesh_transform(Mesh::rid_t) final; + + const MeshMap& get_meshes() final; Camera *getCamera() override; + VulkanEngine& get_engine() final; + private: - std::unordered_map> _meshes; + // std::unordered_map> _meshes; + + // TODO: move meshes and transforms to one map + MeshMap _meshes; VulkanEngine _engine; diff --git a/src/include/core/View.h b/src/include/core/View.h index 73e9012c..e2d07a9b 100644 --- a/src/include/core/View.h +++ b/src/include/core/View.h @@ -4,4 +4,4 @@ #include "interfaces/IModel.h" #include "interfaces/IView.h" -IView::Ptr createView(IController::Ptr controller, IModel::Ptr model); \ No newline at end of file +IView::Ptr createView(IModel::Ptr model); \ No newline at end of file diff --git a/src/include/core/ViewImpl.h b/src/include/core/ViewImpl.h index 1f599ab6..4d8fa8de 100644 --- a/src/include/core/ViewImpl.h +++ b/src/include/core/ViewImpl.h @@ -6,13 +6,14 @@ class ViewImpl : public IView { public: - ViewImpl(IController::Ptr controller, IModel::Ptr model); + explicit ViewImpl(IModel::Ptr model); ~ViewImpl() override; void run() const override; + void process_event(const SDL_Event& e) final; private: - IController::Ptr _controller; + // IController::Ptr _controller; IModel::Ptr _model; struct SDL_Window* window{nullptr}; diff --git a/src/include/graphics/vulkan/RenderableGLTF.h b/src/include/graphics/vulkan/RenderableGLTF.h new file mode 100644 index 00000000..d5b30dd6 --- /dev/null +++ b/src/include/graphics/vulkan/RenderableGLTF.h @@ -0,0 +1,7 @@ +#pragma once + +#include "graphics/vulkan/vk_types.h" +#include "graphics/vulkan/vk_loader.h" +#include "scene/Mesh.h" + +IRenderable::Ptr createRenderableGLTF(RenderableGLTF::LoadedGltfPtr ptr); \ No newline at end of file diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 40db0c65..92e535db 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -1,34 +1,77 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include "interfaces/IModel.h" +#include "scene/Mesh.h" +#include "scene/Camera.h" #include "vk_descriptors.h" +#include "vk_loader.h" +#include "vk_pipelines.h" #include "vk_types.h" -#include "vk_smart_wrappers.h" -#include "pipelines.h" -#include "ComputePipeline.h" +constexpr unsigned int FRAME_OVERLAP = 2; -#include "vk_command_buffers.h" -#include "vk_command_buffers_container.h" +struct GLTFMetallic_Roughness { + MaterialPipeline opaquePipeline; + MaterialPipeline transparentPipeline; -class Camera; -class VulkanEngine; -struct DrawContext; -struct LoadedGLTF; -struct MeshAsset; + VkDescriptorSetLayout materialLayout; + struct MaterialConstants { + glm::vec4 colorFactors; + glm::vec4 metal_rough_factors; + // padding, we need it anyway for uniform buffers + glm::vec4 extra[14]; + }; + + struct MaterialResources { + AllocatedImage colorImage; + VkSampler colorSampler; + AllocatedImage metalRoughImage; + VkSampler metalRoughSampler; + VkBuffer dataBuffer; + uint32_t dataBufferOffset; + }; + + DescriptorWriter writer; + + void build_pipelines(VulkanEngine* engine); + void clear_resources(VkDevice device); + + MaterialInstance write_material( + VkDevice device, MaterialPass pass, + const MaterialResources& resources, + DescriptorAllocatorGrowable& descriptorAllocator); +}; + +struct DeletionQueue { + std::deque> deletors; + + void push_function(std::function&& function) { + deletors.push_back(function); + } + + void flush() { + // reverse iterate the deletion queue to execute all the functions + for (auto it = deletors.rbegin(); it != deletors.rend(); it++) { + (*it)(); // call functors + } + + deletors.clear(); + } +}; + +struct FrameData { + VkCommandPool _commandPool; + VkCommandBuffer _mainCommandBuffer; + + VkSemaphore _swapchainSemaphore, _renderSemaphore; + VkFence _renderFence; + + DeletionQueue _deletionQueue; + DescriptorAllocatorGrowable _frameDescriptors; +}; struct GPUSceneData { glm::mat4 view; @@ -40,11 +83,9 @@ struct GPUSceneData { }; struct MeshNode : public ENode { - MeshNode() = default; - - std::shared_ptr mesh; + std::shared_ptr mesh; - void Draw(const glm::mat4& topMatrix, DrawContext& ctx) override; + virtual void Draw(const glm::mat4& topMatrix, DrawContext& ctx) override; }; struct RenderObject { @@ -64,40 +105,35 @@ struct DrawContext { class VulkanEngine { public: + // они теперь в модели или контроллере + // Mesh::rid_t registerMesh(std::string_view filePath); + // void unregisterMesh(int64_t id); - Pipelines pipelines; + // void setMeshTransform(int64_t id, glm::mat4 mat); + // std::unordered_map> meshes; + // + // std::unordered_map transforms; - CommandBuffers command_buffers; - CommandBuffersContainer command_buffers_container; - - int64_t registerMesh(const std::string& filePath); - - void unregisterMesh(int64_t id); - - void setMeshTransform(int64_t id, glm::mat4 mat); - - std::unordered_map> meshes; - - std::unordered_map transforms; - - std::unordered_map> loadedScenes; + std::unordered_map> loadedScenes; Camera* mainCamera; DrawContext mainDrawContext; std::unordered_map> loadedNodes; - void update_scene(); + void update_scene(const IModel::Ptr& model); + + FrameData _frames[FRAME_OVERLAP]; FrameData& get_current_frame() { - return command_buffers_container.get_current_frame(_frameNumber); + return _frames[_frameNumber % FRAME_OVERLAP]; }; VkQueue _graphicsQueue; uint32_t _graphicsQueueFamily; bool _isInitialized{false}; - unsigned int _frameNumber{0}; + int _frameNumber{0}; bool stop_rendering{false}; VkExtent2D _windowExtent{2560, 1440}; @@ -112,10 +148,10 @@ class VulkanEngine { void cleanup(); // draw loop - void draw(); + void draw(IModel::Ptr model); // run main loop - void update(); + void update(IModel::Ptr model); VkInstance _instance; // Vulkan library handle VkDebugUtilsMessengerEXT _debug_messenger; // Vulkan debug output handle @@ -130,10 +166,12 @@ class VulkanEngine { std::vector _swapchainImageViews; VkExtent2D _swapchainExtent; + DeletionQueue _mainDeletionQueue; + VmaAllocator _allocator; - std::unique_ptr _drawImage; - std::unique_ptr _depthImage; + AllocatedImage _drawImage; + AllocatedImage _depthImage; VkExtent2D _drawExtent; float renderScale = 1.f; @@ -142,13 +180,27 @@ class VulkanEngine { VkDescriptorSet _drawImageDescriptors; VkDescriptorSetLayout _drawImageDescriptorLayout; + VkPipeline _gradientPipeline; + VkPipelineLayout _gradientPipelineLayout; + + // immediate submit structures + VkFence _immFence; + VkCommandBuffer _immCommandBuffer; + VkCommandPool _immCommandPool; + + VkPipelineLayout _trianglePipelineLayout; + VkPipeline _trianglePipeline; + + VkPipelineLayout _meshPipelineLayout; + VkPipeline _meshPipeline; GPUMeshBuffers rectangle; + void immediate_submit(std::function&& function); GPUMeshBuffers uploadMesh(std::span indices, std::span vertices); - std::vector> testMeshes; + // std::vector> testMeshes; bool resize_requested; @@ -159,15 +211,15 @@ class VulkanEngine { AllocatedImage create_image(VkExtent3D size, VkFormat format, VkImageUsageFlags usage, bool mipmapped = false) const; - AllocatedImage create_image(const void* data, VkExtent3D size, - VkFormat format, VkImageUsageFlags usage, - bool mipmapped = false) const; + AllocatedImage create_image(void* data, VkExtent3D size, VkFormat format, + VkImageUsageFlags usage, + bool mipmapped = false); void destroy_image(const AllocatedImage& img) const; - std::unique_ptr _whiteImage; - std::unique_ptr _blackImage; - std::unique_ptr _greyImage; - std::unique_ptr _errorCheckerboardImage; + AllocatedImage _whiteImage; + AllocatedImage _blackImage; + AllocatedImage _greyImage; + AllocatedImage _errorCheckerboardImage; VkSampler _defaultSamplerLinear; VkSampler _defaultSamplerNearest; @@ -181,10 +233,6 @@ class VulkanEngine { VmaMemoryUsage memoryUsage) const; private: - // Smart pointer collections for automatic cleanup - std::vector> _managedBuffers; - std::vector> _managedImages; - static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, @@ -193,17 +241,22 @@ class VulkanEngine { void init_vulkan(); void init_swapchain(); + void init_commands(); + void init_sync_structures(); void create_swapchain(uint32_t width, uint32_t height); void destroy_swapchain(); - void draw_background(VkCommandBuffer cmd) const; + void draw_background(VkCommandBuffer cmd); void init_descriptors(); void init_pipelines(); + void init_background_pipelines(); void init_imgui(); + void init_triangle_pipeline(); + void draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView) const; void draw_geometry(VkCommandBuffer cmd); @@ -215,4 +268,4 @@ class VulkanEngine { void init_mesh_pipeline(); void init_default_data(); -}; +}; \ No newline at end of file diff --git a/src/include/graphics/vulkan/vk_loader.h b/src/include/graphics/vulkan/vk_loader.h index 6df2ba91..f198b7c3 100644 --- a/src/include/graphics/vulkan/vk_loader.h +++ b/src/include/graphics/vulkan/vk_loader.h @@ -1,72 +1,66 @@ #pragma once -#include #include -#include -#include -#include -#include -#include #include -#include -#include #include "vk_descriptors.h" #include "vk_types.h" - -class VulkanEngine; -struct DrawContext; - -struct GLTFMaterial { - MaterialInstance data; -}; - -struct GeoSurface { - uint32_t startIndex; - uint32_t count; - std::shared_ptr material; -}; - -struct MeshAsset { - std::string name; - - std::vector surfaces; - GPUMeshBuffers meshBuffers; -}; - -std::optional>> loadGltfMeshes( - VulkanEngine* engine, const std::filesystem::path& filePath); - -struct LoadedGLTF final : public IRenderable { - LoadedGLTF() = default; - - // storage for all the data on a given glTF file - std::unordered_map> meshes; - std::unordered_map> nodes; - std::unordered_map images; - std::unordered_map> materials; - - // nodes that dont have a parent, for iterating through the file in tree - // order - std::vector> topNodes; - - std::vector samplers; - - DescriptorAllocatorGrowable descriptorPool; - - AllocatedBuffer materialDataBuffer; - - VulkanEngine* creator; - - ~LoadedGLTF() { +#include "scene/Mesh.h" + +// struct GLTFMaterial { +// MaterialInstance data; +// }; +// +// struct GeoSurface { +// uint32_t startIndex; +// uint32_t count; +// std::shared_ptr material; +// }; +// +// struct MeshAsset { +// std::string name; +// +// std::vector surfaces; +// GPUMeshBuffers meshBuffers; +// }; + +// forward declaration +// class VulkanEngine; + +// в vk_engine.cpp +// std::optional>> loadGltfMeshes( +// VulkanEngine* engine, std::filesystem::path filePath); + +class RenderableGLTF : public IRenderable { +public: + // // storage for all the data on a given glTF file + // std::unordered_map> meshes; + // std::unordered_map> nodes; + // std::unordered_map images; + // std::unordered_map> materials; + // + // // nodes that dont have a parent, for iterating through the file in tree + // // order + // std::vector> topNodes; + // + // std::vector samplers; + // + // DescriptorAllocatorGrowable descriptorPool; + // + // AllocatedBuffer materialDataBuffer; + // + // VulkanEngine* creator; + using LoadedGltfPtr = std::shared_ptr; + explicit RenderableGLTF(LoadedGltfPtr gltf); + virtual ~RenderableGLTF() { clearAll(); }; - void Draw(const glm::mat4& topMatrix, DrawContext& ctx); + void Draw(const glm::mat4& topMatrix, DrawContext& ctx) final; private: void clearAll(); + std::shared_ptr _gltf; }; -std::optional> loadGltf(VulkanEngine* engine, - std::string_view filePath); + diff --git a/src/include/graphics/vulkan/vk_types.h b/src/include/graphics/vulkan/vk_types.h index 172f637e..9ed95d32 100644 --- a/src/include/graphics/vulkan/vk_types.h +++ b/src/include/graphics/vulkan/vk_types.h @@ -76,8 +76,11 @@ struct MaterialInstance { struct DrawContext; // base class for a renderable dynamic object -class IRenderable { +struct IRenderable { + virtual ~IRenderable() = default; virtual void Draw(const glm::mat4& topMatrix, DrawContext& ctx) = 0; + + using Ptr = std::shared_ptr; }; // implementation of a drawable scene node. diff --git a/src/include/interfaces/IModel.h b/src/include/interfaces/IModel.h index bdfc421c..a48414d3 100644 --- a/src/include/interfaces/IModel.h +++ b/src/include/interfaces/IModel.h @@ -9,6 +9,7 @@ #include "SDL2/SDL.h" #include "SDL2/SDL_vulkan.h" #include "scene/Camera.h" +#include "scene/Mesh.h" /*! * \brief Interface for managing models and their integration with Vulkan. @@ -39,34 +40,38 @@ class IModel { */ virtual void registerWindow(struct SDL_Window* window) = 0; - /*! - * \brief Updates Vulkan-related states. - * - * This method updates internal Vulkan-related states or data structures. - * Should be called regularly to keep Vulkan rendering in sync with the - * application state. - */ - virtual void updateVulkan() = 0; - /*! * \brief Creates a new mesh with the given name. * - * \param name Name of the mesh to be created. + * \param engine + * \param file_path * * This method creates a new mesh identified by the provided name. */ - virtual void createMesh(std::string name) = 0; + virtual void createMesh(VulkanEngine& engine, std::string_view file_path) = 0; /*! * \brief Sets the transformation matrix for a mesh. * - * \param name Name of the mesh. + * \param rid * \param transform Transformation matrix to be applied to the mesh. * * This method sets the transformation matrix for the mesh identified by the * provided name. */ - virtual void setMeshTransform(std::string name, glm::mat4x4 transform) = 0; + + virtual void delete_mesh(Mesh::rid_t rid) = 0; + + virtual void setMeshTransform(Mesh::rid_t rid, glm::mat4x4 transform) = 0; + + virtual glm::mat4 get_mesh_transform(Mesh::rid_t) = 0; + + struct MeshPair { + std::shared_ptr ptr; + glm::mat4 transform; + }; + using MeshMap = std::unordered_map; + virtual const MeshMap& get_meshes() = 0; /*! * \brief Retrieves the camera instance. @@ -78,6 +83,8 @@ class IModel { */ [[nodiscard]] virtual Camera* getCamera() = 0; + virtual VulkanEngine& get_engine() = 0; + /*! \brief * Gets the chip handler from HIDAPI required to change the settings by the * Controller diff --git a/src/include/interfaces/IView.h b/src/include/interfaces/IView.h index acde9d61..36aec8a4 100644 --- a/src/include/interfaces/IView.h +++ b/src/include/interfaces/IView.h @@ -10,6 +10,7 @@ class IView { virtual ~IView() = default; virtual void run() const = 0; + virtual void process_event(const SDL_Event& e) = 0; /*! \brief * Prints in console current settings @@ -22,5 +23,6 @@ class IView { */ // virtual void runMenu() const = 0; - using Ptr = std::unique_ptr; + using Ptr = std::shared_ptr; }; + diff --git a/src/include/scene/LoaderGLTF.h b/src/include/scene/LoaderGLTF.h new file mode 100644 index 00000000..63d62951 --- /dev/null +++ b/src/include/scene/LoaderGLTF.h @@ -0,0 +1,8 @@ +#pragma once + +#include "scene/Mesh.h" + +struct LoaderGLTF { + static std::optional> loadGLTF( + VulkanEngine& engine, std::string_view filePath); +}; diff --git a/src/include/scene/Mesh.h b/src/include/scene/Mesh.h new file mode 100644 index 00000000..ad08fd5d --- /dev/null +++ b/src/include/scene/Mesh.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +#include "graphics/vulkan/vk_descriptors.h" + +class VulkanEngine; + +namespace Mesh { +// Mesh(std::string filePath); +// ~Mesh(); +// +// void set_transform(glm::mat4 t); +// glm::mat4 get_transform(glm::mat4 t); +using rid_t = int64_t; + +// ид меша +// матрица трансформации +// glm::mat4 transform; + +namespace GLTF { +struct GLTFMaterial { + MaterialInstance data; +}; + +struct GeoSurface { + uint32_t startIndex; + uint32_t count; + std::shared_ptr material; +}; + +struct MeshAsset { + std::string name; + + std::vector surfaces; + GPUMeshBuffers meshBuffers; +}; + +struct LoadedGLTF { + // storage for all the data on a given glTF file + std::unordered_map> meshes; + std::unordered_map> nodes; + std::unordered_map images; + std::unordered_map> materials; + + // nodes that dont have a parent, for iterating through the file in tree + // order + std::vector> topNodes; + + std::vector samplers; + + DescriptorAllocatorGrowable descriptorPool; + + AllocatedBuffer materialDataBuffer; + + VulkanEngine* creator; +}; +} +}; \ No newline at end of file diff --git a/src/scene/CMakeLists.txt b/src/scene/CMakeLists.txt index ebc590df..aceeb696 100644 --- a/src/scene/CMakeLists.txt +++ b/src/scene/CMakeLists.txt @@ -5,4 +5,5 @@ target_sources(${PROJECT_NAME} Node.cpp ParentSystem.cpp TransformSystem.cpp + LoaderGLTF.cpp ) diff --git a/src/scene/LoaderGLTF.cpp b/src/scene/LoaderGLTF.cpp new file mode 100644 index 00000000..b6815c05 --- /dev/null +++ b/src/scene/LoaderGLTF.cpp @@ -0,0 +1,458 @@ +#include "graphics/vulkan/vk_engine.h" +#include "scene/LoaderGLTF.h" + +#define GLM_ENABLE_EXPERIMENTAL + +#include +#include +#include +#include +#include + +VkFilter extract_filter(fastgltf::Filter filter) { + switch (filter) { + // nearest samplers + case fastgltf::Filter::Nearest: + case fastgltf::Filter::NearestMipMapNearest: + case fastgltf::Filter::NearestMipMapLinear: + return VK_FILTER_NEAREST; + + // linear samplers + case fastgltf::Filter::Linear: + case fastgltf::Filter::LinearMipMapNearest: + case fastgltf::Filter::LinearMipMapLinear: + default: + return VK_FILTER_LINEAR; + } +} + +VkSamplerMipmapMode extract_mipmap_mode(fastgltf::Filter filter) { + switch (filter) { + case fastgltf::Filter::NearestMipMapNearest: + case fastgltf::Filter::LinearMipMapNearest: + return VK_SAMPLER_MIPMAP_MODE_NEAREST; + + case fastgltf::Filter::NearestMipMapLinear: + case fastgltf::Filter::LinearMipMapLinear: + default: + return VK_SAMPLER_MIPMAP_MODE_LINEAR; + } +} + +constexpr auto kOptionsGLTF = fastgltf::Options::DontRequireValidAssetMember | + fastgltf::Options::AllowDouble | + fastgltf::Options::LoadGLBBuffers | + fastgltf::Options::LoadExternalBuffers; + +namespace { +bool check_parser_result(fastgltf::Asset& gltf, + fastgltf::Expected& load) { + if (load) { + gltf = std::move(load.get()); + } else { + std::cerr << "Failed to load glTF: " + << fastgltf::to_underlying(load.error()) << std::endl; + return false; + } + return true; +} + +bool load_file(fastgltf::Asset& gltf, std::string_view filePath) { + fastgltf::Parser parser{}; + + fastgltf::GltfDataBuffer data; + data.loadFromFile(filePath); + + std::filesystem::path path = filePath; + + auto type = fastgltf::determineGltfFileType(&data); + if (type == fastgltf::GltfType::glTF) { + auto load = parser.loadGltf(&data, path.parent_path(), kOptionsGLTF); + if (!check_parser_result(gltf, load)) { + return false; + } + } else if (type == fastgltf::GltfType::GLB) { + auto load = + parser.loadGltfBinary(&data, path.parent_path(), kOptionsGLTF); + if (!check_parser_result(gltf, load)) { + return false; + } + } else { + std::cerr << "Failed to determine glTF container" << std::endl; + return false; + } + return true; +} + +void init_descriptor_pool(const VulkanEngine& engine, + Mesh::GLTF::LoadedGLTF& file, + const fastgltf::Asset& gltf) { + // we can stimate the descriptors we will need accurately + std::vector sizes = { + {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3}, + {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3}, + {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}}; + + file.descriptorPool.init(engine._device, gltf.materials.size(), sizes); +} + +void load_samplers(const VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, + fastgltf::Asset& gltf) { + for (auto& [magFilter, minFilter, wrapS, wrapT, name] : gltf.samplers) { + VkSamplerCreateInfo sample = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .pNext = nullptr}; + sample.maxLod = VK_LOD_CLAMP_NONE; + sample.minLod = 0; + + sample.magFilter = + extract_filter(magFilter.value_or(fastgltf::Filter::Nearest)); + sample.minFilter = + extract_filter(minFilter.value_or(fastgltf::Filter::Nearest)); + + sample.mipmapMode = extract_mipmap_mode( + minFilter.value_or(fastgltf::Filter::Nearest)); + + VkSampler newSampler; + vkCreateSampler(engine._device, &sample, nullptr, &newSampler); + + file.samplers.push_back(newSampler); + } +} + +void load_all_textures(const VulkanEngine& engine, fastgltf::Asset& gltf, + std::vector& images) { + for ([[maybe_unused]] fastgltf::Image& image : gltf.images) { + images.push_back(engine._errorCheckerboardImage); + } +} + +void create_material_data_buffer(const VulkanEngine& engine, + Mesh::GLTF::LoadedGLTF& file, + const fastgltf::Asset& gltf) { + file.materialDataBuffer = engine.create_buffer( + sizeof(GLTFMetallic_Roughness::MaterialConstants) * + gltf.materials.size(), + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); +} + +namespace loadMaterialData { +void grab_textures_from_GLTF( + const Mesh::GLTF::LoadedGLTF& file, fastgltf::Asset& gltf, + const fastgltf::Material& mat, + GLTFMetallic_Roughness::MaterialResources& materialResources, + const std::vector& images) { + if (mat.pbrData.baseColorTexture.has_value()) { + const size_t img = + gltf.textures[mat.pbrData.baseColorTexture.value().textureIndex] + .imageIndex.value(); + const size_t sampler = + gltf.textures[mat.pbrData.baseColorTexture.value().textureIndex] + .samplerIndex.value(); + + materialResources.colorImage = images[img]; + materialResources.colorSampler = file.samplers[sampler]; + } +} +} // namespace loadMaterialData + +void load_material_data( + VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, + fastgltf::Asset& gltf, + std::vector>& materials, + const std::vector& images) { + auto* sceneMaterialConstants = + static_cast( + file.materialDataBuffer.info.pMappedData); + + size_t data_index{0}; + for (fastgltf::Material& mat : gltf.materials) { + auto newMat = std::make_shared(); + materials.push_back(newMat); + file.materials[mat.name.c_str()] = newMat; + + const GLTFMetallic_Roughness::MaterialConstants constants{ + .colorFactors{ + mat.pbrData.baseColorFactor[0], + mat.pbrData.baseColorFactor[1], + mat.pbrData.baseColorFactor[2], + mat.pbrData.baseColorFactor[3], + }, + .metal_rough_factors{ + mat.pbrData.metallicFactor, + mat.pbrData.roughnessFactor, + {}, + {}, + }, + }; + // write material parameters to buffer + sceneMaterialConstants[data_index] = constants; + + auto passType = MaterialPass::MainColor; + if (mat.alphaMode == fastgltf::AlphaMode::Blend) { + passType = MaterialPass::Transparent; + } + + GLTFMetallic_Roughness::MaterialResources materialResources{ + // default the material textures + .colorImage{engine._whiteImage}, + .colorSampler{engine._defaultSamplerLinear}, + .metalRoughImage{engine._whiteImage}, + .metalRoughSampler{engine._defaultSamplerLinear}, + }; + + // set the uniform buffer for the material data + materialResources.dataBuffer = file.materialDataBuffer.buffer; + materialResources.dataBufferOffset = + data_index * sizeof(GLTFMetallic_Roughness::MaterialConstants); + + loadMaterialData::grab_textures_from_GLTF(file, gltf, mat, + materialResources, images); + // build material + newMat->data = engine.metalRoughMaterial.write_material( + engine._device, passType, materialResources, + file.descriptorPool); + + ++data_index; + } +} + +namespace uploadMeshToEngine { +void load_indexes(const fastgltf::Asset& gltf, std::vector& indices, + fastgltf::Primitive& p, size_t initial_vtx) { + { + const fastgltf::Accessor& indexaccessor = + gltf.accessors[p.indicesAccessor.value()]; + indices.reserve(indices.size() + indexaccessor.count); + + fastgltf::iterateAccessor( + gltf, indexaccessor, [&](std::uint32_t idx) { + indices.push_back(idx + initial_vtx); + }); + } +} + +void load_vertex_positions(const fastgltf::Asset& gltf, + std::vector& vertices, + fastgltf::Primitive& p, size_t initial_vtx) { + const fastgltf::Accessor& posAccessor = + gltf.accessors[p.findAttribute("POSITION")->second]; + vertices.resize(vertices.size() + posAccessor.count); + + fastgltf::iterateAccessorWithIndex( + gltf, posAccessor, [&](const glm::vec3 v, size_t index) { + vertices[initial_vtx + index] = { + .position = v, + .normal = {1, 0, 0}, + .color = glm::vec4{1.}, + .uv_x = 0, + .uv_y = 0, + }; + }); +} + +void load_vertex_normals(const fastgltf::Asset& gltf, + std::vector& vertices, fastgltf::Primitive& p, + size_t initial_vtx) { + auto normals = p.findAttribute("NORMAL"); + if (normals != p.attributes.end()) { + fastgltf::iterateAccessorWithIndex( + gltf, gltf.accessors[normals->second], + [&](const glm::vec3 v, size_t index) { + vertices[initial_vtx + index].normal = v; + }); + } +} + +void load_UVs(const fastgltf::Asset& gltf, std::vector& vertices, + fastgltf::Primitive& p, size_t initial_vtx) { + auto uv = p.findAttribute("TEXCOORD_0"); + if (uv != p.attributes.end()) { + fastgltf::iterateAccessorWithIndex( + gltf, gltf.accessors[uv->second], + [&](glm::vec2 v, size_t index) { + vertices[initial_vtx + index].uv_x = v.x; + vertices[initial_vtx + index].uv_y = v.y; + }); + } +} + +void load_vertex_colors(const fastgltf::Asset& gltf, + std::vector& vertices, fastgltf::Primitive& p, + size_t initial_vtx) { + auto colors = p.findAttribute("COLOR_0"); + if (colors != p.attributes.end()) { + fastgltf::iterateAccessorWithIndex( + gltf, gltf.accessors[colors->second], + [&](const glm::vec4 v, size_t index) { + vertices[initial_vtx + index].color = v; + }); + } +} + +void define_new_surface_material( + Mesh::GLTF::GeoSurface& newSurface, fastgltf::Primitive& p, + const std::vector>& + materials) { + if (p.materialIndex.has_value()) { + newSurface.material = materials[p.materialIndex.value()]; + } else { + newSurface.material = materials[0]; + } +} + +} // namespace uploadMeshToEngine + +void upload_mesh_to_engine( + VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, + fastgltf::Asset& gltf, + std::vector>& meshes, + const std::vector>& + materials) { + // use the same vectors for all meshes so that the memory doesn't reallocate + // as often + std::vector indices; + std::vector vertices; + + for (auto& [primitives, weights, name] : gltf.meshes) { + auto newmesh = std::make_shared(); + meshes.push_back(newmesh); + file.meshes[name.c_str()] = newmesh; + newmesh->name = name; + + // clear the mesh arrays each mesh, we don't want to merge them by error + indices.clear(); + vertices.clear(); + + for (auto&& p : primitives) { + Mesh::GLTF::GeoSurface newSurface; + newSurface.startIndex = static_cast(indices.size()); + newSurface.count = static_cast( + gltf.accessors[p.indicesAccessor.value()].count); + + size_t initial_vtx = vertices.size(); + + uploadMeshToEngine::load_indexes(gltf, indices, p, initial_vtx); + uploadMeshToEngine::load_vertex_positions(gltf, vertices, p, + initial_vtx); + uploadMeshToEngine::load_vertex_normals(gltf, vertices, p, + initial_vtx); + + uploadMeshToEngine::load_UVs(gltf, vertices, p, initial_vtx); + uploadMeshToEngine::load_vertex_colors(gltf, vertices, p, + initial_vtx); + uploadMeshToEngine::define_new_surface_material(newSurface, p, + materials); + newmesh->surfaces.push_back(newSurface); + } + + newmesh->meshBuffers = engine.uploadMesh(indices, vertices); + } +} + +void load_nodes(Mesh::GLTF::LoadedGLTF& file, fastgltf::Asset& gltf, + std::vector>& meshes, + std::vector>& nodes) { + for (fastgltf::Node& node : gltf.nodes) { + std::shared_ptr newNode; + + // find if the node has a mesh, and if it does hook it to the mesh + // pointer and allocate it with the meshnode class + if (node.meshIndex.has_value()) { + newNode = std::make_shared(); + dynamic_cast(newNode.get())->mesh = + meshes[*node.meshIndex]; + } else { + newNode = std::make_shared(); + } + + nodes.push_back(newNode); + file.nodes[node.name.c_str()]; + + std::visit(fastgltf::visitor{ + [&](fastgltf::Node::TransformMatrix matrix) { + memcpy(&newNode->localTransform, matrix.data(), + sizeof(matrix)); + }, + [&](const fastgltf::TRS& transform) { + const glm::vec3 tl(transform.translation[0], + transform.translation[1], + transform.translation[2]); + const glm::quat rot(transform.rotation[3], + transform.rotation[0], + transform.rotation[1], + transform.rotation[2]); + const glm::vec3 sc(transform.scale[0], + transform.scale[1], + transform.scale[2]); + + const glm::mat4 tm = + glm::translate(glm::mat4(1.f), tl); + const glm::mat4 rm = glm::toMat4(rot); + const glm::mat4 sm = + glm::scale(glm::mat4(1.f), sc); + + newNode->localTransform = tm * rm * sm; + }}, + node.transform); + } +} + +void setup_nodes_relationships(Mesh::GLTF::LoadedGLTF& file, + fastgltf::Asset& gltf, + std::vector>& nodes) { + // run loop again to set up transform hierarchy + for (int i = 0; i < gltf.nodes.size(); i++) { + fastgltf::Node& node = gltf.nodes[i]; + const std::shared_ptr& sceneNode = nodes[i]; + + for (const auto& c : node.children) { + sceneNode->children.push_back(nodes[c]); + nodes[c]->parent = sceneNode; + } + } + + // find the top nodes, with no parents + for (auto& node : nodes) { + if (node->parent.lock() == nullptr) { + file.topNodes.push_back(node); + node->refreshTransform(glm::mat4{1.f}); + } + } +} +} // namespace + +std::optional> +LoaderGLTF::loadGLTF(VulkanEngine& engine, std::string_view filePath) { + fmt::print("Loading GLTF: {}", filePath); + + auto scene = std::make_shared(); + scene->creator = &engine; + Mesh::GLTF::LoadedGLTF& file = *scene; + + fastgltf::Asset gltf; + + if (!load_file(gltf, filePath)) { + return {}; + } + + init_descriptor_pool(engine, file, gltf); + + load_samplers(engine, file, gltf); + + std::vector images; + load_all_textures(engine, gltf, images); + + create_material_data_buffer(engine, file, gltf); + + std::vector> materials; + load_material_data(engine, file, gltf, materials, images); + + std::vector> meshes; + upload_mesh_to_engine(engine, file, gltf, meshes, materials); + + std::vector> nodes; + load_nodes(file, gltf, meshes, nodes); + setup_nodes_relationships(file, gltf, nodes); + return scene; +} From 22485f37cfa46519e4deb32a71b09a68001a5309 Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 21:37:26 +0300 Subject: [PATCH 02/27] chore: delete unnecesary code --- src/core/ControllerImpl.cpp | 15 +- src/core/MeshController.cpp | 3 +- src/core/ModelImpl.cpp | 22 +- src/graphics/vulkan/vk_engine.cpp | 191 +----------- src/graphics/vulkan/vk_loader.cpp | 488 ------------------------------ src/include/core/ModelImpl.h | 4 - 6 files changed, 16 insertions(+), 707 deletions(-) diff --git a/src/core/ControllerImpl.cpp b/src/core/ControllerImpl.cpp index 2caa6a2b..84fbe573 100644 --- a/src/core/ControllerImpl.cpp +++ b/src/core/ControllerImpl.cpp @@ -7,22 +7,23 @@ #define GLM_ENABLE_EXPERIMENTAL #include +#include #include #include -#include +#include ControllerImpl::ControllerImpl(IModel::Ptr model, IView::Ptr view) : _model(std::move(model)), _view(std::move(view)), _mesh_controller(_model) {} double getCurrentGlobalTime() { // Get the current time point - auto now = std::chrono::system_clock::now(); + const auto now = std::chrono::system_clock::now(); // Cast to a time duration since the epoch - auto durationSinceEpoch = now.time_since_epoch(); + const auto durationSinceEpoch = now.time_since_epoch(); // Convert to seconds in double precision - std::chrono::duration seconds = durationSinceEpoch; + const std::chrono::duration seconds = durationSinceEpoch; // Return the double value return seconds.count(); @@ -39,7 +40,7 @@ void updateCube(const MeshController& mesh_controller, Mesh::rid_t rid, int8_t i void updateCubes(const IModel::Ptr& model, const MeshController& mesh_controller) { auto meshes = model->get_meshes(); - for (int8_t i {}; const auto& [key, value] : meshes) { + for (int8_t i {}; const auto& key : std::views::keys(meshes)) { updateCube(mesh_controller, key, i); ++i; } @@ -53,10 +54,6 @@ void update(const IModel::Ptr& model, const MeshController& mesh_controller) { updateCubes(model, mesh_controller); } -// void ControllerImpl::processEvent(SDL_Event &e) const { -// _model->getCamera()->processSDLEvent(e); -// } -// void createCubes(const MeshController& mesh_controller) { for (int i = 0; i < 5; i++) { mesh_controller.create_mesh("/basicmesh.glb"); diff --git a/src/core/MeshController.cpp b/src/core/MeshController.cpp index e699f9f2..5964a280 100644 --- a/src/core/MeshController.cpp +++ b/src/core/MeshController.cpp @@ -7,11 +7,10 @@ MeshController::MeshController(IModel::Ptr model) : _model(std::move(model)) {} void MeshController::create_mesh(std::string_view file_path) const { - //TODO: посмотреть, можно ли _engine засунуть в private VulkanEngine& engine = _model->get_engine(); _model->createMesh(engine, file_path); - + //TODO: вернуть возвращемое значение // return rid; } diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index 7e4b1812..3248e56a 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -1,4 +1,6 @@ #include "core/ModelImpl.h" +#include "core/config.h" +#include "scene/LoaderGLTF.h" #include #include @@ -25,9 +27,6 @@ Camera* ModelImpl::getCamera() { return &_camera; } -#include "core/config.h" -#include "scene/LoaderGLTF.h" - Mesh::rid_t registerMesh( VulkanEngine& engine, ModelImpl::MeshMap& meshes, @@ -58,20 +57,9 @@ Mesh::rid_t registerMesh( void ModelImpl::createMesh(VulkanEngine& engine, std::string_view file_path) { assert(_engine._isInitialized); - Mesh::rid_t rid = registerMesh(engine, _meshes, file_path); - - // auto mesh = std::make_shared("/basicmesh.glb"); - - constexpr glm::mat4 transform{1.}; - _meshes[rid].transform = transform; - // engine.setMeshTransform(rid, transform); - - // mesh->set_transform(glm::mat4(1.0f)); - - // Mesh::rid_t rid = registerMesh(file_path); - - // _meshes[name] = mesh; - + const Mesh::rid_t rid = registerMesh(engine, _meshes, file_path); + _meshes[rid].transform = 1.; +// TODO вернуть возвращаемое значение // return rid; } diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index b01dfeb1..55cd93cb 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -2,6 +2,10 @@ #include "core/config.h" +#include +#include +#include + #define VMA_IMPLEMENTATION #include "SDL_vulkan.h" #include "VkBootstrap.h" @@ -75,153 +79,6 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( return VK_FALSE; } -#include -#include -#include - -// std::optional>> loadGltfMeshes( -// VulkanEngine* engine, std::filesystem::path filePath) { -// if (!std::filesystem::exists(filePath)) { -// std::cout << "Failed to find " << filePath << '\n'; -// return {}; -// } -// -// std::cout << "Loading " << filePath << '\n'; -// -// fastgltf::Asset gltf; -// -// // Parse the glTF file and get the constructed asset -// -// static constexpr auto supportedExtensions = -// fastgltf::Extensions::KHR_mesh_quantization | -// fastgltf::Extensions::KHR_texture_transform | -// fastgltf::Extensions::KHR_materials_variants; -// -// fastgltf::Parser parser(supportedExtensions); -// -// auto path = std::filesystem::path{filePath}; -// -// constexpr auto gltfOptions = -// fastgltf::Options::DontRequireValidAssetMember | -// fastgltf::Options::AllowDouble | fastgltf::Options::LoadGLBBuffers | -// fastgltf::Options::LoadExternalBuffers | -// fastgltf::Options::LoadExternalImages | -// fastgltf::Options::GenerateMeshIndices; -// -// fastgltf::GltfDataBuffer data; -// data.loadFromFile(path); -// -// auto asset = parser.loadGltf(&data, path.parent_path(), gltfOptions); -// -// if (asset) { -// gltf = std::move(asset.get()); -// } else { -// fmt::print("Failed to load glTF: {} \n", -// fastgltf::to_underlying(asset.error())); -// return {}; -// } -// -// std::vector> meshes; -// -// // use the same vectors for all meshes so that the memory doesnt reallocate -// // as often -// std::vector indices; -// std::vector vertices; -// for (fastgltf::Mesh& mesh : gltf.meshes) { -// Mesh::GLTF::MeshAsset newmesh; -// -// newmesh.name = mesh.name; -// -// // clear the mesh arrays each mesh, we dont want to merge them by error -// indices.clear(); -// vertices.clear(); -// -// for (auto&& p : mesh.primitives) { -// Mesh::GLTF::GeoSurface newSurface; -// newSurface.startIndex = (uint32_t)indices.size(); -// newSurface.count = -// (uint32_t)gltf.accessors[p.indicesAccessor.value()].count; -// -// size_t initial_vtx = vertices.size(); -// -// // load indexes -// { -// fastgltf::Accessor& indexaccessor = -// gltf.accessors[p.indicesAccessor.value()]; -// indices.reserve(indices.size() + indexaccessor.count); -// -// fastgltf::iterateAccessor( -// gltf, indexaccessor, [&](std::uint32_t idx) { -// indices.push_back(idx + initial_vtx); -// }); -// } -// -// // load vertex positions -// { -// fastgltf::Accessor& posAccessor = -// gltf.accessors[p.findAttribute("POSITION")->second]; -// vertices.resize(vertices.size() + posAccessor.count); -// -// fastgltf::iterateAccessorWithIndex( -// gltf, posAccessor, [&](glm::vec3 v, size_t index) { -// Vertex newvtx; -// newvtx.position = v; -// newvtx.normal = {1, 0, 0}; -// newvtx.color = glm::vec4{1.f}; -// newvtx.uv_x = 0; -// newvtx.uv_y = 0; -// vertices[initial_vtx + index] = newvtx; -// }); -// } -// -// // load vertex normals -// auto normals = p.findAttribute("NORMAL"); -// if (normals != p.attributes.end()) { -// fastgltf::iterateAccessorWithIndex( -// gltf, gltf.accessors[(*normals).second], -// [&](glm::vec3 v, size_t index) { -// vertices[initial_vtx + index].normal = v; -// }); -// } -// -// // load UVs -// auto uv = p.findAttribute("TEXCOORD_0"); -// if (uv != p.attributes.end()) { -// fastgltf::iterateAccessorWithIndex( -// gltf, gltf.accessors[(*uv).second], -// [&](glm::vec2 v, size_t index) { -// vertices[initial_vtx + index].uv_x = v.x; -// vertices[initial_vtx + index].uv_y = v.y; -// }); -// } -// -// // load vertex colors -// auto colors = p.findAttribute("COLOR_0"); -// if (colors != p.attributes.end()) { -// fastgltf::iterateAccessorWithIndex( -// gltf, gltf.accessors[(*colors).second], -// [&](glm::vec4 v, size_t index) { -// vertices[initial_vtx + index].color = v; -// }); -// } -// newmesh.surfaces.push_back(newSurface); -// } -// -// // display the vertex normals -// constexpr bool OverrideColors = true; -// if (OverrideColors) { -// for (Vertex& vtx : vertices) { -// vtx.color = glm::vec4(vtx.normal, 1.f); -// } -// } -// newmesh.meshBuffers = engine->uploadMesh(indices, vertices); -// -// meshes.emplace_back(std::make_shared(std::move(newmesh))); -// } -// -// return meshes; -// } - void VulkanEngine::init_default_data() { std::array rect_vertices{}; @@ -642,13 +499,6 @@ void VulkanEngine::init(struct SDL_Window* window) { mainCamera->pitch = 0; mainCamera->yaw = 0; - // TODO: возможно, здесь вылезет проблема - // std::string structurePath = {std::string(ASSETS_DIR) + "/basicmesh.glb"}; - // auto structureFile = loadGltf(this, structurePath); - // - // assert(structureFile.has_value()); - // loadedScenes["structure"] = *structureFile; - _isInitialized = true; } @@ -1589,39 +1439,6 @@ void VulkanEngine::update_scene(const IModel::Ptr& model) { auto meshes = model->get_meshes(); for (const auto& [id, mesh_info] : meshes) { const auto renderable_gltf = createRenderableGLTF(mesh_info.ptr); - // const std::shared_ptr loadedMesh = mesh_info.ptr; renderable_gltf->Draw(mesh_info.transform, mainDrawContext); } } - -// Mesh::rid_t VulkanEngine::registerMesh(std::string_view filePath) { -// std::random_device rd; -// -// // Use the Mersenne Twister engine for high-quality random numbers -// std::mt19937_64 generator(rd()); -// -// // Create a uniform distribution for int64_t -// std::uniform_int_distribution distribution; -// -// // Generate and print a random int64_t value -// Mesh::rid_t random_rid_t = distribution(generator); -// -// std::string structurePath = {std::string(ASSETS_DIR) + std::string(filePath)}; -// auto structureFile = loadGltf(this, structurePath); -// -// assert(structureFile.has_value()); -// -// meshes[random_rid_t] = *structureFile; -// transforms[random_rid_t] = glm::mat4(1.0f); -// -// return random_rid_t; -// } - -// void VulkanEngine::unregisterMesh(int64_t id) { -// meshes.erase(id); -// transforms.erase(id); -// } - -// void VulkanEngine::setMeshTransform(int64_t id, glm::mat4 mat) { -// transforms[id] = mat; -// } diff --git a/src/graphics/vulkan/vk_loader.cpp b/src/graphics/vulkan/vk_loader.cpp index 0a571845..96e6c3d5 100644 --- a/src/graphics/vulkan/vk_loader.cpp +++ b/src/graphics/vulkan/vk_loader.cpp @@ -14,494 +14,6 @@ #include #include -// std::optional>> loadGltfMeshes( -// VulkanEngine* engine, std::filesystem::path filePath) { -// if (!std::filesystem::exists(filePath)) { -// std::cout << "Failed to find " << filePath << '\n'; -// return {}; -// } -// -// std::cout << "Loading " << filePath << '\n'; -// -// fastgltf::Asset gltf; -// -// // Parse the glTF file and get the constructed asset -// -// static constexpr auto supportedExtensions = -// fastgltf::Extensions::KHR_mesh_quantization | -// fastgltf::Extensions::KHR_texture_transform | -// fastgltf::Extensions::KHR_materials_variants; -// -// fastgltf::Parser parser(supportedExtensions); -// -// auto path = std::filesystem::path{filePath}; -// -// constexpr auto gltfOptions = -// fastgltf::Options::DontRequireValidAssetMember | -// fastgltf::Options::AllowDouble | fastgltf::Options::LoadGLBBuffers | -// fastgltf::Options::LoadExternalBuffers | -// fastgltf::Options::LoadExternalImages | -// fastgltf::Options::GenerateMeshIndices; -// -// fastgltf::GltfDataBuffer data; -// data.loadFromFile(path); -// -// auto asset = parser.loadGltf(&data, path.parent_path(), gltfOptions); -// -// if (asset) { -// gltf = std::move(asset.get()); -// } else { -// fmt::print("Failed to load glTF: {} \n", -// fastgltf::to_underlying(asset.error())); -// return {}; -// } -// -// std::vector> meshes; -// -// // use the same vectors for all meshes so that the memory doesnt reallocate -// // as often -// std::vector indices; -// std::vector vertices; -// for (fastgltf::Mesh& mesh : gltf.meshes) { -// MeshAsset newmesh; -// -// newmesh.name = mesh.name; -// -// // clear the mesh arrays each mesh, we dont want to merge them by error -// indices.clear(); -// vertices.clear(); -// -// for (auto&& p : mesh.primitives) { -// GeoSurface newSurface; -// newSurface.startIndex = (uint32_t)indices.size(); -// newSurface.count = -// (uint32_t)gltf.accessors[p.indicesAccessor.value()].count; -// -// size_t initial_vtx = vertices.size(); -// -// // load indexes -// { -// fastgltf::Accessor& indexaccessor = -// gltf.accessors[p.indicesAccessor.value()]; -// indices.reserve(indices.size() + indexaccessor.count); -// -// fastgltf::iterateAccessor( -// gltf, indexaccessor, [&](std::uint32_t idx) { -// indices.push_back(idx + initial_vtx); -// }); -// } -// -// // load vertex positions -// { -// fastgltf::Accessor& posAccessor = -// gltf.accessors[p.findAttribute("POSITION")->second]; -// vertices.resize(vertices.size() + posAccessor.count); -// -// fastgltf::iterateAccessorWithIndex( -// gltf, posAccessor, [&](glm::vec3 v, size_t index) { -// Vertex newvtx; -// newvtx.position = v; -// newvtx.normal = {1, 0, 0}; -// newvtx.color = glm::vec4{1.f}; -// newvtx.uv_x = 0; -// newvtx.uv_y = 0; -// vertices[initial_vtx + index] = newvtx; -// }); -// } -// -// // load vertex normals -// auto normals = p.findAttribute("NORMAL"); -// if (normals != p.attributes.end()) { -// fastgltf::iterateAccessorWithIndex( -// gltf, gltf.accessors[(*normals).second], -// [&](glm::vec3 v, size_t index) { -// vertices[initial_vtx + index].normal = v; -// }); -// } -// -// // load UVs -// auto uv = p.findAttribute("TEXCOORD_0"); -// if (uv != p.attributes.end()) { -// fastgltf::iterateAccessorWithIndex( -// gltf, gltf.accessors[(*uv).second], -// [&](glm::vec2 v, size_t index) { -// vertices[initial_vtx + index].uv_x = v.x; -// vertices[initial_vtx + index].uv_y = v.y; -// }); -// } -// -// // load vertex colors -// auto colors = p.findAttribute("COLOR_0"); -// if (colors != p.attributes.end()) { -// fastgltf::iterateAccessorWithIndex( -// gltf, gltf.accessors[(*colors).second], -// [&](glm::vec4 v, size_t index) { -// vertices[initial_vtx + index].color = v; -// }); -// } -// newmesh.surfaces.push_back(newSurface); -// } -// -// // display the vertex normals -// constexpr bool OverrideColors = true; -// if (OverrideColors) { -// for (Vertex& vtx : vertices) { -// vtx.color = glm::vec4(vtx.normal, 1.f); -// } -// } -// newmesh.meshBuffers = engine->uploadMesh(indices, vertices); -// -// meshes.emplace_back(std::make_shared(std::move(newmesh))); -// } -// -// return meshes; -// } - -// см. ModelImpl.cpp -// VkFilter extract_filter(fastgltf::Filter filter) { -// switch (filter) { -// // nearest samplers -// case fastgltf::Filter::Nearest: -// case fastgltf::Filter::NearestMipMapNearest: -// case fastgltf::Filter::NearestMipMapLinear: -// return VK_FILTER_NEAREST; -// -// // linear samplers -// case fastgltf::Filter::Linear: -// case fastgltf::Filter::LinearMipMapNearest: -// case fastgltf::Filter::LinearMipMapLinear: -// default: -// return VK_FILTER_LINEAR; -// } -// } - -// VkSamplerMipmapMode extract_mipmap_mode(fastgltf::Filter filter) { -// switch (filter) { -// case fastgltf::Filter::NearestMipMapNearest: -// case fastgltf::Filter::LinearMipMapNearest: -// return VK_SAMPLER_MIPMAP_MODE_NEAREST; -// -// case fastgltf::Filter::NearestMipMapLinear: -// case fastgltf::Filter::LinearMipMapLinear: -// default: -// return VK_SAMPLER_MIPMAP_MODE_LINEAR; -// } -// } - -// см. ModelImpl.cpp -// std::optional> loadGltf(VulkanEngine* engine, -// std::string_view filePath) { -// fmt::print("Loading GLTF: {}", filePath); -// -// std::shared_ptr scene = std::make_shared(); -// scene->creator = engine; -// LoadedGLTF& file = *scene.get(); -// -// fastgltf::Parser parser{}; -// -// constexpr auto gltfOptions = -// fastgltf::Options::DontRequireValidAssetMember | -// fastgltf::Options::AllowDouble | fastgltf::Options::LoadGLBBuffers | -// fastgltf::Options::LoadExternalBuffers; -// // fastgltf::Options::LoadExternalImages; -// -// fastgltf::GltfDataBuffer data; -// data.loadFromFile(filePath); -// -// fastgltf::Asset gltf; -// -// std::filesystem::path path = filePath; -// -// auto type = fastgltf::determineGltfFileType(&data); -// if (type == fastgltf::GltfType::glTF) { -// auto load = parser.loadGltf(&data, path.parent_path(), gltfOptions); -// if (load) { -// gltf = std::move(load.get()); -// } else { -// std::cerr << "Failed to load glTF: " -// << fastgltf::to_underlying(load.error()) << std::endl; -// return {}; -// } -// } else if (type == fastgltf::GltfType::GLB) { -// auto load = -// parser.loadGltfBinary(&data, path.parent_path(), gltfOptions); -// if (load) { -// gltf = std::move(load.get()); -// } else { -// std::cerr << "Failed to load glTF: " -// << fastgltf::to_underlying(load.error()) << std::endl; -// return {}; -// } -// } else { -// std::cerr << "Failed to determine glTF container" << std::endl; -// return {}; -// } -// -// // we can stimate the descriptors we will need accurately -// std::vector sizes = { -// {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3}, -// {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3}, -// {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}}; -// -// file.descriptorPool.init(engine->_device, gltf.materials.size(), sizes); -// -// // load samplers -// for (fastgltf::Sampler& sampler : gltf.samplers) { -// VkSamplerCreateInfo sampl = { -// .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, -// .pNext = nullptr}; -// sampl.maxLod = VK_LOD_CLAMP_NONE; -// sampl.minLod = 0; -// -// sampl.magFilter = extract_filter( -// sampler.magFilter.value_or(fastgltf::Filter::Nearest)); -// sampl.minFilter = extract_filter( -// sampler.minFilter.value_or(fastgltf::Filter::Nearest)); -// -// sampl.mipmapMode = extract_mipmap_mode( -// sampler.minFilter.value_or(fastgltf::Filter::Nearest)); -// -// VkSampler newSampler; -// vkCreateSampler(engine->_device, &sampl, nullptr, &newSampler); -// -// file.samplers.push_back(newSampler); -// } -// -// std::vector> meshes; -// std::vector> nodes; -// std::vector images; -// std::vector> materials; -// -// // load all textures -// for (fastgltf::Image& image : gltf.images) { -// images.push_back(engine->_errorCheckerboardImage); -// } -// -// // create buffer to hold the material data -// file.materialDataBuffer = engine->create_buffer( -// sizeof(GLTFMetallic_Roughness::MaterialConstants) * -// gltf.materials.size(), -// VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); -// int data_index = 0; -// GLTFMetallic_Roughness::MaterialConstants* sceneMaterialConstants = -// (GLTFMetallic_Roughness::MaterialConstants*) -// file.materialDataBuffer.info.pMappedData; -// -// for (fastgltf::Material& mat : gltf.materials) { -// std::shared_ptr newMat = std::make_shared(); -// materials.push_back(newMat); -// file.materials[mat.name.c_str()] = newMat; -// -// GLTFMetallic_Roughness::MaterialConstants constants; -// constants.colorFactors.x = mat.pbrData.baseColorFactor[0]; -// constants.colorFactors.y = mat.pbrData.baseColorFactor[1]; -// constants.colorFactors.z = mat.pbrData.baseColorFactor[2]; -// constants.colorFactors.w = mat.pbrData.baseColorFactor[3]; -// -// constants.metal_rough_factors.x = mat.pbrData.metallicFactor; -// constants.metal_rough_factors.y = mat.pbrData.roughnessFactor; -// // write material parameters to buffer -// sceneMaterialConstants[data_index] = constants; -// -// MaterialPass passType = MaterialPass::MainColor; -// if (mat.alphaMode == fastgltf::AlphaMode::Blend) { -// passType = MaterialPass::Transparent; -// } -// -// GLTFMetallic_Roughness::MaterialResources materialResources; -// // default the material textures -// materialResources.colorImage = engine->_whiteImage; -// materialResources.colorSampler = engine->_defaultSamplerLinear; -// materialResources.metalRoughImage = engine->_whiteImage; -// materialResources.metalRoughSampler = engine->_defaultSamplerLinear; -// -// // set the uniform buffer for the material data -// materialResources.dataBuffer = file.materialDataBuffer.buffer; -// materialResources.dataBufferOffset = -// data_index * sizeof(GLTFMetallic_Roughness::MaterialConstants); -// // grab textures from gltf file -// if (mat.pbrData.baseColorTexture.has_value()) { -// size_t img = gltf.textures[mat.pbrData.baseColorTexture.value() -// .textureIndex] -// .imageIndex.value(); -// size_t sampler = gltf.textures[mat.pbrData.baseColorTexture.value() -// .textureIndex] -// .samplerIndex.value(); -// -// materialResources.colorImage = images[img]; -// materialResources.colorSampler = file.samplers[sampler]; -// } -// // build material -// newMat->data = engine->metalRoughMaterial.write_material( -// engine->_device, passType, materialResources, -// file.descriptorPool); -// -// data_index++; -// } -// -// // use the same vectors for all meshes so that the memory doesnt reallocate -// // as -// // often -// std::vector indices; -// std::vector vertices; -// -// for (fastgltf::Mesh& mesh : gltf.meshes) { -// std::shared_ptr newmesh = std::make_shared(); -// meshes.push_back(newmesh); -// file.meshes[mesh.name.c_str()] = newmesh; -// newmesh->name = mesh.name; -// -// // clear the mesh arrays each mesh, we dont want to merge them by error -// indices.clear(); -// vertices.clear(); -// -// for (auto&& p : mesh.primitives) { -// GeoSurface newSurface; -// newSurface.startIndex = (uint32_t)indices.size(); -// newSurface.count = -// (uint32_t)gltf.accessors[p.indicesAccessor.value()].count; -// -// size_t initial_vtx = vertices.size(); -// -// // load indexes -// { -// fastgltf::Accessor& indexaccessor = -// gltf.accessors[p.indicesAccessor.value()]; -// indices.reserve(indices.size() + indexaccessor.count); -// -// fastgltf::iterateAccessor( -// gltf, indexaccessor, [&](std::uint32_t idx) { -// indices.push_back(idx + initial_vtx); -// }); -// } -// -// // load vertex positions -// { -// fastgltf::Accessor& posAccessor = -// gltf.accessors[p.findAttribute("POSITION")->second]; -// vertices.resize(vertices.size() + posAccessor.count); -// -// fastgltf::iterateAccessorWithIndex( -// gltf, posAccessor, [&](glm::vec3 v, size_t index) { -// Vertex newvtx; -// newvtx.position = v; -// newvtx.normal = {1, 0, 0}; -// newvtx.color = glm::vec4{1.f}; -// newvtx.uv_x = 0; -// newvtx.uv_y = 0; -// vertices[initial_vtx + index] = newvtx; -// }); -// } -// -// // load vertex normals -// auto normals = p.findAttribute("NORMAL"); -// if (normals != p.attributes.end()) { -// fastgltf::iterateAccessorWithIndex( -// gltf, gltf.accessors[(*normals).second], -// [&](glm::vec3 v, size_t index) { -// vertices[initial_vtx + index].normal = v; -// }); -// } -// -// // load UVs -// auto uv = p.findAttribute("TEXCOORD_0"); -// if (uv != p.attributes.end()) { -// fastgltf::iterateAccessorWithIndex( -// gltf, gltf.accessors[(*uv).second], -// [&](glm::vec2 v, size_t index) { -// vertices[initial_vtx + index].uv_x = v.x; -// vertices[initial_vtx + index].uv_y = v.y; -// }); -// } -// -// // load vertex colors -// auto colors = p.findAttribute("COLOR_0"); -// if (colors != p.attributes.end()) { -// fastgltf::iterateAccessorWithIndex( -// gltf, gltf.accessors[(*colors).second], -// [&](glm::vec4 v, size_t index) { -// vertices[initial_vtx + index].color = v; -// }); -// } -// -// if (p.materialIndex.has_value()) { -// newSurface.material = materials[p.materialIndex.value()]; -// } else { -// newSurface.material = materials[0]; -// } -// -// newmesh->surfaces.push_back(newSurface); -// } -// -// newmesh->meshBuffers = engine->uploadMesh(indices, vertices); -// } -// -// // load all nodes and their meshes -// for (fastgltf::Node& node : gltf.nodes) { -// std::shared_ptr newNode; -// -// // find if the node has a mesh, and if it does hook it to the mesh -// // pointer and allocate it with the meshnode class -// if (node.meshIndex.has_value()) { -// newNode = std::make_shared(); -// static_cast(newNode.get())->mesh = -// meshes[*node.meshIndex]; -// } else { -// newNode = std::make_shared(); -// } -// -// nodes.push_back(newNode); -// file.nodes[node.name.c_str()]; -// -// std::visit( -// fastgltf::visitor{ -// [&](fastgltf::Node::TransformMatrix matrix) { -// memcpy(&newNode->localTransform, matrix.data(), -// sizeof(matrix)); -// }, -// [&](fastgltf::TRS transform) { -// glm::vec3 tl(transform.translation[0], -// transform.translation[1], -// transform.translation[2]); -// glm::quat rot(transform.rotation[3], -// transform.rotation[0], -// transform.rotation[1], -// transform.rotation[2]); -// glm::vec3 sc(transform.scale[0], transform.scale[1], -// transform.scale[2]); -// -// glm::mat4 tm = glm::translate(glm::mat4(1.f), tl); -// glm::mat4 rm = glm::toMat4(rot); -// glm::mat4 sm = glm::scale(glm::mat4(1.f), sc); -// -// newNode->localTransform = tm * rm * sm; -// }}, -// node.transform); -// } -// -// // run loop again to setup transform hierarchy -// for (int i = 0; i < gltf.nodes.size(); i++) { -// fastgltf::Node& node = gltf.nodes[i]; -// std::shared_ptr& sceneNode = nodes[i]; -// -// for (auto& c : node.children) { -// sceneNode->children.push_back(nodes[c]); -// nodes[c]->parent = sceneNode; -// } -// } -// -// // find the top nodes, with no parents -// for (auto& node : nodes) { -// if (node->parent.lock() == nullptr) { -// file.topNodes.push_back(node); -// node->refreshTransform(glm::mat4{1.f}); -// } -// } -// return scene; -// } - -// RenderableGLTF::RenderableGLTF(std::shared_ptr gltf) : _gltf{std::move(gltf)} {} - RenderableGLTF::RenderableGLTF(LoadedGltfPtr gltf) : _gltf(std::move(gltf)) {} void RenderableGLTF::Draw(const glm::mat4& topMatrix, DrawContext& ctx) { diff --git a/src/include/core/ModelImpl.h b/src/include/core/ModelImpl.h index 42e6308a..c49de617 100644 --- a/src/include/core/ModelImpl.h +++ b/src/include/core/ModelImpl.h @@ -28,7 +28,6 @@ class ModelImpl : public IModel { void registerWindow(struct SDL_Window *window) override; - // Mesh::rid_t registerMesh(std::string_view file_path); void createMesh(VulkanEngine& engine, std::string_view file_path) final; void delete_mesh(Mesh::rid_t rid) final; @@ -42,9 +41,6 @@ class ModelImpl : public IModel { VulkanEngine& get_engine() final; private: - // std::unordered_map> _meshes; - - // TODO: move meshes and transforms to one map MeshMap _meshes; VulkanEngine _engine; From c4097f0594b04213dd8e470e7db32713794505aa Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 21:38:18 +0300 Subject: [PATCH 03/27] refactor: change structure binding to values_view --- src/graphics/CMakeLists.txt | 1 + src/graphics/vulkan/MeshNode.cpp | 23 +++++++++++++++++++++++ src/graphics/vulkan/vk_engine.cpp | 22 ++-------------------- src/include/graphics/vulkan/MeshNode.h | 12 ++++++++++++ src/include/graphics/vulkan/vk_engine.h | 6 ------ src/scene/LoaderGLTF.cpp | 1 + 6 files changed, 39 insertions(+), 26 deletions(-) create mode 100644 src/graphics/vulkan/MeshNode.cpp create mode 100644 src/include/graphics/vulkan/MeshNode.h diff --git a/src/graphics/CMakeLists.txt b/src/graphics/CMakeLists.txt index e74974ac..fc3711e7 100644 --- a/src/graphics/CMakeLists.txt +++ b/src/graphics/CMakeLists.txt @@ -1,5 +1,6 @@ target_sources(${PROJECT_NAME} PRIVATE + vulkan/MeshNode.cpp vulkan/RenderableGLTF.cpp vulkan/vk_descriptors.cpp vulkan/vk_engine.cpp diff --git a/src/graphics/vulkan/MeshNode.cpp b/src/graphics/vulkan/MeshNode.cpp new file mode 100644 index 00000000..5170a1f1 --- /dev/null +++ b/src/graphics/vulkan/MeshNode.cpp @@ -0,0 +1,23 @@ +#include "graphics/vulkan/MeshNode.h" +#include "graphics/vulkan/vk_engine.h" + +#include + +void MeshNode::Draw(const glm::mat4& topMatrix, DrawContext& ctx) { + const glm::mat4 nodeMatrix = topMatrix * worldTransform; + + for (auto& [startIndex, count, material] : mesh->surfaces) { + RenderObject def{}; + def.indexCount = count; + def.firstIndex = startIndex; + def.indexBuffer = mesh->meshBuffers.indexBuffer.buffer; + def.material = &material->data; + + def.transform = nodeMatrix; + def.vertexBufferAddress = mesh->meshBuffers.vertexBufferAddress; + + ctx.OpaqueSurfaces.push_back(def); + } + + ENode::Draw(topMatrix, ctx); +} diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index 55cd93cb..1fb50e9d 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #define VMA_IMPLEMENTATION #include "SDL_vulkan.h" @@ -1394,25 +1395,6 @@ MaterialInstance GLTFMetallic_Roughness::write_material( return matData; } -void MeshNode::Draw(const glm::mat4& topMatrix, DrawContext& ctx) { - glm::mat4 nodeMatrix = topMatrix * worldTransform; - - for (auto& s : mesh->surfaces) { - RenderObject def{}; - def.indexCount = s.count; - def.firstIndex = s.startIndex; - def.indexBuffer = mesh->meshBuffers.indexBuffer.buffer; - def.material = &s.material->data; - - def.transform = nodeMatrix; - def.vertexBufferAddress = mesh->meshBuffers.vertexBufferAddress; - - ctx.OpaqueSurfaces.push_back(def); - } - - ENode::Draw(topMatrix, ctx); -} - void VulkanEngine::update_scene(const IModel::Ptr& model) { mainCamera->update(); @@ -1437,7 +1419,7 @@ void VulkanEngine::update_scene(const IModel::Ptr& model) { sceneData.sunlightDirection = glm::vec4(0, 1, 0.5, 1.f); auto meshes = model->get_meshes(); - for (const auto& [id, mesh_info] : meshes) { + for (const auto& mesh_info : std::views::values(meshes)) { const auto renderable_gltf = createRenderableGLTF(mesh_info.ptr); renderable_gltf->Draw(mesh_info.transform, mainDrawContext); } diff --git a/src/include/graphics/vulkan/MeshNode.h b/src/include/graphics/vulkan/MeshNode.h new file mode 100644 index 00000000..6e287318 --- /dev/null +++ b/src/include/graphics/vulkan/MeshNode.h @@ -0,0 +1,12 @@ +#pragma once + +#include "graphics/vulkan/vk_types.h" +#include "scene/Mesh.h" + +#include + +struct MeshNode final : ENode { + std::shared_ptr mesh; + + void Draw(const glm::mat4& topMatrix, DrawContext& ctx) override; +}; diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 92e535db..4123a9fa 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -82,12 +82,6 @@ struct GPUSceneData { glm::vec4 sunlightColor; }; -struct MeshNode : public ENode { - std::shared_ptr mesh; - - virtual void Draw(const glm::mat4& topMatrix, DrawContext& ctx) override; -}; - struct RenderObject { uint32_t indexCount; uint32_t firstIndex; diff --git a/src/scene/LoaderGLTF.cpp b/src/scene/LoaderGLTF.cpp index b6815c05..2a7a01e2 100644 --- a/src/scene/LoaderGLTF.cpp +++ b/src/scene/LoaderGLTF.cpp @@ -1,4 +1,5 @@ #include "graphics/vulkan/vk_engine.h" +#include "graphics/vulkan/MeshNode.h" #include "scene/LoaderGLTF.h" #define GLM_ENABLE_EXPERIMENTAL From 2b6e6c5728f7fd0c385f4b7df4773251fab3f8e8 Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 21:39:34 +0300 Subject: [PATCH 04/27] chore: add const for vars in a loop --- src/graphics/vulkan/MeshNode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphics/vulkan/MeshNode.cpp b/src/graphics/vulkan/MeshNode.cpp index 5170a1f1..170a5333 100644 --- a/src/graphics/vulkan/MeshNode.cpp +++ b/src/graphics/vulkan/MeshNode.cpp @@ -6,7 +6,7 @@ void MeshNode::Draw(const glm::mat4& topMatrix, DrawContext& ctx) { const glm::mat4 nodeMatrix = topMatrix * worldTransform; - for (auto& [startIndex, count, material] : mesh->surfaces) { + for (const auto& [startIndex, count, material] : mesh->surfaces) { RenderObject def{}; def.indexCount = count; def.firstIndex = startIndex; From 6393d8c9187e1bc7f12dc4fd0d3e8f0356cb1556 Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 21:40:23 +0300 Subject: [PATCH 05/27] refactor: remove an useless function --- src/graphics/CMakeLists.txt | 1 - src/graphics/vulkan/RenderableGLTF.cpp | 7 ------- src/graphics/vulkan/vk_engine.cpp | 4 ++-- src/graphics/vulkan/vk_loader.cpp | 2 -- src/include/graphics/vulkan/RenderableGLTF.h | 7 ------- src/include/graphics/vulkan/vk_loader.h | 4 ---- 6 files changed, 2 insertions(+), 23 deletions(-) delete mode 100644 src/graphics/vulkan/RenderableGLTF.cpp delete mode 100644 src/include/graphics/vulkan/RenderableGLTF.h diff --git a/src/graphics/CMakeLists.txt b/src/graphics/CMakeLists.txt index fc3711e7..85ed0a0f 100644 --- a/src/graphics/CMakeLists.txt +++ b/src/graphics/CMakeLists.txt @@ -1,7 +1,6 @@ target_sources(${PROJECT_NAME} PRIVATE vulkan/MeshNode.cpp - vulkan/RenderableGLTF.cpp vulkan/vk_descriptors.cpp vulkan/vk_engine.cpp vulkan/vk_images.cpp diff --git a/src/graphics/vulkan/RenderableGLTF.cpp b/src/graphics/vulkan/RenderableGLTF.cpp deleted file mode 100644 index 90a47eba..00000000 --- a/src/graphics/vulkan/RenderableGLTF.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "graphics/vulkan/RenderableGLTF.h" -#include "graphics/vulkan/vk_loader.h" - -IRenderable::Ptr createRenderableGLTF(RenderableGLTF::LoadedGltfPtr ptr) -{ - return std::make_shared(std::move(ptr)); -} \ No newline at end of file diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index 1fb50e9d..790a22a4 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -1420,7 +1420,7 @@ void VulkanEngine::update_scene(const IModel::Ptr& model) { auto meshes = model->get_meshes(); for (const auto& mesh_info : std::views::values(meshes)) { - const auto renderable_gltf = createRenderableGLTF(mesh_info.ptr); - renderable_gltf->Draw(mesh_info.transform, mainDrawContext); + auto renderable_gltf{RenderableGLTF(mesh_info.ptr)}; + renderable_gltf.Draw(mesh_info.transform, mainDrawContext); } } diff --git a/src/graphics/vulkan/vk_loader.cpp b/src/graphics/vulkan/vk_loader.cpp index 96e6c3d5..8a73a153 100644 --- a/src/graphics/vulkan/vk_loader.cpp +++ b/src/graphics/vulkan/vk_loader.cpp @@ -22,5 +22,3 @@ void RenderableGLTF::Draw(const glm::mat4& topMatrix, DrawContext& ctx) { n->Draw(topMatrix, ctx); } } - -void RenderableGLTF::clearAll() {} diff --git a/src/include/graphics/vulkan/RenderableGLTF.h b/src/include/graphics/vulkan/RenderableGLTF.h deleted file mode 100644 index d5b30dd6..00000000 --- a/src/include/graphics/vulkan/RenderableGLTF.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include "graphics/vulkan/vk_types.h" -#include "graphics/vulkan/vk_loader.h" -#include "scene/Mesh.h" - -IRenderable::Ptr createRenderableGLTF(RenderableGLTF::LoadedGltfPtr ptr); \ No newline at end of file diff --git a/src/include/graphics/vulkan/vk_loader.h b/src/include/graphics/vulkan/vk_loader.h index f198b7c3..528f1eaa 100644 --- a/src/include/graphics/vulkan/vk_loader.h +++ b/src/include/graphics/vulkan/vk_loader.h @@ -52,14 +52,10 @@ class RenderableGLTF : public IRenderable { // VulkanEngine* creator; using LoadedGltfPtr = std::shared_ptr; explicit RenderableGLTF(LoadedGltfPtr gltf); - virtual ~RenderableGLTF() { - clearAll(); - }; void Draw(const glm::mat4& topMatrix, DrawContext& ctx) final; private: - void clearAll(); std::shared_ptr _gltf; }; From 839298ccc67adc2841272d38db80c74d65155408 Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 21:41:24 +0300 Subject: [PATCH 06/27] refactor: rename vk_loader.cpp/.h --- src/graphics/CMakeLists.txt | 2 +- src/graphics/vulkan/{vk_loader.cpp => RenderableGLTF.cpp} | 2 +- src/graphics/vulkan/vk_engine.cpp | 1 - src/include/graphics/vulkan/{vk_loader.h => RenderableGLTF.h} | 0 src/include/graphics/vulkan/vk_engine.h | 2 +- 5 files changed, 3 insertions(+), 4 deletions(-) rename src/graphics/vulkan/{vk_loader.cpp => RenderableGLTF.cpp} (92%) rename src/include/graphics/vulkan/{vk_loader.h => RenderableGLTF.h} (100%) diff --git a/src/graphics/CMakeLists.txt b/src/graphics/CMakeLists.txt index 85ed0a0f..ddbf57fa 100644 --- a/src/graphics/CMakeLists.txt +++ b/src/graphics/CMakeLists.txt @@ -1,11 +1,11 @@ target_sources(${PROJECT_NAME} PRIVATE vulkan/MeshNode.cpp + vulkan/RenderableGLTF.cpp vulkan/vk_descriptors.cpp vulkan/vk_engine.cpp vulkan/vk_images.cpp vulkan/vk_initializers.cpp - vulkan/vk_loader.cpp vulkan/vk_pipelines.cpp Graphics.cpp ) \ No newline at end of file diff --git a/src/graphics/vulkan/vk_loader.cpp b/src/graphics/vulkan/RenderableGLTF.cpp similarity index 92% rename from src/graphics/vulkan/vk_loader.cpp rename to src/graphics/vulkan/RenderableGLTF.cpp index 8a73a153..80ae719e 100644 --- a/src/graphics/vulkan/vk_loader.cpp +++ b/src/graphics/vulkan/RenderableGLTF.cpp @@ -1,4 +1,4 @@ -#include "graphics/vulkan/vk_loader.h" +#include "graphics/vulkan/RenderableGLTF.h" #include diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index 790a22a4..409d1d0c 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -14,7 +14,6 @@ #include "graphics/vulkan/RenderableGLTF.h" #include "graphics/vulkan/vk_images.h" #include "graphics/vulkan/vk_initializers.h" -#include "graphics/vulkan/vk_loader.h" #include "graphics/vulkan/vk_pipelines.h" #include "graphics/vulkan/vk_types.h" #include "imgui.h" diff --git a/src/include/graphics/vulkan/vk_loader.h b/src/include/graphics/vulkan/RenderableGLTF.h similarity index 100% rename from src/include/graphics/vulkan/vk_loader.h rename to src/include/graphics/vulkan/RenderableGLTF.h diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 4123a9fa..180126f5 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -6,7 +6,7 @@ #include "scene/Mesh.h" #include "scene/Camera.h" #include "vk_descriptors.h" -#include "vk_loader.h" +#include "RenderableGLTF.h" #include "vk_pipelines.h" #include "vk_types.h" From b442c209f1b3fec91e641a90b1ac788b62a278df Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 21:42:04 +0300 Subject: [PATCH 07/27] chore: remove useless code --- src/graphics/vulkan/vk_engine.cpp | 1 - src/include/core/Model.h | 1 - src/include/core/ViewImpl.h | 4 +- src/include/graphics/vulkan/RenderableGLTF.h | 43 -------------------- src/include/graphics/vulkan/vk_engine.h | 11 ----- src/include/interfaces/IModel.h | 27 +----------- src/include/interfaces/IView.h | 11 ----- 7 files changed, 4 insertions(+), 94 deletions(-) diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index 409d1d0c..8ad650bb 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -99,7 +99,6 @@ void VulkanEngine::init_default_data() { rect_indices[2] = 2; auto path_to_assets = std::string(ASSETS_DIR) + "/basicmesh.glb"; - // testMeshes = loadGltfMeshes(this, path_to_assets).value(); // 3 default textures, white, grey, black. 1 pixel each uint32_t white = glm::packUnorm4x8(glm::vec4(1, 1, 1, 1)); diff --git a/src/include/core/Model.h b/src/include/core/Model.h index 1a3dbef1..e5ef3990 100644 --- a/src/include/core/Model.h +++ b/src/include/core/Model.h @@ -1,6 +1,5 @@ #pragma once #include "interfaces/IModel.h" -#include "interfaces/IView.h" IModel::Ptr createModel(); \ No newline at end of file diff --git a/src/include/core/ViewImpl.h b/src/include/core/ViewImpl.h index 4d8fa8de..d40c3ac9 100644 --- a/src/include/core/ViewImpl.h +++ b/src/include/core/ViewImpl.h @@ -1,6 +1,7 @@ #pragma once -#include "IController.h" +#include + #include "interfaces/IModel.h" #include "interfaces/IView.h" @@ -13,7 +14,6 @@ class ViewImpl : public IView { void process_event(const SDL_Event& e) final; private: - // IController::Ptr _controller; IModel::Ptr _model; struct SDL_Window* window{nullptr}; diff --git a/src/include/graphics/vulkan/RenderableGLTF.h b/src/include/graphics/vulkan/RenderableGLTF.h index 528f1eaa..414a1f2a 100644 --- a/src/include/graphics/vulkan/RenderableGLTF.h +++ b/src/include/graphics/vulkan/RenderableGLTF.h @@ -1,55 +1,12 @@ #pragma once #include -#include -#include "vk_descriptors.h" #include "vk_types.h" #include "scene/Mesh.h" -// struct GLTFMaterial { -// MaterialInstance data; -// }; -// -// struct GeoSurface { -// uint32_t startIndex; -// uint32_t count; -// std::shared_ptr material; -// }; -// -// struct MeshAsset { -// std::string name; -// -// std::vector surfaces; -// GPUMeshBuffers meshBuffers; -// }; - -// forward declaration -// class VulkanEngine; - -// в vk_engine.cpp -// std::optional>> loadGltfMeshes( -// VulkanEngine* engine, std::filesystem::path filePath); - class RenderableGLTF : public IRenderable { public: - // // storage for all the data on a given glTF file - // std::unordered_map> meshes; - // std::unordered_map> nodes; - // std::unordered_map images; - // std::unordered_map> materials; - // - // // nodes that dont have a parent, for iterating through the file in tree - // // order - // std::vector> topNodes; - // - // std::vector samplers; - // - // DescriptorAllocatorGrowable descriptorPool; - // - // AllocatedBuffer materialDataBuffer; - // - // VulkanEngine* creator; using LoadedGltfPtr = std::shared_ptr; explicit RenderableGLTF(LoadedGltfPtr gltf); diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 180126f5..6f128bc0 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -99,15 +99,6 @@ struct DrawContext { class VulkanEngine { public: - // они теперь в модели или контроллере - // Mesh::rid_t registerMesh(std::string_view filePath); - // void unregisterMesh(int64_t id); - - // void setMeshTransform(int64_t id, glm::mat4 mat); - // std::unordered_map> meshes; - // - // std::unordered_map transforms; - std::unordered_map> loadedScenes; Camera* mainCamera; @@ -194,8 +185,6 @@ class VulkanEngine { GPUMeshBuffers uploadMesh(std::span indices, std::span vertices); - // std::vector> testMeshes; - bool resize_requested; GPUSceneData sceneData; diff --git a/src/include/interfaces/IModel.h b/src/include/interfaces/IModel.h index a48414d3..873f80a3 100644 --- a/src/include/interfaces/IModel.h +++ b/src/include/interfaces/IModel.h @@ -50,6 +50,8 @@ class IModel { */ virtual void createMesh(VulkanEngine& engine, std::string_view file_path) = 0; + virtual void delete_mesh(Mesh::rid_t rid) = 0; + /*! * \brief Sets the transformation matrix for a mesh. * @@ -59,9 +61,6 @@ class IModel { * This method sets the transformation matrix for the mesh identified by the * provided name. */ - - virtual void delete_mesh(Mesh::rid_t rid) = 0; - virtual void setMeshTransform(Mesh::rid_t rid, glm::mat4x4 transform) = 0; virtual glm::mat4 get_mesh_transform(Mesh::rid_t) = 0; @@ -85,27 +84,5 @@ class IModel { virtual VulkanEngine& get_engine() = 0; - /*! \brief - * Gets the chip handler from HIDAPI required to change the settings by the - * Controller - * @return - * Returns the device handler in case, can't be nullptr - */ - //[[nodiscard]] virtual hid_device* getChipHandler() const = 0; - - /*! \brief - * Requests from chip the level of brightness - * @return - * Returns the level of brightness [0; 100] - */ - // virtual uint8_t getBrightness() = 0; - - /*! \brief - * Requests RGB values from the chip - * @return - * Returns the struct with .R, .G, .B fields - */ - // virtual Color getRGB() = 0; - using Ptr = std::shared_ptr; }; diff --git a/src/include/interfaces/IView.h b/src/include/interfaces/IView.h index 36aec8a4..5309eeff 100644 --- a/src/include/interfaces/IView.h +++ b/src/include/interfaces/IView.h @@ -12,17 +12,6 @@ class IView { virtual void run() const = 0; virtual void process_event(const SDL_Event& e) = 0; - /*! \brief - * Prints in console current settings - * (level of brightness and RGB) - */ - // virtual void showCurrentSettings() const = 0; - - /*! \brief - * Runs user interface that helps user interact with backlight - */ - // virtual void runMenu() const = 0; - using Ptr = std::shared_ptr; }; From a92e7b5bae0685f17ced55ac6ad64934631ba6de Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 21:42:39 +0300 Subject: [PATCH 08/27] refactor: move LoaderGLTF to ModelImpl --- src/core/ModelImpl.cpp | 454 +++++++++++++++++++++++++++++++- src/include/scene/LoaderGLTF.h | 8 - src/scene/CMakeLists.txt | 1 - src/scene/LoaderGLTF.cpp | 459 --------------------------------- 4 files changed, 451 insertions(+), 471 deletions(-) delete mode 100644 src/include/scene/LoaderGLTF.h delete mode 100644 src/scene/LoaderGLTF.cpp diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index 3248e56a..8e0a0b7e 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -1,12 +1,460 @@ #include "core/ModelImpl.h" #include "core/config.h" -#include "scene/LoaderGLTF.h" +#include "graphics/vulkan/MeshNode.h" #include #include #include +#include +#include +#include #define GLM_ENABLE_EXPERIMENTAL -#include +#include + +namespace { + +VkFilter extract_filter(fastgltf::Filter filter) { + switch (filter) { + // nearest samplers + case fastgltf::Filter::Nearest: + case fastgltf::Filter::NearestMipMapNearest: + case fastgltf::Filter::NearestMipMapLinear: + return VK_FILTER_NEAREST; + + // linear samplers + case fastgltf::Filter::Linear: + case fastgltf::Filter::LinearMipMapNearest: + case fastgltf::Filter::LinearMipMapLinear: + default: + return VK_FILTER_LINEAR; + } +} + +VkSamplerMipmapMode extract_mipmap_mode(fastgltf::Filter filter) { + switch (filter) { + case fastgltf::Filter::NearestMipMapNearest: + case fastgltf::Filter::LinearMipMapNearest: + return VK_SAMPLER_MIPMAP_MODE_NEAREST; + + case fastgltf::Filter::NearestMipMapLinear: + case fastgltf::Filter::LinearMipMapLinear: + default: + return VK_SAMPLER_MIPMAP_MODE_LINEAR; + } +} + +constexpr auto kOptionsGLTF = fastgltf::Options::DontRequireValidAssetMember | + fastgltf::Options::AllowDouble | + fastgltf::Options::LoadGLBBuffers | + fastgltf::Options::LoadExternalBuffers; + +bool check_parser_result(fastgltf::Asset& gltf, + fastgltf::Expected& load) { + if (load) { + gltf = std::move(load.get()); + } else { + std::cerr << "Failed to load glTF: " + << fastgltf::to_underlying(load.error()) << std::endl; + return false; + } + return true; +} + +bool load_file(fastgltf::Asset& gltf, std::string_view filePath) { + fastgltf::Parser parser{}; + + fastgltf::GltfDataBuffer data; + data.loadFromFile(filePath); + + std::filesystem::path path = filePath; + + auto type = fastgltf::determineGltfFileType(&data); + if (type == fastgltf::GltfType::glTF) { + auto load = parser.loadGltf(&data, path.parent_path(), kOptionsGLTF); + if (!check_parser_result(gltf, load)) { + return false; + } + } else if (type == fastgltf::GltfType::GLB) { + auto load = + parser.loadGltfBinary(&data, path.parent_path(), kOptionsGLTF); + if (!check_parser_result(gltf, load)) { + return false; + } + } else { + std::cerr << "Failed to determine glTF container" << std::endl; + return false; + } + return true; +} + +void init_descriptor_pool(const VulkanEngine& engine, + Mesh::GLTF::LoadedGLTF& file, + const fastgltf::Asset& gltf) { + // we can stimate the descriptors we will need accurately + std::vector sizes = { + {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3}, + {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3}, + {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}}; + + file.descriptorPool.init(engine._device, gltf.materials.size(), sizes); +} + +void load_samplers(const VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, + fastgltf::Asset& gltf) { + for (auto& [magFilter, minFilter, wrapS, wrapT, name] : gltf.samplers) { + VkSamplerCreateInfo sample = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .pNext = nullptr}; + sample.maxLod = VK_LOD_CLAMP_NONE; + sample.minLod = 0; + + sample.magFilter = + extract_filter(magFilter.value_or(fastgltf::Filter::Nearest)); + sample.minFilter = + extract_filter(minFilter.value_or(fastgltf::Filter::Nearest)); + + sample.mipmapMode = extract_mipmap_mode( + minFilter.value_or(fastgltf::Filter::Nearest)); + + VkSampler newSampler; + vkCreateSampler(engine._device, &sample, nullptr, &newSampler); + + file.samplers.push_back(newSampler); + } +} + +void load_all_textures(const VulkanEngine& engine, fastgltf::Asset& gltf, + std::vector& images) { + for ([[maybe_unused]] fastgltf::Image& image : gltf.images) { + images.push_back(engine._errorCheckerboardImage); + } +} + +void create_material_data_buffer(const VulkanEngine& engine, + Mesh::GLTF::LoadedGLTF& file, + const fastgltf::Asset& gltf) { + file.materialDataBuffer = engine.create_buffer( + sizeof(GLTFMetallic_Roughness::MaterialConstants) * + gltf.materials.size(), + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); +} + +void grab_textures_from_GLTF( + const Mesh::GLTF::LoadedGLTF& file, fastgltf::Asset& gltf, + const fastgltf::Material& mat, + GLTFMetallic_Roughness::MaterialResources& materialResources, + const std::vector& images) { + if (mat.pbrData.baseColorTexture.has_value()) { + const size_t img = + gltf.textures[mat.pbrData.baseColorTexture.value().textureIndex] + .imageIndex.value(); + const size_t sampler = + gltf.textures[mat.pbrData.baseColorTexture.value().textureIndex] + .samplerIndex.value(); + + materialResources.colorImage = images[img]; + materialResources.colorSampler = file.samplers[sampler]; + } +} + +void load_material_data( + VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, + fastgltf::Asset& gltf, + std::vector>& materials, + const std::vector& images) { + auto* sceneMaterialConstants = + static_cast( + file.materialDataBuffer.info.pMappedData); + + size_t data_index{0}; + for (fastgltf::Material& mat : gltf.materials) { + auto newMat = std::make_shared(); + materials.push_back(newMat); + file.materials[mat.name.c_str()] = newMat; + + const GLTFMetallic_Roughness::MaterialConstants constants{ + .colorFactors{ + mat.pbrData.baseColorFactor[0], + mat.pbrData.baseColorFactor[1], + mat.pbrData.baseColorFactor[2], + mat.pbrData.baseColorFactor[3], + }, + .metal_rough_factors{ + mat.pbrData.metallicFactor, + mat.pbrData.roughnessFactor, + {}, + {}, + }, + }; + // write material parameters to buffer + sceneMaterialConstants[data_index] = constants; + + auto passType = MaterialPass::MainColor; + if (mat.alphaMode == fastgltf::AlphaMode::Blend) { + passType = MaterialPass::Transparent; + } + + GLTFMetallic_Roughness::MaterialResources materialResources{ + // default the material textures + .colorImage{engine._whiteImage}, + .colorSampler{engine._defaultSamplerLinear}, + .metalRoughImage{engine._whiteImage}, + .metalRoughSampler{engine._defaultSamplerLinear}, + }; + + // set the uniform buffer for the material data + materialResources.dataBuffer = file.materialDataBuffer.buffer; + materialResources.dataBufferOffset = + data_index * sizeof(GLTFMetallic_Roughness::MaterialConstants); + + grab_textures_from_GLTF(file, gltf, mat, + materialResources, images); + // build material + newMat->data = engine.metalRoughMaterial.write_material( + engine._device, passType, materialResources, + file.descriptorPool); + + ++data_index; + } +} + +void load_indexes(const fastgltf::Asset& gltf, std::vector& indices, + fastgltf::Primitive& p, size_t initial_vtx) { + { + const fastgltf::Accessor& indexaccessor = + gltf.accessors[p.indicesAccessor.value()]; + indices.reserve(indices.size() + indexaccessor.count); + + fastgltf::iterateAccessor( + gltf, indexaccessor, [&](std::uint32_t idx) { + indices.push_back(idx + initial_vtx); + }); + } +} + +void load_vertex_positions(const fastgltf::Asset& gltf, + std::vector& vertices, + fastgltf::Primitive& p, size_t initial_vtx) { + const fastgltf::Accessor& posAccessor = + gltf.accessors[p.findAttribute("POSITION")->second]; + vertices.resize(vertices.size() + posAccessor.count); + + fastgltf::iterateAccessorWithIndex( + gltf, posAccessor, [&](const glm::vec3 v, size_t index) { + vertices[initial_vtx + index] = { + .position = v, + .normal = {1, 0, 0}, + .color = glm::vec4{1.}, + .uv_x = 0, + .uv_y = 0, + }; + }); +} + +void load_vertex_normals(const fastgltf::Asset& gltf, + std::vector& vertices, fastgltf::Primitive& p, + size_t initial_vtx) { + auto normals = p.findAttribute("NORMAL"); + if (normals != p.attributes.end()) { + fastgltf::iterateAccessorWithIndex( + gltf, gltf.accessors[normals->second], + [&](const glm::vec3 v, size_t index) { + vertices[initial_vtx + index].normal = v; + }); + } +} + +void load_UVs(const fastgltf::Asset& gltf, std::vector& vertices, + fastgltf::Primitive& p, size_t initial_vtx) { + auto uv = p.findAttribute("TEXCOORD_0"); + if (uv != p.attributes.end()) { + fastgltf::iterateAccessorWithIndex( + gltf, gltf.accessors[uv->second], + [&](glm::vec2 v, size_t index) { + vertices[initial_vtx + index].uv_x = v.x; + vertices[initial_vtx + index].uv_y = v.y; + }); + } +} + +void load_vertex_colors(const fastgltf::Asset& gltf, + std::vector& vertices, fastgltf::Primitive& p, + size_t initial_vtx) { + auto colors = p.findAttribute("COLOR_0"); + if (colors != p.attributes.end()) { + fastgltf::iterateAccessorWithIndex( + gltf, gltf.accessors[colors->second], + [&](const glm::vec4 v, size_t index) { + vertices[initial_vtx + index].color = v; + }); + } +} + +void define_new_surface_material( + Mesh::GLTF::GeoSurface& newSurface, fastgltf::Primitive& p, + const std::vector>& + materials) { + if (p.materialIndex.has_value()) { + newSurface.material = materials[p.materialIndex.value()]; + } else { + newSurface.material = materials[0]; + } +} + +void upload_mesh_to_engine( + VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, + fastgltf::Asset& gltf, + std::vector>& meshes, + const std::vector>& + materials) { + // use the same vectors for all meshes so that the memory doesn't reallocate + // as often + std::vector indices; + std::vector vertices; + + for (auto& [primitives, weights, name] : gltf.meshes) { + auto newmesh = std::make_shared(); + meshes.push_back(newmesh); + file.meshes[name.c_str()] = newmesh; + newmesh->name = name; + + // clear the mesh arrays each mesh, we don't want to merge them by error + indices.clear(); + vertices.clear(); + + for (auto&& p : primitives) { + Mesh::GLTF::GeoSurface newSurface; + newSurface.startIndex = static_cast(indices.size()); + newSurface.count = static_cast( + gltf.accessors[p.indicesAccessor.value()].count); + + size_t initial_vtx = vertices.size(); + + load_indexes(gltf, indices, p, initial_vtx); + load_vertex_positions(gltf, vertices, p, + initial_vtx); + load_vertex_normals(gltf, vertices, p, + initial_vtx); + + load_UVs(gltf, vertices, p, initial_vtx); + load_vertex_colors(gltf, vertices, p, + initial_vtx); + define_new_surface_material(newSurface, p, + materials); + newmesh->surfaces.push_back(newSurface); + } + + newmesh->meshBuffers = engine.uploadMesh(indices, vertices); + } +} + +void load_nodes(Mesh::GLTF::LoadedGLTF& file, fastgltf::Asset& gltf, + std::vector>& meshes, + std::vector>& nodes) { + for (fastgltf::Node& node : gltf.nodes) { + std::shared_ptr newNode; + + // find if the node has a mesh, and if it does hook it to the mesh + // pointer and allocate it with the meshnode class + if (node.meshIndex.has_value()) { + newNode = std::make_shared(); + dynamic_cast(newNode.get())->mesh = + meshes[*node.meshIndex]; + } else { + newNode = std::make_shared(); + } + + nodes.push_back(newNode); + file.nodes[node.name.c_str()]; + + std::visit(fastgltf::visitor{ + [&](fastgltf::Node::TransformMatrix matrix) { + memcpy(&newNode->localTransform, matrix.data(), + sizeof(matrix)); + }, + [&](const fastgltf::TRS& transform) { + const glm::vec3 tl(transform.translation[0], + transform.translation[1], + transform.translation[2]); + const glm::quat rot(transform.rotation[3], + transform.rotation[0], + transform.rotation[1], + transform.rotation[2]); + const glm::vec3 sc(transform.scale[0], + transform.scale[1], + transform.scale[2]); + + const glm::mat4 tm = + glm::translate(glm::mat4(1.f), tl); + const glm::mat4 rm = glm::toMat4(rot); + const glm::mat4 sm = + glm::scale(glm::mat4(1.f), sc); + + newNode->localTransform = tm * rm * sm; + }}, + node.transform); + } +} + +void setup_nodes_relationships(Mesh::GLTF::LoadedGLTF& file, + fastgltf::Asset& gltf, + std::vector>& nodes) { + // run loop again to set up transform hierarchy + for (int i = 0; i < gltf.nodes.size(); i++) { + fastgltf::Node& node = gltf.nodes[i]; + const std::shared_ptr& sceneNode = nodes[i]; + + for (const auto& c : node.children) { + sceneNode->children.push_back(nodes[c]); + nodes[c]->parent = sceneNode; + } + } + + // find the top nodes, with no parents + for (auto& node : nodes) { + if (node->parent.lock() == nullptr) { + file.topNodes.push_back(node); + node->refreshTransform(glm::mat4{1.f}); + } + } +} + +std::optional> loadGLTF( + VulkanEngine& engine, std::string_view filePath) { + fmt::print("Loading GLTF: {}", filePath); + + auto scene = std::make_shared(); + scene->creator = &engine; + Mesh::GLTF::LoadedGLTF& file = *scene; + + fastgltf::Asset gltf; + + if (!load_file(gltf, filePath)) { + return {}; + } + + init_descriptor_pool(engine, file, gltf); + + load_samplers(engine, file, gltf); + + std::vector images; + load_all_textures(engine, gltf, images); + + create_material_data_buffer(engine, file, gltf); + + std::vector> materials; + load_material_data(engine, file, gltf, materials, images); + + std::vector> meshes; + upload_mesh_to_engine(engine, file, gltf, meshes, materials); + + std::vector> nodes; + load_nodes(file, gltf, meshes, nodes); + setup_nodes_relationships(file, gltf, nodes); + return scene; +} +} // unnamed namespace + ModelImpl::~ModelImpl() { _engine.cleanup(); } @@ -44,7 +492,7 @@ Mesh::rid_t registerMesh( std::string structurePath = {std::string(ASSETS_DIR) + std::string(filePath)}; - auto structureFile = LoaderGLTF::loadGLTF(engine, structurePath); + auto structureFile = loadGLTF(engine, structurePath); assert(structureFile.has_value()); diff --git a/src/include/scene/LoaderGLTF.h b/src/include/scene/LoaderGLTF.h deleted file mode 100644 index 63d62951..00000000 --- a/src/include/scene/LoaderGLTF.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#include "scene/Mesh.h" - -struct LoaderGLTF { - static std::optional> loadGLTF( - VulkanEngine& engine, std::string_view filePath); -}; diff --git a/src/scene/CMakeLists.txt b/src/scene/CMakeLists.txt index aceeb696..ebc590df 100644 --- a/src/scene/CMakeLists.txt +++ b/src/scene/CMakeLists.txt @@ -5,5 +5,4 @@ target_sources(${PROJECT_NAME} Node.cpp ParentSystem.cpp TransformSystem.cpp - LoaderGLTF.cpp ) diff --git a/src/scene/LoaderGLTF.cpp b/src/scene/LoaderGLTF.cpp deleted file mode 100644 index 2a7a01e2..00000000 --- a/src/scene/LoaderGLTF.cpp +++ /dev/null @@ -1,459 +0,0 @@ -#include "graphics/vulkan/vk_engine.h" -#include "graphics/vulkan/MeshNode.h" -#include "scene/LoaderGLTF.h" - -#define GLM_ENABLE_EXPERIMENTAL - -#include -#include -#include -#include -#include - -VkFilter extract_filter(fastgltf::Filter filter) { - switch (filter) { - // nearest samplers - case fastgltf::Filter::Nearest: - case fastgltf::Filter::NearestMipMapNearest: - case fastgltf::Filter::NearestMipMapLinear: - return VK_FILTER_NEAREST; - - // linear samplers - case fastgltf::Filter::Linear: - case fastgltf::Filter::LinearMipMapNearest: - case fastgltf::Filter::LinearMipMapLinear: - default: - return VK_FILTER_LINEAR; - } -} - -VkSamplerMipmapMode extract_mipmap_mode(fastgltf::Filter filter) { - switch (filter) { - case fastgltf::Filter::NearestMipMapNearest: - case fastgltf::Filter::LinearMipMapNearest: - return VK_SAMPLER_MIPMAP_MODE_NEAREST; - - case fastgltf::Filter::NearestMipMapLinear: - case fastgltf::Filter::LinearMipMapLinear: - default: - return VK_SAMPLER_MIPMAP_MODE_LINEAR; - } -} - -constexpr auto kOptionsGLTF = fastgltf::Options::DontRequireValidAssetMember | - fastgltf::Options::AllowDouble | - fastgltf::Options::LoadGLBBuffers | - fastgltf::Options::LoadExternalBuffers; - -namespace { -bool check_parser_result(fastgltf::Asset& gltf, - fastgltf::Expected& load) { - if (load) { - gltf = std::move(load.get()); - } else { - std::cerr << "Failed to load glTF: " - << fastgltf::to_underlying(load.error()) << std::endl; - return false; - } - return true; -} - -bool load_file(fastgltf::Asset& gltf, std::string_view filePath) { - fastgltf::Parser parser{}; - - fastgltf::GltfDataBuffer data; - data.loadFromFile(filePath); - - std::filesystem::path path = filePath; - - auto type = fastgltf::determineGltfFileType(&data); - if (type == fastgltf::GltfType::glTF) { - auto load = parser.loadGltf(&data, path.parent_path(), kOptionsGLTF); - if (!check_parser_result(gltf, load)) { - return false; - } - } else if (type == fastgltf::GltfType::GLB) { - auto load = - parser.loadGltfBinary(&data, path.parent_path(), kOptionsGLTF); - if (!check_parser_result(gltf, load)) { - return false; - } - } else { - std::cerr << "Failed to determine glTF container" << std::endl; - return false; - } - return true; -} - -void init_descriptor_pool(const VulkanEngine& engine, - Mesh::GLTF::LoadedGLTF& file, - const fastgltf::Asset& gltf) { - // we can stimate the descriptors we will need accurately - std::vector sizes = { - {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3}, - {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3}, - {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}}; - - file.descriptorPool.init(engine._device, gltf.materials.size(), sizes); -} - -void load_samplers(const VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, - fastgltf::Asset& gltf) { - for (auto& [magFilter, minFilter, wrapS, wrapT, name] : gltf.samplers) { - VkSamplerCreateInfo sample = { - .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .pNext = nullptr}; - sample.maxLod = VK_LOD_CLAMP_NONE; - sample.minLod = 0; - - sample.magFilter = - extract_filter(magFilter.value_or(fastgltf::Filter::Nearest)); - sample.minFilter = - extract_filter(minFilter.value_or(fastgltf::Filter::Nearest)); - - sample.mipmapMode = extract_mipmap_mode( - minFilter.value_or(fastgltf::Filter::Nearest)); - - VkSampler newSampler; - vkCreateSampler(engine._device, &sample, nullptr, &newSampler); - - file.samplers.push_back(newSampler); - } -} - -void load_all_textures(const VulkanEngine& engine, fastgltf::Asset& gltf, - std::vector& images) { - for ([[maybe_unused]] fastgltf::Image& image : gltf.images) { - images.push_back(engine._errorCheckerboardImage); - } -} - -void create_material_data_buffer(const VulkanEngine& engine, - Mesh::GLTF::LoadedGLTF& file, - const fastgltf::Asset& gltf) { - file.materialDataBuffer = engine.create_buffer( - sizeof(GLTFMetallic_Roughness::MaterialConstants) * - gltf.materials.size(), - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); -} - -namespace loadMaterialData { -void grab_textures_from_GLTF( - const Mesh::GLTF::LoadedGLTF& file, fastgltf::Asset& gltf, - const fastgltf::Material& mat, - GLTFMetallic_Roughness::MaterialResources& materialResources, - const std::vector& images) { - if (mat.pbrData.baseColorTexture.has_value()) { - const size_t img = - gltf.textures[mat.pbrData.baseColorTexture.value().textureIndex] - .imageIndex.value(); - const size_t sampler = - gltf.textures[mat.pbrData.baseColorTexture.value().textureIndex] - .samplerIndex.value(); - - materialResources.colorImage = images[img]; - materialResources.colorSampler = file.samplers[sampler]; - } -} -} // namespace loadMaterialData - -void load_material_data( - VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, - fastgltf::Asset& gltf, - std::vector>& materials, - const std::vector& images) { - auto* sceneMaterialConstants = - static_cast( - file.materialDataBuffer.info.pMappedData); - - size_t data_index{0}; - for (fastgltf::Material& mat : gltf.materials) { - auto newMat = std::make_shared(); - materials.push_back(newMat); - file.materials[mat.name.c_str()] = newMat; - - const GLTFMetallic_Roughness::MaterialConstants constants{ - .colorFactors{ - mat.pbrData.baseColorFactor[0], - mat.pbrData.baseColorFactor[1], - mat.pbrData.baseColorFactor[2], - mat.pbrData.baseColorFactor[3], - }, - .metal_rough_factors{ - mat.pbrData.metallicFactor, - mat.pbrData.roughnessFactor, - {}, - {}, - }, - }; - // write material parameters to buffer - sceneMaterialConstants[data_index] = constants; - - auto passType = MaterialPass::MainColor; - if (mat.alphaMode == fastgltf::AlphaMode::Blend) { - passType = MaterialPass::Transparent; - } - - GLTFMetallic_Roughness::MaterialResources materialResources{ - // default the material textures - .colorImage{engine._whiteImage}, - .colorSampler{engine._defaultSamplerLinear}, - .metalRoughImage{engine._whiteImage}, - .metalRoughSampler{engine._defaultSamplerLinear}, - }; - - // set the uniform buffer for the material data - materialResources.dataBuffer = file.materialDataBuffer.buffer; - materialResources.dataBufferOffset = - data_index * sizeof(GLTFMetallic_Roughness::MaterialConstants); - - loadMaterialData::grab_textures_from_GLTF(file, gltf, mat, - materialResources, images); - // build material - newMat->data = engine.metalRoughMaterial.write_material( - engine._device, passType, materialResources, - file.descriptorPool); - - ++data_index; - } -} - -namespace uploadMeshToEngine { -void load_indexes(const fastgltf::Asset& gltf, std::vector& indices, - fastgltf::Primitive& p, size_t initial_vtx) { - { - const fastgltf::Accessor& indexaccessor = - gltf.accessors[p.indicesAccessor.value()]; - indices.reserve(indices.size() + indexaccessor.count); - - fastgltf::iterateAccessor( - gltf, indexaccessor, [&](std::uint32_t idx) { - indices.push_back(idx + initial_vtx); - }); - } -} - -void load_vertex_positions(const fastgltf::Asset& gltf, - std::vector& vertices, - fastgltf::Primitive& p, size_t initial_vtx) { - const fastgltf::Accessor& posAccessor = - gltf.accessors[p.findAttribute("POSITION")->second]; - vertices.resize(vertices.size() + posAccessor.count); - - fastgltf::iterateAccessorWithIndex( - gltf, posAccessor, [&](const glm::vec3 v, size_t index) { - vertices[initial_vtx + index] = { - .position = v, - .normal = {1, 0, 0}, - .color = glm::vec4{1.}, - .uv_x = 0, - .uv_y = 0, - }; - }); -} - -void load_vertex_normals(const fastgltf::Asset& gltf, - std::vector& vertices, fastgltf::Primitive& p, - size_t initial_vtx) { - auto normals = p.findAttribute("NORMAL"); - if (normals != p.attributes.end()) { - fastgltf::iterateAccessorWithIndex( - gltf, gltf.accessors[normals->second], - [&](const glm::vec3 v, size_t index) { - vertices[initial_vtx + index].normal = v; - }); - } -} - -void load_UVs(const fastgltf::Asset& gltf, std::vector& vertices, - fastgltf::Primitive& p, size_t initial_vtx) { - auto uv = p.findAttribute("TEXCOORD_0"); - if (uv != p.attributes.end()) { - fastgltf::iterateAccessorWithIndex( - gltf, gltf.accessors[uv->second], - [&](glm::vec2 v, size_t index) { - vertices[initial_vtx + index].uv_x = v.x; - vertices[initial_vtx + index].uv_y = v.y; - }); - } -} - -void load_vertex_colors(const fastgltf::Asset& gltf, - std::vector& vertices, fastgltf::Primitive& p, - size_t initial_vtx) { - auto colors = p.findAttribute("COLOR_0"); - if (colors != p.attributes.end()) { - fastgltf::iterateAccessorWithIndex( - gltf, gltf.accessors[colors->second], - [&](const glm::vec4 v, size_t index) { - vertices[initial_vtx + index].color = v; - }); - } -} - -void define_new_surface_material( - Mesh::GLTF::GeoSurface& newSurface, fastgltf::Primitive& p, - const std::vector>& - materials) { - if (p.materialIndex.has_value()) { - newSurface.material = materials[p.materialIndex.value()]; - } else { - newSurface.material = materials[0]; - } -} - -} // namespace uploadMeshToEngine - -void upload_mesh_to_engine( - VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, - fastgltf::Asset& gltf, - std::vector>& meshes, - const std::vector>& - materials) { - // use the same vectors for all meshes so that the memory doesn't reallocate - // as often - std::vector indices; - std::vector vertices; - - for (auto& [primitives, weights, name] : gltf.meshes) { - auto newmesh = std::make_shared(); - meshes.push_back(newmesh); - file.meshes[name.c_str()] = newmesh; - newmesh->name = name; - - // clear the mesh arrays each mesh, we don't want to merge them by error - indices.clear(); - vertices.clear(); - - for (auto&& p : primitives) { - Mesh::GLTF::GeoSurface newSurface; - newSurface.startIndex = static_cast(indices.size()); - newSurface.count = static_cast( - gltf.accessors[p.indicesAccessor.value()].count); - - size_t initial_vtx = vertices.size(); - - uploadMeshToEngine::load_indexes(gltf, indices, p, initial_vtx); - uploadMeshToEngine::load_vertex_positions(gltf, vertices, p, - initial_vtx); - uploadMeshToEngine::load_vertex_normals(gltf, vertices, p, - initial_vtx); - - uploadMeshToEngine::load_UVs(gltf, vertices, p, initial_vtx); - uploadMeshToEngine::load_vertex_colors(gltf, vertices, p, - initial_vtx); - uploadMeshToEngine::define_new_surface_material(newSurface, p, - materials); - newmesh->surfaces.push_back(newSurface); - } - - newmesh->meshBuffers = engine.uploadMesh(indices, vertices); - } -} - -void load_nodes(Mesh::GLTF::LoadedGLTF& file, fastgltf::Asset& gltf, - std::vector>& meshes, - std::vector>& nodes) { - for (fastgltf::Node& node : gltf.nodes) { - std::shared_ptr newNode; - - // find if the node has a mesh, and if it does hook it to the mesh - // pointer and allocate it with the meshnode class - if (node.meshIndex.has_value()) { - newNode = std::make_shared(); - dynamic_cast(newNode.get())->mesh = - meshes[*node.meshIndex]; - } else { - newNode = std::make_shared(); - } - - nodes.push_back(newNode); - file.nodes[node.name.c_str()]; - - std::visit(fastgltf::visitor{ - [&](fastgltf::Node::TransformMatrix matrix) { - memcpy(&newNode->localTransform, matrix.data(), - sizeof(matrix)); - }, - [&](const fastgltf::TRS& transform) { - const glm::vec3 tl(transform.translation[0], - transform.translation[1], - transform.translation[2]); - const glm::quat rot(transform.rotation[3], - transform.rotation[0], - transform.rotation[1], - transform.rotation[2]); - const glm::vec3 sc(transform.scale[0], - transform.scale[1], - transform.scale[2]); - - const glm::mat4 tm = - glm::translate(glm::mat4(1.f), tl); - const glm::mat4 rm = glm::toMat4(rot); - const glm::mat4 sm = - glm::scale(glm::mat4(1.f), sc); - - newNode->localTransform = tm * rm * sm; - }}, - node.transform); - } -} - -void setup_nodes_relationships(Mesh::GLTF::LoadedGLTF& file, - fastgltf::Asset& gltf, - std::vector>& nodes) { - // run loop again to set up transform hierarchy - for (int i = 0; i < gltf.nodes.size(); i++) { - fastgltf::Node& node = gltf.nodes[i]; - const std::shared_ptr& sceneNode = nodes[i]; - - for (const auto& c : node.children) { - sceneNode->children.push_back(nodes[c]); - nodes[c]->parent = sceneNode; - } - } - - // find the top nodes, with no parents - for (auto& node : nodes) { - if (node->parent.lock() == nullptr) { - file.topNodes.push_back(node); - node->refreshTransform(glm::mat4{1.f}); - } - } -} -} // namespace - -std::optional> -LoaderGLTF::loadGLTF(VulkanEngine& engine, std::string_view filePath) { - fmt::print("Loading GLTF: {}", filePath); - - auto scene = std::make_shared(); - scene->creator = &engine; - Mesh::GLTF::LoadedGLTF& file = *scene; - - fastgltf::Asset gltf; - - if (!load_file(gltf, filePath)) { - return {}; - } - - init_descriptor_pool(engine, file, gltf); - - load_samplers(engine, file, gltf); - - std::vector images; - load_all_textures(engine, gltf, images); - - create_material_data_buffer(engine, file, gltf); - - std::vector> materials; - load_material_data(engine, file, gltf, materials, images); - - std::vector> meshes; - upload_mesh_to_engine(engine, file, gltf, meshes, materials); - - std::vector> nodes; - load_nodes(file, gltf, meshes, nodes); - setup_nodes_relationships(file, gltf, nodes); - return scene; -} From 544ca7472738f4d018a3f3f8a710798241062ae6 Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 21:44:34 +0300 Subject: [PATCH 09/27] refactor: simplify if-else in function --- src/core/ModelImpl.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index 8e0a0b7e..604b1633 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -50,13 +50,12 @@ constexpr auto kOptionsGLTF = fastgltf::Options::DontRequireValidAssetMember | bool check_parser_result(fastgltf::Asset& gltf, fastgltf::Expected& load) { - if (load) { - gltf = std::move(load.get()); - } else { + if (!load) { std::cerr << "Failed to load glTF: " << fastgltf::to_underlying(load.error()) << std::endl; return false; } + gltf = std::move(load.get()); return true; } From 9953d9c5c63212fc8070171d4151d23452c482e0 Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 21:45:11 +0300 Subject: [PATCH 10/27] chore: add const to the variable --- src/core/ModelImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index 604b1633..64ab60ff 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -67,7 +67,7 @@ bool load_file(fastgltf::Asset& gltf, std::string_view filePath) { std::filesystem::path path = filePath; - auto type = fastgltf::determineGltfFileType(&data); + const auto type = fastgltf::determineGltfFileType(&data); if (type == fastgltf::GltfType::glTF) { auto load = parser.loadGltf(&data, path.parent_path(), kOptionsGLTF); if (!check_parser_result(gltf, load)) { From 9cdba6f6b5508873d7c3dedd1e790c74e28faa08 Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 21:45:48 +0300 Subject: [PATCH 11/27] fix if-else, because always return false --- src/core/ModelImpl.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index 64ab60ff..bf5f00b7 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -70,20 +70,15 @@ bool load_file(fastgltf::Asset& gltf, std::string_view filePath) { const auto type = fastgltf::determineGltfFileType(&data); if (type == fastgltf::GltfType::glTF) { auto load = parser.loadGltf(&data, path.parent_path(), kOptionsGLTF); - if (!check_parser_result(gltf, load)) { - return false; - } - } else if (type == fastgltf::GltfType::GLB) { + return check_parser_result(gltf, load); + } + if (type == fastgltf::GltfType::GLB) { auto load = parser.loadGltfBinary(&data, path.parent_path(), kOptionsGLTF); - if (!check_parser_result(gltf, load)) { - return false; - } - } else { - std::cerr << "Failed to determine glTF container" << std::endl; - return false; + return check_parser_result(gltf, load); } - return true; + std::cerr << "Failed to determine glTF container" << std::endl; + return false; } void init_descriptor_pool(const VulkanEngine& engine, From 596d658183c630ba4896134c0876ec9be4096215 Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 21:47:25 +0300 Subject: [PATCH 12/27] refactor: apply designated initializer --- src/core/ModelImpl.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index bf5f00b7..6c80b098 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -80,7 +80,7 @@ bool load_file(fastgltf::Asset& gltf, std::string_view filePath) { std::cerr << "Failed to determine glTF container" << std::endl; return false; } - +eInfo samp void init_descriptor_pool(const VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, const fastgltf::Asset& gltf) { @@ -98,17 +98,15 @@ void load_samplers(const VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, for (auto& [magFilter, minFilter, wrapS, wrapT, name] : gltf.samplers) { VkSamplerCreateInfo sample = { .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .pNext = nullptr}; - sample.maxLod = VK_LOD_CLAMP_NONE; - sample.minLod = 0; - - sample.magFilter = - extract_filter(magFilter.value_or(fastgltf::Filter::Nearest)); - sample.minFilter = - extract_filter(minFilter.value_or(fastgltf::Filter::Nearest)); - - sample.mipmapMode = extract_mipmap_mode( - minFilter.value_or(fastgltf::Filter::Nearest)); + .pNext = nullptr, + .magFilter = + extract_filter(magFilter.value_or(fastgltf::Filter::Nearest)), + .minFilter = + extract_filter(minFilter.value_or(fastgltf::Filter::Nearest)), + .mipmapMode = extract_mipmap_mode( + minFilter.value_or(fastgltf::Filter::Nearest)), + .minLod = 0, + .maxLod = VK_LOD_CLAMP_NONE}; VkSampler newSampler; vkCreateSampler(engine._device, &sample, nullptr, &newSampler); From 4cdc8b7f39a0b642f7c70b20583c079a9ab8475d Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 21:48:09 +0300 Subject: [PATCH 13/27] fix: delete symbols --- src/core/ModelImpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index 6c80b098..55b92e70 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -80,7 +80,7 @@ bool load_file(fastgltf::Asset& gltf, std::string_view filePath) { std::cerr << "Failed to determine glTF container" << std::endl; return false; } -eInfo samp + void init_descriptor_pool(const VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, const fastgltf::Asset& gltf) { @@ -103,7 +103,7 @@ void load_samplers(const VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, extract_filter(magFilter.value_or(fastgltf::Filter::Nearest)), .minFilter = extract_filter(minFilter.value_or(fastgltf::Filter::Nearest)), - .mipmapMode = extract_mipmap_mode( + .mipmapMode = extract_mipmap_mode( minFilter.value_or(fastgltf::Filter::Nearest)), .minLod = 0, .maxLod = VK_LOD_CLAMP_NONE}; From f4c53dd3f456276a25957df71911baa8f9d66def Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 22:03:33 +0300 Subject: [PATCH 14/27] chore: delete useless var --- src/core/ModelImpl.cpp | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index 55b92e70..f10bd1b8 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -164,22 +164,21 @@ void load_material_data( materials.push_back(newMat); file.materials[mat.name.c_str()] = newMat; - const GLTFMetallic_Roughness::MaterialConstants constants{ - .colorFactors{ - mat.pbrData.baseColorFactor[0], - mat.pbrData.baseColorFactor[1], - mat.pbrData.baseColorFactor[2], - mat.pbrData.baseColorFactor[3], - }, - .metal_rough_factors{ - mat.pbrData.metallicFactor, - mat.pbrData.roughnessFactor, - {}, - {}, - }, - }; // write material parameters to buffer - sceneMaterialConstants[data_index] = constants; + sceneMaterialConstants[data_index] = { + .colorFactors{ + mat.pbrData.baseColorFactor[0], + mat.pbrData.baseColorFactor[1], + mat.pbrData.baseColorFactor[2], + mat.pbrData.baseColorFactor[3], + }, + .metal_rough_factors{ + mat.pbrData.metallicFactor, + mat.pbrData.roughnessFactor, + {}, + {}, + }, +};; auto passType = MaterialPass::MainColor; if (mat.alphaMode == fastgltf::AlphaMode::Blend) { From d8b08d9d40ce08449625b63d2f28cfe49e747b6a Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 22:05:11 +0300 Subject: [PATCH 15/27] refactor: apply designated initializer --- src/core/ModelImpl.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index f10bd1b8..dfb77074 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -191,13 +191,12 @@ void load_material_data( .colorSampler{engine._defaultSamplerLinear}, .metalRoughImage{engine._whiteImage}, .metalRoughSampler{engine._defaultSamplerLinear}, + // set the uniform buffer for the material data + .dataBuffer{file.materialDataBuffer.buffer}, + //TODO: the place should be checked because of cast u64 -> u32 + .dataBufferOffset{static_cast(data_index * sizeof(GLTFMetallic_Roughness::MaterialConstants))} }; - // set the uniform buffer for the material data - materialResources.dataBuffer = file.materialDataBuffer.buffer; - materialResources.dataBufferOffset = - data_index * sizeof(GLTFMetallic_Roughness::MaterialConstants); - grab_textures_from_GLTF(file, gltf, mat, materialResources, images); // build material From 65ea896a7210c78d6e02fd3590141c7421d9ea63 Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 22:06:17 +0300 Subject: [PATCH 16/27] chore: delete useless semicolon --- src/core/ModelImpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index dfb77074..0dd32f88 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -178,7 +178,7 @@ void load_material_data( {}, {}, }, -};; +}; auto passType = MaterialPass::MainColor; if (mat.alphaMode == fastgltf::AlphaMode::Blend) { From 5421de1d56686b2f53421f8ee55716a61dc605b7 Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 22:06:57 +0300 Subject: [PATCH 17/27] refactor: move a var in for-init --- src/core/ModelImpl.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index 0dd32f88..070b22e3 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -158,8 +158,7 @@ void load_material_data( static_cast( file.materialDataBuffer.info.pMappedData); - size_t data_index{0}; - for (fastgltf::Material& mat : gltf.materials) { + for (size_t data_index{}; fastgltf::Material& mat : gltf.materials) { auto newMat = std::make_shared(); materials.push_back(newMat); file.materials[mat.name.c_str()] = newMat; From 45ef4bbb0f93ebdc8719ac2ed4b0bc5b92c0b178 Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 22:07:51 +0300 Subject: [PATCH 18/27] chore: add const to vars --- include/IController.h | 21 --------------------- src/core/ModelImpl.cpp | 8 ++++---- 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/include/IController.h b/include/IController.h index 3eb14461..9608f07a 100644 --- a/include/IController.h +++ b/include/IController.h @@ -32,27 +32,6 @@ class IController { */ virtual void init() = 0; - /*! - * \brief Sends data to change the brightness of the backlight. - * - * \param lvl Level of brightness [0; 100]. - * - * This method sends data to the chip to change the brightness level of the - * backlight. The lvl parameter specifies the brightness level from 0 (off) - * to 100 (maximum brightness). - */ - // virtual void setBrightness(uint16_t lvl) const = 0; - - /*! - * \brief Sends data to change the color of the backlight. - * - * \param color Struct with RGB values (each [0; 255]). - * - * This method sends data to the chip to change the color of the backlight. - * The color parameter is a struct containing RGB values, each ranging from - * 0 to 255. - */ - // virtual void setColor(Color color) const = 0; [[nodiscard]] virtual MeshController& getMeshController() = 0; /*! * \brief Shared pointer type for IController. diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index 070b22e3..56b77762 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -243,7 +243,7 @@ void load_vertex_positions(const fastgltf::Asset& gltf, void load_vertex_normals(const fastgltf::Asset& gltf, std::vector& vertices, fastgltf::Primitive& p, size_t initial_vtx) { - auto normals = p.findAttribute("NORMAL"); + const auto normals = p.findAttribute("NORMAL"); if (normals != p.attributes.end()) { fastgltf::iterateAccessorWithIndex( gltf, gltf.accessors[normals->second], @@ -255,7 +255,7 @@ void load_vertex_normals(const fastgltf::Asset& gltf, void load_UVs(const fastgltf::Asset& gltf, std::vector& vertices, fastgltf::Primitive& p, size_t initial_vtx) { - auto uv = p.findAttribute("TEXCOORD_0"); + const auto uv = p.findAttribute("TEXCOORD_0"); if (uv != p.attributes.end()) { fastgltf::iterateAccessorWithIndex( gltf, gltf.accessors[uv->second], @@ -269,7 +269,7 @@ void load_UVs(const fastgltf::Asset& gltf, std::vector& vertices, void load_vertex_colors(const fastgltf::Asset& gltf, std::vector& vertices, fastgltf::Primitive& p, size_t initial_vtx) { - auto colors = p.findAttribute("COLOR_0"); + const auto colors = p.findAttribute("COLOR_0"); if (colors != p.attributes.end()) { fastgltf::iterateAccessorWithIndex( gltf, gltf.accessors[colors->second], @@ -317,7 +317,7 @@ void upload_mesh_to_engine( newSurface.count = static_cast( gltf.accessors[p.indicesAccessor.value()].count); - size_t initial_vtx = vertices.size(); + const size_t initial_vtx = vertices.size(); load_indexes(gltf, indices, p, initial_vtx); load_vertex_positions(gltf, vertices, p, From cc1e4ef079a3e11b323620fc07da511bd7f5bf34 Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 22:08:31 +0300 Subject: [PATCH 19/27] refactor: give MeshController by weak_ptr --- include/IController.h | 2 +- src/core/ControllerImpl.cpp | 29 +++++++++++++++++++++-------- src/include/core/ControllerImpl.h | 4 ++-- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/include/IController.h b/include/IController.h index 9608f07a..aea5cebd 100644 --- a/include/IController.h +++ b/include/IController.h @@ -32,7 +32,7 @@ class IController { */ virtual void init() = 0; - [[nodiscard]] virtual MeshController& getMeshController() = 0; + [[nodiscard]] virtual std::weak_ptr getMeshController() = 0; /*! * \brief Shared pointer type for IController. */ diff --git a/src/core/ControllerImpl.cpp b/src/core/ControllerImpl.cpp index 84fbe573..4a0b0898 100644 --- a/src/core/ControllerImpl.cpp +++ b/src/core/ControllerImpl.cpp @@ -13,7 +13,8 @@ #include ControllerImpl::ControllerImpl(IModel::Ptr model, IView::Ptr view) - : _model(std::move(model)), _view(std::move(view)), _mesh_controller(_model) {} + : _model(std::move(model)), _view(std::move(view)) { +} double getCurrentGlobalTime() { // Get the current time point @@ -29,16 +30,21 @@ double getCurrentGlobalTime() { return seconds.count(); } -void updateCube(const MeshController& mesh_controller, Mesh::rid_t rid, int8_t i) { +void updateCube(const std::weak_ptr& mesh_controller, Mesh::rid_t rid, int8_t i) { const double sinValue = std::sin(getCurrentGlobalTime() + static_cast(i)) * 5.0f; const glm::mat4 scale = glm::scale(glm::vec3{0.2}); const glm::mat4 translation = glm::translate(glm::vec3{static_cast(i) - 2.5f, sinValue, 0}); - mesh_controller.set_transform(rid, scale * translation); + if (const auto sp = mesh_controller.lock()) { + sp->set_transform(rid, scale * translation); + } else { + throw std::runtime_error("Cubes could not be transformed"); + } } -void updateCubes(const IModel::Ptr& model, const MeshController& mesh_controller) { +void updateCubes(const IModel::Ptr& model, + const std::weak_ptr& mesh_controller) { auto meshes = model->get_meshes(); for (int8_t i {}; const auto& key : std::views::keys(meshes)) { updateCube(mesh_controller, key, i); @@ -46,7 +52,7 @@ void updateCubes(const IModel::Ptr& model, const MeshController& mesh_controller } } -void update(const IModel::Ptr& model, const MeshController& mesh_controller) { +void update(const IModel::Ptr& model, const std::weak_ptr& mesh_controller) { model->get_engine().update(model); model->getCamera()->update(); @@ -54,9 +60,13 @@ void update(const IModel::Ptr& model, const MeshController& mesh_controller) { updateCubes(model, mesh_controller); } -void createCubes(const MeshController& mesh_controller) { +void createCubes(const std::weak_ptr& mesh_controller) { for (int i = 0; i < 5; i++) { - mesh_controller.create_mesh("/basicmesh.glb"); + if (const auto sp = mesh_controller.lock()) { + sp->create_mesh("/basicmesh.glb"); + } else { + throw std::runtime_error("Cubes could not be created"); + } } } @@ -101,6 +111,9 @@ void ControllerImpl::init() { } -MeshController& ControllerImpl::getMeshController() { +std::weak_ptr ControllerImpl::getMeshController() { + if (!_mesh_controller) { + _mesh_controller = std::make_shared(_model); + } return _mesh_controller; } diff --git a/src/include/core/ControllerImpl.h b/src/include/core/ControllerImpl.h index 29b97f56..6cc0f2fd 100644 --- a/src/include/core/ControllerImpl.h +++ b/src/include/core/ControllerImpl.h @@ -12,10 +12,10 @@ class ControllerImpl : public IController { explicit ControllerImpl(IModel::Ptr model, IView::Ptr view); void init() override; - MeshController& getMeshController() final; + std::weak_ptr getMeshController() final; private: IModel::Ptr _model; IView::Ptr _view; - MeshController _mesh_controller; + std::shared_ptr _mesh_controller; }; From c80cb3ba8fa19b7eb377fc2c2ac9782e90378a79 Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 22:09:46 +0300 Subject: [PATCH 20/27] refactor: removing the code logic from CtrlImpl --- demo/main.cpp | 89 ++++++++++++++++++++++++- include/IController.h | 10 ++- src/core/ControllerImpl.cpp | 106 ++++-------------------------- src/core/MeshController.cpp | 11 ++++ src/core/ViewImpl.cpp | 1 - src/include/core/ControllerImpl.h | 4 +- src/include/core/MeshController.h | 2 + 7 files changed, 117 insertions(+), 106 deletions(-) diff --git a/demo/main.cpp b/demo/main.cpp index 99434856..2ff15de9 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -1,13 +1,96 @@ -#include +#define GLM_ENABLE_EXPERIMENTAL +#include +#include +#include #include "IController.h" +void createCubes(const std::weak_ptr& mesh_controller) { + for (int i = 0; i < 5; i++) { + if (const auto sp = mesh_controller.lock()) { + sp->create_mesh("/basicmesh.glb"); + } else { + throw std::runtime_error("Cubes could not be created"); + } + } +} + +double getCurrentGlobalTime() { + // Get the current time point + const auto now = std::chrono::system_clock::now(); + + // Cast to a time duration since the epoch + const auto durationSinceEpoch = now.time_since_epoch(); + + // Convert to seconds in double precision + const std::chrono::duration seconds = durationSinceEpoch; + + // Return the double value + return seconds.count(); +} + +void updateCube(const std::shared_ptr& mesh_controller, Mesh::rid_t rid, int8_t i) { + const double sinValue = std::sin(getCurrentGlobalTime() + static_cast(i)) * 5.; + + const glm::mat4 scale = glm::scale(glm::vec3{0.2f}); + const glm::mat4 translation = glm::translate(glm::vec3{static_cast(i) - 2.5f, sinValue, 0}); + mesh_controller->set_transform(rid, scale * translation); +} + +void updateCubes(const std::weak_ptr& mesh_controller) { + if (const auto sp = mesh_controller.lock()) { + auto meshes = sp->get_meshes(); + for (int8_t i {}; const auto key : meshes) { + updateCube(sp, key, i); + ++i; + } + } else { + throw std::runtime_error("Cubes could not be updated"); + } +} + int main([[maybe_unused]] int argc, [[maybe_unused]] char* args[]) { try { - auto controller = createInstance(); - controller->init(); + const auto controller = createInstance(); + const auto mesh_controller = controller->getMeshController(); + createCubes(mesh_controller); + + SDL_Event e; + bool bQuit = false; + bool stop_rendering = false; + + // main loop + while (!bQuit) { + // Handle events on queue + while (SDL_PollEvent(&e) != 0) { + // close the window when user alt-f4s or clicks the X button + if (e.type == SDL_QUIT) bQuit = true; + + if (e.type == SDL_WINDOWEVENT) { + if (e.window.event == SDL_WINDOWEVENT_MINIMIZED) { + stop_rendering = true; + } + if (e.window.event == SDL_WINDOWEVENT_RESTORED) { + stop_rendering = false; + } + } + + controller->process_event(e); + } + + // do not draw if we are minimized + if (stop_rendering) { + // throttle the speed to avoid the endless spinning + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + continue; + } + + controller->run(); + controller->update(); + updateCubes(mesh_controller); + } } catch (std::runtime_error const& e) { std::cerr << "Unhandled exception: " << e.what() << '\n'; diff --git a/include/IController.h b/include/IController.h index aea5cebd..9621e5b5 100644 --- a/include/IController.h +++ b/include/IController.h @@ -25,14 +25,12 @@ class IController { */ virtual ~IController() = default; - /*! - * \brief Initializes the controller. - * - * This method initializes the necessary components for the controller. - */ - virtual void init() = 0; + virtual void run() = 0; + virtual void update() = 0; [[nodiscard]] virtual std::weak_ptr getMeshController() = 0; + + virtual void process_event(const SDL_Event& e) = 0; /*! * \brief Shared pointer type for IController. */ diff --git a/src/core/ControllerImpl.cpp b/src/core/ControllerImpl.cpp index 4a0b0898..98457043 100644 --- a/src/core/ControllerImpl.cpp +++ b/src/core/ControllerImpl.cpp @@ -5,115 +5,31 @@ #include #include -#define GLM_ENABLE_EXPERIMENTAL #include -#include -#include #include -#include ControllerImpl::ControllerImpl(IModel::Ptr model, IView::Ptr view) : _model(std::move(model)), _view(std::move(view)) { } -double getCurrentGlobalTime() { - // Get the current time point - const auto now = std::chrono::system_clock::now(); - - // Cast to a time duration since the epoch - const auto durationSinceEpoch = now.time_since_epoch(); - - // Convert to seconds in double precision - const std::chrono::duration seconds = durationSinceEpoch; - - // Return the double value - return seconds.count(); -} - -void updateCube(const std::weak_ptr& mesh_controller, Mesh::rid_t rid, int8_t i) { - const double sinValue = std::sin(getCurrentGlobalTime() + static_cast(i)) * 5.0f; - - const glm::mat4 scale = glm::scale(glm::vec3{0.2}); - const glm::mat4 translation = glm::translate(glm::vec3{static_cast(i) - 2.5f, sinValue, 0}); - - if (const auto sp = mesh_controller.lock()) { - sp->set_transform(rid, scale * translation); - } else { - throw std::runtime_error("Cubes could not be transformed"); - } -} - -void updateCubes(const IModel::Ptr& model, - const std::weak_ptr& mesh_controller) { - auto meshes = model->get_meshes(); - for (int8_t i {}; const auto& key : std::views::keys(meshes)) { - updateCube(mesh_controller, key, i); - ++i; - } -} - -void update(const IModel::Ptr& model, const std::weak_ptr& mesh_controller) { - model->get_engine().update(model); - - model->getCamera()->update(); - - updateCubes(model, mesh_controller); +void ControllerImpl::update() { + _model->get_engine().update(_model); + _model->getCamera()->update(); } -void createCubes(const std::weak_ptr& mesh_controller) { - for (int i = 0; i < 5; i++) { - if (const auto sp = mesh_controller.lock()) { - sp->create_mesh("/basicmesh.glb"); - } else { - throw std::runtime_error("Cubes could not be created"); - } - } +void ControllerImpl::run() { + _view->run(); } -void ControllerImpl::init() { - const auto mesh_controller = this->getMeshController(); - createCubes(mesh_controller); - - SDL_Event e; - bool bQuit = false; - bool stop_rendering = false; - - // main loop - while (!bQuit) { - // Handle events on queue - while (SDL_PollEvent(&e) != 0) { - // close the window when user alt-f4s or clicks the X button - if (e.type == SDL_QUIT) bQuit = true; - - if (e.type == SDL_WINDOWEVENT) { - if (e.window.event == SDL_WINDOWEVENT_MINIMIZED) { - stop_rendering = true; - } - if (e.window.event == SDL_WINDOWEVENT_RESTORED) { - stop_rendering = false; - } - } - - _view->process_event(e); - } - - // do not draw if we are minimized - if (stop_rendering) { - // throttle the speed to avoid the endless spinning - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - continue; - } - - _view->run(); - - update(_model, mesh_controller); - } -} - - std::weak_ptr ControllerImpl::getMeshController() { if (!_mesh_controller) { _mesh_controller = std::make_shared(_model); } return _mesh_controller; } + +void ControllerImpl::process_event(const SDL_Event& e) { + _model->getCamera()->processSDLEvent(e); + _view->process_event(e); +} + diff --git a/src/core/MeshController.cpp b/src/core/MeshController.cpp index 5964a280..d75b49df 100644 --- a/src/core/MeshController.cpp +++ b/src/core/MeshController.cpp @@ -1,6 +1,7 @@ #include "core/MeshController.h" #include +#include #include "scene/Mesh.h" @@ -26,3 +27,13 @@ glm::mat4 MeshController::get_transform(Mesh::rid_t id) const { const auto transform = _model->get_mesh_transform(id); return transform; } + +std::vector MeshController::get_meshes() const { + const auto map = _model->get_meshes(); + std::vector v; + v.reserve(map.size()); + for (const auto rid : std::views::keys(map)) { + v.push_back(rid); + } + return v; +} diff --git a/src/core/ViewImpl.cpp b/src/core/ViewImpl.cpp index 349ddf87..c08247b0 100644 --- a/src/core/ViewImpl.cpp +++ b/src/core/ViewImpl.cpp @@ -47,7 +47,6 @@ void ViewImpl::run() const { } void ViewImpl::process_event(const SDL_Event& e) { - _model->getCamera()->processSDLEvent(e); ImGui_ImplSDL2_ProcessEvent(&e); } diff --git a/src/include/core/ControllerImpl.h b/src/include/core/ControllerImpl.h index 6cc0f2fd..6e0ad27e 100644 --- a/src/include/core/ControllerImpl.h +++ b/src/include/core/ControllerImpl.h @@ -11,8 +11,10 @@ class ControllerImpl : public IController { public: explicit ControllerImpl(IModel::Ptr model, IView::Ptr view); - void init() override; + void run() final; + void update() final; std::weak_ptr getMeshController() final; + void process_event(const SDL_Event& e) final; private: IModel::Ptr _model; diff --git a/src/include/core/MeshController.h b/src/include/core/MeshController.h index b4684f43..3c4bf081 100644 --- a/src/include/core/MeshController.h +++ b/src/include/core/MeshController.h @@ -17,6 +17,8 @@ class MeshController { void set_transform(Mesh::rid_t id, glm::mat4 t) const; [[nodiscard]] glm::mat4 get_transform(Mesh::rid_t id) const; + + [[nodiscard]] std::vector get_meshes() const; private: IModel::Ptr _model; }; \ No newline at end of file From e6d1800f321b6b6f63fe55a6e786a6afd74be4ad Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 22:10:49 +0300 Subject: [PATCH 21/27] chore: add description to classes --- include/IController.h | 17 ++++++++++++++--- src/include/core/MeshController.h | 22 ++++++++++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/include/IController.h b/include/IController.h index 9621e5b5..d80f24ac 100644 --- a/include/IController.h +++ b/include/IController.h @@ -24,12 +24,23 @@ class IController { * \brief Virtual destructor for the interface. */ virtual ~IController() = default; - + /*! + * \brief Renders a new frame + */ virtual void run() = 0; + /*! + * \brief Applies updates + */ virtual void update() = 0; - + /*! + * \brief Gets a controller to interact with meshes + * \return Pointer to the MeshController + */ [[nodiscard]] virtual std::weak_ptr getMeshController() = 0; - + /*! + * \brief Handles user actions (SDL events) + * @param e SDL event + */ virtual void process_event(const SDL_Event& e) = 0; /*! * \brief Shared pointer type for IController. diff --git a/src/include/core/MeshController.h b/src/include/core/MeshController.h index 3c4bf081..32bf6d11 100644 --- a/src/include/core/MeshController.h +++ b/src/include/core/MeshController.h @@ -6,18 +6,36 @@ #include "scene/Mesh.h" #include "interfaces/IModel.h" +/*! \brief + * A class that allows you to add, delete, modify meshes + */ class MeshController { public: MeshController() = delete; explicit MeshController(IModel::Ptr model); ~MeshController() = default; - + /*! \brief Adds a mesh to the ones to be rendered + * \param file_path The path to the mesh + */ void create_mesh(std::string_view file_path) const; + /*! \brief Removes meshes from those to be rendered + * \param id Render-id of the mesh + */ void delete_mesh(Mesh::rid_t id) const; + /*! \brief Sets a transform matrix of a mesh + * \param id Render-id of the mesh + * \param t Transform matrix + */ void set_transform(Mesh::rid_t id, glm::mat4 t) const; + /*! \brief Gets a transform matrix of a mesh + * \param id Render-id of the mesh + * \return Returns a transform matrix of the mesh + */ [[nodiscard]] glm::mat4 get_transform(Mesh::rid_t id) const; - + /*! \brief Gets all meshes render-ids + * \return Returns a vector of all render-ids + */ [[nodiscard]] std::vector get_meshes() const; private: IModel::Ptr _model; From a2722cb1ada410468141e0a030d45247db97182b Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 22:11:29 +0300 Subject: [PATCH 22/27] refactor: delete a redundant arg --- src/core/MeshController.cpp | 4 +--- src/core/ModelImpl.cpp | 4 ++-- src/include/core/ModelImpl.h | 2 +- src/include/interfaces/IModel.h | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/core/MeshController.cpp b/src/core/MeshController.cpp index d75b49df..0ba34043 100644 --- a/src/core/MeshController.cpp +++ b/src/core/MeshController.cpp @@ -8,9 +8,7 @@ MeshController::MeshController(IModel::Ptr model) : _model(std::move(model)) {} void MeshController::create_mesh(std::string_view file_path) const { - VulkanEngine& engine = _model->get_engine(); - - _model->createMesh(engine, file_path); + _model->createMesh(file_path); //TODO: вернуть возвращемое значение // return rid; } diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index 56b77762..60243c37 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -492,9 +492,9 @@ Mesh::rid_t registerMesh( return random_rid_t; } -void ModelImpl::createMesh(VulkanEngine& engine, std::string_view file_path) { +void ModelImpl::createMesh(std::string_view file_path) { assert(_engine._isInitialized); - const Mesh::rid_t rid = registerMesh(engine, _meshes, file_path); + const Mesh::rid_t rid = registerMesh(_engine, _meshes, file_path); _meshes[rid].transform = 1.; // TODO вернуть возвращаемое значение // return rid; diff --git a/src/include/core/ModelImpl.h b/src/include/core/ModelImpl.h index c49de617..78507912 100644 --- a/src/include/core/ModelImpl.h +++ b/src/include/core/ModelImpl.h @@ -28,7 +28,7 @@ class ModelImpl : public IModel { void registerWindow(struct SDL_Window *window) override; - void createMesh(VulkanEngine& engine, std::string_view file_path) final; + void createMesh(std::string_view file_path) final; void delete_mesh(Mesh::rid_t rid) final; void setMeshTransform(Mesh::rid_t rid, glm::mat4x4 transform) final; diff --git a/src/include/interfaces/IModel.h b/src/include/interfaces/IModel.h index 873f80a3..d11bd7dc 100644 --- a/src/include/interfaces/IModel.h +++ b/src/include/interfaces/IModel.h @@ -48,7 +48,7 @@ class IModel { * * This method creates a new mesh identified by the provided name. */ - virtual void createMesh(VulkanEngine& engine, std::string_view file_path) = 0; + virtual void createMesh(std::string_view file_path) = 0; virtual void delete_mesh(Mesh::rid_t rid) = 0; From 89a68a12a779d99439d8c71e63d149d8f583c357 Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 22:12:09 +0300 Subject: [PATCH 23/27] refactor: string_view to filesystem::path --- src/core/MeshController.cpp | 2 +- src/core/ModelImpl.cpp | 6 +++--- src/include/core/MeshController.h | 2 +- src/include/core/ModelImpl.h | 2 +- src/include/interfaces/IModel.h | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/MeshController.cpp b/src/core/MeshController.cpp index 0ba34043..dcb4bb9a 100644 --- a/src/core/MeshController.cpp +++ b/src/core/MeshController.cpp @@ -7,7 +7,7 @@ MeshController::MeshController(IModel::Ptr model) : _model(std::move(model)) {} -void MeshController::create_mesh(std::string_view file_path) const { +void MeshController::create_mesh(std::filesystem::path file_path) const { _model->createMesh(file_path); //TODO: вернуть возвращемое значение // return rid; diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index 60243c37..358fc424 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -467,7 +467,7 @@ Camera* ModelImpl::getCamera() { Mesh::rid_t registerMesh( VulkanEngine& engine, ModelImpl::MeshMap& meshes, - std::string_view filePath) { + const std::filesystem::path& filePath) { std::random_device rd; // Use the Mersenne Twister engine for high-quality random numbers @@ -480,7 +480,7 @@ Mesh::rid_t registerMesh( const Mesh::rid_t random_rid_t = distribution(generator); std::string structurePath = {std::string(ASSETS_DIR) + - std::string(filePath)}; + filePath.string()}; auto structureFile = loadGLTF(engine, structurePath); assert(structureFile.has_value()); @@ -492,7 +492,7 @@ Mesh::rid_t registerMesh( return random_rid_t; } -void ModelImpl::createMesh(std::string_view file_path) { +void ModelImpl::createMesh(const std::filesystem::path& file_path) { assert(_engine._isInitialized); const Mesh::rid_t rid = registerMesh(_engine, _meshes, file_path); _meshes[rid].transform = 1.; diff --git a/src/include/core/MeshController.h b/src/include/core/MeshController.h index 32bf6d11..a0bfb805 100644 --- a/src/include/core/MeshController.h +++ b/src/include/core/MeshController.h @@ -17,7 +17,7 @@ class MeshController { /*! \brief Adds a mesh to the ones to be rendered * \param file_path The path to the mesh */ - void create_mesh(std::string_view file_path) const; + void create_mesh(std::filesystem::path file_path) const; /*! \brief Removes meshes from those to be rendered * \param id Render-id of the mesh */ diff --git a/src/include/core/ModelImpl.h b/src/include/core/ModelImpl.h index 78507912..46cc0dc3 100644 --- a/src/include/core/ModelImpl.h +++ b/src/include/core/ModelImpl.h @@ -28,7 +28,7 @@ class ModelImpl : public IModel { void registerWindow(struct SDL_Window *window) override; - void createMesh(std::string_view file_path) final; + void createMesh(const std::filesystem::path& file_path) final; void delete_mesh(Mesh::rid_t rid) final; void setMeshTransform(Mesh::rid_t rid, glm::mat4x4 transform) final; diff --git a/src/include/interfaces/IModel.h b/src/include/interfaces/IModel.h index d11bd7dc..3b5b84ce 100644 --- a/src/include/interfaces/IModel.h +++ b/src/include/interfaces/IModel.h @@ -48,7 +48,7 @@ class IModel { * * This method creates a new mesh identified by the provided name. */ - virtual void createMesh(std::string_view file_path) = 0; + virtual void createMesh(const std::filesystem::path& file_path) = 0; virtual void delete_mesh(Mesh::rid_t rid) = 0; From 4cec30dd3d50299a0a47f4c2d968fa3197b08850 Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 22:12:49 +0300 Subject: [PATCH 24/27] refactor: return value return by create_mesh() --- src/core/MeshController.cpp | 5 ++--- src/core/ModelImpl.cpp | 5 ++--- src/include/core/MeshController.h | 3 ++- src/include/core/ModelImpl.h | 2 +- src/include/interfaces/IModel.h | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/core/MeshController.cpp b/src/core/MeshController.cpp index dcb4bb9a..07af4814 100644 --- a/src/core/MeshController.cpp +++ b/src/core/MeshController.cpp @@ -7,10 +7,9 @@ MeshController::MeshController(IModel::Ptr model) : _model(std::move(model)) {} -void MeshController::create_mesh(std::filesystem::path file_path) const { - _model->createMesh(file_path); +Mesh::rid_t MeshController::create_mesh(std::filesystem::path file_path) const { //TODO: вернуть возвращемое значение - // return rid; + return _model->createMesh(file_path); } void MeshController::delete_mesh(Mesh::rid_t id) const { diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index 358fc424..5fc25d55 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -492,12 +492,11 @@ Mesh::rid_t registerMesh( return random_rid_t; } -void ModelImpl::createMesh(const std::filesystem::path& file_path) { +Mesh::rid_t ModelImpl::createMesh(const std::filesystem::path& file_path) { assert(_engine._isInitialized); const Mesh::rid_t rid = registerMesh(_engine, _meshes, file_path); _meshes[rid].transform = 1.; -// TODO вернуть возвращаемое значение - // return rid; + return rid; } void ModelImpl::setMeshTransform(Mesh::rid_t rid, glm::mat4x4 transform) { diff --git a/src/include/core/MeshController.h b/src/include/core/MeshController.h index a0bfb805..6c3cd2bc 100644 --- a/src/include/core/MeshController.h +++ b/src/include/core/MeshController.h @@ -16,8 +16,9 @@ class MeshController { ~MeshController() = default; /*! \brief Adds a mesh to the ones to be rendered * \param file_path The path to the mesh + * \return Returns render-id of the added mesh */ - void create_mesh(std::filesystem::path file_path) const; + Mesh::rid_t create_mesh(std::filesystem::path file_path) const; /*! \brief Removes meshes from those to be rendered * \param id Render-id of the mesh */ diff --git a/src/include/core/ModelImpl.h b/src/include/core/ModelImpl.h index 46cc0dc3..a221f330 100644 --- a/src/include/core/ModelImpl.h +++ b/src/include/core/ModelImpl.h @@ -28,7 +28,7 @@ class ModelImpl : public IModel { void registerWindow(struct SDL_Window *window) override; - void createMesh(const std::filesystem::path& file_path) final; + Mesh::rid_t createMesh(const std::filesystem::path& file_path) final; void delete_mesh(Mesh::rid_t rid) final; void setMeshTransform(Mesh::rid_t rid, glm::mat4x4 transform) final; diff --git a/src/include/interfaces/IModel.h b/src/include/interfaces/IModel.h index 3b5b84ce..3422b051 100644 --- a/src/include/interfaces/IModel.h +++ b/src/include/interfaces/IModel.h @@ -48,7 +48,7 @@ class IModel { * * This method creates a new mesh identified by the provided name. */ - virtual void createMesh(const std::filesystem::path& file_path) = 0; + virtual Mesh::rid_t createMesh(const std::filesystem::path& file_path) = 0; virtual void delete_mesh(Mesh::rid_t rid) = 0; From 30f5ed23301e7ac520a036bfe727a21d13f99dfe Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 22:14:45 +0300 Subject: [PATCH 25/27] fix descriptors --- src/core/MeshController.cpp | 2 +- src/core/ModelImpl.cpp | 19 ++++----- src/include/core/MeshController.h | 2 +- src/include/core/ModelImpl.h | 3 -- src/include/graphics/vulkan/vk_types.h | 4 +- src/include/interfaces/IModel.h | 58 +++++++++++++++++--------- src/include/scene/Mesh.h | 9 ---- 7 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/core/MeshController.cpp b/src/core/MeshController.cpp index 07af4814..416ba851 100644 --- a/src/core/MeshController.cpp +++ b/src/core/MeshController.cpp @@ -7,7 +7,7 @@ MeshController::MeshController(IModel::Ptr model) : _model(std::move(model)) {} -Mesh::rid_t MeshController::create_mesh(std::filesystem::path file_path) const { +Mesh::rid_t MeshController::create_mesh(const std::filesystem::path& file_path) const { //TODO: вернуть возвращемое значение return _model->createMesh(file_path); } diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index 5fc25d55..3058bb3f 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -90,7 +90,7 @@ void init_descriptor_pool(const VulkanEngine& engine, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}}; - file.descriptorPool.init(engine._device, gltf.materials.size(), sizes); + file.descriptorPool.init(engine._device, static_cast(gltf.materials.size()), sizes); } void load_samplers(const VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, @@ -186,14 +186,13 @@ void load_material_data( GLTFMetallic_Roughness::MaterialResources materialResources{ // default the material textures - .colorImage{engine._whiteImage}, - .colorSampler{engine._defaultSamplerLinear}, - .metalRoughImage{engine._whiteImage}, - .metalRoughSampler{engine._defaultSamplerLinear}, + engine._whiteImage, + engine._defaultSamplerLinear, + engine._whiteImage, + engine._defaultSamplerLinear, // set the uniform buffer for the material data - .dataBuffer{file.materialDataBuffer.buffer}, - //TODO: the place should be checked because of cast u64 -> u32 - .dataBufferOffset{static_cast(data_index * sizeof(GLTFMetallic_Roughness::MaterialConstants))} + file.materialDataBuffer.buffer, + static_cast(data_index * sizeof(GLTFMetallic_Roughness::MaterialConstants)) }; grab_textures_from_GLTF(file, gltf, mat, @@ -216,7 +215,7 @@ void load_indexes(const fastgltf::Asset& gltf, std::vector& indices, fastgltf::iterateAccessor( gltf, indexaccessor, [&](std::uint32_t idx) { - indices.push_back(idx + initial_vtx); + indices.push_back(idx + static_cast(initial_vtx)); }); } } @@ -389,7 +388,7 @@ void setup_nodes_relationships(Mesh::GLTF::LoadedGLTF& file, fastgltf::Asset& gltf, std::vector>& nodes) { // run loop again to set up transform hierarchy - for (int i = 0; i < gltf.nodes.size(); i++) { + for (size_t i = 0; i < gltf.nodes.size(); i++) { fastgltf::Node& node = gltf.nodes[i]; const std::shared_ptr& sceneNode = nodes[i]; diff --git a/src/include/core/MeshController.h b/src/include/core/MeshController.h index 6c3cd2bc..ff70558f 100644 --- a/src/include/core/MeshController.h +++ b/src/include/core/MeshController.h @@ -18,7 +18,7 @@ class MeshController { * \param file_path The path to the mesh * \return Returns render-id of the added mesh */ - Mesh::rid_t create_mesh(std::filesystem::path file_path) const; + Mesh::rid_t create_mesh(const std::filesystem::path& file_path) const; /*! \brief Removes meshes from those to be rendered * \param id Render-id of the mesh */ diff --git a/src/include/core/ModelImpl.h b/src/include/core/ModelImpl.h index a221f330..724a2410 100644 --- a/src/include/core/ModelImpl.h +++ b/src/include/core/ModelImpl.h @@ -14,9 +14,6 @@ class Mesh; class ModelImpl : public IModel { public: - - - using MeshMap = std::unordered_map; /*! \brief * throws std::runtime_error() */ diff --git a/src/include/graphics/vulkan/vk_types.h b/src/include/graphics/vulkan/vk_types.h index 9ed95d32..016fea2b 100644 --- a/src/include/graphics/vulkan/vk_types.h +++ b/src/include/graphics/vulkan/vk_types.h @@ -41,10 +41,10 @@ struct AllocatedBuffer { struct Vertex { glm::vec3 position; - float uv_x; glm::vec3 normal; - float uv_y; glm::vec4 color; + float uv_x; + float uv_y; }; // holds the resources needed for a mesh diff --git a/src/include/interfaces/IModel.h b/src/include/interfaces/IModel.h index 3422b051..356ebe12 100644 --- a/src/include/interfaces/IModel.h +++ b/src/include/interfaces/IModel.h @@ -41,36 +41,50 @@ class IModel { virtual void registerWindow(struct SDL_Window* window) = 0; /*! - * \brief Creates a new mesh with the given name. - * - * \param engine - * \param file_path - * - * This method creates a new mesh identified by the provided name. + * \brief Loads (parses) a new mesh to the model + * \param file_path The path to the mesh + * \return Returns render-id of the mesh that helps identify the provided mesh. */ virtual Mesh::rid_t createMesh(const std::filesystem::path& file_path) = 0; - + /*! + * \brief Deletes the provided mesh from a storage + * @param rid Render-id of the mesh to be deleted + */ virtual void delete_mesh(Mesh::rid_t rid) = 0; /*! - * \brief Sets the transformation matrix for a mesh. - * - * \param rid + * \brief Sets the transformation matrix for a mesh identified by render-id + * \param rid Render-id of the mesh to be transformed * \param transform Transformation matrix to be applied to the mesh. - * - * This method sets the transformation matrix for the mesh identified by the - * provided name. */ virtual void setMeshTransform(Mesh::rid_t rid, glm::mat4x4 transform) = 0; + /*! + * \brief Gets the transformation matrix of a mesh. + * \param rid Render-id of the mesh + * \return Returns a transform matrix of the mesh + */ + virtual glm::mat4 get_mesh_transform(Mesh::rid_t rid) = 0; - virtual glm::mat4 get_mesh_transform(Mesh::rid_t) = 0; - - struct MeshPair { - std::shared_ptr ptr; - glm::mat4 transform; + /*! + * \brief Stores mesh data + */ +struct MeshPair { + /*! + * \brief Pointer to the loaded mesh (GLTF) + */ +std::shared_ptr ptr; + /*! + * \brief Transform matrix of the mesh + */ +glm::mat4 transform; }; using MeshMap = std::unordered_map; - virtual const MeshMap& get_meshes() = 0; +/*! + * \brief Gets all the stored meshes + * \return Returns a MeshMap that stores pairs of the render-id of the mesh and + * the mesh data + */ +virtual const MeshMap& get_meshes() = 0; /*! * \brief Retrieves the camera instance. @@ -82,7 +96,11 @@ class IModel { */ [[nodiscard]] virtual Camera* getCamera() = 0; - virtual VulkanEngine& get_engine() = 0; + /*! + * \brief Gets the stored engine + * \return Returns a ref to the engine + */ +virtual VulkanEngine& get_engine() = 0; using Ptr = std::shared_ptr; }; diff --git a/src/include/scene/Mesh.h b/src/include/scene/Mesh.h index ad08fd5d..df095ba9 100644 --- a/src/include/scene/Mesh.h +++ b/src/include/scene/Mesh.h @@ -7,17 +7,8 @@ class VulkanEngine; namespace Mesh { -// Mesh(std::string filePath); -// ~Mesh(); -// -// void set_transform(glm::mat4 t); -// glm::mat4 get_transform(glm::mat4 t); using rid_t = int64_t; -// ид меша -// матрица трансформации -// glm::mat4 transform; - namespace GLTF { struct GLTFMaterial { MaterialInstance data; From 32a3bea5f51a551a5d480e81bed84a6bbf79933f Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 22:16:32 +0300 Subject: [PATCH 26/27] fix --- include/IController.h | 3 +- src/core/ControllerImpl.cpp | 12 +- src/core/MeshController.cpp | 10 +- src/core/Model.cpp | 2 +- src/core/ModelImpl.cpp | 132 ++++---- src/core/ViewImpl.cpp | 26 +- src/core/createInstance.cpp | 1 - src/graphics/vulkan/MeshNode.cpp | 5 +- src/graphics/vulkan/RenderableGLTF.cpp | 14 +- src/graphics/vulkan/vk_engine.cpp | 300 ++++++++++--------- src/include/core/ControllerImpl.h | 2 + src/include/core/MeshController.h | 9 +- src/include/core/ModelImpl.h | 12 +- src/include/core/View.h | 1 - src/include/core/ViewImpl.h | 3 +- src/include/graphics/vulkan/MeshNode.h | 7 +- src/include/graphics/vulkan/RenderableGLTF.h | 8 +- src/include/graphics/vulkan/vk_engine.h | 30 +- src/include/graphics/vulkan/vk_types.h | 6 +- src/include/interfaces/IModel.h | 43 +-- src/include/interfaces/IView.h | 1 - src/include/scene/Mesh.h | 5 +- 22 files changed, 338 insertions(+), 294 deletions(-) diff --git a/include/IController.h b/include/IController.h index d80f24ac..28ea1879 100644 --- a/include/IController.h +++ b/include/IController.h @@ -36,7 +36,8 @@ class IController { * \brief Gets a controller to interact with meshes * \return Pointer to the MeshController */ - [[nodiscard]] virtual std::weak_ptr getMeshController() = 0; + [[nodiscard]] virtual std::weak_ptr + getMeshController() = 0; /*! * \brief Handles user actions (SDL events) * @param e SDL event diff --git a/src/core/ControllerImpl.cpp b/src/core/ControllerImpl.cpp index 98457043..212c2d83 100644 --- a/src/core/ControllerImpl.cpp +++ b/src/core/ControllerImpl.cpp @@ -1,16 +1,13 @@ #include "core/ControllerImpl.h" -#include -#include -#include +#include #include -#include -#include +#include "core/MeshController.h" +#include "scene/Camera.h" ControllerImpl::ControllerImpl(IModel::Ptr model, IView::Ptr view) - : _model(std::move(model)), _view(std::move(view)) { -} + : _model(std::move(model)), _view(std::move(view)) {} void ControllerImpl::update() { _model->get_engine().update(_model); @@ -32,4 +29,3 @@ void ControllerImpl::process_event(const SDL_Event& e) { _model->getCamera()->processSDLEvent(e); _view->process_event(e); } - diff --git a/src/core/MeshController.cpp b/src/core/MeshController.cpp index 416ba851..65f261c1 100644 --- a/src/core/MeshController.cpp +++ b/src/core/MeshController.cpp @@ -1,14 +1,14 @@ #include "core/MeshController.h" - -#include +#include +#include #include - +#include #include "scene/Mesh.h" MeshController::MeshController(IModel::Ptr model) : _model(std::move(model)) {} -Mesh::rid_t MeshController::create_mesh(const std::filesystem::path& file_path) const { - //TODO: вернуть возвращемое значение +Mesh::rid_t MeshController::create_mesh( + const std::filesystem::path& file_path) const { return _model->createMesh(file_path); } diff --git a/src/core/Model.cpp b/src/core/Model.cpp index 0df2b63f..3afde71d 100644 --- a/src/core/Model.cpp +++ b/src/core/Model.cpp @@ -1,6 +1,6 @@ #include - #include "core/ModelImpl.h" +#include "interfaces/IModel.h" IModel::Ptr createModel() { return std::make_shared(); diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index 3058bb3f..8d6a7642 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -1,13 +1,15 @@ #include "core/ModelImpl.h" -#include "core/config.h" -#include "graphics/vulkan/MeshNode.h" +#include #include #include -#include #include #include #include +#include + +#include "core/config.h" +#include "graphics/vulkan/MeshNode.h" #define GLM_ENABLE_EXPERIMENTAL #include @@ -90,7 +92,11 @@ void init_descriptor_pool(const VulkanEngine& engine, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}}; - file.descriptorPool.init(engine._device, static_cast(gltf.materials.size()), sizes); + file.descriptorPool.init( + engine._device, + static_cast( + std::max(gltf.materials.size(), static_cast(1))), + sizes); } void load_samplers(const VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, @@ -99,12 +105,12 @@ void load_samplers(const VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, VkSamplerCreateInfo sample = { .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .pNext = nullptr, - .magFilter = - extract_filter(magFilter.value_or(fastgltf::Filter::Nearest)), - .minFilter = - extract_filter(minFilter.value_or(fastgltf::Filter::Nearest)), - .mipmapMode = extract_mipmap_mode( - minFilter.value_or(fastgltf::Filter::Nearest)), + .magFilter = extract_filter( + magFilter.value_or(fastgltf::Filter::Nearest)), + .minFilter = extract_filter( + minFilter.value_or(fastgltf::Filter::Nearest)), + .mipmapMode = extract_mipmap_mode( + minFilter.value_or(fastgltf::Filter::Nearest)), .minLod = 0, .maxLod = VK_LOD_CLAMP_NONE}; @@ -117,6 +123,7 @@ void load_samplers(const VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, void load_all_textures(const VulkanEngine& engine, fastgltf::Asset& gltf, std::vector& images) { + images.reserve(gltf.images.size()); for ([[maybe_unused]] fastgltf::Image& image : gltf.images) { images.push_back(engine._errorCheckerboardImage); } @@ -125,9 +132,10 @@ void load_all_textures(const VulkanEngine& engine, fastgltf::Asset& gltf, void create_material_data_buffer(const VulkanEngine& engine, Mesh::GLTF::LoadedGLTF& file, const fastgltf::Asset& gltf) { + const size_t material_сount = + !gltf.materials.empty() ? gltf.materials.size() : 1; file.materialDataBuffer = engine.create_buffer( - sizeof(GLTFMetallic_Roughness::MaterialConstants) * - gltf.materials.size(), + sizeof(GLTFMetallic_Roughness::MaterialConstants) * material_сount, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); } @@ -158,26 +166,26 @@ void load_material_data( static_cast( file.materialDataBuffer.info.pMappedData); - for (size_t data_index{}; fastgltf::Material& mat : gltf.materials) { + for (size_t data_index{}; fastgltf::Material & mat : gltf.materials) { auto newMat = std::make_shared(); materials.push_back(newMat); file.materials[mat.name.c_str()] = newMat; // write material parameters to buffer sceneMaterialConstants[data_index] = { - .colorFactors{ - mat.pbrData.baseColorFactor[0], - mat.pbrData.baseColorFactor[1], - mat.pbrData.baseColorFactor[2], - mat.pbrData.baseColorFactor[3], - }, - .metal_rough_factors{ - mat.pbrData.metallicFactor, - mat.pbrData.roughnessFactor, - {}, - {}, - }, -}; + .colorFactors{ + mat.pbrData.baseColorFactor[0], + mat.pbrData.baseColorFactor[1], + mat.pbrData.baseColorFactor[2], + mat.pbrData.baseColorFactor[3], + }, + .metal_rough_factors{ + mat.pbrData.metallicFactor, + mat.pbrData.roughnessFactor, + {}, + {}, + }, + }; auto passType = MaterialPass::MainColor; if (mat.alphaMode == fastgltf::AlphaMode::Blend) { @@ -186,17 +194,15 @@ void load_material_data( GLTFMetallic_Roughness::MaterialResources materialResources{ // default the material textures - engine._whiteImage, - engine._defaultSamplerLinear, - engine._whiteImage, - engine._defaultSamplerLinear, + engine._whiteImage, engine._defaultSamplerLinear, + engine._whiteImage, engine._defaultSamplerLinear, // set the uniform buffer for the material data file.materialDataBuffer.buffer, - static_cast(data_index * sizeof(GLTFMetallic_Roughness::MaterialConstants)) - }; + static_cast( + data_index * + sizeof(GLTFMetallic_Roughness::MaterialConstants))}; - grab_textures_from_GLTF(file, gltf, mat, - materialResources, images); + grab_textures_from_GLTF(file, gltf, mat, materialResources, images); // build material newMat->data = engine.metalRoughMaterial.write_material( engine._device, passType, materialResources, @@ -204,6 +210,28 @@ void load_material_data( ++data_index; } + + // Add a fallback material if no materials were defined in the GLTF + if (materials.empty()) { + auto default_mat = std::make_shared(); + materials.push_back(default_mat); + GLTFMetallic_Roughness::MaterialConstants constants = {}; + constants = { + .colorFactors = glm::vec4(1.0f), // White base color + .metal_rough_factors = glm::vec4(0.0f), // Non-metallic, smooth + }; + sceneMaterialConstants[0] = constants; + GLTFMetallic_Roughness::MaterialResources resources{}; + resources = {.colorImage = engine._whiteImage, + .colorSampler = engine._defaultSamplerLinear, + .metalRoughImage = engine._whiteImage, + .metalRoughSampler = engine._defaultSamplerLinear, + .dataBuffer = file.materialDataBuffer.buffer, + .dataBufferOffset = 0}; + default_mat->data = engine.metalRoughMaterial.write_material( + engine._device, MaterialPass::MainColor, resources, + file.descriptorPool); + } } void load_indexes(const fastgltf::Asset& gltf, std::vector& indices, @@ -319,16 +347,12 @@ void upload_mesh_to_engine( const size_t initial_vtx = vertices.size(); load_indexes(gltf, indices, p, initial_vtx); - load_vertex_positions(gltf, vertices, p, - initial_vtx); - load_vertex_normals(gltf, vertices, p, - initial_vtx); + load_vertex_positions(gltf, vertices, p, initial_vtx); + load_vertex_normals(gltf, vertices, p, initial_vtx); load_UVs(gltf, vertices, p, initial_vtx); - load_vertex_colors(gltf, vertices, p, - initial_vtx); - define_new_surface_material(newSurface, p, - materials); + load_vertex_colors(gltf, vertices, p, initial_vtx); + define_new_surface_material(newSurface, p, materials); newmesh->surfaces.push_back(newSurface); } @@ -408,7 +432,7 @@ void setup_nodes_relationships(Mesh::GLTF::LoadedGLTF& file, } std::optional> loadGLTF( - VulkanEngine& engine, std::string_view filePath) { + VulkanEngine& engine, std::string_view filePath) { fmt::print("Loading GLTF: {}", filePath); auto scene = std::make_shared(); @@ -441,7 +465,7 @@ std::optional> loadGLTF( setup_nodes_relationships(file, gltf, nodes); return scene; } -} // unnamed namespace +} // unnamed namespace ModelImpl::~ModelImpl() { _engine.cleanup(); @@ -449,7 +473,7 @@ ModelImpl::~ModelImpl() { ModelImpl::ModelImpl() = default; -void ModelImpl::registerWindow(struct SDL_Window* window) { +void ModelImpl::registerWindow(SDL_Window* window) { _engine.mainCamera = &_camera; _engine.init(window); } @@ -463,10 +487,8 @@ Camera* ModelImpl::getCamera() { return &_camera; } -Mesh::rid_t registerMesh( - VulkanEngine& engine, - ModelImpl::MeshMap& meshes, - const std::filesystem::path& filePath) { +Mesh::rid_t registerMesh(VulkanEngine& engine, ModelImpl::MeshMap& meshes, + const std::filesystem::path& filePath) { std::random_device rd; // Use the Mersenne Twister engine for high-quality random numbers @@ -478,8 +500,7 @@ Mesh::rid_t registerMesh( // Generate and print a random int64_t value const Mesh::rid_t random_rid_t = distribution(generator); - std::string structurePath = {std::string(ASSETS_DIR) + - filePath.string()}; + std::string structurePath = {std::string(ASSETS_DIR) + filePath.string()}; auto structureFile = loadGLTF(engine, structurePath); assert(structureFile.has_value()); @@ -499,18 +520,19 @@ Mesh::rid_t ModelImpl::createMesh(const std::filesystem::path& file_path) { } void ModelImpl::setMeshTransform(Mesh::rid_t rid, glm::mat4x4 transform) { - _meshes[rid].transform = transform; + _meshes.at(rid).transform = transform; } glm::mat4 ModelImpl::get_mesh_transform(Mesh::rid_t rid) { - return _meshes[rid].transform; + return _meshes.at(rid).transform; } void ModelImpl::delete_mesh(Mesh::rid_t rid) { - _meshes.erase(rid); + if (!_meshes.erase(rid)) { + throw std::invalid_argument("Invalid id of the mesh"); + } } const ModelImpl::MeshMap& ModelImpl::get_meshes() { return _meshes; } - diff --git a/src/core/ViewImpl.cpp b/src/core/ViewImpl.cpp index c08247b0..c67b792d 100644 --- a/src/core/ViewImpl.cpp +++ b/src/core/ViewImpl.cpp @@ -1,13 +1,12 @@ #include "core/ViewImpl.h" #include -#include -#include +#include +#include + #include -#include -#include -#include -#include +#include +#include #include #include #include @@ -17,15 +16,16 @@ #include "imgui_impl_sdl2.h" #include "imgui_impl_vulkan.h" -ViewImpl::ViewImpl(IModel::Ptr model) - : _model(std::move(model)) { - SDL_Init(SDL_INIT_VIDEO); +namespace { +constexpr auto kWindowFlags = + static_cast(SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE); +} - auto window_flags = - (SDL_WindowFlags)(SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE); +ViewImpl::ViewImpl(IModel::Ptr model) : _model(std::move(model)) { + SDL_Init(SDL_INIT_VIDEO); window = SDL_CreateWindow("engine", SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, 1700, 900, window_flags); + SDL_WINDOWPOS_UNDEFINED, 1700, 900, kWindowFlags); _model->registerWindow(window); } @@ -36,7 +36,7 @@ void ViewImpl::run() const { ImGui::NewFrame(); if (ImGui::Begin("background")) { - VulkanEngine &engine = VulkanEngine::Get(); + VulkanEngine& engine = VulkanEngine::Get(); ImGui::SliderFloat("Render Scale", &engine.renderScale, 0.3f, 1.f); // other code } diff --git a/src/core/createInstance.cpp b/src/core/createInstance.cpp index bbc3ef70..50b6cb2e 100644 --- a/src/core/createInstance.cpp +++ b/src/core/createInstance.cpp @@ -2,7 +2,6 @@ #include "core/Controller.h" #include "core/Model.h" #include "core/View.h" -#include "interfaces/IModel.h" IController::Ptr createInstance() { const auto model = createModel(); diff --git a/src/graphics/vulkan/MeshNode.cpp b/src/graphics/vulkan/MeshNode.cpp index 170a5333..17075741 100644 --- a/src/graphics/vulkan/MeshNode.cpp +++ b/src/graphics/vulkan/MeshNode.cpp @@ -1,7 +1,8 @@ +#include + #include "graphics/vulkan/MeshNode.h" #include "graphics/vulkan/vk_engine.h" - -#include +#include "scene/Mesh.h" void MeshNode::Draw(const glm::mat4& topMatrix, DrawContext& ctx) { const glm::mat4 nodeMatrix = topMatrix * worldTransform; diff --git a/src/graphics/vulkan/RenderableGLTF.cpp b/src/graphics/vulkan/RenderableGLTF.cpp index 80ae719e..14be73da 100644 --- a/src/graphics/vulkan/RenderableGLTF.cpp +++ b/src/graphics/vulkan/RenderableGLTF.cpp @@ -1,19 +1,11 @@ #include "graphics/vulkan/RenderableGLTF.h" - -#include - -#include "graphics/vulkan/vk_engine.h" +#include +#include +#include #include "graphics/vulkan/vk_types.h" -#include "stb_image.h" #define GLM_ENABLE_EXPERIMENTAL -#include -#include -#include -#include -#include - RenderableGLTF::RenderableGLTF(LoadedGltfPtr gltf) : _gltf(std::move(gltf)) {} void RenderableGLTF::Draw(const glm::mat4& topMatrix, DrawContext& ctx) { diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index 8ad650bb..b227be8d 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -1,16 +1,27 @@ #include "graphics/vulkan/vk_engine.h" -#include "core/config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core/Logging.h" +#include "graphics/vulkan/vk_descriptors.h" +#include "scene/Camera.h" -#include #include -#include #include -#define VMA_IMPLEMENTATION +#include "core/config.h" + + #include "SDL_vulkan.h" #include "VkBootstrap.h" -#include #include "graphics/vulkan/RenderableGLTF.h" #include "graphics/vulkan/vk_images.h" #include "graphics/vulkan/vk_initializers.h" @@ -20,6 +31,7 @@ #include "imgui_impl_sdl2.h" #include "imgui_impl_vulkan.h" #include "interfaces/IModel.h" +#define VMA_IMPLEMENTATION #include "vk_mem_alloc.h" #define GLM_ENABLE_EXPERIMENTAL @@ -41,7 +53,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, - void* pUserData) { + [[maybe_unused]] void* pUserData) { std::string type; switch (messageType) { @@ -59,6 +71,11 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( type = "Performance"; break; + case VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT: + type = "Modified set of GPU-visible virtual addresses"; + break; + default: + type = "Unknown"; } std::string message = "(" + type + ")" + pCallbackData->pMessage; @@ -74,6 +91,8 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( } else if (messageSeverity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { LOGE("{}", message) + } else { + LOGE("{}", message) } return VK_FALSE; @@ -101,26 +120,26 @@ void VulkanEngine::init_default_data() { auto path_to_assets = std::string(ASSETS_DIR) + "/basicmesh.glb"; // 3 default textures, white, grey, black. 1 pixel each - uint32_t white = glm::packUnorm4x8(glm::vec4(1, 1, 1, 1)); + const uint32_t white = glm::packUnorm4x8(glm::vec4(1, 1, 1, 1)); _whiteImage = - create_image((void*)&white, VkExtent3D{1, 1, 1}, - VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); + create_image(&white, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_SAMPLED_BIT); - uint32_t grey = glm::packUnorm4x8(glm::vec4(0.66f, 0.66f, 0.66f, 1)); + const uint32_t grey = glm::packUnorm4x8(glm::vec4(0.66f, 0.66f, 0.66f, 1)); _greyImage = - create_image((void*)&grey, VkExtent3D{1, 1, 1}, - VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); + create_image(&grey, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_SAMPLED_BIT); - uint32_t black = glm::packUnorm4x8(glm::vec4(0, 0, 0, 0)); + const uint32_t black = glm::packUnorm4x8(glm::vec4(0, 0, 0, 0)); _blackImage = - create_image((void*)&black, VkExtent3D{1, 1, 1}, - VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); + create_image(&black, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_SAMPLED_BIT); // checkerboard image - uint32_t magenta = glm::packUnorm4x8(glm::vec4(1, 0, 1, 1)); + const uint32_t magenta = glm::packUnorm4x8(glm::vec4(1, 0, 1, 1)); std::array pixels{}; // for 16x16 checkerboard texture - for (int x = 0; x < 16; x++) { - for (int y = 0; y < 16; y++) { + for (size_t x = 0; x < 16; x++) { + for (size_t y = 0; y < 16; y++) { pixels[y * 16 + x] = ((x % 2) ^ (y % 2)) ? magenta : black; } } @@ -148,19 +167,19 @@ void VulkanEngine::init_default_data() { materialResources.metalRoughSampler = _defaultSamplerLinear; // set the uniform buffer for the material data - AllocatedBuffer materialConstants = create_buffer( + const AllocatedBuffer materialConstants = create_buffer( sizeof(GLTFMetallic_Roughness::MaterialConstants), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); // write the buffer auto* sceneUniformData = - (GLTFMetallic_Roughness::MaterialConstants*) - materialConstants.allocation->GetMappedData(); + static_cast( + materialConstants.allocation->GetMappedData()); sceneUniformData->colorFactors = glm::vec4{1, 1, 1, 1}; sceneUniformData->metal_rough_factors = glm::vec4{1, 0.5, 0, 0}; _mainDeletionQueue.push_function( - [=, this]() { destroy_buffer(materialConstants); }); + [=, this] { destroy_buffer(materialConstants); }); materialResources.dataBuffer = materialConstants.buffer; materialResources.dataBufferOffset = 0; @@ -232,7 +251,7 @@ void VulkanEngine::init_mesh_pipeline() { vkDestroyShaderModule(_device, triangleFragShader, nullptr); vkDestroyShaderModule(_device, triangleVertexShader, nullptr); - _mainDeletionQueue.push_function([&]() { + _mainDeletionQueue.push_function([&] { vkDestroyPipelineLayout(_device, _meshPipelineLayout, nullptr); vkDestroyPipeline(_device, _meshPipeline, nullptr); }); @@ -258,7 +277,7 @@ void VulkanEngine::init_triangle_pipeline() { // build the pipeline layout that controls the inputs/outputs of the shader // we are not using descriptor sets or other systems yet, so no need to use // anything other than empty default - VkPipelineLayoutCreateInfo pipeline_layout_info = + const VkPipelineLayoutCreateInfo pipeline_layout_info = vkinit::pipeline_layout_create_info(); VK_CHECK(vkCreatePipelineLayout(_device, &pipeline_layout_info, nullptr, &_trianglePipelineLayout)); @@ -293,7 +312,7 @@ void VulkanEngine::init_triangle_pipeline() { vkDestroyShaderModule(_device, triangleFragShader, nullptr); vkDestroyShaderModule(_device, triangleVertexShader, nullptr); - _mainDeletionQueue.push_function([&]() { + _mainDeletionQueue.push_function([&] { vkDestroyPipelineLayout(_device, _trianglePipelineLayout, nullptr); vkDestroyPipeline(_device, _trianglePipeline, nullptr); }); @@ -303,7 +322,7 @@ void VulkanEngine::init_imgui() { // 1: create descriptor pool for IMGUI // the size of the pool is very oversize, but it's copied from imgui demo // itself. - VkDescriptorPoolSize pool_sizes[] = { + const VkDescriptorPoolSize pool_sizes[] = { {VK_DESCRIPTOR_TYPE_SAMPLER, 1000}, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000}, {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000}, @@ -359,7 +378,7 @@ void VulkanEngine::init_imgui() { ImGui_ImplVulkan_CreateFontsTexture(); // add destroy the imgui created structures - _mainDeletionQueue.push_function([=, this]() { + _mainDeletionQueue.push_function([=, this] { ImGui_ImplVulkan_Shutdown(); vkDestroyDescriptorPool(_device, imguiPool, nullptr); }); @@ -407,7 +426,7 @@ void VulkanEngine::init_descriptors() { writer.update_set(_device, _drawImageDescriptors); - for (int i = 0; i < FRAME_OVERLAP; i++) { + for (auto& _frame : _frames) { // create a descriptor pool std::vector frame_sizes = { {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 3}, @@ -416,12 +435,11 @@ void VulkanEngine::init_descriptors() { {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 4}, }; - _frames[i]._frameDescriptors = DescriptorAllocatorGrowable{}; - _frames[i]._frameDescriptors.init(_device, 1000, frame_sizes); + _frame._frameDescriptors = DescriptorAllocatorGrowable{}; + _frame._frameDescriptors.init(_device, 1000, frame_sizes); - _mainDeletionQueue.push_function([&, i]() { - _frames[i]._frameDescriptors.destroy_pools(_device); - }); + _mainDeletionQueue.push_function( + [&] { _frame._frameDescriptors.destroy_pools(_device); }); } } @@ -461,7 +479,7 @@ void VulkanEngine::init_background_pipelines() { vkDestroyShaderModule(_device, computeDrawShader, nullptr); - _mainDeletionQueue.push_function([&]() { + _mainDeletionQueue.push_function([&] { vkDestroyPipelineLayout(_device, _gradientPipelineLayout, nullptr); vkDestroyPipeline(_device, _gradientPipeline, nullptr); }); @@ -476,7 +494,7 @@ void VulkanEngine::init_pipelines() { metalRoughMaterial.build_pipelines(this); } -void VulkanEngine::init(struct SDL_Window* window) { +void VulkanEngine::init(SDL_Window* window) { _window = window; // only one engine initialization is allowed with the application. @@ -518,8 +536,8 @@ void VulkanEngine::init_vulkan() { LOGI("Available extensions:") - for (auto& extension : system_info.available_extensions) { - LOGI(extension.extensionName); + for (auto& [extensionName, _] : system_info.available_extensions) { + LOGI(extensionName); } vkb::InstanceBuilder builder; @@ -573,7 +591,7 @@ void VulkanEngine::init_vulkan() { physical_device_ret.error().message()); } - vkb::PhysicalDevice physicalDevice = physical_device_ret.value(); + const vkb::PhysicalDevice& physicalDevice = physical_device_ret.value(); vkb::DeviceBuilder deviceBuilder{physicalDevice}; @@ -583,7 +601,7 @@ void VulkanEngine::init_vulkan() { dev_ret.error().message()); } - vkb::Device vkbDevice = dev_ret.value(); + const vkb::Device& vkbDevice = dev_ret.value(); // Get the VkDevice handle used in the rest of a vulkan application _device = vkbDevice.device; @@ -613,64 +631,64 @@ void VulkanEngine::init_vulkan() { allocatorInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; vmaCreateAllocator(&allocatorInfo, &_allocator); - _mainDeletionQueue.push_function( - [&]() { vmaDestroyAllocator(_allocator); }); + _mainDeletionQueue.push_function([&] { vmaDestroyAllocator(_allocator); }); } void VulkanEngine::init_commands() { // create a command pool for commands submitted to the graphics queue. // we also want the pool to allow for resetting of individual command // buffers - VkCommandPoolCreateInfo commandPoolInfo = vkinit::command_pool_create_info( - _graphicsQueueFamily, - VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); + const VkCommandPoolCreateInfo commandPoolInfo = + vkinit::command_pool_create_info( + _graphicsQueueFamily, + VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); - for (int i = 0; i < FRAME_OVERLAP; i++) { + for (auto& _frame : _frames) { VK_CHECK(vkCreateCommandPool(_device, &commandPoolInfo, nullptr, - &_frames[i]._commandPool)); + &_frame._commandPool)); // allocate the default command buffer that we will use for rendering VkCommandBufferAllocateInfo cmdAllocInfo = - vkinit::command_buffer_allocate_info(_frames[i]._commandPool, - 1); + vkinit::command_buffer_allocate_info(_frame._commandPool, 1); VK_CHECK(vkAllocateCommandBuffers(_device, &cmdAllocInfo, - &_frames[i]._mainCommandBuffer)); + &_frame._mainCommandBuffer)); } VK_CHECK(vkCreateCommandPool(_device, &commandPoolInfo, nullptr, &_immCommandPool)); // allocate the command buffer for immediate submits - VkCommandBufferAllocateInfo cmdAllocInfo = + const VkCommandBufferAllocateInfo cmdAllocInfo = vkinit::command_buffer_allocate_info(_immCommandPool, 1); VK_CHECK(vkAllocateCommandBuffers(_device, &cmdAllocInfo, &_immCommandBuffer)); - _mainDeletionQueue.push_function([=, this]() { + _mainDeletionQueue.push_function([=, this] { vkDestroyCommandPool(_device, _immCommandPool, nullptr); }); } void VulkanEngine::init_sync_structures() { - VkFenceCreateInfo fenceCreateInfo = + const VkFenceCreateInfo fenceCreateInfo = vkinit::fence_create_info(VK_FENCE_CREATE_SIGNALED_BIT); - VkSemaphoreCreateInfo semaphoreCreateInfo = vkinit::semaphore_create_info(); + const VkSemaphoreCreateInfo semaphoreCreateInfo = + vkinit::semaphore_create_info(); - for (int i = 0; i < FRAME_OVERLAP; i++) { + for (auto& _frame : _frames) { VK_CHECK(vkCreateFence(_device, &fenceCreateInfo, nullptr, - &_frames[i]._renderFence)); + &_frame._renderFence)); VK_CHECK(vkCreateSemaphore(_device, &semaphoreCreateInfo, nullptr, - &_frames[i]._swapchainSemaphore)); + &_frame._swapchainSemaphore)); VK_CHECK(vkCreateSemaphore(_device, &semaphoreCreateInfo, nullptr, - &_frames[i]._renderSemaphore)); + &_frame._renderSemaphore)); } VK_CHECK(vkCreateFence(_device, &fenceCreateInfo, nullptr, &_immFence)); _mainDeletionQueue.push_function( - [=, this]() { vkDestroyFence(_device, _immFence, nullptr); }); + [=, this] { vkDestroyFence(_device, _immFence, nullptr); }); } void VulkanEngine::create_swapchain(uint32_t width, uint32_t height) { @@ -708,7 +726,8 @@ void VulkanEngine::init_swapchain() { create_swapchain(_windowExtent.width, _windowExtent.height); // draw image size will match the window - VkExtent3D drawImageExtent = {_windowExtent.width, _windowExtent.height, 1}; + const VkExtent3D drawImageExtent = {_windowExtent.width, + _windowExtent.height, 1}; // hardcoding the draw format to 32-bit float _drawImage.imageFormat = VK_FORMAT_R16G16B16A16_SFLOAT; @@ -720,21 +739,21 @@ void VulkanEngine::init_swapchain() { drawImageUsages |= VK_IMAGE_USAGE_STORAGE_BIT; drawImageUsages |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; - VkImageCreateInfo rimg_info = vkinit::image_create_info( + const VkImageCreateInfo rimg_info = vkinit::image_create_info( _drawImage.imageFormat, drawImageUsages, drawImageExtent); // for the draw image, we want to allocate it from gpu local memory VmaAllocationCreateInfo rimg_allocinfo = {}; rimg_allocinfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - rimg_allocinfo.requiredFlags = - VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + rimg_allocinfo.requiredFlags = static_cast( + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); // allocate and create the image vmaCreateImage(_allocator, &rimg_info, &rimg_allocinfo, &_drawImage.image, &_drawImage.allocation, nullptr); // build an image-view for the draw image to use for rendering - VkImageViewCreateInfo rview_info = vkinit::imageview_create_info( + const VkImageViewCreateInfo rview_info = vkinit::imageview_create_info( _drawImage.imageFormat, _drawImage.image, VK_IMAGE_ASPECT_COLOR_BIT); @@ -742,7 +761,7 @@ void VulkanEngine::init_swapchain() { &_drawImage.imageView)); // add to deletion queues - _mainDeletionQueue.push_function([=, this]() { + _mainDeletionQueue.push_function([=, this] { vkDestroyImageView(_device, _drawImage.imageView, nullptr); vmaDestroyImage(_allocator, _drawImage.image, _drawImage.allocation); }); @@ -752,8 +771,8 @@ void VulkanEngine::destroy_swapchain() { vkDestroySwapchainKHR(_device, _swapchain, nullptr); // destroy swapchain resources - for (int i = 0; i < _swapchainImageViews.size(); i++) { - vkDestroyImageView(_device, _swapchainImageViews[i], nullptr); + for (const auto& _swapchainImageView : _swapchainImageViews) { + vkDestroyImageView(_device, _swapchainImageView, nullptr); } } @@ -766,15 +785,14 @@ void VulkanEngine::cleanup() { _mainDeletionQueue.flush(); - for (int i = 0; i < FRAME_OVERLAP; i++) { + for (const auto& _frame : _frames) { // already written from before - vkDestroyCommandPool(_device, _frames[i]._commandPool, nullptr); + vkDestroyCommandPool(_device, _frame._commandPool, nullptr); // destroy sync objects - vkDestroyFence(_device, _frames[i]._renderFence, nullptr); - vkDestroySemaphore(_device, _frames[i]._renderSemaphore, nullptr); - vkDestroySemaphore(_device, _frames[i]._swapchainSemaphore, - nullptr); + vkDestroyFence(_device, _frame._renderFence, nullptr); + vkDestroySemaphore(_device, _frame._renderSemaphore, nullptr); + vkDestroySemaphore(_device, _frame._swapchainSemaphore, nullptr); } destroy_swapchain(); @@ -834,7 +852,7 @@ GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, VMA_MEMORY_USAGE_GPU_ONLY); // find the address of the vertex buffer - VkBufferDeviceAddressInfo deviceAddressInfo{ + const VkBufferDeviceAddressInfo deviceAddressInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, .buffer = newSurface.vertexBuffer.buffer}; newSurface.vertexBufferAddress = @@ -846,9 +864,9 @@ GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_GPU_ONLY); - AllocatedBuffer staging = create_buffer(vertexBufferSize + indexBufferSize, - VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VMA_MEMORY_USAGE_CPU_ONLY); + const AllocatedBuffer staging = create_buffer( + vertexBufferSize + indexBufferSize, + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY); void* data = staging.allocation->GetMappedData(); @@ -874,13 +892,16 @@ GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, vkCmdCopyBuffer(cmd, staging.buffer, newSurface.indexBuffer.buffer, 1, &indexCopy); }); - + _mainDeletionQueue.push_function( + [=, this] { destroy_buffer(newSurface.vertexBuffer); }); + _mainDeletionQueue.push_function( + [=, this] { destroy_buffer(newSurface.indexBuffer); }); destroy_buffer(staging); return newSurface; } -void VulkanEngine::draw_background(VkCommandBuffer cmd) { +void VulkanEngine::draw_background(VkCommandBuffer cmd) const { // bind the gradient drawing compute pipeline vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, _gradientPipeline); @@ -892,15 +913,16 @@ void VulkanEngine::draw_background(VkCommandBuffer cmd) { // execute the compute pipeline dispatch. We are using 16x16 workgroup size, // so we need to divide by it - vkCmdDispatch(cmd, std::ceil(_drawExtent.width / 16.0), - std::ceil(_drawExtent.height / 16.0), 1); + vkCmdDispatch( + cmd, static_cast(std::ceil(_drawExtent.width / 16.0)), + static_cast(std::ceil(_drawExtent.height / 16.0)), 1); } void VulkanEngine::draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView) const { - VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info( + const VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info( targetImageView, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - VkRenderingInfo renderInfo = + const VkRenderingInfo renderInfo = vkinit::rendering_info(_swapchainExtent, &colorAttachment, nullptr); vkCmdBeginRendering(cmd, &renderInfo); @@ -919,7 +941,7 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { // add it to the deletion queue of this frame, so it gets deleted once it's // been used get_current_frame()._deletionQueue.push_function( - [=, this]() { destroy_buffer(gpuSceneDataBuffer); }); + [=, this] { destroy_buffer(gpuSceneDataBuffer); }); // write the buffer auto* sceneUniformData = @@ -949,8 +971,8 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { VkViewport viewport = {}; viewport.x = 0; viewport.y = 0; - viewport.width = _drawExtent.width; - viewport.height = _drawExtent.height; + viewport.width = static_cast(_drawExtent.width); + viewport.height = static_cast(_drawExtent.height); viewport.minDepth = 0.f; viewport.maxDepth = 1.f; @@ -967,39 +989,37 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { // bind a texture VkDescriptorSet imageSet = get_current_frame()._frameDescriptors.allocate( _device, _singleImageDescriptorLayout); - { - DescriptorWriter writer; - writer.write_image(0, _errorCheckerboardImage.imageView, - _defaultSamplerNearest, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); - - writer.update_set(_device, imageSet); - } + DescriptorWriter single_image_writer; + single_image_writer.write_image(0, _errorCheckerboardImage.imageView, + _defaultSamplerNearest, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + single_image_writer.update_set(_device, imageSet); vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _meshPipelineLayout, 0, 1, &imageSet, 0, nullptr); - for (const RenderObject& draw : mainDrawContext.OpaqueSurfaces) { + for (const auto& [indexCount, firstIndex, indexBuffer, material, transform, + vertexBufferAddress] : mainDrawContext.OpaqueSurfaces) { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, - draw.material->pipeline->pipeline); + material->pipeline->pipeline); vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, - draw.material->pipeline->layout, 0, 1, + material->pipeline->layout, 0, 1, &globalDescriptor, 0, nullptr); vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, - draw.material->pipeline->layout, 1, 1, - &draw.material->materialSet, 0, nullptr); + material->pipeline->layout, 1, 1, + &material->materialSet, 0, nullptr); - vkCmdBindIndexBuffer(cmd, draw.indexBuffer, 0, VK_INDEX_TYPE_UINT32); + vkCmdBindIndexBuffer(cmd, indexBuffer, 0, VK_INDEX_TYPE_UINT32); GPUDrawPushConstants pushConstants{}; - pushConstants.vertexBuffer = draw.vertexBufferAddress; - pushConstants.worldMatrix = draw.transform; - vkCmdPushConstants(cmd, draw.material->pipeline->layout, + pushConstants.vertexBuffer = vertexBufferAddress; + pushConstants.worldMatrix = transform; + vkCmdPushConstants(cmd, material->pipeline->layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(GPUDrawPushConstants), &pushConstants); - vkCmdDrawIndexed(cmd, draw.indexCount, 1, draw.firstIndex, 0, 0); + vkCmdDrawIndexed(cmd, indexCount, 1, firstIndex, 0, 0); } vkCmdEndRendering(cmd); @@ -1020,16 +1040,17 @@ void VulkanEngine::draw(const IModel::Ptr model) { // request image from the swapchain uint32_t swapchainImageIndex; - VkResult e = vkAcquireNextImageKHR(_device, _swapchain, 1000000000, - get_current_frame()._swapchainSemaphore, - nullptr, &swapchainImageIndex); + const VkResult e = + vkAcquireNextImageKHR(_device, _swapchain, 1000000000, + get_current_frame()._swapchainSemaphore, + nullptr, &swapchainImageIndex); if (e == VK_ERROR_OUT_OF_DATE_KHR) { resize_requested = true; return; } // naming it cmd for shorter writing - VkCommandBuffer cmd = get_current_frame()._mainCommandBuffer; + const VkCommandBuffer cmd = get_current_frame()._mainCommandBuffer; // now that we are sure that the commands finished executing, we can safely // reset the command buffer to begin recording again. @@ -1037,15 +1058,18 @@ void VulkanEngine::draw(const IModel::Ptr model) { // begin the command buffer recording. We will use this command buffer // exactly once, so we want to let vulkan know that - VkCommandBufferBeginInfo cmdBeginInfo = vkinit::command_buffer_begin_info( - VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); - - _drawExtent.height = - std::min(_swapchainExtent.height, _drawImage.imageExtent.height) * - renderScale; - _drawExtent.width = - std::min(_swapchainExtent.width, _drawImage.imageExtent.width) * - renderScale; + const VkCommandBufferBeginInfo cmdBeginInfo = + vkinit::command_buffer_begin_info( + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + + _drawExtent.height = static_cast( + (float)std::min(_swapchainExtent.height, + _drawImage.imageExtent.height) * + renderScale); + _drawExtent.width = static_cast( + (float)std::min(_swapchainExtent.width, + _drawImage.imageExtent.width) * + renderScale); VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo)); @@ -1098,16 +1122,17 @@ void VulkanEngine::draw(const IModel::Ptr model) { // when the swapchain is ready we will signal the _renderSemaphore, to // signal that rendering has finished - VkCommandBufferSubmitInfo cmdinfo = vkinit::command_buffer_submit_info(cmd); + const VkCommandBufferSubmitInfo cmdinfo = + vkinit::command_buffer_submit_info(cmd); - VkSemaphoreSubmitInfo waitInfo = vkinit::semaphore_submit_info( + const VkSemaphoreSubmitInfo waitInfo = vkinit::semaphore_submit_info( VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR, get_current_frame()._swapchainSemaphore); - VkSemaphoreSubmitInfo signalInfo = + const VkSemaphoreSubmitInfo signalInfo = vkinit::semaphore_submit_info(VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT, get_current_frame()._renderSemaphore); - VkSubmitInfo2 submit = + const VkSubmitInfo2 submit = vkinit::submit_info(&cmdinfo, &signalInfo, &waitInfo); // submit command buffer to the queue and execute it. @@ -1147,8 +1172,8 @@ void VulkanEngine::resize_swapchain() { int w, h; SDL_GetWindowSize(_window, &w, &h); - _windowExtent.width = w; - _windowExtent.height = h; + _windowExtent.width = static_cast(w); + _windowExtent.height = static_cast(h); create_swapchain(_windowExtent.width, _windowExtent.height); @@ -1156,14 +1181,15 @@ void VulkanEngine::resize_swapchain() { } void VulkanEngine::immediate_submit( - std::function&& function) { + std::function&& function) const { VK_CHECK(vkResetFences(_device, 1, &_immFence)); VK_CHECK(vkResetCommandBuffer(_immCommandBuffer, 0)); - VkCommandBuffer cmd = _immCommandBuffer; + const VkCommandBuffer cmd = _immCommandBuffer; - VkCommandBufferBeginInfo cmdBeginInfo = vkinit::command_buffer_begin_info( - VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + const VkCommandBufferBeginInfo cmdBeginInfo = + vkinit::command_buffer_begin_info( + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo)); @@ -1171,8 +1197,10 @@ void VulkanEngine::immediate_submit( VK_CHECK(vkEndCommandBuffer(cmd)); - VkCommandBufferSubmitInfo cmdinfo = vkinit::command_buffer_submit_info(cmd); - VkSubmitInfo2 submit = vkinit::submit_info(&cmdinfo, nullptr, nullptr); + const VkCommandBufferSubmitInfo cmdinfo = + vkinit::command_buffer_submit_info(cmd); + const VkSubmitInfo2 submit = + vkinit::submit_info(&cmdinfo, nullptr, nullptr); // submit command buffer to the queue and execute it. // _renderFence will now block until the graphic commands finish execution @@ -1206,8 +1234,8 @@ AllocatedImage VulkanEngine::create_image(VkExtent3D size, VkFormat format, // always allocate images on dedicated GPU memory VmaAllocationCreateInfo allocinfo = {}; allocinfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - allocinfo.requiredFlags = - VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + allocinfo.requiredFlags = static_cast( + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); // allocate and create the image VK_CHECK(vmaCreateImage(_allocator, &img_info, &allocinfo, &newImage.image, @@ -1231,18 +1259,18 @@ AllocatedImage VulkanEngine::create_image(VkExtent3D size, VkFormat format, return newImage; } -AllocatedImage VulkanEngine::create_image(void* data, VkExtent3D size, +AllocatedImage VulkanEngine::create_image(const void* data, VkExtent3D size, VkFormat format, VkImageUsageFlags usage, - bool mipmapped) { - size_t data_size = size.depth * size.width * size.height * 4; - AllocatedBuffer uploadbuffer = + bool mipmapped) const { + const size_t data_size = size.depth * size.width * size.height * 4; + const AllocatedBuffer uploadbuffer = create_buffer(data_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); memcpy(uploadbuffer.info.pMappedData, data, data_size); - AllocatedImage new_image = + const AllocatedImage new_image = create_image(size, format, usage | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, diff --git a/src/include/core/ControllerImpl.h b/src/include/core/ControllerImpl.h index 6e0ad27e..97185297 100644 --- a/src/include/core/ControllerImpl.h +++ b/src/include/core/ControllerImpl.h @@ -7,6 +7,8 @@ #include "interfaces/IModel.h" #include "interfaces/IView.h" +class MeshController; + class ControllerImpl : public IController { public: explicit ControllerImpl(IModel::Ptr model, IView::Ptr view); diff --git a/src/include/core/MeshController.h b/src/include/core/MeshController.h index ff70558f..b577ad06 100644 --- a/src/include/core/MeshController.h +++ b/src/include/core/MeshController.h @@ -1,10 +1,10 @@ #pragma once -#include -#include - -#include "scene/Mesh.h" +#include +#include +#include #include "interfaces/IModel.h" +#include "scene/Mesh.h" /*! \brief * A class that allows you to add, delete, modify meshes @@ -38,6 +38,7 @@ class MeshController { * \return Returns a vector of all render-ids */ [[nodiscard]] std::vector get_meshes() const; + private: IModel::Ptr _model; }; \ No newline at end of file diff --git a/src/include/core/ModelImpl.h b/src/include/core/ModelImpl.h index 724a2410..6bc44a5f 100644 --- a/src/include/core/ModelImpl.h +++ b/src/include/core/ModelImpl.h @@ -1,17 +1,11 @@ #pragma once #include -#include -#include -#include - #include "graphics/vulkan/vk_engine.h" #include "interfaces/IModel.h" #include "scene/Camera.h" #include "scene/Mesh.h" -class Mesh; - class ModelImpl : public IModel { public: /*! \brief @@ -25,17 +19,17 @@ class ModelImpl : public IModel { void registerWindow(struct SDL_Window *window) override; - Mesh::rid_t createMesh(const std::filesystem::path& file_path) final; + Mesh::rid_t createMesh(const std::filesystem::path &file_path) final; void delete_mesh(Mesh::rid_t rid) final; void setMeshTransform(Mesh::rid_t rid, glm::mat4x4 transform) final; glm::mat4 get_mesh_transform(Mesh::rid_t) final; - const MeshMap& get_meshes() final; + const MeshMap &get_meshes() final; Camera *getCamera() override; - VulkanEngine& get_engine() final; + VulkanEngine &get_engine() final; private: MeshMap _meshes; diff --git a/src/include/core/View.h b/src/include/core/View.h index e2d07a9b..9d01bd9e 100644 --- a/src/include/core/View.h +++ b/src/include/core/View.h @@ -1,6 +1,5 @@ #pragma once -#include "IController.h" #include "interfaces/IModel.h" #include "interfaces/IView.h" diff --git a/src/include/core/ViewImpl.h b/src/include/core/ViewImpl.h index d40c3ac9..77747390 100644 --- a/src/include/core/ViewImpl.h +++ b/src/include/core/ViewImpl.h @@ -1,6 +1,7 @@ #pragma once -#include +#include +#include #include "interfaces/IModel.h" #include "interfaces/IView.h" diff --git a/src/include/graphics/vulkan/MeshNode.h b/src/include/graphics/vulkan/MeshNode.h index 6e287318..b27286a7 100644 --- a/src/include/graphics/vulkan/MeshNode.h +++ b/src/include/graphics/vulkan/MeshNode.h @@ -1,9 +1,12 @@ #pragma once +#include +#include #include "graphics/vulkan/vk_types.h" -#include "scene/Mesh.h" -#include +namespace Mesh::GLTF { +struct MeshAsset; +} struct MeshNode final : ENode { std::shared_ptr mesh; diff --git a/src/include/graphics/vulkan/RenderableGLTF.h b/src/include/graphics/vulkan/RenderableGLTF.h index 414a1f2a..f0e5f584 100644 --- a/src/include/graphics/vulkan/RenderableGLTF.h +++ b/src/include/graphics/vulkan/RenderableGLTF.h @@ -1,9 +1,9 @@ #pragma once -#include - -#include "vk_types.h" +#include +#include #include "scene/Mesh.h" +#include "vk_types.h" class RenderableGLTF : public IRenderable { public: @@ -15,5 +15,3 @@ class RenderableGLTF : public IRenderable { private: std::shared_ptr _gltf; }; - - diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 6f128bc0..6128d4ba 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -1,15 +1,18 @@ #pragma once #include +#include +#include "RenderableGLTF.h" #include "interfaces/IModel.h" -#include "scene/Mesh.h" #include "scene/Camera.h" +#include "scene/Mesh.h" #include "vk_descriptors.h" -#include "RenderableGLTF.h" #include "vk_pipelines.h" #include "vk_types.h" +struct MeshAsset; + constexpr unsigned int FRAME_OVERLAP = 2; struct GLTFMetallic_Roughness { @@ -54,8 +57,8 @@ struct DeletionQueue { void flush() { // reverse iterate the deletion queue to execute all the functions - for (auto it = deletors.rbegin(); it != deletors.rend(); it++) { - (*it)(); // call functors + for (auto& deletor : std::ranges::reverse_view(deletors)) { + deletor(); // call functors } deletors.clear(); @@ -99,7 +102,9 @@ struct DrawContext { class VulkanEngine { public: - std::unordered_map> loadedScenes; + std::unordered_map> + loadedScenes; Camera* mainCamera; @@ -118,7 +123,7 @@ class VulkanEngine { uint32_t _graphicsQueueFamily; bool _isInitialized{false}; - int _frameNumber{0}; + unsigned int _frameNumber{0}; bool stop_rendering{false}; VkExtent2D _windowExtent{2560, 1440}; @@ -181,10 +186,13 @@ class VulkanEngine { GPUMeshBuffers rectangle; - void immediate_submit(std::function&& function); + void immediate_submit( + std::function&& function) const; GPUMeshBuffers uploadMesh(std::span indices, std::span vertices); + std::vector> testMeshes; + bool resize_requested; GPUSceneData sceneData; @@ -194,9 +202,9 @@ class VulkanEngine { AllocatedImage create_image(VkExtent3D size, VkFormat format, VkImageUsageFlags usage, bool mipmapped = false) const; - AllocatedImage create_image(void* data, VkExtent3D size, VkFormat format, - VkImageUsageFlags usage, - bool mipmapped = false); + AllocatedImage create_image(const void* data, VkExtent3D size, + VkFormat format, VkImageUsageFlags usage, + bool mipmapped = false) const; void destroy_image(const AllocatedImage& img) const; AllocatedImage _whiteImage; @@ -230,7 +238,7 @@ class VulkanEngine { void create_swapchain(uint32_t width, uint32_t height); void destroy_swapchain(); - void draw_background(VkCommandBuffer cmd); + void draw_background(VkCommandBuffer cmd) const; void init_descriptors(); diff --git a/src/include/graphics/vulkan/vk_types.h b/src/include/graphics/vulkan/vk_types.h index 016fea2b..e0c8e55b 100644 --- a/src/include/graphics/vulkan/vk_types.h +++ b/src/include/graphics/vulkan/vk_types.h @@ -1,18 +1,14 @@ #pragma once -#include -#include -#include #include -#include #include #include -#include #include #include "core/Logging.h" #include "glm/mat4x4.hpp" #include "glm/vec4.hpp" + #include "vk_mem_alloc.h" #include "vulkan/vulkan.h" diff --git a/src/include/interfaces/IModel.h b/src/include/interfaces/IModel.h index 356ebe12..8f5dff95 100644 --- a/src/include/interfaces/IModel.h +++ b/src/include/interfaces/IModel.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "SDL2/SDL.h" #include "SDL2/SDL_vulkan.h" @@ -43,7 +44,8 @@ class IModel { /*! * \brief Loads (parses) a new mesh to the model * \param file_path The path to the mesh - * \return Returns render-id of the mesh that helps identify the provided mesh. + * \return Returns render-id of the mesh that helps identify the provided + * mesh. */ virtual Mesh::rid_t createMesh(const std::filesystem::path& file_path) = 0; /*! @@ -58,33 +60,34 @@ class IModel { * \param transform Transformation matrix to be applied to the mesh. */ virtual void setMeshTransform(Mesh::rid_t rid, glm::mat4x4 transform) = 0; - /*! - * \brief Gets the transformation matrix of a mesh. - * \param rid Render-id of the mesh - * \return Returns a transform matrix of the mesh - */ + /*! + * \brief Gets the transformation matrix of a mesh. + * \param rid Render-id of the mesh + * \return Returns a transform matrix of the mesh + */ virtual glm::mat4 get_mesh_transform(Mesh::rid_t rid) = 0; /*! * \brief Stores mesh data */ -struct MeshPair { + struct MeshPair { /*! * \brief Pointer to the loaded mesh (GLTF) */ -std::shared_ptr ptr; + std::shared_ptr ptr; /*! * \brief Transform matrix of the mesh */ -glm::mat4 transform; + glm::mat4 transform; }; + using MeshMap = std::unordered_map; -/*! - * \brief Gets all the stored meshes - * \return Returns a MeshMap that stores pairs of the render-id of the mesh and - * the mesh data - */ -virtual const MeshMap& get_meshes() = 0; + /*! + * \brief Gets all the stored meshes + * \return Returns a MeshMap that stores pairs of the render-id of the mesh + * and the mesh data + */ + virtual const MeshMap& get_meshes() = 0; /*! * \brief Retrieves the camera instance. @@ -96,11 +99,11 @@ virtual const MeshMap& get_meshes() = 0; */ [[nodiscard]] virtual Camera* getCamera() = 0; - /*! - * \brief Gets the stored engine - * \return Returns a ref to the engine - */ -virtual VulkanEngine& get_engine() = 0; + /*! + * \brief Gets the stored engine + * \return Returns a ref to the engine + */ + virtual VulkanEngine& get_engine() = 0; using Ptr = std::shared_ptr; }; diff --git a/src/include/interfaces/IView.h b/src/include/interfaces/IView.h index 5309eeff..0b7fb751 100644 --- a/src/include/interfaces/IView.h +++ b/src/include/interfaces/IView.h @@ -14,4 +14,3 @@ class IView { using Ptr = std::shared_ptr; }; - diff --git a/src/include/scene/Mesh.h b/src/include/scene/Mesh.h index df095ba9..c8c31559 100644 --- a/src/include/scene/Mesh.h +++ b/src/include/scene/Mesh.h @@ -3,6 +3,7 @@ #include #include "graphics/vulkan/vk_descriptors.h" +#include "graphics/vulkan/vk_types.h" class VulkanEngine; @@ -46,5 +47,5 @@ struct LoadedGLTF { VulkanEngine* creator; }; -} -}; \ No newline at end of file +} // namespace GLTF +}; // namespace Mesh \ No newline at end of file From 61fdc1287e253ed3b552afb4b6243172ee925ee7 Mon Sep 17 00:00:00 2001 From: Varnill Date: Thu, 9 Oct 2025 22:24:42 +0300 Subject: [PATCH 27/27] fix spdlog --- CMakePresets.json | 90 ++++++++++++++++++-------------------- cmake/compileShaders.cmake | 2 +- vcpkg | 2 +- vcpkg.json | 4 ++ 4 files changed, 48 insertions(+), 50 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 94effdfc..34465a50 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -8,11 +8,11 @@ "binaryDir": "${sourceDir}/out/build/${presetName}", "installDir": "${sourceDir}/out/install/${presetName}", "cacheVariables": { - "CMAKE_COMPILE_WARNING_AS_ERROR": "ON", - "CMAKE_EXPORT_COMPILE_COMMANDS": "ON" + "CMAKE_COMPILE_WARNING_AS_ERROR": "OFF" }, "toolchainFile": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake" }, + { "name": "windows-base", "hidden": true, @@ -20,8 +20,8 @@ "cacheVariables": { "CMAKE_C_COMPILER": "cl", "CMAKE_CXX_COMPILER": "cl", - "CMAKE_C_FLAGS_INIT": "/W4 /wd4700", - "CMAKE_CXX_FLAGS_INIT": "/W4 /wd4700" + "CMAKE_C_FLAGS_INIT": "/W4", + "CMAKE_CXX_FLAGS_INIT": "/W4" }, "condition": { "type": "equals", @@ -29,6 +29,7 @@ "rhs": "Windows" } }, + { "name": "wx64-debug", "displayName": "x64 Debug", @@ -49,6 +50,26 @@ "CMAKE_BUILD_TYPE": "Release" } }, + { + "name": "wx86-debug", + "displayName": "x86 Debug", + "inherits": "windows-base", + "architecture": { + "value": "x86", + "strategy": "external" + }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "wx86-release", + "displayName": "x86 Release", + "inherits": "wx86-debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, { "name": "linux-base", "hidden": true, @@ -56,8 +77,8 @@ "cacheVariables": { "CMAKE_C_COMPILER": "clang", "CMAKE_CXX_COMPILER": "clang++", - "CMAKE_C_FLAGS_INIT": "-Wall -Wextra -pedantic -Wconversion -Wdouble-promotion -Wshadow -Wno-missing-field-initializers", - "CMAKE_CXX_FLAGS_INIT": "-Wall -Wextra -pedantic -Wconversion -Wdouble-promotion -Wshadow -Wno-missing-field-initializers" + "CMAKE_C_FLAGS_INIT": "-Wall -Wextra -pedantic -Wconversion -Wdouble-promotion -Wshadow -Wno-missing-field-initializers -Wno-nullability-extension", + "CMAKE_CXX_FLAGS_INIT": "-Wall -Wextra -pedantic -Wconversion -Wdouble-promotion -Wshadow -Wno-missing-field-initializers -Wno-nullability-extension" }, "condition": { "type": "equals", @@ -84,53 +105,26 @@ "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } - } - ], - "buildPresets": [ - { - "name": "wx64-debug", - "configurePreset": "wx64-debug" - }, - { - "name": "wx64-release", - "configurePreset": "wx64-release" - }, - { - "name": "lx64-debug", - "configurePreset": "lx64-debug" }, { - "name": "lx64-release", - "configurePreset": "lx64-release" - } - ], - "testPresets": [ - { - "name": "base", - "output": { - "outputOnFailure": true + "name": "lx86-debug", + "displayName": "x86 Debug", + "inherits": "linux-base", + "architecture": { + "value": "x86", + "strategy": "external" }, - "hidden": true - }, - { - "name": "wx64-debug", - "inherits": "base", - "configurePreset": "wx64-debug" - }, - { - "name": "wx64-release", - "inherits": "base", - "configurePreset": "wx64-release" - }, - { - "name": "lx64-debug", - "inherits": "base", - "configurePreset": "lx64-debug" + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } }, { - "name": "lx64-release", - "inherits": "base", - "configurePreset": "lx64-release" + "name": "lx86-release", + "displayName": "x86 Release", + "inherits": "lx86-debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } } ] } \ No newline at end of file diff --git a/cmake/compileShaders.cmake b/cmake/compileShaders.cmake index 3b09d959..44a5c967 100644 --- a/cmake/compileShaders.cmake +++ b/cmake/compileShaders.cmake @@ -1,7 +1,7 @@ set(BUILD_SHADERS_SCRIPT "${CMAKE_SOURCE_DIR}/scripts/build_shaders.py") set(SHADERS_SOURCE_DIR "${CMAKE_SOURCE_DIR}/shaders") -set(SHADERS_DEST_DIR "${CMAKE_BINARY_DIR}/shaders") +set(SHADERS_DEST_DIR "${CMAKE_BINARY_DIR}/demo/shaders") add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E echo "Running build_shaders.py script..." diff --git a/vcpkg b/vcpkg index 9b75e789..b1b19307 160000 --- a/vcpkg +++ b/vcpkg @@ -1 +1 @@ -Subproject commit 9b75e789ece3f942159b8500584e35aafe3979ff +Subproject commit b1b19307e2d2ec1eefbdb7ea069de7d4bcd31f01 diff --git a/vcpkg.json b/vcpkg.json index e932e3d7..49225b84 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -29,6 +29,10 @@ { "name": "fastgltf", "version": "0.7.1" + }, + { + "name": "fmt", + "version": "11.2.0" } ] } \ No newline at end of file