diff --git a/CMakeLists.txt b/CMakeLists.txt index ad7ae4be..edf8da3d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,6 +91,7 @@ set_packages( flecs tinyobjloader + fastgltf ${SHADERC_PACKAGE} watcher vulkan-cpp @@ -108,6 +109,7 @@ set_packages( stb::stb flecs::flecs_static tinyobjloader::tinyobjloader + fastgltf::fastgltf ${SHADERC_LINK_PACKAGE} watcher::watcher vulkan-cpp @@ -208,9 +210,10 @@ target_sources(${PROJECT_NAME} PUBLIC atlas/drivers/vulkan/hash.cppm - # 3D Model Importers + # 3D Model File Format Importers atlas/drivers/importer/importer.cppm atlas/drivers/importer/obj_loader.cppm + atlas/drivers/importer/gltf_importer.cppm ) target_sources(${PROJECT_NAME} PUBLIC diff --git a/atlas/core/application.cppm b/atlas/core/application.cppm index 1f3cf5dc..70d682fd 100644 --- a/atlas/core/application.cppm +++ b/atlas/core/application.cppm @@ -2,7 +2,6 @@ module; #include #include -#include #include #include @@ -150,7 +149,6 @@ export namespace atlas { m_color_format, m_depth_format, m_window_params); - std::println("images.size() = {}", images.size()); m_window->center_window(); @@ -418,14 +416,15 @@ export namespace atlas { invoke_post_update(m_current_scene.get()); invoke_post_update(m_world.get()); + + m_next_image_frame_idx = + (m_next_image_frame_idx + 1) % m_frames_in_flight; } // Experimental: Will look into later once we dive into scene // transitioning void on_scene_transition(event::scene_transition& // p_scene_transition) { // m_world->current(p_scene_transition.next_scene); - // std::println("Attempting to switch to the scene: {}", - // p_scene_transition.next_scene); // // We only want to set the current scene if that specific scene // is valid if(m_world->current() != nullptr) { @@ -484,6 +483,7 @@ export namespace atlas { std::vector m_command_buffers; float m_delta_time = 0.f; window_params m_window_params{}; + uint32_t m_frames_in_flight = 2; static application* s_instance; }; diff --git a/atlas/core/entry_point/main.cpp b/atlas/core/entry_point/main.cpp index 724d3fb8..7c13addb 100644 --- a/atlas/core/entry_point/main.cpp +++ b/atlas/core/entry_point/main.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include #include @@ -30,12 +29,11 @@ import vk; atlas::event::bus& p_bus); static VKAPI_ATTR VkBool32 VKAPI_CALL -debug_callback( - [[maybe_unused]] VkDebugUtilsMessageSeverityFlagBitsEXT p_message_severity, - [[maybe_unused]] VkDebugUtilsMessageTypeFlagsEXT p_message_type, - const VkDebugUtilsMessengerCallbackDataEXT* p_callback_data, - [[maybe_unused]] void* p_user_data) { - std::print("validation layer:\t\t{}\n\n", p_callback_data->pMessage); +debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT, + VkDebugUtilsMessageTypeFlagsEXT, + const VkDebugUtilsMessengerCallbackDataEXT* p_callback_data, + void*) { + console_log_info_tagged("vk::validation", "{}", p_callback_data->pMessage); return false; } @@ -47,7 +45,7 @@ get_instance_extensions() { glfwGetRequiredInstanceExtensions(&extension_count); for (uint32_t i = 0; i < extension_count; i++) { - std::println("Required Extension = {}", required_extensions[i]); + console_log_info("Required Extension = {}", required_extensions[i]); extension_names.emplace_back(required_extensions[i]); } @@ -73,7 +71,7 @@ main() { } if (!glfwVulkanSupported()) { - std::println("GLFW: Vulkan is not supported!"); + console_log_error("GLFW: Vulkan is not supported!"); return -1; } @@ -114,6 +112,9 @@ main() { vk::physical_device physical_device = physical_device_expected.value(); std::array priorities = { 0.f }; + + // VK_KHR_dynamic_rendering is required to be explicitly specified in the + // extensions to prevent imgui from crashing. #if defined(__APPLE__) std::array extensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, diff --git a/atlas/core/logger.cppm b/atlas/core/logger.cppm index 54b4a1a8..b2fd700b 100644 --- a/atlas/core/logger.cppm +++ b/atlas/core/logger.cppm @@ -8,7 +8,6 @@ module; #include #include #include -#include export module atlas.core.utilities:logger; import :common; @@ -24,13 +23,14 @@ export namespace atlas { public: console_log_manager( const std::string& p_pattern = "%^[%n] [%T]: %v%$") { - std::println("Constructing console_log_manager!"); //! @note Setting up logs for different log stdout's //! @note Logs for p_tag is logs specific to the game. m_loggers.insert({ "atlas", spdlog::stdout_color_mt("atlas") }); m_loggers.insert({ "physics", spdlog::stdout_color_mt("physics") }); m_loggers.insert({ "vulkan", spdlog::stdout_color_mt("vulkan") }); + m_loggers.insert( + { "vk::validation", spdlog::stdout_color_mt("vk::validation") }); m_loggers.insert( { "assert", spdlog::stdout_color_mt("core assertion") }); @@ -45,6 +45,9 @@ export namespace atlas { m_loggers["assert"]->set_level(spdlog::level::trace); m_loggers["assert"]->set_pattern(p_pattern); + + m_loggers["vk::validation"]->set_level(spdlog::level::trace); + m_loggers["vk::validation"]->set_pattern(p_pattern); s_instance = this; } diff --git a/atlas/core/serialize/serializer.cppm b/atlas/core/serialize/serializer.cppm index 3d66df17..191a1a3c 100644 --- a/atlas/core/serialize/serializer.cppm +++ b/atlas/core/serialize/serializer.cppm @@ -7,7 +7,6 @@ module; #include #include #include -#include export module atlas.core.serialize; @@ -208,7 +207,8 @@ namespace atlas { .build(); q.each([&output](flecs::entity p_entity) { - std::println("Serialize Entity: {}", p_entity.name().c_str()); + console_log_error("Serialize Entity: {}", + p_entity.name().c_str()); serialize_entity(output, p_entity); }); diff --git a/atlas/drivers/importer/gltf_importer.cppm b/atlas/drivers/importer/gltf_importer.cppm new file mode 100644 index 00000000..bb5a0073 --- /dev/null +++ b/atlas/drivers/importer/gltf_importer.cppm @@ -0,0 +1,213 @@ +module; + +#include +#include + +#include + +#include +#include +#include +#include +#include + +export module atlas.drivers.importer:gltf_importer; + +import atlas.core.utilities; +import vk; + +namespace atlas { + export class gltf_importer { + public: + gltf_importer(const std::string& p_path, bool p_flip) + : m_flip(p_flip) { + m_load_successful = load(p_path); + } + + bool load(const std::string& p_path) { + if (!std::filesystem::exists(p_path)) { + console_log_error("GLTF Error: Path does not exist: {}", + p_path); + return false; + } + + size_t base_color_uv_index = 0; + + // Create the Parser configuration (enable SIMD parsing if + // available) + fastgltf::Parser parser(fastgltf::Extensions::None); + + // 2. Map buffers directly without copying them into memory + // prematurely + constexpr auto gltf_options = + fastgltf::Options::DontRequireValidAssetMember | + fastgltf::Options::LoadExternalBuffers; + + std::filesystem::path filepath = std::filesystem::path(p_path); + + auto data = fastgltf::GltfDataBuffer::FromPath(filepath); + if (data.error() != fastgltf::Error::None) { + console_log_error("GLTF Error: Failed to load data buffer."); + console_log_info("GLTF Error Message: {}", + fastgltf::getErrorMessage(data.error())); + m_load_successful = false; + return m_load_successful; + } + + auto asset = + parser.loadGltf(data.get(), filepath.parent_path(), gltf_options); + if (asset.error() != fastgltf::Error::None) { + console_log_error("GLTF Error: Parsing failed with code {}", + static_cast(asset.error())); + console_log_info("GLTF Error Message: {}", + fastgltf::getErrorMessage(asset.error())); + m_load_successful = false; + return m_load_successful; + } + + fastgltf::Asset& model = asset.get(); + + // Position + for (const auto& mesh : model.meshes) { + for (const auto& primitive : mesh.primitives) { + + // Get output primitive mesh texture color index + // (base_color_uv_index) + auto* primitive_mesh = primitive.findAttribute("POSITION"); + + if (primitive.materialIndex.has_value()) { + auto& material = + model.materials[primitive.materialIndex.value()]; + + auto& base_color_texture = + material.pbrData.baseColorTexture; + + if (base_color_texture.has_value()) { + auto& texture = + model.textures[base_color_texture->textureIndex]; + + if (!texture.imageIndex.has_value()) { + return false; + } + + if (base_color_texture->transform and + base_color_texture->transform->texCoordIndex + .has_value()) { + base_color_uv_index = + base_color_texture->transform->texCoordIndex + .value(); + } + else { + base_color_uv_index = + material.pbrData.baseColorTexture + ->texCoordIndex; + } + } + } + + // Required to have a primitive that contains the meshes + // position + if (primitive_mesh == primitive.attributes.end()) { + m_load_successful = false; + return m_load_successful; + } + + // Required to have indices associated with the mesh + if (!primitive.indicesAccessor.value()) { + m_load_successful = false; + return m_load_successful; + } + + auto& position_accessor = + model.accessors[primitive_mesh->accessorIndex]; + + if (!position_accessor.bufferViewIndex.value()) { + m_load_successful = false; + return m_load_successful; + } + + const auto& pos_accessor = + model.accessors[primitive_mesh->accessorIndex]; + uint32_t base_vertex = + static_cast(m_vertices.size()); + m_vertices.reserve(base_vertex + pos_accessor.count); + + // Check if this 3D model as texture coordinates or normals + auto uv_attribute = primitive.findAttribute( + std::format("TEXCOORD_{}", base_color_uv_index)); + bool has_uv = (uv_attribute != primitive.attributes.end()); + + auto normal_attribute = primitive.findAttribute("NORMAL"); + bool has_normals = + (normal_attribute != primitive.attributes.end()); + + fastgltf::iterateAccessorWithIndex( + model, + position_accessor, + [&](fastgltf::math::fvec3 pos, size_t index) { + vk::vertex_input vertex{}; + vertex.position = { pos.x(), pos.y(), pos.z() }; + + if (has_normals) { + const auto& normal_accessor = + model + .accessors[normal_attribute->accessorIndex]; + + fastgltf::math::fvec3 norm = + fastgltf::getAccessorElement< + fastgltf::math::fvec3>( + model, normal_accessor, index); + vertex.normals = { norm.x(), norm.y(), norm.z() }; + } + + if (has_uv) { + const auto& uv_accessor = + model.accessors[uv_attribute->accessorIndex]; + + // Extract 2D texture coordinate element matching + // based from the current primitive index + glm::vec2 uv = + fastgltf::getAccessorElement( + model, uv_accessor, index); + + glm::vec2 flipped_uv = { uv.x, 1.0f - uv.y }; + vertex.uv = m_flip ? flipped_uv : uv; + } + + m_vertices.push_back(vertex); + }); + + // Indices + if (primitive.indicesAccessor.has_value()) { + const auto& index_accessor = + model.accessors[primitive.indicesAccessor.value()]; + m_indices.reserve(m_indices.size() + + index_accessor.count); + + fastgltf::iterateAccessor( + model, index_accessor, [&](uint32_t index) { + m_indices.push_back(base_vertex + index); + }); + } + } + } + + m_load_successful = true; + return m_load_successful; + } + + [[nodiscard]] bool load() const { return m_load_successful; } + + //! @return the geometry vertices + std::span vertices() { return m_vertices; } + + //! @return the geometry indices + std::span indices() { return m_indices; } + + private: + std::vector m_vertices; + std::vector m_indices; + bool m_load_successful = false; + bool m_flip = false; + }; +}; \ No newline at end of file diff --git a/atlas/drivers/importer/importer.cppm b/atlas/drivers/importer/importer.cppm index e745b282..29c1f6da 100644 --- a/atlas/drivers/importer/importer.cppm +++ b/atlas/drivers/importer/importer.cppm @@ -3,4 +3,5 @@ module; export module atlas.drivers.importer; // Importers to load .obj 3D models -export import :obj_loader; \ No newline at end of file +export import :obj_loader; +export import :gltf_importer; \ No newline at end of file diff --git a/atlas/drivers/importer/obj_loader.cppm b/atlas/drivers/importer/obj_loader.cppm index 42d76fc1..97a4d925 100644 --- a/atlas/drivers/importer/obj_loader.cppm +++ b/atlas/drivers/importer/obj_loader.cppm @@ -127,7 +127,7 @@ export namespace atlas { return true; } - [[nodiscard]] bool is_load() const { return m_load; } + [[nodiscard]] bool load() const { return m_load; } //! @return the geometry vertices std::span vertices() { return m_vertices; } diff --git a/atlas/drivers/vulkan/environment_map.cppm b/atlas/drivers/vulkan/environment_map.cppm index 0a9c2a87..59d6830f 100644 --- a/atlas/drivers/vulkan/environment_map.cppm +++ b/atlas/drivers/vulkan/environment_map.cppm @@ -7,7 +7,6 @@ module; #include #include #include -#include #define GLM_FORCE_RADIANS #include @@ -31,6 +30,7 @@ module; export module atlas.drivers.vulkan:environment_map; +import atlas.core.utilities; import vk; struct environment_uniforms { @@ -90,12 +90,9 @@ public: bool res = create_hdr(p_filename); if (!res) { - std::println("Cannot load environment: {}", p_filename); + console_log_error("Cannot load environment: {}", p_filename); } - if (res) { - std::println("Loaded succesfully: {}", p_filename); - } create_pipelines(); } @@ -108,11 +105,7 @@ public: bool res = create_hdr(p_color, p_extent); if (!res) { - std::println("Cannot load environment"); - } - - if (res) { - std::println("Loaded succesfully raw pixels successfully"); + console_log_error("Cannot load environment"); } create_pipelines(); } diff --git a/atlas/drivers/vulkan/imgui_context.cppm b/atlas/drivers/vulkan/imgui_context.cppm index 5a167309..0a3566e7 100644 --- a/atlas/drivers/vulkan/imgui_context.cppm +++ b/atlas/drivers/vulkan/imgui_context.cppm @@ -9,7 +9,6 @@ module; #include #include #include -#include #include #include #include @@ -138,7 +137,6 @@ namespace atlas { m_instance = p_context->instance_handle(); m_device = p_context->logical_device(); m_physical = p_context->physical_device(); - std::println("Constructing imgui_context"); m_color_format = p_color_format; m_depth_format = p_depth_format; @@ -157,7 +155,7 @@ namespace atlas { std::ifstream file(path.string()); if (!file) { - std::println("Cannot load {}", path.string()); + console_log_error("Cannot load {}", path.string()); } std::stringstream ss; @@ -242,15 +240,12 @@ namespace atlas { m_viewport_image = vk::sample_image(*m_device, viewport_params); - std::println("before transition image layout"); transition_image_layout(*m_device, m_viewport_image, m_color_format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - std::println("after transition image layout"); - // Perform additional configurations for specific handles vk::image_params viewport_depth_params = { .extent = { .width = m_params.width, diff --git a/atlas/drivers/vulkan/render_context.cppm b/atlas/drivers/vulkan/render_context.cppm index de80a8e9..9f0c70ae 100644 --- a/atlas/drivers/vulkan/render_context.cppm +++ b/atlas/drivers/vulkan/render_context.cppm @@ -5,6 +5,7 @@ module; #include #include #include +#include #include #include @@ -223,6 +224,7 @@ export namespace atlas { } void prebake() { + flecs::query<> all_meshes = m_world->query_builder().build(); @@ -245,18 +247,37 @@ export namespace atlas { }; // importing .obj 3d models here - obj_importer importer(src->model_path, src->flip); - gpu_mesh_data gpu_mesh{}; - gpu_mesh.vertex = vk::vertex_buffer( - *m_device, importer.vertices(), vertex_params); - gpu_mesh.index = - vk::index_buffer(*m_device, importer.indices(), index_params); - gpu_mesh.has_indices_buffer = - (importer.indices().size() >= 0) ? true : false; - gpu_mesh.vertices_size = importer.vertices().size(); - gpu_mesh.indices_size = importer.indices().size(); - - m_meshes.emplace(p_entity.id(), gpu_mesh); + if (std::filesystem::path(src->model_path).extension() == + ".obj") { + gpu_mesh_data gpu_mesh{}; + obj_importer importer(src->model_path, src->flip); + gpu_mesh.vertex = vk::vertex_buffer( + *m_device, importer.vertices(), vertex_params); + gpu_mesh.index = vk::index_buffer( + *m_device, importer.indices(), index_params); + gpu_mesh.has_indices_buffer = + (importer.indices().size() >= 0) ? true : false; + gpu_mesh.vertices_size = importer.vertices().size(); + gpu_mesh.indices_size = importer.indices().size(); + m_meshes.emplace(p_entity.id(), gpu_mesh); + } + else if (std::filesystem::path(src->model_path).extension() == + ".gltf" || + std::filesystem::path(src->model_path).extension() == + ".glb") { + gpu_mesh_data gpu_mesh{}; + gltf_importer importer(src->model_path, src->flip); + gpu_mesh.vertex = vk::vertex_buffer( + *m_device, importer.vertices(), vertex_params); + gpu_mesh.index = vk::index_buffer( + *m_device, importer.indices(), index_params); + gpu_mesh.has_indices_buffer = + (importer.indices().size() >= 0) ? true : false; + gpu_mesh.vertices_size = importer.vertices().size(); + gpu_mesh.indices_size = importer.indices().size(); + + m_meshes.emplace(p_entity.id(), gpu_mesh); + } vk::texture_params config_texture = { .memory_mask = m_physical->memory_properties( diff --git a/atlas/drivers/vulkan/window.cppm b/atlas/drivers/vulkan/window.cppm index 21d8e88c..32c5da5d 100644 --- a/atlas/drivers/vulkan/window.cppm +++ b/atlas/drivers/vulkan/window.cppm @@ -59,10 +59,6 @@ export namespace atlas { m_params.width = static_cast(framebuffer_width); m_params.height = static_cast(framebuffer_height); - std::println("Window created with extent: {}x{}", - m_params.width, - m_params.height); - vk::swapchain_params swapchain_params = { .width = static_cast(m_params.width), .height = static_cast(m_params.height), diff --git a/conanfile.py b/conanfile.py index 83fc4cd6..9f342f81 100644 --- a/conanfile.py +++ b/conanfile.py @@ -42,7 +42,8 @@ def requirements(self): # Vulkan-related headers and includes packages self.requires("vulkan-cpp/6.2") self.requires("tinyobjloader/2.0.0-rc10") - self.requires("stb/cci.20230920") + self.requires("fastgltf/0.9.0") + self.requires("stb/cci.20240531") self.requires("nfd/3.0") self.requires("watcher/0.12.0") diff --git a/editor/editor_world.cppm b/editor/editor_world.cppm index 16606299..14df7365 100644 --- a/editor/editor_world.cppm +++ b/editor/editor_world.cppm @@ -73,7 +73,7 @@ public: m_device, m_host_bit, "assets/icons/PlayButton.png"); if (!m_play_icon.loaded()) { - std::println("m_play_icon not loaded!"); + console_log_error("m_play_icon not loaded!"); } m_stop_icon = @@ -84,7 +84,7 @@ public: "assets/icons/StopButton.png"); if (!m_stop_icon.loaded()) { - std::println("m_stop_icon not loaded!"); + console_log_error("m_stop_icon not loaded!"); } m_content_browser = content_browser_panel(m_device, m_host_bit); diff --git a/editor/level_scene.cppm b/editor/level_scene.cppm index 8d3ac496..2050b3a0 100644 --- a/editor/level_scene.cppm +++ b/editor/level_scene.cppm @@ -194,7 +194,6 @@ public: // Signal to trigger this kind of scene transition // Experimental: This was used for testing. // if(atlas::event::is_key_pressed(key_n)) { - // std::println("Signaling to transition to next_scene"); // atlas::event::scene_transition scene_transition = { // .next_scene = "Level Scene 2", // }; diff --git a/editor/level_scene2.cppm b/editor/level_scene2.cppm index 962a8d55..79de7d77 100644 --- a/editor/level_scene2.cppm +++ b/editor/level_scene2.cppm @@ -1,7 +1,6 @@ module; #include -#include #include #include @@ -109,7 +108,7 @@ public: // Signal to trigger this kind of scene transition if (atlas::event::is_key_pressed(key_n)) { - std::println("Signaling to transition to level scene"); + console_log_error("Signaling to transition to level scene"); atlas::event::scene_transition scene_transition = { .next_scene = "Level Scene", };