<2021SC@SDUSC>开源游戏引擎Overload代码分析二:OvWindowing——Context

2021SC@SDUSC

前言

这是Overload引擎相关的第四篇文章,同时也是OvWindowing分析的第二篇。Overload引擎的Github主页在这里。
本篇文章会开始介绍OvWindowing的各类头文件及函数定义,首先是在Context文件夹中的各类文件,以及Cursor文件夹中的文件。

Context

咱先看.h文件,后看.cpp文件。

一、Device.h

我们在上一篇博客中已经提到了Device.h,其中提到了Device类,咱们再在这里放一下代码:

namespace OvWindowing::Context
{
	/**
	* The Device represents the windowing context. It is necessary to create a device
	* to create a window
	*/
	class Device
	{
	public:
		/**
		* Bind a listener to this event to receive device errors
		*/
		static OvTools::Eventing::Event<EDeviceError, std::string> ErrorEvent;

		/**
		* The constructor of the device will take care about GLFW initialization
		*/
		Device(const Settings::DeviceSettings& p_deviceSettings);

		/**
		* The destructor of the device will take care about GLFW destruction
		*/
		~Device();

		/**
		* Return the size, in pixels, of the primary monity
		*/
		std::pair<int16_t, int16_t> GetMonitorSize() const;

		/**
		* Return an instance of GLFWcursor corresponding to the given shape
		* @param p_cursorShape
		*/
		GLFWcursor* GetCursorInstance(Cursor::ECursorShape p_cursorShape) const;

		/**
		* Return true if the vsync is currently enabled
		*/
		bool HasVsync() const;

		/**
		* Enable or disable the vsync
		* @note You must call this method after creating and defining a window as the current context
		* @param p_value (True to enable vsync)
		*/
		void SetVsync(bool p_value);

		/**
		* Enable the inputs and events managments with created windows
		* @note Should be called every frames
		*/
		void PollEvents() const;

		/**
		* Returns the elapsed time since the device startup
		*/
		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
{
	/**
	* Some errors that the driver can return
	*/
	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
{
	/**
	* Some cursor modes.
	* They defines if the mouse pointer should be visible, locked or normal
	*/
	enum class ECursorMode
	{
		NORMAL		= 0x00034001,
		DISABLED	= 0x00034003,
		HIDDEN		= 0x00034002
	};
}

枚举定义了指针的模式,分别是正常,无效和隐藏。

二、ECursorShape.h

namespace OvWindowing::Cursor
{
	/**
	* Some cursor shapes.
	* They specify how the mouse pointer should look
	*/
	enum class ECursorShape
	{
		ARROW		= 0x00036001,
		IBEAM		= 0x00036002,
		CROSSHAIR	= 0x00036003,
		HAND		= 0x00036004,
		HRESIZE		= 0x00036005,
		VRESIZE		= 0x00036006
	};
}

同样是枚举定义,不过定义的是指针样式,名字对应形状,不翻译了。

总结

本次文章介绍的代码量和上次相比少了不少,同时也很简单易懂,清晰明了,应该算是很好理解的一章了。之后咱们会将其他文件夹中的头文件等,也不难,所以已经没有什么好怕的了。到了OvEditor中,会再难起来,在此之前,不用担心。

Diana

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值