引言 上一节我们添加了 glm 并讨论了游戏的一般数学库。今天我们将做一些 ImGUI 调整并修复一些新功能。
目前我们的 ImGUI 还需添加 Docking 和 Viewports。Docking 就是 dock 窗口,比如在 VisualStudio 或者其他软件中,我们可以剥离某些窗口,可以将它们拖拽并放在某处。
ImGUI 实际上已经集成了这个功能,你可以像上面这样组织我们的窗口,然后在其中可以有多个选项卡,可以根据需要停靠它们。这非常重要,因为我希望引擎有一个关卡编辑器。
绑定视口也是它们制作的东西,我们可以将 ImGUI 的窗口拖动到应用程序外部,而我实际上只是即时创建新的窗口、新的渲染上下文以及所有内容。
我认为这两个功能确实有助于引擎,并且我们不需要像 WPF、QT 这样的 UI 框架,ImGUI 完全可以满足我们。
Docking 分支 显然,我们如果拖拽 ImGUI 到窗口外,它并没有走到外面。
打开 Github,我们将切换到 ImGUI 的 docking 分支。
打开命令行窗口:
cd '/e/Infinite Engine/Infinite/Infinite/vendor/imgui' git status git checkout docking git status git pull origin/docking
切换 imgui 至 docking 分支,与上游仓库保持同步。
预生成文件 回到 Infinite/premake5.lua1
让 imgui 包含于沙盒程序:
project "Sandbox" location "Sandbox" kind "ConsoleApp" staticruntime "off" language "C++" targetdir ("bin/" .. outputdir .. "/%{prj.name}") objdir ("bin-int/" .. outputdir .. "/%{prj.name}") files { "%{prj.name}/src/**.h", "%{prj.name}/src/**.cpp" } includedirs { "Infinite/vendor/spdlog/include", "Infinite/src/Infinite", "Infinite/src/Events", "Infinite/src/**.h", "Infinite/src/**.cpp", + "Infinite/vendor/imgui", "%{IncludeDir.glm}" } links { "Infinite" } filter "system:windows" cppdialect "C++17" systemversion "latest" defines { "IFN_PLATFORM_WINDOWS" } filter "configurations:Debug" defines "IFN_DEBUG" runtime "Debug" symbols "On" filter "configurations:Release" defines "IFN_RELEASE" runtime "Release" optimize "On" filter "configurations:Dist" defines "IFN_DIST" runtime "Release" optimize "On"
ImGui 调整 新建一个新的 ImGuiBuild.cpp
:
#include "ifnpch.h" #define IMGUI_IMPL_OPENGL_LOADER_GLAD #include "../imgui/examples/imgui_impl_opengl3.cpp" #include "../imgui/examples/imgui_impl_glfw.cpp"
引入之前 imgui 文件中的示例程序,相对的我们不再需要复杂的拷贝整个项目,所以删除 Platform/OpenGL
下的两个文件:
ImGuiOpenGLRenderer.cpp
ImGuiOpenGLRenderer.h
ImGuiLayer 调整 在 ImGuiLayer.h
头文件中我们取消了之前特定方法的定义;
#pragma once #include "../Layer.h" #include "../Events/KeyEvent.h" #include "../Events/MouseEvent.h" #include "../Events/ApplicationEvent.h" namespace Infinite { class INFINITE_API ImGuiLayer : public Layer { public : ImGuiLayer (); ~ImGuiLayer (); virtual void OnAttach () override ; virtual void OnDetach () override ; virtual void OnImGuiRender () override ; void Begin () ; void End () ; private : float m_Time = 0.0f ; }; }
新加入三个方法:
void Begin()
void End()
virtual void OnImGuiRender()
在 ImGuiLayer.cpp
我们实现方法:
#include "ifnpch.h" #include "ImGuiLayer.h" #include "../imgui/imgui.h" #include "../imgui/examples/imgui_impl_glfw.h" #include "../imgui/examples/imgui_impl_opengl3.h" #include <GLFW/glfw3.h> #include <glad/glad.h> #include "../Application.h" namespace Infinite { ImGuiLayer::ImGuiLayer () : Layer ("ImGuiLayer" ) { } ImGuiLayer::~ImGuiLayer () { } void ImGuiLayer::OnAttach () { IMGUI_CHECKVERSION (); ImGui::CreateContext (); ImGuiIO& io = ImGui::GetIO (); (void )io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; ImGui::StyleColorsDark (); ImGuiStyle& style = ImGui::GetStyle (); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { style.WindowRounding = 0.0f ; style.Colors[ImGuiCol_WindowBg].w = 1.0f ; } Application& app = Application::Get (); GLFWwindow* window = static_cast <GLFWwindow*>(app.GetWindow ().GetNativeWindow ()); ImGui_ImplGlfw_InitForOpenGL (window, true ); ImGui_ImplOpenGL3_Init ("#version 410" ); } void ImGuiLayer::OnDetach () { ImGui_ImplOpenGL3_Shutdown (); ImGui_ImplGlfw_Shutdown (); ImGui::DestroyContext (); } void ImGuiLayer::Begin () { ImGui_ImplOpenGL3_NewFrame (); ImGui_ImplGlfw_NewFrame (); ImGui::NewFrame (); } void ImGuiLayer::End () { ImGuiIO& io = ImGui::GetIO (); Application& app = Application::Get (); io.DisplaySize = ImVec2 (app.GetWindow ().GetWidth (), app.GetWindow ().GetHeight ()); ImGui::Render (); ImGui_ImplOpenGL3_RenderDrawData (ImGui::GetDrawData ()); if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { GLFWwindow* backup_current_context = glfwGetCurrentContext (); ImGui::UpdatePlatformWindows (); ImGui::RenderPlatformWindowsDefault (); glfwMakeContextCurrent (backup_current_context); } } void ImGuiLayer::OnImGuiRender () { static bool show = true ; ImGui::ShowDemoWindow (&show); } }
LayerStack 调整 层级栈的调整比较简单,头文件 LayerStack.h
由普通的 int
变量替换迭代器:
#pragma once #include "Core.h" #include "Layer.h" #include <vector> namespace Infinite { class INFINITE_API LayerStack { public: LayerStack(); ~LayerStack(); void PushLayer(Layer* layer); void PushOverlay(Layer* layer); void PopLayer(Layer* layer); void PopOverlay(Layer* layer); std::vector<Layer*>::iterator begin() { return m_Layers.begin(); } std::vector<Layer*>::iterator end() { return m_Layers.end(); } private: std::vector<Layer*> m_Layers; + unsigned int m_LayerInsertIndex = 0; }; }
LayerStack.cpp
同理:
#include "ifnpch.h" #include "LayerStack.h" namespace Infinite { LayerStack::LayerStack() { } LayerStack::~LayerStack() { for (Layer* layer : m_Layers) delete layer; } void LayerStack::PushLayer(Layer* layer) { m_Layers.emplace(m_Layers.begin() + m_LayerInsertIndex, layer); + m_LayerInsertIndex++; } void LayerStack::PushOverlay(Layer* overlay) { m_Layers.emplace_back(overlay); } void LayerStack::PopLayer(Layer* layer) { auto it = std::find(m_Layers.begin(),m_Layers.end(),layer); if (it != m_Layers.end()) { m_Layers.erase(it); + m_LayerInsertIndex--; } } void LayerStack::PopOverlay(Layer* overlay) { auto it = std::find(m_Layers.begin(),m_Layers.end(),overlay); if (it != m_Layers.end()) m_Layers.erase(it); } }
调试
我们的 imgui 窗口终于可以拖拽到引擎之外!
但别高兴的太早,当我们在沙盒程序 SandboxApp.cpp
中重载 OnImGuiRender()
方法:
#include <Infinite.h> #include "../imgui/imgui.h" class ExampleLayer : public Infinite::Layer { public: ExampleLayer() : Layer("Example") { } void OnUpdate() override { // IFN_INFO("ExampleLayer::Update"); if (Infinite::Input::IsKeyPressed(IFN_KEY_TAB)) IFN_INFO("Tab key is pressed (poll)!"); } + virtual void OnImGuiRender() override + { + ImGui::Begin("Test"); + ImGui::Text("Hello World"); + ImGui::End(); + } void OnEvent(Infinite::Event& event) override { if (event.GetEventType() == Infinite::EventType::KeyPressed) { Infinite::KeyPressedEvent& e = (Infinite::KeyPressedEvent&)event; if (Infinite::Input::IsKeyPressed(IFN_KEY_TAB)) IFN_INFO("Tab key is pressed (event)!"); IFN_TRACE("{0}", (char)e.GetKeyCode()); } } }; class Sandbox : public Infinite::Application { public: Sandbox() { PushLayer(new ExampleLayer()); } ~Sandbox() {} }; Infinite::Application* Infinite::CreateApplication() { return new Sandbox(); }
再次运行项目,会出现 3 个外部符号解析的报错(事实上做到这里为了解决 3 个报错,我让错误变成了12 个,之后变成了 128 个……感谢 git 回滚版本)。
出错的原因在于动态链接库的导入导出有问题,这个我们会在接下来修复报错和警告。
补充:可以在 vendor/imgui/imconfig.h
中取消注释解决该问题:
-//#define IMGUI_API _declspec(dllexport) +#define IMGUI_API _declspec(dllexport)