diff --git a/assets/basicmesh.glb b/assets/basicmesh.glb new file mode 100644 index 0000000..ab41bcb Binary files /dev/null and b/assets/basicmesh.glb differ diff --git a/assets/structure.glb b/assets/structure.glb new file mode 100644 index 0000000..7728cef Binary files /dev/null and b/assets/structure.glb differ diff --git a/meson.build b/meson.build index 7578700..68e05c2 100644 --- a/meson.build +++ b/meson.build @@ -60,6 +60,17 @@ add_project_arguments( '-Wno-implicit-int-float-conversion', '-Wno-implicit-float-conversion', '-Wno-c++98-compat', + # Thanks fastgltf + '-Wno-ctad-maybe-unsupported', + '-Wno-switch-default', + '-Wno-nrvo', + '-Wno-shadow-field-in-constructor', + '-Wno-deprecated-copy-with-dtor', + '-Wno-double-promotion', + '-Wno-weak-vtables', + '-Wno-switch-enum', + '-Wno-sign-conversion', + '-Wno-documentation', ], language : 'cpp' ) @@ -99,6 +110,7 @@ exe = executable('vr-compositor', 'src/DescriptorLayoutBuilder.cpp', 'src/DescriptorAllocator.cpp', 'src/GraphicsPipelineBuilder.cpp', + 'src/Loader.cpp', 'src/VulkanRenderer.cpp', 'src/Application.cpp', ], diff --git a/src/Application.h b/src/Application.h index 6ae590c..f60d5e7 100644 --- a/src/Application.h +++ b/src/Application.h @@ -10,7 +10,7 @@ namespace Lunar { -class VulkanRenderer; +struct VulkanRenderer; struct Application { Application(); diff --git a/src/Loader.cpp b/src/Loader.cpp new file mode 100644 index 0000000..a95d1c7 --- /dev/null +++ b/src/Loader.cpp @@ -0,0 +1,201 @@ +#include "Loader.h" + +#include +#include +#include + +#include "VulkanRenderer.h" + +namespace fastgltf { +template<> +struct ElementTraits + : ElementTraitsBase { }; +template<> +struct ElementTraits + : ElementTraitsBase { }; +template<> +struct ElementTraits + : ElementTraitsBase { }; +} + +#ifndef __cpp_lib_format_path + +# include +# include +# include +# include + +template +struct std::formatter + : std::formatter, CharT> { + template + auto format(std::filesystem::path const &p, FormatContext &ctx) const + { + std::basic_string s; + + if constexpr (std::is_same_v) { + s = p.generic_string(); + } else if constexpr (std::is_same_v) { + s = p.generic_wstring(); + } +# if defined(__cpp_lib_char8_t) + else if constexpr (std::is_same_v) { + s = p.generic_u8string(); + } +# endif + else if constexpr (std::is_same_v) { + s = p.generic_u16string(); + } else if constexpr (std::is_same_v) { + s = p.generic_u32string(); + } + + return std::formatter, CharT>::format(s, ctx); + } +}; + +namespace Lunar { + +auto Mesh::load_gltf_meshes( + VulkanRenderer &renderer, std::filesystem::path const path) + -> std::optional>> +{ + renderer.logger().debug("Loading GLTF from file: {}", path); + + auto data = fastgltf::GltfDataBuffer::FromPath(path); + if (data.error() != fastgltf::Error::None) { + renderer.logger().err("Failed to open glTF file: {} (error {})", path, + fastgltf::to_underlying(data.error())); + return {}; + } + + constexpr auto gltfOptions { fastgltf::Options::LoadExternalBuffers }; + + fastgltf::Parser parser; + + auto load { parser.loadGltf(data.get(), path.parent_path(), gltfOptions) }; + if (load.error() != fastgltf::Error::None) { + renderer.logger().err( + "Failed to load glTF: {}", fastgltf::to_underlying(load.error())); + return {}; + } + fastgltf::Asset gltf { std::move(load.get()) }; + + std::vector> meshes; + + std::vector indices; + std::vector vertices; + for (auto &mesh : gltf.meshes) { + Mesh new_mesh; + + new_mesh.name = mesh.name; + + indices.clear(); + vertices.clear(); + + for (auto &&p : mesh.primitives) { + Surface new_surface; + new_surface.start_index = static_cast(indices.size()); + new_surface.count = static_cast( + gltf.accessors[p.indicesAccessor.value()].count); + + size_t initial_vertex = vertices.size(); + + { // Indices + auto &accessor = gltf.accessors[p.indicesAccessor.value()]; + indices.reserve(indices.size() + accessor.count); + fastgltf::iterateAccessor( + gltf, accessor, [&](std::uint32_t idx) { + indices.emplace_back(idx + initial_vertex); + }); + } + + { // Vertex positions + auto &accessor = gltf.accessors[p.findAttribute("POSITION") + ->accessorIndex]; + + for (auto pos : + fastgltf::iterateAccessor(gltf, accessor)) { + Vertex v { + .position = pos, + .u = 0, + .normal = { 0, 0, 0 }, + .v = 0, + .color = { 1.0f, 1.0f, 1.0f, 1.0f }, + }; + vertices.emplace_back(v); + } + } + + if (auto attr = p.findAttribute("NORMAL")) { // Normals + auto &accessor = gltf.accessors[attr->accessorIndex]; + size_t local_index = 0; + for (auto normal : + fastgltf::iterateAccessor(gltf, accessor)) { + vertices[initial_vertex + local_index].normal = normal; + local_index++; + } + } + + if (auto attr = p.findAttribute("TEXCOORD_0")) { // UVs + auto &accessor = gltf.accessors[attr->accessorIndex]; + size_t local_index = 0; + for (auto uv : + fastgltf::iterateAccessor(gltf, accessor)) { + uv.unpack(vertices[initial_vertex + local_index].u, + vertices[initial_vertex + local_index].v); + local_index++; + } + } + + if (auto attr = p.findAttribute("COLOR_0")) { // Colors + auto &accessor = gltf.accessors[attr->accessorIndex]; + size_t local_index = 0; + + switch (accessor.type) { + case fastgltf::AccessorType::Vec3: { + for (auto c3 : fastgltf::iterateAccessor( + gltf, accessor)) { + auto &dst + = vertices[initial_vertex + local_index].color; + dst = { c3.x(), c3.y(), c3.z(), 1.0f }; + ++local_index; + } + break; + } + case fastgltf::AccessorType::Vec4: { + for (auto c4 : fastgltf::iterateAccessor( + gltf, accessor)) { + vertices[initial_vertex + local_index].color = c4; + ++local_index; + } + break; + } + default: + renderer.logger().warn( + "Unsupported COLOR_0 accessor type ({}) on mesh '{}'", + static_cast(accessor.type), new_mesh.name); + break; + } + } + + constexpr bool OVERRIDE_COLORS = true; + if (OVERRIDE_COLORS) { + for (auto &vtx : vertices) { + vtx.color = smath::Vec4(vtx.normal, 1.f); + } + } + + new_mesh.surfaces.emplace_back(new_surface); + } + + new_mesh.mesh_buffers = renderer.upload_mesh(indices, vertices); + + meshes.emplace_back(std::make_shared(std::move(new_mesh))); + } + + return meshes; +} + +#endif // __cpp_lib_format_path + +} // namespace Lunar diff --git a/src/Loader.h b/src/Loader.h new file mode 100644 index 0000000..6dbfc08 --- /dev/null +++ b/src/Loader.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "Types.h" + +namespace Lunar { + +struct VulkanRenderer; + +struct Mesh { + struct Surface { + uint32_t start_index; + uint32_t count; + }; + + std::string name; + std::vector surfaces; + GPUMeshBuffers mesh_buffers; + + static auto load_gltf_meshes( + VulkanRenderer &renderer, std::filesystem::path const path) + -> std::optional>>; +}; + +} // namespace Lunar diff --git a/src/Types.h b/src/Types.h new file mode 100644 index 0000000..9170c70 --- /dev/null +++ b/src/Types.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include + +#include "DeletionQueue.h" + +namespace Lunar { + +struct AllocatedImage { + VkImage image; + VkImageView image_view; + VmaAllocation allocation; + VkExtent3D extent; + VkFormat format; +}; + +struct AllocatedBuffer { + VkBuffer buffer; + VmaAllocation allocation; + VmaAllocationInfo info; +}; + +struct FrameData { + VkCommandPool command_pool; + VkCommandBuffer main_command_buffer; + VkSemaphore swapchain_semaphore; + VkFence render_fence; + + DeletionQueue deletion_queue; +}; + +struct Vertex { + smath::Vec3 position; + float u; + smath::Vec3 normal; + float v; + smath::Vec4 color; +}; + +struct GPUMeshBuffers { + AllocatedBuffer index_buffer, vertex_buffer; + VkDeviceAddress vertex_buffer_address; +}; + +} // namespace Lunar diff --git a/src/VulkanRenderer.cpp b/src/VulkanRenderer.cpp index 06af77c..4f32fc2 100644 --- a/src/VulkanRenderer.cpp +++ b/src/VulkanRenderer.cpp @@ -151,7 +151,8 @@ auto VulkanRenderer::vk_init() -> void features_13.pNext = nullptr; features_13.synchronization2 = VK_TRUE; features_13.dynamicRendering = VK_TRUE; - VkPhysicalDeviceBufferDeviceAddressFeatures buffer_device_address_features {}; + VkPhysicalDeviceBufferDeviceAddressFeatures + buffer_device_address_features {}; buffer_device_address_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES; buffer_device_address_features.bufferDeviceAddress = VK_TRUE; @@ -587,7 +588,15 @@ auto VulkanRenderer::default_data_init() -> void m_vk.rectangle = upload_mesh(rect_indices, rect_vertices); + m_vk.test_meshes + = Mesh::load_gltf_meshes(*this, "assets/basicmesh.glb").value(); + m_vk.deletion_queue.emplace([&]() { + for (auto &mesh : m_vk.test_meshes) { + destroy_buffer(mesh->mesh_buffers.index_buffer); + destroy_buffer(mesh->mesh_buffers.vertex_buffer); + } + destroy_buffer(m_vk.rectangle.index_buffer); destroy_buffer(m_vk.rectangle.vertex_buffer); }); @@ -763,6 +772,25 @@ auto VulkanRenderer::draw_geometry(VkCommandBuffer cmd) -> void vkCmdDrawIndexed(cmd, 6, 1, 0, 0, 0); + push_constants.vertex_buffer + = m_vk.test_meshes[2]->mesh_buffers.vertex_buffer_address; + + auto const view { smath::translate(smath::Vec3 { 0, 0, -0.1 }) }; + auto projection { smath::matrix_perspective(smath::deg(70.0f), + static_cast(m_vk.draw_extent.width) + / static_cast(m_vk.draw_extent.height), + 10000.0f, 0.1f) }; + push_constants.world_matrix = projection * view; + + vkCmdPushConstants(cmd, m_vk.mesh_pipeline_layout, + VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(push_constants), &push_constants); + vkCmdBindIndexBuffer(cmd, + m_vk.test_meshes[2]->mesh_buffers.index_buffer.buffer, 0, + VK_INDEX_TYPE_UINT32); + + vkCmdDrawIndexed(cmd, m_vk.test_meshes[2]->surfaces[0].count, 1, + m_vk.test_meshes[2]->surfaces[0].start_index, 0, 0); + vkCmdEndRendering(cmd); } diff --git a/src/VulkanRenderer.h b/src/VulkanRenderer.h index 19e645f..bef2a1e 100644 --- a/src/VulkanRenderer.h +++ b/src/VulkanRenderer.h @@ -11,46 +11,12 @@ #include "DeletionQueue.h" #include "DescriptorAllocator.h" +#include "Loader.h" #include "Logger.h" +#include "Types.h" namespace Lunar { -struct AllocatedImage { - VkImage image; - VkImageView image_view; - VmaAllocation allocation; - VkExtent3D extent; - VkFormat format; -}; - -struct AllocatedBuffer { - VkBuffer buffer; - VmaAllocation allocation; - VmaAllocationInfo info; -}; - -struct FrameData { - VkCommandPool command_pool; - VkCommandBuffer main_command_buffer; - VkSemaphore swapchain_semaphore; - VkFence render_fence; - - DeletionQueue deletion_queue; -}; - -struct Vertex { - smath::Vec3 position; - float uv_x; - smath::Vec3 normal; - float uv_y; - smath::Vec4 color; -}; - -struct GPUMeshBuffers { - AllocatedBuffer index_buffer, vertex_buffer; - VkDeviceAddress vertex_buffer_address; -}; - struct GPUDrawPushConstants { smath::Mat4 world_matrix; VkDeviceAddress vertex_buffer; @@ -58,8 +24,7 @@ struct GPUDrawPushConstants { constexpr unsigned FRAME_OVERLAP = 2; -class VulkanRenderer { -public: +struct VulkanRenderer { VulkanRenderer(SDL_Window *window, Logger &logger); ~VulkanRenderer(); @@ -68,6 +33,10 @@ public: auto immediate_submit(std::function &&function) -> void; + auto upload_mesh(std::span indices, std::span vertices) + -> GPUMeshBuffers; + + auto logger() const -> Logger & { return m_logger; } private: auto vk_init() -> void; @@ -96,8 +65,6 @@ private: auto create_buffer(size_t alloc_size, VkBufferUsageFlags usage, VmaMemoryUsage memory_usage) -> AllocatedBuffer; auto destroy_buffer(AllocatedBuffer &buffer) -> void; - auto upload_mesh(std::span indices, std::span vertices) - -> GPUMeshBuffers; struct { vkb::Instance instance; @@ -154,6 +121,8 @@ private: VkCommandPool imm_command_pool {}; uint64_t frame_number { 0 }; + + std::vector> test_meshes; } m_vk; SDL_Window *m_window { nullptr }; diff --git a/thirdparty/smath b/thirdparty/smath index e32204d..b5e0aab 160000 --- a/thirdparty/smath +++ b/thirdparty/smath @@ -1 +1 @@ -Subproject commit e32204db0cf87870d6fa85ed5623520c682c29ea +Subproject commit b5e0aabe379b363ebfbf2172d5325d7fbf099c29