美文网首页
Dragon Engine:窗口

Dragon Engine:窗口

作者: Dragon_boy | 来源:发表于2020-08-15 15:45 被阅读0次

    本节介绍窗口的实现。这里使用开源库GLFW,之前也介绍过,只不过这里的主要任务就是将GLFW的API抽象出来,方便我们使用。

    首先建立一个窗口基类,Window.h

    #include "dgpch.h"
    
    #include "Dragon/Core/Core.h"
    #include "Dragon/Events/Event.h"
    
    namespace Dragon
    {
        struct WindowProps
        {
            std::string Title;
            unsigned int Width;
            unsigned int Height;
    
            WindowProps(const std::string& title = "Dragon Engine",
                unsigned int width = 1280,
                unsigned int height = 720)
                : Title(title), Width(width), Height(height)
            {
    
            }
        };
    
        class  Window
        {
        public:
            using EventCallbackFn = std::function<void(Event&)>;
    
            virtual ~Window() = default;
    
            virtual void OnUpdate() = 0;
    
            virtual unsigned int GetWidth() const = 0;
            virtual unsigned int GetHeight() const = 0;
    
            virtual void SetEventCallback(const EventCallbackFn& callback) = 0;
            virtual void SetVSync(bool enabled) = 0;
            virtual bool IsVSync() const = 0;
    
            virtual void* GetNativeWindow() const = 0;
    
            static Window* Create(const WindowProps& props = WindowProps());
            
        };
    }
    

    类成员方法中主要是获取宽高函数、设置事件响应函数、设置垂直同步函数、获取窗口对象函数、以及一个静态的创建窗口的类函数。

    之后是创建相应平台的窗口类,常见的系统平台是Windows,MacOS和Linux,这里暂时只考虑Windows。

    WindowsWindow:

    #include "Dragon/Core/Window.h"
    
    #include <GLFW/glfw3.h>
    
    #include "Dragon/Renderer/GraphicsContext.h"
    
    namespace Dragon
    {
        class WindowsWindow : public Window
        {
        public:
            WindowsWindow(const WindowProps& props);
            virtual ~WindowsWindow();
    
            void OnUpdate() override;
    
            inline unsigned int GetWidth() const override { return m_Data.Width; }
            inline unsigned int GetHeight() const override { return m_Data.Height; }
    
            inline void SetEventCallback(const EventCallbackFn& callback) override { m_Data.EventCallback = callback; }
            void SetVSync(bool enabled) override;
            bool IsVSync() const override;
    
            inline void* GetNativeWindow() const { return m_Window; };
        private:
            virtual void Init(const WindowProps& props);
            virtual void Shutdown();
        private:
            GLFWwindow* m_Window;
            GraphicsContext* m_Context;
            struct WindowData
            {
                std::string Title;
                unsigned int Width, Height;
                bool VSync;
    
                EventCallbackFn EventCallback;
            };
    
            WindowData m_Data;
        };
    }
    

    主要是继承Window.h,大部分方法没什么区别,只不过新增了private的Init和Shutdown函数,用于初始化窗口以及关闭窗口。(暂时忽略Dragon/Renderer/GraphicsContext.h

    接下来看各个方法的实现:

    #include "dgpch.h"
    #include "WindowsWindow.h"
    
    #include "Dragon/Events/ApplicationEvent.h"
    #include "Dragon/Events/MouseEvent.h"
    #include "Dragon/Events/KeyEvents.h"
    
    #include "Platform/OpenGL/OpenGLContext.h"
    
    namespace Dragon
    {
        static bool s_GLFWInitialized = false;
        static void GLFWErrorCallback(int error, const char* description)
        {
            DG_CORE_ERROR("GLFW Error ({0}): {1}", error, description);
        }
    
        Window* Window::Create(const WindowProps& props)
        {
            return new WindowsWindow(props);
        }
    
        WindowsWindow::WindowsWindow(const WindowProps& props)
        {
            Init(props);
        }
    
        WindowsWindow::~WindowsWindow()
        {
            Shutdown();
        }
    
        void WindowsWindow::Init(const WindowProps& props)
        {
            m_Data.Title = props.Title;
            m_Data.Width = props.Width;
            m_Data.Height = props.Height;
    
            DG_CORE_INFO("Creating window {0} ({1}, {2})", props.Title, props.Width, props.Height);
    
            if (!s_GLFWInitialized)
            {
                int success = glfwInit();
                DG_CORE_ASSERT(success, "Could not initialize GLFW.");
                glfwSetErrorCallback(GLFWErrorCallback);
                s_GLFWInitialized = true;
            }
    
            m_Window = glfwCreateWindow((int)props.Width, (int)props.Height, m_Data.Title.c_str(), nullptr, nullptr);
            
            m_Context = new OpenGLContext(m_Window);
            m_Context->Init();
    
            glfwSetWindowUserPointer(m_Window, &m_Data);
            SetVSync(true);
    
            //Set GLFW callback
            glfwSetWindowSizeCallback(m_Window, [](GLFWwindow* window, int width, int height)
            {
                WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);
                data.Width = width;
                data.Height = height;
    
                WindowResizeEvent event(width, height);
                data.EventCallback(event);
            });
    
            glfwSetWindowCloseCallback(m_Window, [](GLFWwindow* window)
            {
                WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);
                WindowCloseEvent event;
                data.EventCallback(event);
            });
    
            glfwSetKeyCallback(m_Window, [](GLFWwindow* window, int key, int scancode, int action, int mods)
            {
                WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);
                switch (action)
                {
                case GLFW_PRESS:
                {
                    KeyPressedEvent event(key, 0);
                    data.EventCallback(event);
                    break;
                }
                case GLFW_RELEASE:
                {
                    KeyReleasedEvent event(key);
                    data.EventCallback(event);
                    break;
                }
                case GLFW_REPEAT:
                {
                    KeyPressedEvent event(key, 1);
                    data.EventCallback(event);
                    break;
                }
                }
            });
    
            glfwSetCharCallback(m_Window, [](GLFWwindow* window, unsigned int codepoint)
            {
                WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);
                KeyTypedEvent event(codepoint);
                data.EventCallback(event);
            });
    
            glfwSetMouseButtonCallback(m_Window, [](GLFWwindow* window, int button, int action, int mods)
            {
                WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);
    
                switch (action)
                {
                case GLFW_PRESS:
                {   
                    MouseButtonPressedEvent event(button);
                    data.EventCallback(event);
                    break;
                }
                case GLFW_RELEASE:
                {
                    MouseButtonReleasedEvent event(button);
                    data.EventCallback(event);
                    break;
                }
                }
            });
    
            glfwSetScrollCallback(m_Window, [](GLFWwindow* window, double xOffset, double yOffset)
            {
                WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);
    
                MouseScrolledEvent event((float)xOffset, (float)yOffset);
                data.EventCallback(event);
            });
    
            glfwSetCursorPosCallback(m_Window, [](GLFWwindow* window, double xPos, double yPos)
            {
                WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);
    
                MouseMovedEvent event((float)xPos, (float)yPos);
                data.EventCallback(event);
            });
    
            glfwSetInputMode(m_Window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
        }
    
        void WindowsWindow::Shutdown()
        {
            glfwDestroyWindow(m_Window);
        }
    
        void WindowsWindow::OnUpdate()
        {
            glfwPollEvents();
            m_Context->SwapBuffers();
        }
    
        void WindowsWindow::SetVSync(bool enabled)
        {
            if (enabled)
                glfwSwapInterval(1);
            else
                glfwSwapInterval(0);
    
            m_Data.VSync = enabled;
        }
    
        bool WindowsWindow::IsVSync() const
        {
            return m_Data.VSync;
        }
    }
    

    Init函数就是GLFW初始化的那一套代码,没什么可说的,重点要说的是其中的事件调用函数设置,使用的API是GLFW里的,比如:

     //Set GLFW callback
            glfwSetWindowSizeCallback(m_Window, [](GLFWwindow* window, int width, int height)
            {
                WindowData& data = *(WindowData*)glfwGetWindowUserPointer(window);
                data.Width = width;
                data.Height = height;
    
                WindowResizeEvent event(width, height);
                data.EventCallback(event);
            });
    

    第二个参数接受的是一个事件注册函数,这里我们使用Lambda构建一个匿名函数。调用glfwGetWindowUserPointer获取窗口指针,得设置其上的窗口事件。

    我们回过头看我们的Application类,加上之前介绍过的一些元素:
    Application.h

    namespace Dragon
    {
        class  Application
        {
        public :
            Application();
    
            virtual ~Application() = default;
    
            void Run();
    
            void OnEvent(Event& e);
    
            void PushLayer(Layer* layer);
            void PushOverlay(Layer* overlay);
    
            inline static Application& Get() { return *s_Instance; }
            inline Window& GetWindow() { return *m_Window; }
    
        private:
            bool OnWindowClose(WindowCloseEvent& e);
            bool OnKeyBoard(KeyPressedEvent& e);
        private:
            std::unique_ptr<Window> m_Window;
            ImGuiLayer* m_ImGuiLayer;
            bool m_Running = true;
            bool m_Cursor = true;
            LayerStack m_LayerStack;
            Timestep timestep;
            float m_LastFrameTime = 0.0f;
    
        private:
            static Application* s_Instance;
        };
    
        // to be defined in the client
        Application* CreateApplication();
    }
    

    加上了一些事件响应以及窗口对象获取。

    然后看一下实现:

    namespace Dragon
    {
    #define BIND_EVENT_FN(x) std::bind(&Application::x, this, std::placeholders::_1)
    
        Application* Application::s_Instance = nullptr;
    
        Application::Application()
        {
            DG_CORE_ASSERT(!s_Instance, "Application already exists!");
            s_Instance = this;
    
            m_Window = std::unique_ptr<Window>(Window::Create());
            m_Window->SetEventCallback(BIND_EVENT_FN(OnEvent));
    
            Renderer::Init();
    
            m_ImGuiLayer = new ImGuiLayer();
            PushOverlay(m_ImGuiLayer);
    
        }
    
        void Application::PushLayer(Layer* layer)
        {
            m_LayerStack.PushLayer(layer);
        }
    
        void Application::PushOverlay(Layer* overlay)
        {
            m_LayerStack.PushOverlay(overlay);
        }
    
        void Application::OnEvent(Event& e)
        {
            EventDispatcher dispatcher(e);
            dispatcher.Dispatch<WindowCloseEvent>(BIND_EVENT_FN(OnWindowClose));
            dispatcher.Dispatch<KeyPressedEvent>(BIND_EVENT_FN(OnKeyBoard));
    
            for (auto it = m_LayerStack.end(); it != m_LayerStack.begin();)
            {
                (*--it)->OnEvent(e);
                if (e.Handled)
                    break;
            }
        }
    
        void Application::Run()
        {
            while (m_Running)
            {
                if(!m_Cursor)
                    glfwSetInputMode((GLFWwindow*)m_Window->GetNativeWindow(), GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
                else
                    glfwSetInputMode((GLFWwindow*)m_Window->GetNativeWindow(), GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    
                float time = (float)glfwGetTime();
                Timestep timestep = time - m_LastFrameTime;
                m_LastFrameTime = time;
    
                for (Layer* layer : m_LayerStack)
                    layer->OnUpdate(timestep);
    
                m_ImGuiLayer->Begin();
                for (Layer* layer : m_LayerStack)
                    layer->OnImGuiRender();
                m_ImGuiLayer->End();
    
                m_Window->OnUpdate();
            }
        }
    
        bool Application::OnWindowClose(WindowCloseEvent& e)
        {
            m_Running = false;
            return true;
        }
        bool Application::OnKeyBoard(KeyPressedEvent& e)
        {
            if (e.GetKeyCode() == DG_KEY_Q)
            {
                if (!m_Cursor)
                    m_Cursor = true;
                else
                    m_Cursor = false;
            }
            return false;
        }
    }
    

    加上了一些窗口的更新函数以及事件响应,其它大部分和之前一样。

    下一节介绍GUI的实现。

    项目github地址:https://github.com/Dragon-Baby/Dragon

    相关文章

      网友评论

          本文标题:Dragon Engine:窗口

          本文链接:https://www.haomeiwen.com/subject/jpsadktx.html