diff --git a/include/application.h b/include/application.h index 8bb6657..583aa51 100644 --- a/include/application.h +++ b/include/application.h @@ -20,78 +20,80 @@ namespace engine { class Application { - public: - Application(const char* app_name, const char* app_version, - gfx::GraphicsSettings graphics_settings); - ~Application(); - Application(const Application&) = delete; - Application& operator=(const Application&) = delete; + public: + struct Configuration { + bool enable_frame_limiter; + }; - /* resource stuff */ + Application(const char* app_name, const char* app_version, gfx::GraphicsSettings graphics_settings, Configuration configuration); + ~Application(); + Application(const Application&) = delete; + Application& operator=(const Application&) = delete; - template - void RegisterResourceManager() { - size_t hash = typeid(T).hash_code(); - assert(resource_managers_.contains(hash) == false && - "Registering resource manager type more than once."); - resource_managers_.emplace(hash, std::make_unique>()); - } + /* resource stuff */ - template - std::shared_ptr AddResource(const std::string& name, - std::unique_ptr&& resource) { - auto resource_manager = GetResourceManager(); - return resource_manager->Add(name, std::move(resource)); - } - - template - std::shared_ptr GetResource(const std::string& name) { - auto resource_manager = GetResourceManager(); - return resource_manager->Get(name); - } - - /* methods */ - void GameLoop(); - - void SetFrameLimiter(bool on) { enable_frame_limiter_ = on; } - - /* getters */ - Window* window() { return window_.get(); } - InputManager* input_manager() { return input_manager_.get(); } - SceneManager* scene_manager() { return scene_manager_.get(); } - Renderer* renderer() { return renderer_.get(); } - - std::string GetResourcePath(const std::string relative_path) { - return (resources_path_ / relative_path).string(); - } - - private: - std::unique_ptr window_; - std::unique_ptr input_manager_; - std::unique_ptr renderer_; - std::unordered_map> - resource_managers_{}; - std::filesystem::path resources_path_; - - // Most resources and class instances in the game exist in this object - std::unique_ptr scene_manager_; - - bool enable_frame_limiter_ = true; - - template - ResourceManager* GetResourceManager() { - size_t hash = typeid(T).hash_code(); - auto it = resource_managers_.find(hash); - if (it == resource_managers_.end()) { - throw std::runtime_error("Cannot find resource manager."); + template + void RegisterResourceManager() + { + size_t hash = typeid(T).hash_code(); + assert(resource_managers_.contains(hash) == false && "Registering resource manager type more than once."); + resource_managers_.emplace(hash, std::make_unique>()); + } + + template + std::shared_ptr AddResource(const std::string& name, std::unique_ptr&& resource) + { + auto resource_manager = GetResourceManager(); + return resource_manager->Add(name, std::move(resource)); + } + + template + std::shared_ptr GetResource(const std::string& name) + { + auto resource_manager = GetResourceManager(); + return resource_manager->Get(name); + } + + /* methods */ + void GameLoop(); + + void SetFrameLimiter(bool on) { configuration_.enable_frame_limiter = on; } + + /* getters */ + Window* window() { return window_.get(); } + InputManager* input_manager() { return input_manager_.get(); } + SceneManager* scene_manager() { return scene_manager_.get(); } + Renderer* renderer() { return renderer_.get(); } + + std::string GetResourcePath(const std::string relative_path) const { return (resources_path_ / relative_path).string(); } + + private: + std::unique_ptr window_; + std::unique_ptr input_manager_; + std::unique_ptr renderer_; + std::unordered_map> resource_managers_{}; + std::filesystem::path resources_path_; + + // Most resources and class instances in the game exist in this object + std::unique_ptr scene_manager_; + + Configuration configuration_; + + template + ResourceManager* GetResourceManager() + { + size_t hash = typeid(T).hash_code(); + auto it = resource_managers_.find(hash); + if (it == resource_managers_.end()) { + throw std::runtime_error("Cannot find resource manager."); + } + auto ptr = it->second.get(); + auto casted_ptr = dynamic_cast*>(ptr); + assert(casted_ptr != nullptr); + return casted_ptr; } - auto ptr = it->second.get(); - auto casted_ptr = dynamic_cast*>(ptr); - assert(casted_ptr != nullptr); - return casted_ptr; - } }; -} // namespace engine +} // namespace engine #endif \ No newline at end of file diff --git a/include/gfx.h b/include/gfx.h index 8e0cc11..8e5d83a 100644 --- a/include/gfx.h +++ b/include/gfx.h @@ -37,6 +37,7 @@ struct GraphicsSettings { // not all GPUs/drivers support immediate present with V-sync enabled wait_for_present = true; msaa_level = MSAALevel::kOff; + enable_anisotropy = false; // anisotropic filtering can severely affect performance on intel iGPUs } bool enable_validation; @@ -45,6 +46,7 @@ struct GraphicsSettings { // (no affect with V-sync disabled) bool wait_for_present; MSAALevel msaa_level; + bool enable_anisotropy; }; enum class ImageFormat { @@ -131,7 +133,7 @@ struct SamplerInfo { Filter minify = gfx::Filter::kLinear; Filter magnify = gfx::Filter::kLinear; Filter mipmap = gfx::Filter::kLinear; - bool anisotropic_filtering = true; + bool anisotropic_filtering = true; // this can be force disabled by a global setting bool operator==(const SamplerInfo&) const = default; }; diff --git a/src/application.cpp b/src/application.cpp index 93598a9..d4cbc8b 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -73,7 +73,8 @@ static std::filesystem::path getResourcesPath() return resourcesPath; } -Application::Application(const char* appName, const char* appVersion, gfx::GraphicsSettings graphicsSettings) +Application::Application(const char* appName, const char* appVersion, gfx::GraphicsSettings graphicsSettings, Configuration configuration) + : configuration_(configuration) { window_ = std::make_unique(appName, true, false); input_manager_ = std::make_unique(window_.get()); @@ -114,7 +115,7 @@ Application::Application(const char* appName, const char* appVersion, gfx::Graph shaderSettings.write_z = true; shaderSettings.render_order = 0; auto texturedShader = std::make_unique(renderer(), GetResourcePath("engine/shaders/standard.vert").c_str(), - GetResourcePath("engine/shaders/standard.frag").c_str(), shaderSettings); + GetResourcePath("engine/shaders/standard.frag").c_str(), shaderSettings); GetResourceManager()->AddPersistent("builtin.standard", std::move(texturedShader)); } { @@ -129,7 +130,7 @@ Application::Application(const char* appName, const char* appVersion, gfx::Graph shaderSettings.write_z = true; shaderSettings.render_order = 0; auto fancyShader = std::make_unique(renderer(), GetResourcePath("engine/shaders/fancy.vert").c_str(), - GetResourcePath("engine/shaders/fancy.frag").c_str(), shaderSettings); + GetResourcePath("engine/shaders/fancy.frag").c_str(), shaderSettings); GetResourceManager()->AddPersistent("builtin.fancy", std::move(fancyShader)); } { @@ -144,7 +145,7 @@ Application::Application(const char* appName, const char* appVersion, gfx::Graph shaderSettings.write_z = true; shaderSettings.render_order = 0; auto skyboxShader = std::make_unique(renderer(), GetResourcePath("engine/shaders/skybox.vert").c_str(), - GetResourcePath("engine/shaders/skybox.frag").c_str(), shaderSettings); + GetResourcePath("engine/shaders/skybox.frag").c_str(), shaderSettings); GetResourceManager()->AddPersistent("builtin.skybox", std::move(skyboxShader)); } #if 0 @@ -260,8 +261,7 @@ void Application::GameLoop() return find_depth(parent, current_depth + 1); } }; - if (scene) - { + if (scene) { for (Entity i = 1; i < scene->next_entity_id_; ++i) { auto t = scene->GetComponent(i); std::string tabs{}; @@ -296,7 +296,7 @@ void Application::GameLoop() window_->GetInputAndEvents(); /* fps limiter */ - if (enable_frame_limiter_) { + if (configuration_.enable_frame_limiter) { std::this_thread::sleep_until(endFrame); } beginFrame = endFrame; diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index ab4aeee..2dba3e2 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -233,6 +233,8 @@ static VkShaderStageFlags getShaderStageFlags(gfx::ShaderStageFlags::Flags flags return VK_CULL_MODE_BACK_BIT; case gfx::CullMode::kCullFrontAndBack: return VK_CULL_MODE_FRONT_AND_BACK; + default: + throw std::runtime_error("Unknown cull mode"); } } @@ -243,6 +245,8 @@ static VkFormat getImageFormat(gfx::ImageFormat format) return VK_FORMAT_R8G8B8A8_UNORM; case gfx::ImageFormat::kSRGB: return VK_FORMAT_R8G8B8A8_SRGB; + default: + throw std::runtime_error("Unknown image format"); } } @@ -352,7 +356,7 @@ struct GFXDevice::Impl { SwapchainInfo swapchainInfo{}; Swapchain swapchain{}; - VkDescriptorPool descriptorPool; + VkDescriptorPool descriptorPool = VK_NULL_HANDLE; struct WriteQueues { std::unordered_set uniform_buffer_writes{}; @@ -1640,7 +1644,7 @@ const gfx::Sampler* GFXDevice::CreateSampler(const gfx::SamplerInfo& info) samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; samplerInfo.mipLodBias = 0.0f; - samplerInfo.anisotropyEnable = info.anisotropic_filtering ? VK_TRUE : VK_FALSE; + samplerInfo.anisotropyEnable = (info.anisotropic_filtering && pimpl->graphicsSettings.enable_anisotropy) ? VK_TRUE : VK_FALSE; samplerInfo.maxAnisotropy = pimpl->device.properties.limits.maxSamplerAnisotropy; samplerInfo.minLod = 0.0f; samplerInfo.maxLod = VK_LOD_CLAMP_NONE; diff --git a/src/util/gltf_loader.cpp b/src/util/gltf_loader.cpp index 53195ec..2e0e2ef 100644 --- a/src/util/gltf_loader.cpp +++ b/src/util/gltf_loader.cpp @@ -53,10 +53,10 @@ static glm::mat4 MatFromDoubleArray(const std::vector& arr) { glm::mat4 mat{}; for (int i = 0; i < 4; ++i) { - mat[i][0] = static_cast(arr[i * 4 + 0]); - mat[i][1] = static_cast(arr[i * 4 + 1]); - mat[i][2] = static_cast(arr[i * 4 + 2]); - mat[i][3] = static_cast(arr[i * 4 + 3]); + mat[i][0] = static_cast(arr[static_cast(i) * 4 + 0]); + mat[i][1] = static_cast(arr[static_cast(i) * 4 + 1]); + mat[i][2] = static_cast(arr[static_cast(i) * 4 + 2]); + mat[i][3] = static_cast(arr[static_cast(i) * 4 + 3]); } return mat; } @@ -84,8 +84,6 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) throw std::runtime_error("Failed to load glTF file!"); } - LOG_INFO("Loaded glTF model: {}, contains {} scenes", path, model.scenes.size()); - // test model loading if (model.scenes.size() < 1) { @@ -210,7 +208,8 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) if (material.pbrMetallicRoughness.baseColorTexture.index != -1) { if (material.pbrMetallicRoughness.baseColorTexture.texCoord == 0) { materials.back()->SetAlbedoTexture(textures.at(material.pbrMetallicRoughness.baseColorTexture.index)); - } else { + } + else { LOG_WARN("Material {} base color texture specifies a UV channel other than zero which is unsupported."); LOG_WARN("Material will be created with a white base color"); } @@ -331,11 +330,7 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) std::vector vertices; vertices.reserve(num_vertices); for (size_t i = 0; i < num_vertices; ++i) { - Vertex v; - v.pos = positions[i]; - v.norm = normals[i]; - v.tangent = tangents[i]; - v.uv = uv0s[i]; + Vertex v{.pos = positions[i], .norm = normals[i], .tangent = tangents[i], .uv = uv0s[i]}; vertices.push_back(v); } @@ -360,13 +355,76 @@ engine::Entity LoadGLTF(Scene& scene, const std::string& path, bool isStatic) } } + // glTF uses the Y-up convention so objects must be rotated to Z-up const Entity parent = - // scene.CreateEntity("test_node", 0, glm::vec3{}, glm::quat{glm::one_over_root_two(), glm::one_over_root_two(), 0.0f, 0.0f}); - scene.CreateEntity("test_node", 0); + scene.CreateEntity("test_node", 0, glm::vec3{}, glm::quat{glm::one_over_root_two(), glm::one_over_root_two(), 0.0f, 0.0f}); - auto ren = scene.AddComponent(parent); - ren->material = primitive_arrays.at(0).at(0).material; - ren->mesh = primitive_arrays.at(0).at(0).mesh; + std::vector entities(model.nodes.size(), 0); + std::function generateEntities = [&](Entity parent_entity, const tg::Node& node) -> void { + const Entity e = scene.CreateEntity(node.name.empty() ? "anode" : node.name, parent_entity); + + // transform + auto t = scene.GetComponent(e); + t->position.x = 0.0f; + t->position.y = 0.0f; + t->position.z = 0.0f; + t->rotation.x = 0.0f; + t->rotation.y = 0.0f; + t->rotation.z = 0.0f; + t->rotation.w = 1.0f; + t->scale.x = 1.0f; + t->scale.y = 1.0f; + t->scale.z = 1.0f; + + if (node.matrix.size() == 16) { + const glm::mat4 matrix = MatFromDoubleArray(node.matrix); + DecomposeTransform(matrix, t->position, t->rotation, t->scale); + } + else { + if (node.translation.size() == 3) { + t->position.x = static_cast(node.translation[0]); + t->position.y = static_cast(node.translation[1]); + t->position.z = static_cast(node.translation[2]); + } + if (node.rotation.size() == 4) { + t->rotation.x = static_cast(node.rotation[0]); + t->rotation.y = static_cast(node.rotation[1]); + t->rotation.z = static_cast(node.rotation[2]); + t->rotation.w = static_cast(node.rotation[3]); + } + if (node.scale.size() == 3) { + t->scale.x = static_cast(node.scale[0]); + t->scale.y = static_cast(node.scale[1]); + t->scale.z = static_cast(node.scale[2]); + } + } + + // ignoring cameras + // ignoring skin + // ignoring weights + + if (node.mesh != -1) { + const auto& primitives = primitive_arrays.at(node.mesh); + int i = 0; + for (const EnginePrimitive& prim : primitives) { + auto prim_entity = scene.CreateEntity(std::string("_mesh") + std::to_string(i), e); + auto meshren = scene.AddComponent(prim_entity); + meshren->mesh = prim.mesh; + meshren->material = prim.material; + ++i; + } + } + + for (int i : node.children) { + generateEntities(e, model.nodes.at(i)); + } + }; + + for (int i : s.nodes) { + generateEntities(parent, model.nodes.at(i)); + } + + LOG_INFO("Loaded glTF model: {}", path); return parent; } diff --git a/test/res/models/walls_with_tangents.glb b/test/res/models/walls_with_tangents.glb new file mode 100644 index 0000000..0437090 Binary files /dev/null and b/test/res/models/walls_with_tangents.glb differ diff --git a/test/src/game.cpp b/test/src/game.cpp index d9b63a5..6cf242c 100644 --- a/test/src/game.cpp +++ b/test/src/game.cpp @@ -22,22 +22,22 @@ #include "config.h" -static void ConfigureInputs(engine::InputManager* input_manager) +static void ConfigureInputs(engine::InputManager& input_manager) { // user interface mappings - input_manager->AddInputButton("fullscreen", engine::inputs::Key::K_F11); - input_manager->AddInputButton("exit", engine::inputs::Key::K_ESCAPE); + input_manager.AddInputButton("fullscreen", engine::inputs::Key::K_F11); + input_manager.AddInputButton("exit", engine::inputs::Key::K_ESCAPE); // game buttons - input_manager->AddInputButton("fire", engine::inputs::MouseButton::M_LEFT); - input_manager->AddInputButton("aim", engine::inputs::MouseButton::M_RIGHT); - input_manager->AddInputButton("jump", engine::inputs::Key::K_SPACE); - input_manager->AddInputButton("sprint", engine::inputs::Key::K_LSHIFT); + input_manager.AddInputButton("fire", engine::inputs::MouseButton::M_LEFT); + input_manager.AddInputButton("aim", engine::inputs::MouseButton::M_RIGHT); + input_manager.AddInputButton("jump", engine::inputs::Key::K_SPACE); + input_manager.AddInputButton("sprint", engine::inputs::Key::K_LSHIFT); // game movement - input_manager->AddInputButtonAsAxis("movex", engine::inputs::Key::K_D, engine::inputs::Key::K_A); - input_manager->AddInputButtonAsAxis("movey", engine::inputs::Key::K_W, engine::inputs::Key::K_S); + input_manager.AddInputButtonAsAxis("movex", engine::inputs::Key::K_D, engine::inputs::Key::K_A); + input_manager.AddInputButtonAsAxis("movey", engine::inputs::Key::K_W, engine::inputs::Key::K_S); // looking around - input_manager->AddInputAxis("lookx", engine::inputs::MouseAxis::X); - input_manager->AddInputAxis("looky", engine::inputs::MouseAxis::Y); + input_manager.AddInputAxis("lookx", engine::inputs::MouseAxis::X); + input_manager.AddInputAxis("looky", engine::inputs::MouseAxis::Y); } void PlayGame(GameSettings settings) @@ -50,11 +50,14 @@ void PlayGame(GameSettings settings) graphics_settings.vsync = true; graphics_settings.wait_for_present = false; graphics_settings.msaa_level = engine::gfx::MSAALevel::kOff; + graphics_settings.enable_anisotropy = false; - engine::Application app(PROJECT_NAME, PROJECT_VERSION, graphics_settings); - app.SetFrameLimiter(settings.enable_frame_limiter); + engine::Application::Configuration configuration{}; + configuration.enable_frame_limiter = settings.enable_frame_limiter; + + engine::Application app(PROJECT_NAME, PROJECT_VERSION, graphics_settings, configuration); app.window()->SetRelativeMouseMode(true); - ConfigureInputs(app.input_manager()); + ConfigureInputs(*app.input_manager()); engine::Scene* my_scene = app.scene_manager()->CreateEmptyScene(); { @@ -201,7 +204,7 @@ void PlayGame(GameSettings settings) //scene2->GetComponent(teapot2)->position.y += 5.0f; //scene2->GetComponent(teapot2)->rotation = glm::angleAxis(glm::pi(), glm::vec3{ 0.0f, 0.0f, 1.0f }); //scene2->GetComponent(teapot2)->rotation *= glm::angleAxis(glm::half_pi(), glm::vec3{1.0f, 0.0f, 0.0f}); - //auto walls = engine::util::LoadGLTF(*scene2, app.GetResourcePath("models/walls.glb")); + auto walls = engine::util::LoadGLTF(*scene2, app.GetResourcePath("models/walls_with_tangents.glb")); } my_scene->GetSystem()->next_scene_ = scene2;