引入GLFW:
在vendor里创建GLFW文件夹:
在github上下载,把包下载到GLFW包下。
GitHub - TheCherno/glfw: A multi-platform library for OpenGL, OpenGL ES, Vulkan, window and input修改SRC/premake5.lua的配置:12、13、15、36、37、38、39、40行的代码是新加上去的:
workspace "YOTOEngine" -- sln文件名
architecture "x64"
configurations{
"Debug",
"Release",
"Dist"
}
-- https://github.com/premake/premake-core/wiki/Tokens#value-tokens
-- 组成输出目录:Debug-windows-x86_64
outputdir = "%{cfg.buildcfg}-%{cfg.system}-%{cfg.architecture}"
IncludeDir={}
IncludeDir["GLFW"]="YOTOEngine/vendor/GLFW/include"
include "YOTOEngine/vendor/GLFW"
project "YOTOEngine" --Hazel项目
location "YOTOEngine"--在sln所属文件夹下的Hazel文件夹
kind "SharedLib"--dll动态库
language "C++"
targetdir ("bin/" .. outputdir .. "/%{prj.name}") -- 输出目录
objdir ("bin-int/" .. outputdir .. "/%{prj.name}")-- 中间目录
pchheader "ytpch.h"
pchsource "YOTOEngine/src/ytpch.cpp"
-- 包含的所有h和cpp文件
files{
"%{prj.name}/src/**.h",
"%{prj.name}/src/**.cpp"
}
-- 包含目录
includedirs{
"%{prj.name}/src",
"%{prj.name}/vendor/spdlog-1.x/include",
"%{IncludeDir.GLFW}"
}
links{
"GLFW",
"opengl32.lib"
}
-- 如果是window系统
filter "system:windows"
cppdialect "C++17"
-- On:代码生成的运行库选项是MTD,静态链接MSVCRT.lib库;
-- Off:代码生成的运行库选项是MDD,动态链接MSVCRT.dll库;打包后的exe放到另一台电脑上若无这个dll会报错
staticruntime "On"
systemversion "latest" -- windowSDK版本
-- 预处理器定义
defines{
"YT_PLATFORM_WINDOWS",
"YT_BUILD_DLL",
"YT_ENABLE_ASSERTS",
}
-- 编译好后移动Hazel.dll文件到Sandbox文件夹下
postbuildcommands{
("{COPY} %{cfg.buildtarget.relpath} ../bin/" .. outputdir .. "/Sandbox")
}
-- 不同配置下的预定义不同
filter "configurations:Debug"
defines "YT_DEBUG"
symbols "On"
filter "configurations:Release"
defines "YT_RELEASE"
optimize "On"
filter "configurations:Dist"
defines "YT_DIST"
optimize "On"
project "Sandbox"
location "Sandbox"
kind "ConsoleApp"
language "C++"
targetdir ("bin/" .. outputdir .. "/%{prj.name}")
objdir ("bin-int/" .. outputdir .. "/%{prj.name}")
files{
"%{prj.name}/src/**.h",
"%{prj.name}/src/**.cpp"
}
-- 同样包含spdlog头文件
includedirs{
"YOTOEngine/vendor/spdlog-1.x/include",
"YOTOEngine/src"
}
-- 引用hazel
links{
"YOTOEngine",
"GLFW",
"opengl32.lib"
}
filter "system:windows"
cppdialect "C++17"
staticruntime "On"
systemversion "latest"
defines{
"YT_PLATFORM_WINDOWS"
}
filter "configurations:Debug"
defines "YT_DEBUG"
symbols "On"
filter "configurations:Release"
defines "YT_RELEASE"
optimize "On"
filter "configurations:Dist"
defines "YT_DIST"
optimize "On"
GLFW中的premake5.lua:
project "GLFW"
kind "StaticLib"
language "C"
staticruntime "off"
warnings "off"
targetdir ("bin/" .. outputdir .. "/%{prj.name}")
objdir ("bin-int/" .. outputdir .. "/%{prj.name}")
files
{
"include/GLFW/glfw3.h",
"include/GLFW/glfw3native.h",
"src/glfw_config.h",
"src/context.c",
"src/init.c",
"src/input.c",
"src/monitor.c",
"src/null_init.c",
"src/null_joystick.c",
"src/null_monitor.c",
"src/null_window.c",
"src/platform.c",
"src/vulkan.c",
"src/window.c",
}
filter "system:linux"
pic "On"
systemversion "latest"
files
{
"src/x11_init.c",
"src/x11_monitor.c",
"src/x11_window.c",
"src/xkb_unicode.c",
"src/posix_module.c",
"src/posix_time.c",
"src/posix_thread.c",
"src/posix_module.c",
"src/glx_context.c",
"src/egl_context.c",
"src/osmesa_context.c",
"src/linux_joystick.c"
}
defines
{
"_GLFW_X11"
}
filter "system:macosx"
pic "On"
files
{
"src/cocoa_init.m",
"src/cocoa_monitor.m",
"src/cocoa_window.m",
"src/cocoa_joystick.m",
"src/cocoa_time.c",
"src/nsgl_context.m",
"src/posix_thread.c",
"src/posix_module.c",
"src/osmesa_context.c",
"src/egl_context.c"
}
defines
{
"_GLFW_COCOA"
}
filter "system:windows"
systemversion "latest"
files
{
"src/win32_init.c",
"src/win32_joystick.c",
"src/win32_module.c",
"src/win32_monitor.c",
"src/win32_time.c",
"src/win32_thread.c",
"src/win32_window.c",
"src/wgl_context.c",
"src/egl_context.c",
"src/osmesa_context.c"
}
defines
{
"_GLFW_WIN32",
"_CRT_SECURE_NO_WARNINGS"
}
--解决bug的部分:buildoptions "/MTD" 和"/MT"
filter "configurations:Debug"
defines "YT_DEBUG"
buildoptions "/MTd"
symbols "On"
filter { "system:windows", "configurations:Debug-AS" }
runtime "Debug"
symbols "on"
sanitize { "Address" }
flags { "NoRuntimeChecks", "NoIncrementalLink" }
filter "configurations:Release"
defines "YT_RELEASE"
buildoptions "/MT"
symbols "On"
filter "configurations:Dist"
defines "YT_DIST"
buildoptions "/MT"
symbols "On"
运行测试:
如出现此BUG:请找GLFW中的premake5文件,把上述的premake5.lua的bug解决部分改一下:
使GLFW项目的运行库,只能是MT或者MTD,不能是MD或者MDD
执行GenerateProject.bat文件:
刷新解决方案得到GLFW的包:
窗口抽象:
创建window基类,用于不同平台window的实现。
YOTO/Window.h:
#pragma once
#include"ytpch.h"
#include"YOTO/Core.h"
#include"YOTO/Event/Event.h"
namespace YOTO {
struct WindowProps {
std::string Title;
unsigned int Width;
unsigned int Height;
WindowProps(const std::string &title="YOTO Engine",unsigned int width =1280, unsigned int height = 1280 )
:Title(title),Width(width),Height(height){}
};
class YOTO_API Window {
public:
//用EventCallbackFn代替std::function<void(Event&)>:输入为Event&返回值为void 的函数
using EventCallbackFn = std::function<void(Event&)>;
virtual ~Window(){}
//=0为纯虚函数
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 SetSync(bool enable)const = 0;
virtual bool IsVSync() const = 0;
static Window* Creat(const WindowProps& props = WindowProps());
};
}
实现类:
创建文件夹src/Platform/Windows
WindowsWindow.h:
#pragma once
#include "YOTO/Window.h"
#include<GLFW/glfw3.h>
#include"YOTO/Log.h"
namespace YOTO {
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 enable) ;
bool IsVSync()const;
private:
virtual void Init(const WindowProps& props);
virtual void ShutDown();
private:
GLFWwindow* m_Window;
struct WindowData {
std::string Title;
unsigned int Width, Height;
bool VSync;
EventCallbackFn EventCallback;
};
WindowData m_Data;
};
}
WindowsWindow.cpp:
#include "ytpch.h"
#include "WindowsWindow.h"
namespace YOTO {
static bool s_GLFWInitialized = false;
Window* Window::Creat(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;
YT_CORE_INFO("创建了{0},{1},{2}", props.Title, props.Width, props.Height);
if (!s_GLFWInitialized) {
int success = glfwInit();
YT_CLIENT_ASSERT("不能创建新的glfw,{0}", success);
s_GLFWInitialized = true;
}
m_Window = glfwCreateWindow((int)props.Width, (int)props.Height, m_Data.Title.c_str(), nullptr, nullptr);
glfwMakeContextCurrent(m_Window);
glfwSetWindowUserPointer(m_Window, &m_Data);
SetVSync(true);
}
void WindowsWindow::ShutDown() {
glfwDestroyWindow(m_Window);
}
void WindowsWindow::OnUpdate()
{
//轮询事件
glfwPollEvents();
//交换缓冲区
glfwSwapBuffers(m_Window);
}
void WindowsWindow::SetVSync(bool enable) {
if (enable)
glfwSwapInterval(1);
else
glfwSwapInterval(0);
m_Data.VSync = enable;
}
bool WindowsWindow::IsVSync() const {
return m_Data.VSync;
}
}
Core.h:添加新的Error
#pragma once
//用于dll的宏
#ifdef YT_PLATFORM_WINDOWS
#ifdef YT_BUILD_DLL
#define YOTO_API __declspec(dllexport)
#else
#define YOTO_API __declspec(dllimport)
#endif // DEBUG
#else
#error YOTO_ONLY_SUPPORT_WINDOWS
#endif // YOTO_PLATFORM_WINDOWS
#ifdef YT_ENABLE_ASSERTS
#define YT_CLIENT_ASSERT(x,...) {if(!(x)){YT_CLIENT_ERROR("断言错误:{0}",__VA_ARGS__);__debugbreak();}}
#define YT_CORE_ASSERT(x,...) {if(!(x)){YT_CORE_ERROR("断言错误:{0}",__VA_ARGS__);__debugbreak();}}
#else
#define YT_ASSERT(x,...)
#define YT_CORE_ASSERT(x,...)
#endif // YT_ENABLE_ASSERTS
#define BIT(x)(1<<x)
Application.h:创建智能指针作为窗口的指针
#pragma once
#include"Core.h"
#include"Event/Event.h"
#include <YOTO/Window.h>
namespace YOTO {
class YOTO_API Application
{
public:
Application();
virtual ~Application();
void Run();
private:
std::unique_ptr<Window> m_Window;
bool m_Running = true;
};
//在客户端定义
Application* CreateApplication();
}
测试:
Application.cpp:在构造函数中创建窗口,在run的while循环中调用Update,写一段opengl测试代码窗口改变颜色
#include"ytpch.h"
#include "Application.h"
#include"Event/ApplicationEvent.h"
#include"Log.h"
#include<GLFW/glfw3.h>
namespace YOTO {
Application::Application() {
//智能指针
m_Window = std::unique_ptr<Window>(Window::Creat());
}
Application::~Application() {
}
void Application::Run() {
WindowResizeEvent e(1280, 720);
if (e.IsInCategory(EventCategoryApplication)) {
YT_CORE_TRACE(e);
}
if (e.IsInCategory(EventCategoryInput)) {
YT_CORE_ERROR(e);
}
while (m_Running)
{
glClearColor(1,0,1,1);
glClear(GL_COLOR_BUFFER_BIT);
m_Window->OnUpdate();
}
}
}
BUG:只需要重新生成一下YOTOEngine就好了
BUG:可抽象的一个bug。原因是WindowsWindow.cpp错误的添加了#include"YOTO.h"把它删掉就好了。
结果:
窗口事件:
核心机制:
在windowswindow.cpp的Init方法中,绑定GLFW回调,每次glfw发生某事件之后,调用回调函数。
在Application.cpp的构造函数中,将所有事件绑定到OnEvent,通过多态传进来的事件信息,用EventDispatcher判断事件类型,并且调用绑定的事件。
Application.h:添加了OnEvent和OnWindowClosed的定义
#pragma once
#include"Core.h"
#include"Event/Event.h"
#include"Event/ApplicationEvent.h"
#include "YOTO/Window.h"
namespace YOTO {
class YOTO_API Application
{
public:
Application();
virtual ~Application();
void Run();
void OnEvent(Event &e);
private:
bool OnWindowClosed(WindowCloseEvent& e);
std::unique_ptr<Window> m_Window;
bool m_Running = true;
};
//在客户端定义
Application* CreateApplication();
}
Application.cpp: 绑定了OnEvent和OnWindowClosed
#include"ytpch.h"
#include "Application.h"
#include"Log.h"
#include<GLFW/glfw3.h>
namespace YOTO {
#define BIND_EVENT_FN(x) std::bind(&x, this, std::placeholders::_1)
Application::Application() {
//智能指针
m_Window = std::unique_ptr<Window>(Window::Creat());
//设置回调函数
m_Window->SetEventCallback(BIND_EVENT_FN(Application::OnEvent));
}
/// <summary>
/// 所有的Window事件都会在这触发,作为参数e
/// </summary>
/// <param name="e"></param>
void Application::OnEvent(Event& e) {
//根据事件类型绑定对应事件
EventDispatcher dispatcher(e);
dispatcher.Dispatch<WindowCloseEvent>(BIND_EVENT_FN(Application::OnWindowClosed));
//输出事件信息
YT_CORE_INFO("{0}",e);
}
Application::~Application() {
}
bool Application::OnWindowClosed(WindowCloseEvent& e) {
m_Running = false;
return true;
}
void Application::Run() {
WindowResizeEvent e(1280, 720);
if (e.IsInCategory(EventCategoryApplication)) {
YT_CORE_TRACE(e);
}
if (e.IsInCategory(EventCategoryInput)) {
YT_CORE_ERROR(e);
}
while (m_Running)
{
glClearColor(1,0,1,1);
glClear(GL_COLOR_BUFFER_BIT);
m_Window->OnUpdate();
}
}
}
WindowsWindow.cpp:Init方法事件添加回调(lambda表达式)
#include "ytpch.h"
#include "WindowsWindow.h"
#include"YOTO/Event/ApplicationEvent.h"
#include"YOTO/Event/MouseEvent.h"
#include"YOTO/Event/KeyEvent.h"
namespace YOTO {
static bool s_GLFWInitialized = false;
Window* Window::Creat(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;
YT_CORE_INFO("创建了{0},{1},{2}", props.Title, props.Width, props.Height);
if (!s_GLFWInitialized) {
int success = glfwInit();
YT_CLIENT_ASSERT("不能创建新的glfw,{0}", success);
glfwSetErrorCallback([](int error_code, const char* description) {
YT_CORE_ERROR("GLFW错误:错误码({0}):{1} ", error_code, description);
});
s_GLFWInitialized = true;
}
m_Window = glfwCreateWindow((int)props.Width, (int)props.Height, m_Data.Title.c_str(), nullptr, nullptr);
glfwMakeContextCurrent(m_Window);
glfwSetWindowUserPointer(m_Window, &m_Data);
SetVSync(true);
//GLFW回调,每次改变调用lambda里的部分
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;
}
}
});
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);
});
}
void WindowsWindow::ShutDown() {
glfwDestroyWindow(m_Window);
}
void WindowsWindow::OnUpdate()
{
//轮询事件
glfwPollEvents();
//交换缓冲区
glfwSwapBuffers(m_Window);
}
void WindowsWindow::SetVSync(bool enable) {
if (enable)
glfwSwapInterval(1);
else
glfwSwapInterval(0);
m_Data.VSync = enable;
}
bool WindowsWindow::IsVSync() const {
return m_Data.VSync;
}
}
测试:
cool 运行成功,事件系统投入使用。