#程序入口
程序启动点文件EntryPoint.主函数在此文件之中,忽略日志类的设置。主要代码就是这三行
auto app = Hazel::CreateApplication();
app->Run();
delete app;
Hazel::Application* Hazel::CreateApplication()
{
return new Sandbox();
}
**值得注意的是创建Sandbox子类会先在其内部创建一个父类实例,并且调用其构造函数后才调用子类构造函数。相当于子类内嵌了一个父类。
#Application-程序第一步
Application::Application()
{
HZ_CORE_ASSERT(!s_Instance, "Application already exists!");
s_Instance = this;//不采用单例模式,而是自行判断APPlication是否只有一个实例
m_Window = std::unique_ptr<Window>(Window::Create());//实际就是运转Init函数
m_Window->SetEventCallback(BIND_EVENT_FN(OnEvent));
}
1. m_Window = std::unique_ptr<Window>(Window::Create()); 实际就是运转Init函数
//WindowsWindow.cpp文件下
---------------
Window* Window::Create(const WindowProps& props)
{
return new WindowsWindow(props);
}
WindowsWindow::WindowsWindow(const WindowProps& props)
{
Init(props);
}
void WindowsWindow::Init(const WindowProps& props)
{
m_Data.Title = props.Title;
m_Data.Width = props.Width;
m_Data.Height = props.Height;
HZ_CORE_INFO("Creating window {0} ({1}, {2})", props.Title, props.Width, props.Height);//log日志系统里面的函数
if (!s_GLFWInitialized)
{
// TODO: glfwTerminate on system shutdown
int success = glfwInit();
HZ_CORE_ASSERT(success, "Could not intialize GLFW!");
glfwSetErrorCallback(GLFWErrorCallback);
s_GLFWInitialized = true;
}
m_Window = glfwCreateWindow((int)props.Width, (int)props.Height, m_Data.Title.c_str(), nullptr, nullptr);
glfwMakeContextCurrent(m_Window);
int status = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
HZ_CORE_ASSERT(status, "Failed to initialize Glad!");
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);
});
....其他glfw的回调函数
}
//Init函数基本上只是设置窗口,初始化OpenGL的函数
//(设置上下文,取得OpenGL函数,设置垂直同步)打印消息和安全检查,
//以及设置glfw的回调函数。为i/o设备提供交互
1.回调函数的简单解释。
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);
});
//以这个两个回调函数为例,第一行代码指定跟踪的事件是在哪个窗口,如果该窗口发生了事件,那么操作系统会把对应事件放入对应的回调函数。
//相当于在指定窗口放在了一堆坑一样,只等事件发生来填入
2. m_Window->SetEventCallback(BIND_EVENT_FN(OnEvent));
Application类的OnEvent函数
最重要的一个功能就是对每个层栈都调用对应的OnEvent函数
//Application文件下
------------
define BIND_EVENT_FN(x) std::bind(&Application::x, this, std::placeholders::_1)
//绑定函数用于设置回调函数
void Application::OnEvent(Event& e)
{
EventDispatcher dispatcher(e);
dispatcher.Dispatch<WindowCloseEvent>(BIND_EVENT_FN(OnWindowClose));
//将传入参数用EventDispatcher类来判断事件是否为窗口关闭事件,如果是调用窗口关闭函数
for (auto it = m_LayerStack.end(); it != m_LayerStack.begin(); )//循环层栈,对每个层调用对应的OnEvent函数
{
(*--it)->OnEvent(e);
if (e.Handled)
break;
}
}
bool Application::OnWindowClose(WindowCloseEvent& e)//窗口关闭函数,用于终止循环
{
m_Running = false;//因为后面的Run的循环需要该变量来条件判断,所以修改m_Running以用来终止循环
return true;//指示事件已经消耗了
}
#Sandbox-程序第二步
相关:Sandbox类为Application子类,ExampleLayer为Layer的子类
Sandbox()
{
PushLayer(new ExampleLayer());//示例层推入层栈
PushOverlay(new Hazel::ImGuiLayer());//将ImGui层推入栈底,即最后操作该层
}
层推入函数
将层推入层栈,以及调用每个层的OnAttach函数
void Application::PushLayer(Layer* layer)
{
m_LayerStack.PushLayer(layer);//在层栈组尾部加入一个函数
layer->OnAttach();
}
//因为m_LayerStack是Application类中一个变量,并且是LayerStack类,同时上述函数实际就是单纯调用了
//LayerStack类中的推入函数,所以后面把layer->OnAttach()改为放入LayerStack类中的推入函数中
void Application::PushOverlay(Layer* layer)
{
m_LayerStack.PushOverlay(layer);
layer->OnAttach();
}
ExampleLayer类暂时无OnAttact函数函数,但其被推入层栈,会被前面提及过的Application::OnEvent函数调用其OnEvent函数
void OnEvent(Hazel::Event& event) override
{
HZ_TRACE("{0}", event);
}
//此处就是跟踪打印发生什么事件
ImguiLayer类的OnAttact函数
void ImGuiLayer::OnAttach()
{
ImGui::CreateContext();
ImGui::StyleColorsDark();//设定ImGui的样式为暗色系
ImGuiIO& io = ImGui::GetIO();//获取 ImGui 的 IO 对象
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;// 当设置此标志时,ImGui会使用后端的鼠标光标。例如,当鼠标浮动在可点击的项目上时,该光标可能会更改为手形。
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos;//位运算的优势可以设置多个标志,当需要更改鼠标光标形状时,应该使用应用程序的原生光标。如果不使用此标志,ImGui 将会使用它自带的基础光标
// TEMPORARY: should eventually use Hazel key codes
io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; //把GLFW的GLFW_KEY_TAB键映射到ImGui的Tab操作上,Tab代表Tab键,在GLFW窗口按下Tab键,ImGui会进行特定操作
io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT;
io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT;
io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP;
io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN;
io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP;
io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN;
io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME;
io.KeyMap[ImGuiKey_End] = GLFW_KEY_END;
io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT;
io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE;
io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE;
io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE;
io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER;
io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE;
io.KeyMap[ImGuiKey_A] = GLFW_KEY_A;
io.KeyMap[ImGuiKey_C] = GLFW_KEY_C;
io.KeyMap[ImGuiKey_V] = GLFW_KEY_V;
io.KeyMap[ImGuiKey_X] = GLFW_KEY_X;
io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y;
io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z;
ImGui_ImplOpenGL3_Init("#version 410");//初始化OpenGL3的上下文和设定GLSL版本,需要环境OpenGL上下文
//在创建渲染上下文后并每次的窗口大小改变时,需正确地设置视图窗口,否则ImGui将无法正确渲染。
}
#run函数
void Application::Run()
{
while (m_Running)
{
glClearColor(1, 0, 1, 1);//glClearColor()函数用于设置颜色缓冲区清除后应用的颜色,
glClear(GL_COLOR_BUFFER_BIT);//清除颜色缓存区,清除缓存区后填入之前设置好的颜色
for (Layer* layer : m_LayerStack)
layer->OnUpdate();//对层栈内的所有层运用更新函数
m_Window->OnUpdate();
}
}
关于m_Window->OnUpdate();
void WindowsWindow::OnUpdate()
{
glfwPollEvents();//从事件队列获取事件
glfwSwapBuffers(m_Window);//交换前台缓冲区和后台缓冲区,把你的渲染结果展示在窗口上.
}
调用所有层栈的更新函数
void ImGuiLayer::OnUpdate()
{
ImGuiIO& io = ImGui::GetIO();//更新函数中加入IO对象,因为ImGui的io对象也用于控制渲染选项。io对象还可以用于监视ImGui的当前状态
Application& app = Application::Get();
io.DisplaySize = ImVec2(app.GetWindow().GetWidth(), app.GetWindow().GetHeight());//ImGui 的显示大小设置为应用程序窗口的大小,ImVec2 是一个存储二维向量/点的结构,
float time = (float)glfwGetTime();//glfwGetTime()函数来获取当前的时间(单位是秒)。
io.DeltaTime = m_Time > 0.0f ? (time - m_Time) : (1.0f / 60.0f);//设定了一个"时间步",如果上一帧的时间大于0(即不是我们正在处理的第一帧)否则,它将被默认设置为1/60秒,这对应于60帧每秒(FPS)的渲染
m_Time = time;
ImGui_ImplOpenGL3_NewFrame();//OpenGL3实际上是指OpenGL版本3为基础的实现。这个函数主要是为了确保每一帧的时候,所有的图形资源都准备好,并且设定正确的状态。
ImGui::NewFrame();//它表示每一帧的开始。这通常被放在你的主循环中,为即将创建的任何ImGui窗口、小组件等设置新的帧上下文。
static bool show = true;
ImGui::ShowDemoWindow(&show);//创建一个ImGui演示窗口
ImGui::Render();//这行代码在你完成用户界面命令的构建后调用,它将计算并生成绘制命令列表。
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());//我们用OpenGL3的实现来实际执行绘制命令。ImGui::GetDrawData()提供了我们需要的所有绘图数据
}
#小结
1.Application::OnEvent会调用所有层栈的OnEvent函数
2.开始为Appicalition的层推入函数调用,后面改为放入层栈类推入函数,会调用层对应的OnAttach函数
3.Run函数,会调用层对应的OnUpdate函数
4.预设....,会调用层的OnDetach函数