diff options
| -rw-r--r-- | .gitignore | 6 | ||||
| -rw-r--r-- | premake5.lua | 34 | ||||
| -rw-r--r-- | src/Core/Application.cpp | 86 | ||||
| -rw-r--r-- | src/Core/Application.h | 35 | ||||
| -rw-r--r-- | src/Core/Event.h | 338 | ||||
| -rw-r--r-- | src/Core/Window.cpp | 157 | ||||
| -rw-r--r-- | src/Core/Window.h | 32 | ||||
| -rw-r--r-- | src/main.cpp | 14 |
8 files changed, 655 insertions, 47 deletions
@@ -8,4 +8,8 @@ builddir/ .vscode/ *.make -Makefile
\ No newline at end of file +Makefile + +*.sln +*.vcxproj +*.vcxproj.*
\ No newline at end of file diff --git a/premake5.lua b/premake5.lua index 11c92ff..7b615c0 100644 --- a/premake5.lua +++ b/premake5.lua @@ -25,8 +25,7 @@ group "Dependencies" project "GLAD" kind "StaticLib" language "C" - staticruntime "off" - + staticruntime "on" targetdir ("bin/" .. outputdir .. "/%{prj.name}") objdir ("bin-int/" .. outputdir .. "/%{prj.name}") @@ -57,7 +56,7 @@ project "GLAD" project "GLFW" kind "StaticLib" language "C" - staticruntime "off" + staticruntime "on" warnings "off" targetdir ("bin/" .. outputdir .. "/%{prj.name}") @@ -65,9 +64,6 @@ project "GLFW" files { - "Vendor/glfw/include/GLFW/glfw3.h", - "Vendor/glfw/include/GLFW/glfw3native.h", - "Vendor/glfw/src/context.c", "Vendor/glfw/src/init.c", "Vendor/glfw/src/input.c", @@ -180,20 +176,6 @@ project "GLFW" "_CRT_SECURE_NO_WARNINGS", } - links - { - "gdi32", - "user32", - "kernel32", - "shell32", - "msvcrt" - } - - linkoptions - { - "/NODEFAULTLIB:library" - } - filter "configurations:Debug" runtime "Debug" symbols "on" @@ -256,19 +238,9 @@ project "Donut" links { - "gdi32", - "user32", - "kernel32", - "shell32", - "opengl32", - "msvcrt" + "opengl32.lib", } - linkoptions - { - "/NODEFAULTLIB:library" - } - filter "configurations:Debug" defines "DONUT_DEBUG" runtime "Debug" diff --git a/src/Core/Application.cpp b/src/Core/Application.cpp new file mode 100644 index 0000000..9ca060c --- /dev/null +++ b/src/Core/Application.cpp @@ -0,0 +1,86 @@ +#include "Application.h" +#include <iostream> + +namespace Donut +{ + Application* Application::s_Instance = nullptr; + + Application::Application(const std::string& name, int width, int height) + : m_Running(true), m_Minimized(false) + { + s_Instance = this; + + m_Window = std::make_unique<Window>(name, width, height); + m_Window->SetEventCallback([this](Event& event) + { + OnEvent(event); + }); + + OnInit(); + } + + Application::~Application() + { + OnShutdown(); + s_Instance = nullptr; + } + + void Application::Run() + { + while (m_Running) + { + if (!m_Minimized) + { + OnUpdate(); + OnRender(); + } + + m_Window->OnUpdate(); + } + } + + void Application::Close() + { + m_Running = false; + } + + void Application::OnEvent(Event& event) + { + EventDispatcher dispatcher(event); + dispatcher.Dispatch<WindowCloseEvent>([this, &event](WindowCloseEvent& e) + { + m_Running = false; + event.Handled = true; + return true; + }); + + dispatcher.Dispatch<WindowResizeEvent>([this, &event](WindowResizeEvent& e) + { + if (e.GetWidth() == 0 || e.GetHeight() == 0) + m_Minimized = true; + else + m_Minimized = false; + + event.Handled = true; + return true; + }); + + dispatcher.Dispatch<KeyPressedEvent>([this, &event](KeyPressedEvent& e) + { + if (e.GetKeyCode() == 256) + Close(); + return true; + }); + } + + void Application::OnInit() { } + void Application::OnShutdown() { } + + void Application::OnUpdate() + { + } + + void Application::OnRender() + { + } +} diff --git a/src/Core/Application.h b/src/Core/Application.h new file mode 100644 index 0000000..fca4c42 --- /dev/null +++ b/src/Core/Application.h @@ -0,0 +1,35 @@ +#pragma once + +#include "Window.h" +#include "Event.h" +#include <memory> +#include <functional> + +namespace Donut +{ + class Application + { + public: + Application(const std::string& name = "Donut", + int width = 1280, int height = 720); + ~Application(); + + void Run(); + void Close(); + + Window& GetWindow() { return *m_Window; } + static Application& Get() { return *s_Instance; } + private: + void OnInit(); + void OnShutdown(); + void OnUpdate(); + void OnRender(); + void OnEvent(Event& event); + private: + std::unique_ptr<Window> m_Window; + bool m_Running; + bool m_Minimized; + + static Application* s_Instance; + }; +}
\ No newline at end of file diff --git a/src/Core/Event.h b/src/Core/Event.h new file mode 100644 index 0000000..32cdde4 --- /dev/null +++ b/src/Core/Event.h @@ -0,0 +1,338 @@ +#pragma once + +#include <functional> +#include <unordered_map> +#include <vector> +#include <string> +#include <sstream> + +namespace Donut +{ + class Event; + class EventDispatcher; + + enum class EventType + { + None = 0, + + WindowClose, + WindowResize, + WindowFocus, + WindowLostFocus, + WindowMoved, + + KeyPressed, + KeyReleased, + KeyTyped, + + MouseButtonPressed, + MouseButtonReleased, + MouseMoved, + MouseScrolled + }; + + enum EventCategory + { + None = 0, + EventCategoryApplication = 1 << 0, + EventCategoryInput = 1 << 1, + EventCategoryKeyboard = 1 << 2, + EventCategoryMouse = 1 << 3, + EventCategoryMouseButton = 1 << 4 + }; + + class Event + { + public: + virtual ~Event() = default; + + bool Handled = false; + + virtual EventType GetEventType() const = 0; + virtual const char* GetName() const = 0; + virtual int GetCategoryFlags() const = 0; + virtual std::string ToString() const { return GetName(); } + + bool IsInCategory(EventCategory category) { return GetCategoryFlags() & category; } + }; + + #define EVENT_CLASS_TYPE(type) \ + static EventType GetStaticType() { return EventType::type; } \ + virtual EventType GetEventType() const override { return GetStaticType(); } \ + virtual const char* GetName() const override { return #type; } + + #define APPLICATION_EVENT_CLASS_TYPE(type) \ + EVENT_CLASS_TYPE(type) \ + virtual int GetCategoryFlags() const override { return EventCategoryApplication; } + + #define MOUSE_EVENT_CLASS_TYPE(type) \ + EVENT_CLASS_TYPE(type) \ + virtual int GetCategoryFlags() const override { return EventCategoryMouse | \ + EventCategoryInput; } + + #define MOUSE_BUTTON_EVENT_CLASS_TYPE(type) \ + EVENT_CLASS_TYPE(type) \ + virtual int GetCategoryFlags() const override { return EventCategoryMouseButton | \ + EventCategoryMouse | \ + EventCategoryInput; } + + #define KEYBOARD_EVENT_CLASS_TYPE(type) \ + EVENT_CLASS_TYPE(type) \ + virtual int GetCategoryFlags() const override { return EventCategoryKeyboard | \ + EventCategoryInput; } + + class WindowResizeEvent : public Event + { + public: + WindowResizeEvent(unsigned int width, unsigned int height) + : m_Width(width), m_Height(height) { } + + unsigned int GetWidth() const { return m_Width; } + unsigned int GetHeight() const { return m_Height; } + + std::string ToString() const override + { + return "WindowResizeEvent: " + std::to_string(m_Width) + ", " + std::to_string(m_Height); + } + + APPLICATION_EVENT_CLASS_TYPE(WindowResize) + + private: + unsigned int m_Width, m_Height; + }; + + class WindowCloseEvent : public Event + { + public: + WindowCloseEvent() = default; + APPLICATION_EVENT_CLASS_TYPE(WindowClose) + }; + + class WindowFocusEvent : public Event + { + public: + WindowFocusEvent() = default; + APPLICATION_EVENT_CLASS_TYPE(WindowFocus) + }; + + class WindowLostFocusEvent : public Event + { + public: + WindowLostFocusEvent() = default; + APPLICATION_EVENT_CLASS_TYPE(WindowLostFocus) + }; + + class WindowMovedEvent : public Event + { + public: + WindowMovedEvent(int x, int y) + : m_X(x), m_Y(y) { } + + int GetX() const { return m_X; } + int GetY() const { return m_Y; } + + std::string ToString() const override + { + return "WindowMovedEvent: " + std::to_string(m_X) + ", " + std::to_string(m_Y); + } + + APPLICATION_EVENT_CLASS_TYPE(WindowMoved) + + private: + int m_X, m_Y; + }; + + class KeyEvent : public Event + { + public: + int GetKeyCode() const { return m_KeyCode; } + virtual int GetCategoryFlags() const override { return EventCategoryKeyboard | + EventCategoryInput; } + + protected: + KeyEvent(int keycode) : m_KeyCode(keycode) { } + int m_KeyCode; + }; + + class KeyPressedEvent : public KeyEvent + { + public: + KeyPressedEvent(int keycode, bool isRepeat = false) + : KeyEvent(keycode), m_IsRepeat(isRepeat) { } + + bool IsRepeat() const { return m_IsRepeat; } + + std::string ToString() const override + { + return "KeyPressedEvent: " + std::to_string(m_KeyCode) + + " (repeat = " + std::to_string(m_IsRepeat) + ")"; + } + + KEYBOARD_EVENT_CLASS_TYPE(KeyPressed) + + private: + bool m_IsRepeat; + }; + + class KeyReleasedEvent : public KeyEvent + { + public: + KeyReleasedEvent(int keycode) : KeyEvent(keycode) { } + + std::string ToString() const override + { + return "KeyReleasedEvent: " + std::to_string(m_KeyCode); + } + + KEYBOARD_EVENT_CLASS_TYPE(KeyReleased) + }; + + class KeyTypedEvent : public KeyEvent + { + public: + KeyTypedEvent(int keycode) : KeyEvent(keycode) { } + + std::string ToString() const override + { + return "KeyTypedEvent: " + std::to_string(m_KeyCode); + } + + KEYBOARD_EVENT_CLASS_TYPE(KeyTyped) + }; + + class MouseMovedEvent : public Event + { + public: + MouseMovedEvent(float x, float y) + : m_MouseX(x), m_MouseY(y) { } + + float GetX() const { return m_MouseX; } + float GetY() const { return m_MouseY; } + + std::string ToString() const override + { + return "MouseMovedEvent: " + std::to_string(m_MouseX) + ", " + std::to_string(m_MouseY); + } + + MOUSE_EVENT_CLASS_TYPE(MouseMoved) + + private: + float m_MouseX, m_MouseY; + }; + + class MouseScrolledEvent : public Event + { + public: + MouseScrolledEvent(float xOffset, float yOffset) + : m_XOffset(xOffset), m_YOffset(yOffset) { } + + float GetXOffset() const { return m_XOffset; } + float GetYOffset() const { return m_YOffset; } + + std::string ToString() const override + { + return "MouseScrolledEvent: " + std::to_string(m_XOffset) + + ", " + std::to_string(m_YOffset); + } + + MOUSE_EVENT_CLASS_TYPE(MouseScrolled) + + private: + float m_XOffset, m_YOffset; + }; + + class MouseButtonEvent : public Event + { + public: + int GetMouseButton() const { return m_Button; } + virtual int GetCategoryFlags() const override { return EventCategoryMouseButton | + EventCategoryMouse | + EventCategoryInput; } + + protected: + MouseButtonEvent(int button) : m_Button(button) { } + int m_Button; + }; + + class MouseButtonPressedEvent : public MouseButtonEvent + { + public: + MouseButtonPressedEvent(int button) : MouseButtonEvent(button) { } + + std::string ToString() const override + { + return "MouseButtonPressedEvent: " + std::to_string(m_Button); + } + + MOUSE_BUTTON_EVENT_CLASS_TYPE(MouseButtonPressed) + }; + + class MouseButtonReleasedEvent : public MouseButtonEvent + { + public: + MouseButtonReleasedEvent(int button) : MouseButtonEvent(button) { } + + std::string ToString() const override + { + return "MouseButtonReleasedEvent: " + std::to_string(m_Button); + } + + MOUSE_BUTTON_EVENT_CLASS_TYPE(MouseButtonReleased) + }; + + class EventDispatcher + { + public: + EventDispatcher(Event& event) : m_Event(event) { } + + template<typename T, typename F> + bool Dispatch(const F& func) + { + if (m_Event.GetEventType() == T::GetStaticType()) + { + m_Event.Handled = func(static_cast<T&>(m_Event)); + return true; + } + return false; + } + + private: + Event& m_Event; + }; + + class EventHandler + { + public: + using EventCallbackFn = std::function<void(Event&)>; + + EventHandler() = default; + ~EventHandler() = default; + + void SetEventCallback(const EventCallbackFn& callback) { m_EventCallback = callback; } + + void OnEvent(Event& event) + { + if (m_EventCallback) + m_EventCallback(event); + } + + template<typename T> + void BindEvent(const std::function<void(T&)>& callback) + { + m_EventCallback = [callback](Event& event) + { + if (event.GetEventType() == T::GetStaticType()) + callback(static_cast<T&>(event)); + }; + } + + private: + EventCallbackFn m_EventCallback; + }; + + #undef EVENT_CLASS_TYPE + #undef APPLICATION_EVENT_CLASS_TYPE + #undef MOUSE_EVENT_CLASS_TYPE + #undef MOUSE_BUTTON_EVENT_CLASS_TYPE + #undef KEYBOARD_EVENT_CLASS_TYPE +}
\ No newline at end of file diff --git a/src/Core/Window.cpp b/src/Core/Window.cpp index 8f1c6ea..13025cf 100644 --- a/src/Core/Window.cpp +++ b/src/Core/Window.cpp @@ -1,5 +1,6 @@ #include "Window.h" #include <cstdint> +#include <iostream> namespace Donut { @@ -7,21 +8,57 @@ namespace Donut static uint32_t s_GLFWWindowCount = 0; Window::Window(const std::string& title, int width, int height) - : m_Title(title), m_Width(width), m_Height(height) + : m_Title(title), m_Width(width), + m_Height(height), m_IsClosed(false) + { + Init(); + } + + Window::~Window() + { + Shutdown(); + } + + void Window::Init() { if (!s_GLFWInitialized) { - glfwInit(); + int success = glfwInit(); + if (!success) + { + std::cout << "Could not initialize GLFW!" << std::endl; + return; + } + s_GLFWInitialized = true; } - m_Window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr); + m_Window = glfwCreateWindow(m_Width, m_Height, m_Title.c_str(), nullptr, nullptr); + if (!m_Window) + { + std::cout << "Could not create GLFW window!" << std::endl; + glfwTerminate(); + return; + } + glfwMakeContextCurrent(m_Window); + glfwSetWindowUserPointer(m_Window, this); + + glfwSetErrorCallback(GLFWErrorCallback); + glfwSetWindowCloseCallback(m_Window, GLFWWindowCloseCallback); + glfwSetWindowSizeCallback(m_Window, GLFWWindowSizeCallback); + glfwSetWindowFocusCallback(m_Window, GLFWWindowFocusCallback); + glfwSetWindowPosCallback(m_Window, GLFWWindowPosCallback); + glfwSetKeyCallback(m_Window, GLFWKeyCallback); + glfwSetCharCallback(m_Window, GLFWCharCallback); + glfwSetMouseButtonCallback(m_Window, GLFWMouseButtonCallback); + glfwSetScrollCallback(m_Window, GLFWMouseScrollCallback); + glfwSetCursorPosCallback(m_Window, GLFWCursorPosCallback); s_GLFWWindowCount++; } - Window::~Window() + void Window::Shutdown() { glfwDestroyWindow(m_Window); s_GLFWWindowCount--; @@ -41,6 +78,116 @@ namespace Donut bool Window::ShouldClose() const { - return glfwWindowShouldClose(m_Window); + return glfwWindowShouldClose(m_Window) || m_IsClosed; + } + + void Window::GLFWErrorCallback(int error, const char* description) + { + std::cout << "GLFW Error (" << error << "): " << description << std::endl; + } + + void Window::GLFWWindowCloseCallback(GLFWwindow* window) + { + Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window)); + WindowCloseEvent event; + win->m_EventHandler.OnEvent(event); + } + + void Window::GLFWWindowSizeCallback(GLFWwindow* window, int width, int height) + { + Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window)); + win->m_Width = width; + win->m_Height = height; + + WindowResizeEvent event(width, height); + win->m_EventHandler.OnEvent(event); + } + + void Window::GLFWWindowFocusCallback(GLFWwindow* window, int focused) + { + Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window)); + + if (focused) + { + WindowFocusEvent event; + win->m_EventHandler.OnEvent(event); + } + else + { + WindowLostFocusEvent event; + win->m_EventHandler.OnEvent(event); + } + } + + void Window::GLFWWindowPosCallback(GLFWwindow* window, int xpos, int ypos) + { + Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window)); + WindowMovedEvent event(xpos, ypos); + win->m_EventHandler.OnEvent(event); + } + + void Window::GLFWKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) + { + Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window)); + + switch (action) + { + case GLFW_PRESS: + { + KeyPressedEvent event(key, false); + win->m_EventHandler.OnEvent(event); + } break; + case GLFW_RELEASE: + { + KeyReleasedEvent event(key); + win->m_EventHandler.OnEvent(event); + } break; + case GLFW_REPEAT: + { + KeyPressedEvent event(key, true); + win->m_EventHandler.OnEvent(event); + } break; + } + } + + void Window::GLFWCharCallback(GLFWwindow* window, unsigned int keycode) + { + Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window)); + KeyTypedEvent event(keycode); + win->m_EventHandler.OnEvent(event); + } + + void Window::GLFWMouseButtonCallback(GLFWwindow* window, int button, int action, int mods) + { + Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window)); + + switch (action) + { + case GLFW_PRESS: + { + MouseButtonPressedEvent event(button); + win->m_EventHandler.OnEvent(event); + } break; + case GLFW_RELEASE: + { + MouseButtonReleasedEvent event(button); + win->m_EventHandler.OnEvent(event); + break; + } + } + } + + void Window::GLFWMouseScrollCallback(GLFWwindow* window, double xOffset, double yOffset) + { + Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window)); + MouseScrolledEvent event((float)xOffset, (float)yOffset); + win->m_EventHandler.OnEvent(event); + } + + void Window::GLFWCursorPosCallback(GLFWwindow* window, double xPos, double yPos) + { + Window* win = static_cast<Window*>(glfwGetWindowUserPointer(window)); + MouseMovedEvent event((float)xPos, (float)yPos); + win->m_EventHandler.OnEvent(event); } }
\ No newline at end of file diff --git a/src/Core/Window.h b/src/Core/Window.h index 866d144..f954ed6 100644 --- a/src/Core/Window.h +++ b/src/Core/Window.h @@ -2,23 +2,51 @@ #include <GLFW/glfw3.h> #include <string> +#include "Event.h" namespace Donut { class Window { public: + using EventCallbackFn = std::function<void(Event&)>; + Window(const std::string& title, int width, int height); ~Window(); bool ShouldClose() const; void OnUpdate() const; + + void SetEventCallback(const EventCallbackFn& callback) { m_EventHandler.SetEventCallback(callback); } + EventHandler& GetEventHandler() { return m_EventHandler; } + + unsigned int GetWidth() const { return m_Width; } + unsigned int GetHeight() const { return m_Height; } + + void* GetNativeWindow() const { return m_Window; } + + private: + void Init(); + void Shutdown(); + + static void GLFWErrorCallback(int error, const char* description); + static void GLFWWindowCloseCallback(GLFWwindow* window); + static void GLFWWindowSizeCallback(GLFWwindow* window, int width, int height); + static void GLFWWindowFocusCallback(GLFWwindow* window, int focused); + static void GLFWWindowPosCallback(GLFWwindow* window, int xpos, int ypos); + static void GLFWKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); + static void GLFWCharCallback(GLFWwindow* window, unsigned int keycode); + static void GLFWMouseButtonCallback(GLFWwindow* window, int button, int action, int mods); + static void GLFWMouseScrollCallback(GLFWwindow* window, double xOffset, double yOffset); + static void GLFWCursorPosCallback(GLFWwindow* window, double xPos, double yPos); + private: GLFWwindow* m_Window; + EventHandler m_EventHandler; std::string m_Title; - int m_Width; - int m_Height; + unsigned int m_Width; + unsigned int m_Height; bool m_IsClosed; }; }
\ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 7637a5b..1290268 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,9 @@ -#include "Core/Window.h" +#include "Core/Application.h" int main() -{ - Donut::Window window("Donut", 1280, 720); - - while (!window.ShouldClose()) - { - window.OnUpdate(); - } +{ + Donut::Application* app = new Donut::Application() ; + app->Run(); + delete app; + return 0; }
\ No newline at end of file |
