From 9941a797a9811bb68cff3269c0d87af07a1bd63a Mon Sep 17 00:00:00 2001 From: anunknowperson Date: Mon, 2 Jun 2025 18:38:28 +0300 Subject: [PATCH 1/3] Various fixes for meshes loading logic, model reload, new default models Fix gltf loading with 0 materials, Add more meshes, Fix default mesh, Add ability to load another model on mesh --- assets/basicmesh.glb | Bin 141384 -> 69812 bytes assets/cube.glb | Bin 0 -> 1732 bytes assets/sphere.glb | Bin 0 -> 70220 bytes src/core/Mesh.cpp | 29 +++++++- src/graphics/vulkan/vk_engine.cpp | 6 +- src/graphics/vulkan/vk_loader.cpp | 114 ++++++++++++++---------------- src/include/core/Mesh.h | 7 +- 7 files changed, 90 insertions(+), 66 deletions(-) create mode 100644 assets/cube.glb create mode 100644 assets/sphere.glb diff --git a/assets/basicmesh.glb b/assets/basicmesh.glb index ab41bcb71157131a96f486f4115b034f585cc39e..51d8890b2152f53224cd74f81483d1c98a2b75e5 100644 GIT binary patch delta 7676 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 diff --git a/assets/cube.glb b/assets/cube.glb new file mode 100644 index 0000000000000000000000000000000000000000..9e5f005c062abd3198b2bfdd9d5a214625654797 GIT binary patch literal 1732 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 Ip1LjiKkmnH-2eap literal 0 HcmV?d00001 diff --git a/src/core/Mesh.cpp b/src/core/Mesh.cpp index 5f94c47d..f496c69d 100644 --- a/src/core/Mesh.cpp +++ b/src/core/Mesh.cpp @@ -1,17 +1,40 @@ +// Mesh.cpp #include "core/Mesh.h" -Mesh::Mesh(const std::string &filePath) { +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(); - engine.unregisterMesh(_rid); + // 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(); diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index 97fa88ae..8167d2b4 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -1157,8 +1157,10 @@ int64_t VulkanEngine::registerMesh(const std::string& filePath) { } void VulkanEngine::unregisterMesh(int64_t id) { - meshes.erase(id); - transforms.erase(id); + if (meshes.find(id) != meshes.end()) { + meshes.erase(id); + transforms.erase(id); + } } void VulkanEngine::setMeshTransform(int64_t id, glm::mat4 mat) { diff --git a/src/graphics/vulkan/vk_loader.cpp b/src/graphics/vulkan/vk_loader.cpp index 16eeb94c..52afa543 100644 --- a/src/graphics/vulkan/vk_loader.cpp +++ b/src/graphics/vulkan/vk_loader.cpp @@ -203,26 +203,18 @@ VkSamplerMipmapMode extract_mipmap_mode(fastgltf::Filter filter) { std::optional> loadGltf(VulkanEngine* engine, std::string_view filePath) { fmt::print("Loading GLTF: {}", 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::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); @@ -248,35 +240,31 @@ std::optional> loadGltf(VulkanEngine* engine, return {}; } - // we can stimate the descriptors we will need accurately + // 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); - file.descriptorPool.init(engine->_device, - static_cast(gltf.materials.size()), - sizes); - - // load samplers + // 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); } @@ -285,22 +273,24 @@ std::optional> loadGltf(VulkanEngine* engine, std::vector images; std::vector> materials; - // load all textures + // Load all textures images.reserve(gltf.images.size()); for (size_t i = 0; i < gltf.images.size(); i++) { images.push_back(engine->_errorCheckerboardImage); } - // create buffer to hold the material data + // 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) * - gltf.materials.size(), + 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); @@ -311,10 +301,9 @@ std::optional> loadGltf(VulkanEngine* engine, 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; auto passType = MaterialPass::MainColor; @@ -323,17 +312,14 @@ std::optional> loadGltf(VulkanEngine* engine, } 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] @@ -341,21 +327,40 @@ std::optional> loadGltf(VulkanEngine* engine, 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 + // 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; + resources.colorSampler = engine->_defaultSamplerLinear; + resources.metalRoughImage = engine->_whiteImage; + 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; @@ -364,8 +369,6 @@ std::optional> loadGltf(VulkanEngine* engine, meshes.push_back(newmesh); file.meshes[name.c_str()] = newmesh; newmesh->name = name; - - // clear the mesh arrays each mesh, we dont want to merge them by error indices.clear(); vertices.clear(); @@ -374,27 +377,24 @@ std::optional> loadGltf(VulkanEngine* engine, 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 + // 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 + // 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; @@ -407,7 +407,7 @@ std::optional> loadGltf(VulkanEngine* engine, }); } - // load vertex normals + // Load vertex normals auto normals = p.findAttribute("NORMAL"); if (normals != p.attributes.end()) { fastgltf::iterateAccessorWithIndex( @@ -417,7 +417,7 @@ std::optional> loadGltf(VulkanEngine* engine, }); } - // load UVs + // Load UVs auto uv = p.findAttribute("TEXCOORD_0"); if (uv != p.attributes.end()) { fastgltf::iterateAccessorWithIndex( @@ -428,7 +428,7 @@ std::optional> loadGltf(VulkanEngine* engine, }); } - // load vertex colors + // Load vertex colors auto colors = p.findAttribute("COLOR_0"); if (colors != p.attributes.end()) { fastgltf::iterateAccessorWithIndex( @@ -438,24 +438,21 @@ std::optional> loadGltf(VulkanEngine* engine, }); } + // ✅ Assign material safely if (p.materialIndex.has_value()) { newSurface.material = materials[p.materialIndex.value()]; } else { - newSurface.material = materials[0]; + newSurface.material = materials[0]; // Always valid now } newmesh->surfaces.push_back(newSurface); } - newmesh->meshBuffers = engine->uploadMesh(indices, vertices); } - // load all nodes and their meshes + // 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(); dynamic_cast(newNode.get())->mesh = @@ -463,7 +460,6 @@ std::optional> loadGltf(VulkanEngine* engine, } else { newNode = std::make_shared(); } - nodes.push_back(newNode); file.nodes[node.name.c_str()]; @@ -483,36 +479,34 @@ std::optional> loadGltf(VulkanEngine* engine, 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); } - // run loop again to setup transform hierarchy + // 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 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}); + // Find top-level nodes + for (auto& n : nodes) { + if (n->parent.lock() == nullptr) { + file.topNodes.push_back(n); + n->refreshTransform(glm::mat4{1.f}); } } + return scene; } diff --git a/src/include/core/Mesh.h b/src/include/core/Mesh.h index 637a0481..a8e22857 100644 --- a/src/include/core/Mesh.h +++ b/src/include/core/Mesh.h @@ -1,3 +1,4 @@ +// Mesh.h #pragma once #include @@ -10,11 +11,15 @@ class Mesh { 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 From f0a711524dcd7946abd012720cc6916f83012080 Mon Sep 17 00:00:00 2001 From: Sergei Kiselev <54563399+anunknowperson@users.noreply.github.com> Date: Mon, 2 Jun 2025 18:46:40 +0300 Subject: [PATCH 2/3] Update vk_loader.cpp - Fix bad merge --- src/graphics/vulkan/vk_loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphics/vulkan/vk_loader.cpp b/src/graphics/vulkan/vk_loader.cpp index 68cdfd87..d22ab31d 100644 --- a/src/graphics/vulkan/vk_loader.cpp +++ b/src/graphics/vulkan/vk_loader.cpp @@ -313,7 +313,7 @@ std::optional> loadGltf(VulkanEngine* engine, GLTFMetallic_Roughness::MaterialResources materialResources; - materialResources.colorImage = engine->_whiteImage->get(); + materialResources.colorImage = engine->_whiteImage; materialResources.colorSampler = engine->_defaultSamplerLinear; materialResources.metalRoughImage = engine->_whiteImage->get(); From c18086cfb49b2b72dbc2161646af48552cf21b4d Mon Sep 17 00:00:00 2001 From: anunknowperson Date: Mon, 2 Jun 2025 18:53:16 +0300 Subject: [PATCH 3/3] Fix broken merge --- src/graphics/vulkan/vk_loader.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/graphics/vulkan/vk_loader.cpp b/src/graphics/vulkan/vk_loader.cpp index d22ab31d..d33c8ffc 100644 --- a/src/graphics/vulkan/vk_loader.cpp +++ b/src/graphics/vulkan/vk_loader.cpp @@ -313,7 +313,7 @@ std::optional> loadGltf(VulkanEngine* engine, GLTFMetallic_Roughness::MaterialResources materialResources; - materialResources.colorImage = engine->_whiteImage; + materialResources.colorImage = engine->_whiteImage->get(); materialResources.colorSampler = engine->_defaultSamplerLinear; materialResources.metalRoughImage = engine->_whiteImage->get(); @@ -351,9 +351,9 @@ std::optional> loadGltf(VulkanEngine* engine, sceneMaterialConstants[0] = constants; GLTFMetallic_Roughness::MaterialResources resources; - resources.colorImage = engine->_whiteImage; + resources.colorImage = engine->_whiteImage->get(); resources.colorSampler = engine->_defaultSamplerLinear; - resources.metalRoughImage = engine->_whiteImage; + resources.metalRoughImage = engine->_whiteImage->get(); resources.metalRoughSampler = engine->_defaultSamplerLinear; resources.dataBuffer = file.materialDataBuffer.buffer; resources.dataBufferOffset = 0;