2021SC@SDUSC
Overload代码分析二:OvWindowing——Context
前言
这是Overload引擎相关的第四篇文章,同时也是OvWindowing分析的第二篇。Overload引擎的Github主页在这里。 本篇文章会开始介绍OvWindowing的各类头文件及函数定义,首先是在Context文件夹中的各类文件,以及Cursor文件夹中的文件。
Context
咱先看.h文件,后看.cpp文件。
一、Device.h
我们在上一篇博客中已经提到了Device.h,其中提到了Device类,咱们再在这里放一下代码:
namespace OvWindowing::Context
{
class Device
{
public:
static OvTools::Eventing::Event<EDeviceError, std::string> ErrorEvent;
Device(const Settings::DeviceSettings& p_deviceSettings);
~Device();
std::pair<int16_t, int16_t> GetMonitorSize() const;
GLFWcursor* GetCursorInstance(Cursor::ECursorShape p_cursorShape) const;
bool HasVsync() const;
void SetVsync(bool p_value);
void PollEvents() const;
float GetElapsedTime() const;
private:
void BindErrorCallback();
void CreateCursors();
void DestroyCursors();
private:
bool m_vsync = true;
bool m_isAlive = false;
std::unordered_map<Cursor::ECursorShape, GLFWcursor*> m_cursors;
};
}
具体的功能我们在讲函数实现的时候在细说。
二、EDeviceError.h
定义了EDeviceError,见代码:
namespace OvWindowing::Context
{
enum class EDeviceError
{
NOT_INITIALIZED = 0x00010001,
NO_CURRENT_CONTEXT = 0x00010002,
INVALID_ENUM = 0x00010003,
INVALID_VALUE = 0x00010004,
OUT_OF_MEMORY = 0x00010005,
API_UNAVAILABLE = 0x00010006,
VERSION_UNAVAILABLE = 0x00010007,
PLATFORM_ERROR = 0x00010008,
FORMAT_UNAVAILABLE = 0x00010009,
NO_WINDOW_CONTEXT = 0x0001000A
};
}
看到enum class就很明了了,就是用枚举定义了一些可返回的错误类型。
三、Device.cpp
cpp内部按顺序讲解函数。
1.构造函数
OvWindowing::Context::Device::Device(const Settings::DeviceSettings& p_deviceSettings)
{
BindErrorCallback();
int initializationCode = glfwInit();
if (initializationCode == GLFW_FALSE)
{
throw std::runtime_error("Failed to Init GLFW");
glfwTerminate();
}
else
{
CreateCursors();
if (p_deviceSettings.debugProfile)
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, p_deviceSettings.contextMajorVersion);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, p_deviceSettings.contextMinorVersion);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_SAMPLES, p_deviceSettings.samples);
m_isAlive = true;
}
}
第一句调用BindErrorCallback(),用于绑定错误回调,当发生错误时可以返回易读的错误,也是主要基于glfw,内部实现为:
void OvWindowing::Context::Device::BindErrorCallback()
{
auto errorCallback = [](int p_code, const char* p_description)
{
ErrorEvent.Invoke(static_cast<EDeviceError>(p_code), p_description);
};
glfwSetErrorCallback(errorCallback);
}
其实就是唤醒错误事件,用glfw进行错误回调的设置。 第二步是初始化glfw,如果初始化失败(initializationCode == GLFW_FALSE),那就报错并且调用glfwTerminate()来终止glfw程序。 如果初始化没有失败,首先调用CreateCursors()来创建指针,内部实现为:
void OvWindowing::Context::Device::CreateCursors()
{
m_cursors[Cursor::ECursorShape::ARROW] = glfwCreateStandardCursor(static_cast<int>(Cursor::ECursorShape::ARROW));
m_cursors[Cursor::ECursorShape::IBEAM] = glfwCreateStandardCursor(static_cast<int>(Cursor::ECursorShape::IBEAM));
m_cursors[Cursor::ECursorShape::CROSSHAIR] = glfwCreateStandardCursor(static_cast<int>(Cursor::ECursorShape::CROSSHAIR));
m_cursors[Cursor::ECursorShape::HAND] = glfwCreateStandardCursor(static_cast<int>(Cursor::ECursorShape::HAND));
m_cursors[Cursor::ECursorShape::HRESIZE] = glfwCreateStandardCursor(static_cast<int>(Cursor::ECursorShape::HRESIZE));
m_cursors[Cursor::ECursorShape::VRESIZE] = glfwCreateStandardCursor(static_cast<int>(Cursor::ECursorShape::VRESIZE));
}
实际上就是把指针的各个参数做了个赋值。 之后确定是否开启了debug,如果开启了,那么就把window设置中对应的属性(GLFW_OPENGL_DEBUG_CONTEXT)设为true。 之后就是对glfw创建的window设定了各种参数,最后把m_isAlive设为true,表示window有效。
2.析构函数
OvWindowing::Context::Device::~Device()
{
if (m_isAlive)
{
DestroyCursors();
glfwTerminate();
}
}
普通的析构函数,首先判断是否window有效(存在),如果存在就先销毁指针,内部也是调用glfw销毁每个参数。之后把glfw中止,释放资源。
3.GetMonitorSize()
用于获得基础窗口的长宽,单位为像素。
std::pair<int16_t, int16_t> OvWindowing::Context::Device::GetMonitorSize() const
{
const GLFWvidmode * mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
return std::pair<int16_t, int16_t>(static_cast<int16_t>(mode->width), static_cast<int16_t>(mode->height));
}
首先通过glfw获得控制器,可以视作获得当前页面的句柄,然后获取页面的video mode,通过它就能得到屏幕空间下的窗口长宽。
4.GetCursorInstance()
用于获得指定指针形状下的一个指针实例。
GLFWcursor * OvWindowing::Context::Device::GetCursorInstance(Cursor::ECursorShape p_cursorShape) const
{
return m_cursors.at(p_cursorShape);
}
内部就只有一句代码,at()是unordered_map的方法,用于获得指定键的对应值,不细讲了,需要的可以自行搜索。
5.HasVsync()
返回是否开启vsync。
bool OvWindowing::Context::Device::HasVsync() const
{
return m_vsync;
}
单纯返回属性值。
6.SetVsync()
设置是否开启vsync。
void OvWindowing::Context::Device::SetVsync(bool p_value)
{
glfwSwapInterval(p_value ? 1 : 0);
m_vsync = p_value;
}
首先给定是否要开启,如果要的话,会把交换帧的间隔变为1,否则可以视作未改变,之后更改对应属性(m_vsync)。
7.PollEvents()
只是对glfw的一层封装,用于开启输入及事件管理。
void OvWindowing::Context::Device::PollEvents() const
{
glfwPollEvents();
}
8.GetElapsedTime()
获得经过的时间,也就是程序运行的时间。
float OvWindowing::Context::Device::GetElapsedTime() const
{
return static_cast<float>(glfwGetTime());
}
实现是直接glfw获得经过时间。
9.已提过的函数
以下函数我们在上面讲过了,就不再重复。
void OvWindowing::Context::Device::BindErrorCallback()
{
auto errorCallback = [](int p_code, const char* p_description)
{
ErrorEvent.Invoke(static_cast<EDeviceError>(p_code), p_description);
};
glfwSetErrorCallback(errorCallback);
}
void OvWindowing::Context::Device::CreateCursors()
{
m_cursors[Cursor::ECursorShape::ARROW] = glfwCreateStandardCursor(static_cast<int>(Cursor::ECursorShape::ARROW));
m_cursors[Cursor::ECursorShape::IBEAM] = glfwCreateStandardCursor(static_cast<int>(Cursor::ECursorShape::IBEAM));
m_cursors[Cursor::ECursorShape::CROSSHAIR] = glfwCreateStandardCursor(static_cast<int>(Cursor::ECursorShape::CROSSHAIR));
m_cursors[Cursor::ECursorShape::HAND] = glfwCreateStandardCursor(static_cast<int>(Cursor::ECursorShape::HAND));
m_cursors[Cursor::ECursorShape::HRESIZE] = glfwCreateStandardCursor(static_cast<int>(Cursor::ECursorShape::HRESIZE));
m_cursors[Cursor::ECursorShape::VRESIZE] = glfwCreateStandardCursor(static_cast<int>(Cursor::ECursorShape::VRESIZE));
}
void OvWindowing::Context::Device::DestroyCursors()
{
glfwDestroyCursor(m_cursors[Cursor::ECursorShape::ARROW]);
glfwDestroyCursor(m_cursors[Cursor::ECursorShape::IBEAM]);
glfwDestroyCursor(m_cursors[Cursor::ECursorShape::CROSSHAIR]);
glfwDestroyCursor(m_cursors[Cursor::ECursorShape::HAND]);
glfwDestroyCursor(m_cursors[Cursor::ECursorShape::HRESIZE]);
glfwDestroyCursor(m_cursors[Cursor::ECursorShape::VRESIZE]);
}
Cursor
虽说标题中我们只说到了Context文件夹,但我们要顺便说一下Cursor文件夹中的文件,因为这和咱们Context文件夹中的一些函数和属性有关。
一、ECursorMode.h
namespace OvWindowing::Cursor
{
enum class ECursorMode
{
NORMAL = 0x00034001,
DISABLED = 0x00034003,
HIDDEN = 0x00034002
};
}
枚举定义了指针的模式,分别是正常,无效和隐藏。
二、ECursorShape.h
namespace OvWindowing::Cursor
{
enum class ECursorShape
{
ARROW = 0x00036001,
IBEAM = 0x00036002,
CROSSHAIR = 0x00036003,
HAND = 0x00036004,
HRESIZE = 0x00036005,
VRESIZE = 0x00036006
};
}
同样是枚举定义,不过定义的是指针样式,名字对应形状,不翻译了。
总结
本次文章介绍的代码量和上次相比少了不少,同时也很简单易懂,清晰明了,应该算是很好理解的一章了。之后咱们会将其他文件夹中的头文件等,也不难,所以已经没有什么好怕的了。到了OvEditor中,会再难起来,在此之前,不用担心。
|