OpenGL + Win32 SDK 开发框架的搭建(C语言版)

项目介绍:

近来公司需要建立一个OpenGL的开发环境,我需要设计一个框架,以便不熟悉Win32 API的用户能够直接”书写 glXXX代码”。

卖点:

1. 只使用C语言。

2. 只使用Win32 API和操作系统自带的动态库,决不使用第三方的库。

3. 支持多窗口管理。

开始摆 了~~~~~~~

=====================================================================================

设计思路:

GLWinApp.h 框架的头文件。

GLWinApp.c  实现了框架。

Example.c 一个测试例子。

先来按照“测试驱动开发模式”来说明我的Example.c如何使用我这个库。

#include "GLWinApp.h"

GLMY_WINDOW *gpWnd1, *gpWnd2, *gpWnd3;

/** 应用程序启动后的钩子 */
void myPreHook()
{
	RECT rect;
	GetClientRect(GetDesktopWindow(), &rect);
	gpWnd1 = glmyCreateWindow(TEXT("test0"), TRUE, rect, 32);

	rect.top  = 0;
	rect.left = 0;
	rect.right = 300;
	rect.bottom = 300;
	gpWnd2 = glmyCreateWindow(TEXT("test1"), FALSE, rect, 32);

	rect.left = 350;
	rect.top = 0;
	rect.right = 650;
	rect.bottom = 300;
	gpWnd3 = glmyCreateWindow(TEXT("test2"), FALSE, rect, 32);


	glmyTimer(gpWnd1, 0, 10, TRUE);		/**< 设定定时器0 */
	glmyTimer(gpWnd2, 0, 10, TRUE);		/**< 设定定时器0 */
	glmyRefreshRate(1);					/**< 设置刷新频率 */

	return;
}

/** 应用程序结束前的钩子 */
void myPostHook()
{
}

/** 窗口尺寸改变后的钩子 */
void myResizeHook(GLMY_WINDOW * pWnd)
{
	glViewport(0, 0, pWnd->nWidth, pWnd->nHeight);		/**< 设置视口 */

	glMatrixMode(GL_PROJECTION);			/**< 重置投影矩阵 */
	glLoadIdentity();

	//gluPerspective(45.0f, (float)pWnd->nWidth / (float)pWnd->nHeight, 0, 1000.0f);	/**< 使用对称透视视景体 */
	gluOrtho2D(0, pWnd->nWidth, pWnd->nHeight, 0);		/**< 使用平面坐标 */

	glMatrixMode(GL_MODELVIEW);				/**< 重置模型视点矩阵 */
	glLoadIdentity();
}

/** 绘制的钩子 */
void myDrawHook(GLMY_WINDOW * pWnd)
{
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	glClear(GL_COLOR_BUFFER_BIT);

	glColor3f(1, 0, 0);
	glRecti(0, 0, 100, 100);
	if (pWnd == gpWnd1)
	{
		//Draw wind1
	}
	if (pWnd == gpWnd2)
	{
		//Draw wind2
	}
}

/** 定时器的钩子 */
void myTimerHook(GLMY_WINDOW * pWnd, unsigned char nIndex)
{
	if (pWnd == gpWnd1 && nIndex == 0)
	{
		//Do something
	}
	if (pWnd == gpWnd2 && nIndex == 0)
	{
		//Do something
	}
}
/** 键盘触发 */
void myKeyboardHook(GLMY_WINDOW * pWnd, GLMY_KEYBOARD_KEY key)
{
}

/** 鼠标触发 */
void myMouseHook(GLMY_WINDOW * pWnd, GLMY_MOUSE_BUTTON button, GLMY_MOUSE_ACTION action)
{
}

/** GLMY程序入口,给用户设置Hook的机会(必须由客户端实现) */
void	GLMYENTRY glmyEntry()
{
	glmySetPreHook(myPreHook);
	glmySetPostHook(myPostHook);
	glmySetResizeHook(myResizeHook);
	glmySetDrawHook(myDrawHook);
	glmySetTimerHook(myTimerHook);
	glmySetKeyboardHook(myKeyboardHook);
	glmySetMouseHook(myMouseHook);
}

=====================================================================================

框架介绍:

直接上代码吧:

先来头文件GLWinApp.h

/************************************************************************/
/* 

   建立OpenGL Windows应用程序的通用框架(C语言版)

   特点:
   1. 仅使用Windows系统自带的SDK。
   2. 使用C语言,不使用C++语言。
   3. 使用glmy前缀,表示自定义的库

   设计说明:
   1. 配置文件使用说明:
		修改GLWinAppConfig.h文件,使用自定义的配置信息。

		//可配置项说明
		GLMY_PRE_HOOK		glmyPreHook
		GLMY_POST_HOOK		glmyPostHook
		GLMY_KEYBOARD_HOOK	glmyKeyboardHook
		GLMY_MOUSE_HOOK		glmyMouseHook
		GLMY_DRAW_HOOK		glmyDrawHook
		GLMY_TIMER_HOOK		glmyTimerHook
		
   2. 接口说明:
		新建一个或多个*.c的文件,写相应的函数。
		
		//HOOK函数说明
		glmyPreHook			程序启动时的Hook,可以创建窗口
		glmyPostHook		程序结束时的Hook,可以销毁窗口
		glmyKeyboardHook	键盘响应Hook
		glmyMouseHook		鼠标响应Hook
		glmyDrawHook		绘图需求Hook
		glmyTimerHook		定时器响应Hook

		//可调用函数说明
		glmyCreateWindow		创建一个GL窗口
		glmyDestroyWindow		销毁一个GL窗口

*/
/************************************************************************/

#ifndef	_GLWINAPP_H_
#define	_GLWINAPP_H_

#ifdef __cplusplus
	extern "C" {
#endif

/************************************************************************/
/* 宏定义,预处理定义,头文件 声明                                      */
/************************************************************************/

#define	GLMYAPI		__declspec(dllexport)	/* 是否导出?			*/
#define	GLMYENTRY	__stdcall				/* 约定程序调用方式		*/

#pragma comment (lib, "opengl32.lib")	/* link Microsoft OpenGL lib	*/
#pragma comment (lib, "glu32.lib")		/* link OpenGL Utility lib		*/

#define _CRT_SECURE_NO_WARNINGS		/**< c4996 warning, _stprintf() */
#define _CRT_NON_CONFORMING_SWPRINTFS

#include <windows.h>
#include <tchar.h>
#include <assert.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include "GLWinAppConfig.h"		/**< 编译配置文件 */

/************************************************************************/
/* 数据结构定义                                                         */
/************************************************************************/

/** 鼠标按键 */
typedef enum
{
	GLMY_MOUSE_LEFT_BUTTON		= 0x0001,
	GLMY_MOUSE_RIGHT_BUTTON		= 0x0002,
	GLMY_MOUSE_MIDDLE_BUTTON	= 0x0004
} GLMY_MOUSE_BUTTON;

/** 鼠标动作 */
typedef enum
{
	GLMY_MOUSE_DOWN		= 0x0001,
	GLMY_MOUSE_UP		= 0x0002,
	GLMY_MOUSE_MOVE		= 0x0004
} GLMY_MOUSE_ACTION;

/** 键盘按键 */
typedef enum
{
	GLMY_KEY_ESC,
	GLMY_KEY_F1,
	GLMY_KEY_F2,
	GLMY_KEY_F3,
	GLMY_KEY_LEFT,
	GLMY_KEY_RIGHT,
	GLMY_KEY_UP,
	GLMY_KEY_DOWN,
	GLMY_KEY_SPACE
} GLMY_KEYBOARD_KEY;

/** 结构 GLMY程序 */
typedef struct _GLMY_WINDOW
{
	HWND	hWnd;
	HDC		hDC;
	HGLRC	hRC;
	BOOL	bFullScreen;
	unsigned int	nWidth;
	unsigned int	nHeight;
	unsigned int	nBitsPerPixel;
	struct _GLMY_WINDOW	*pNext;
} GLMY_WINDOW;

/************************************************************************/
/* 接口声明                                                             */
/************************************************************************/

/**** 窗口管理函数 ****/

/** 建立窗口 */
GLMY_WINDOW *	GLMYENTRY glmyCreateWindow(TCHAR * title, BOOL bFullscreen, 
								 RECT rect, int nBitsPerPixel);
/** 销毁窗口 */
void	GLMYENTRY glmyDestroyWindow(GLMY_WINDOW * pWnd);
/** 设置全屏 */
void	GLMYENTRY glmyFullscreen(BOOL bEnable);
/** 设置刷新帧率 */
void	GLMYENTRY glmyRefreshRate(unsigned int nMs);
/** 更改窗口大小 */
void	GLMYENTRY glmyResize(GLMY_WINDOW * pWnd, int nWidth, int nHeight);

/** 设置定时器 */
void	GLMYENTRY glmyTimer(GLMY_WINDOW * pWnd, unsigned char nIndex, unsigned int nMs, BOOL bEnable);

/**** 注册回调函数 ****/

/** GLMY程序入口,给用户设置Hook的机会(必须由客户端实现) */
void	GLMYENTRY glmyEntry();

/** 设置 程序启动后的Hook */
void	GLMYENTRY glmySetPreHook( void (*pFunc)());
/** 设置 程序结束前的Hook */
void	GLMYENTRY glmySetPostHook(void (*pFunc)());
/** 设置 窗口尺寸改变后的Hook */
void	GLMYENTRY glmySetResizeHook(void (*pFunc)(GLMY_WINDOW * pWnd));
/** 设置 绘制的Hook */
void	GLMYENTRY glmySetDrawHook(void (*pFunc)());
/** 设置 定时器触发的Hook */
void	GLMYENTRY glmySetTimerHook(void (*pFunc)(GLMY_WINDOW * pWnd, unsigned char nIndex));
/** 设置 键盘触发的Hook */
void	GLMYENTRY glmySetKeyboardHook(void (*pFunc)(GLMY_WINDOW * pWnd, GLMY_KEYBOARD_KEY key));
/** 设置 鼠标触发的Hook */
void	GLMYENTRY glmySetMouseHook(void (*pFunc)(GLMY_WINDOW * pWnd, GLMY_MOUSE_BUTTON button, GLMY_MOUSE_ACTION action));

#ifdef	__cplusplus
	}
#endif

/*** END OF FILE ***/
#endif	/* _GLWINAPP_H_ */

下面介绍GLWinApp.c的主要内容

#include "GLWinApp.h"

/************************************************************************/
/* 私有成员                                                             */
/************************************************************************/

/** 常量 */
#define GLMY_WINDOW_MAX		99							/**< 最大的窗口数量 */

/** 字段 */
static HINSTANCE		m_hInstance = NULL;				/**< 程序实例		*/
static TCHAR const *	m_sAppName = TEXT("GLMYApplication");	/**< 窗口类的名称 */
static GLMY_WINDOW *	m_pWndListHead = NULL;						/**< GL窗口对象链表	*/
static unsigned int		m_nWndListCount = 0;			/**< GL窗口对象链表的当前长度 */
static unsigned int		m_nRefreshRate = 35;			/**< 刷新帧率(ms)		*/

/** Hook函数指针 */
static void ( * m_PreHook )( void ) = 0;					/**< 程序启动后的Hook	*/

static void ( * m_PostHook )( void ) = 0;					/**< 程序退出前的Hook	*/

static void ( * m_ResizeHook)( GLMY_WINDOW * pWnd ) = 0;	/**< 窗口尺寸改变后的Hook */

static void ( * m_DrawHook )( GLMY_WINDOW * pWnd ) = 0;			/**< 绘制的Hook */

static void ( * m_TimerHook )( GLMY_WINDOW * pWnd, 
							  unsigned char nIndex ) = 0;		/**< 定时器触发的Hook */

static void ( * m_KeyboardHook )( GLMY_WINDOW * pWnd, 
							 GLMY_KEYBOARD_KEY key ) = 0;		/**< 键盘触发的Hook */

static void ( * m_MouseHook )( GLMY_WINDOW * pWnd, 
							 GLMY_MOUSE_BUTTON button, 
							 GLMY_MOUSE_ACTION action ) = 0;	/**< 鼠标触发的Hook */

/************************************************************************/
/* 内部函数声明			                                                    */
/************************************************************************/

/** 注册窗口类 */
static BOOL glmy_RegWndClass(HINSTANCE hInstance, WNDPROC wndProc);
/** 消息处理函数 */
static LRESULT CALLBACK glmy_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
/** 设置屏幕模式 */
static BOOL glmyChangeScreenMode(BOOL bFullscreen, int nWidth, int nHeight, int nBitsPerPixel);
/** 启动OpenGL渲染环境 */
static void glmyEnableGL(GLMY_WINDOW * pApp);
/** 停止OpenGL渲染环境 */
static void glmyDisableGL(GLMY_WINDOW *pApp);

/** 窗口链表 - 增加 */
static BOOL glmy_WndList_Add(GLMY_WINDOW * pWnd);
/** 窗口链表 - 删除 */
static BOOL glmy_WndList_Delete(GLMY_WINDOW * pWnd);
/** 窗口链表 - 查找 */
static GLMY_WINDOW * glmy_WndList_Search(HWND hWnd);
/** 窗口链表 - 反向销毁所有的窗口对象 */
static void glmy_WndList_ReverseFreeAll();

/************************************************************************/
/* 内部函数实现															*/
/************************************************************************/

/** Windows程序入口 */
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	MSG	msg;				/**< 定义一个消息		*/
	BOOL bQuit = FALSE;		/**< 是否退出消息循环	*/

	m_hInstance = hInstance;	/**< 记录程序实例 */

	if (!glmy_RegWndClass(hInstance, glmy_WndProc))		/**< 注册一个窗口类 */
	{
		return -1;
	}

	glmyChangeScreenMode(FALSE, 0, 0, 0);		/**< 强行切换一次显示模式,在以下的创建过程中,如果是全屏,才会修改一次显示模式 */

#if GLMY_HOOK
	glmyEntry();
#endif

#if	GLMY_PRE_HOOK
	if (m_PreHook != NULL)	/**< 启动Hook */
	{
		m_PreHook();
	}
#endif

	/** 传统的消息循环 */
	/*
	while (GetMessage (&msg, NULL, 0, 0))
	{
	TranslateMessage (&msg) ;
	DispatchMessage (&msg) ;
	}
	return msg.wParam ;
	*/

	/** 不使用定时器的消息循环 */

	while (!bQuit)	/**< 消息循环 */
	{		
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))	/**< 检查消息  */
		{
			if (msg.message == WM_QUIT)			/**< 检查退出消息 */
			{
				bQuit = TRUE;
			}
			else
			{
				TranslateMessage(&msg);		/**< 翻译消息 */
				DispatchMessage(&msg);		/**< 分发消息 */
			}
		}
		else
		{
			GLMY_WINDOW * pNextWnd = m_pWndListHead;
			int nWndCnt = m_nWndListCount > 0 ? m_nWndListCount : 1;
			int nSleepMs = m_nRefreshRate / nWndCnt;		/**< 计算休眠时间 */
			nSleepMs = nSleepMs < 1 ? 1 : nSleepMs;				/**< 防止为0 */
			while (pNextWnd != NULL)
			{
#if GLMY_DRAW_HOOK
				if (m_DrawHook != NULL)
				{
					wglMakeCurrent(pNextWnd->hDC, pNextWnd->hRC);
					m_DrawHook(pNextWnd);
					SwapBuffers(pNextWnd->hDC);		/**< 交换到缓冲区	*/
					wglMakeCurrent(NULL, NULL);
				}
#endif
				Sleep(nSleepMs);
				pNextWnd = pNextWnd->pNext;
			}
		}
	}

#if	GLMY_POST_HOOK
	if (m_PostHook != NULL)	/**< 结束Hook	*/
	{
		m_PostHook();
	}
#endif


	glmy_WndList_ReverseFreeAll();	/**< 反向清空所有的窗体对象 */

	return msg.wParam;
}

/** 注册窗口类 */
static BOOL glmy_RegWndClass(HINSTANCE hInstance, WNDPROC wndProc)
{
	WNDCLASSEX	wcex;

	/* 注册窗口类 */
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wcex.lpfnWndProc = wndProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
	wcex.lpszMenuName = NULL;
	wcex.lpszClassName = m_sAppName;
	wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

	if(!RegisterClassEx(&wcex))
	{
		return FALSE;
	}
	return TRUE;
}

/** 消息处理函数 */
static LRESULT CALLBACK glmy_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	GLMY_WINDOW* pWnd = NULL;
	GLMY_KEYBOARD_KEY key = GLMY_KEY_SPACE;					/**< 键盘按键 */

	pWnd = glmy_WndList_Search(hWnd);	

	switch(uMsg)
	{
	case WM_CREATE:
		break;

	case WM_CLOSE:
		if (pWnd != NULL)
		{
			glmy_WndList_Delete(pWnd);	/**< 关闭当前窗口,并清除相关资源 */
		}

		if (m_pWndListHead == NULL)		/**< 如果窗口列表为空,则退出程序 */
		{
			PostQuitMessage(0);
		}
		break;

	case WM_SIZE:
		if (pWnd != NULL)
		{
#if GLMY_RESIZE_HOOK

			pWnd->nWidth = LOWORD(lParam);		/**< 记录宽度和高度 */
			pWnd->nHeight = HIWORD(lParam);
			pWnd->nHeight = pWnd->nHeight > 1 ? pWnd->nHeight : 1;	/**< 防止为0 */

			wglMakeCurrent(pWnd->hDC, pWnd->hRC);	/**< 选中DC和RC	*/

			if (m_ResizeHook != NULL)
			{
				m_ResizeHook(pWnd, pWnd->nWidth, pWnd->nHeight);	/**< 调用Hook */
			}

			wglMakeCurrent(NULL, NULL);	/**< 释放DC和RC */
#endif
		}
		break;

	case WM_KEYDOWN:
		switch(wParam)
		{
		case VK_F1:
			key = GLMY_KEY_F1;
			break;
		case VK_F2:
			key = GLMY_KEY_F2;
			break;
		case VK_F3:
			key = GLMY_KEY_F3;
			break;

		case VK_ESCAPE:
			PostQuitMessage(0);
			break;
		}
#if GLMY_KEYBOARD_HOOK
		if (m_KeyboardHook != NULL)
		{
			m_KeyboardHook(pWnd, key);
		}
#endif
		break;

	case WM_LBUTTONDOWN:
#if GLMY_KEYBOARD_HOOK
		if (m_MouseHook != NULL)
		{
			m_MouseHook(pWnd, GLMY_MOUSE_LEFT_BUTTON, GLMY_MOUSE_DOWN);
		}
#endif
		break;

	case WM_LBUTTONUP:
#if GLMY_KEYBOARD_HOOK
		if (m_MouseHook != NULL)
		{
			m_MouseHook(pWnd, GLMY_MOUSE_LEFT_BUTTON, GLMY_MOUSE_UP);
		}
#endif
		break;

	case WM_RBUTTONDOWN:
#if GLMY_KEYBOARD_HOOK
		if (m_MouseHook != NULL)
		{
			m_MouseHook(pWnd, GLMY_MOUSE_RIGHT_BUTTON, GLMY_MOUSE_DOWN);
		}
#endif
		break;

	case WM_RBUTTONUP:
#if GLMY_KEYBOARD_HOOK
		if (m_MouseHook != NULL)
		{
			m_MouseHook(pWnd, GLMY_MOUSE_RIGHT_BUTTON, GLMY_MOUSE_UP);
		}
#endif
		break;

	case WM_MBUTTONDOWN:
#if GLMY_KEYBOARD_HOOK
		if (m_MouseHook != NULL)
		{
			m_MouseHook(pWnd, GLMY_MOUSE_MIDDLE_BUTTON, GLMY_MOUSE_DOWN);
		}
#endif
		break;

	case WM_MBUTTONUP:
#if GLMY_KEYBOARD_HOOK
		if (m_MouseHook != NULL)
		{
			m_MouseHook(pWnd, GLMY_MOUSE_MIDDLE_BUTTON, GLMY_MOUSE_UP);
		}
#endif
		break;

	case WM_MOUSEMOVE:
#if GLMY_KEYBOARD_HOOK
		if (m_MouseHook != NULL)
		{
			m_MouseHook(pWnd, GLMY_MOUSE_LEFT_BUTTON, GLMY_MOUSE_MOVE);
		}
#endif
		break;

	case WM_TIMER:
#if	GLMY_TIMER_HOOK
		if (m_TimerHook != NULL)
		{
			m_TimerHook(pWnd, (unsigned int)wParam);
		}
#endif
		break;

	default:
		return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}

	return 0;
}


/** 设置屏幕模式 */
static BOOL glmyChangeScreenMode(BOOL bFullscreen, int nWidth, int nHeight, int nBitsPerPixel)
{
	if (!bFullscreen)
	{
		ChangeDisplaySettings(NULL, 0);			/**< 回到桌面 */
	}
	else
	{
		DEVMODE dmScreenSettings;									/**< 设备模式	*/
		memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
		dmScreenSettings.dmSize			= sizeof(DEVMODE);			
		dmScreenSettings.dmPelsWidth	= nWidth;					/**< 屏幕宽度 */
		dmScreenSettings.dmPelsHeight	= nHeight;					/**< 屏幕高度 */
		dmScreenSettings.dmBitsPerPel	= nBitsPerPixel;			/**< 色彩深度 */
		dmScreenSettings.dmFields		= DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

		// 尝试设置显示模式并返回结果。注: CDS_FULLSCREEN 移去了状态栏
		if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
		{
			return FALSE;
		}
	}

	return TRUE;
}

/** 启动OpenGL渲染环境 */
static void glmyEnableGL(GLMY_WINDOW * pApp)
{
	PIXELFORMATDESCRIPTOR pfd =		/**< 像素格式设置 */
	{
		sizeof(PIXELFORMATDESCRIPTOR),
		1,						/**< 版本号 */
		PFD_DRAW_TO_WINDOW		/**< 格式支持窗口 */
		| PFD_SUPPORT_OPENGL	/**< 格式必须支持OpenGL */
		| PFD_DOUBLEBUFFER		/**< 必须支持双缓冲 */
		| PFD_TYPE_RGBA,		/**< RGBA颜色格式 */
		pApp->nBitsPerPixel,	/**< 选定色彩深度 */
		0, 0, 0, 0, 0, 0,		/**< 忽略的色彩位 */
		0,						/**< 无Alpha缓存 */
		0,						/**< 忽略Shift Bit */
		0,						/**< 无累加缓存 */
		0, 0, 0, 0,				/**< 忽略聚集位 */
		16,						/**< 16位 Z-缓存(深度缓存) */
		0,						/**< 无模板缓存 */
		0,						/**< 无辅助缓存 */
		PFD_MAIN_PLANE,			/**< 主绘图层 */
		0,						/**< 不使用重叠层 */
		0, 0, 0					/**< 忽略层遮罩 */
	};
	int nPixelFormatIndex;		/**< 像素格式序号 */

	pApp->hDC = GetDC(pApp->hWnd);		/**< 获取HDC */

	nPixelFormatIndex = ChoosePixelFormat(pApp->hDC, &pfd);	/**< 取得索引 */

	SetPixelFormat(pApp->hDC, nPixelFormatIndex, &pfd);		/**< 设置索引 */

	pApp->hRC = wglCreateContext(pApp->hDC);		/**< 创建RC */

	wglMakeCurrent(pApp->hDC, pApp->hRC);			/**< 测试选中,取消 */
	wglMakeCurrent(NULL, NULL);
}

/** 停止OpenGL渲染环境 */
static void glmyDisableGL(GLMY_WINDOW *pApp)
{
	if (pApp->hRC)
	{
		wglMakeCurrent(NULL, NULL);		/**< 释放DC和RC */
		wglDeleteContext(pApp->hRC);	/**< 释放RC */
		pApp->hRC = NULL;
	}

	if (pApp->hDC)
	{
		ReleaseDC(pApp->hWnd, pApp->hDC);	/**< 释放DC */
		pApp->hDC = NULL;
	}
}


/************************************************************************/
/* 接口函数实现															*/
/************************************************************************/

/** 建立GL窗口 */
GLMY_WINDOW *	GLMYENTRY glmyCreateWindow(TCHAR * title, BOOL bFullscreen, 
									   RECT rect, int nBitsPerPixel)
{
	GLMY_WINDOW	*pWnd;
	BOOL		bQuit = FALSE;
	DWORD		dwStyle;
	DWORD		dwExStyle;

	pWnd = calloc(1, sizeof(GLMY_WINDOW));		/**< 在堆上分配内存,一定要注意释放 */
	pWnd->bFullScreen = bFullscreen;
	pWnd->nWidth = rect.right - rect.left;
	pWnd->nHeight = rect.bottom - rect.top;
	pWnd->nBitsPerPixel = nBitsPerPixel;

	if (pWnd->bFullScreen)		/**< 如果是全屏,则要切换显示模式 */
	{
		glmyChangeScreenMode(bFullscreen, 
			rect.right - rect.left, 
			rect.bottom - rect.top, 
			nBitsPerPixel);
	}

	/** 创建窗口 */
	if (bFullscreen)		
	{
		dwStyle = WS_POPUP;
		dwExStyle = WS_EX_APPWINDOW;
		//ShowCursor(FALSE);
	}
	else
	{
		dwStyle = WS_OVERLAPPEDWINDOW;
		dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
		//ShowCursor(TRUE);
	}
	dwStyle |= WS_CLIPCHILDREN	/**< 排除子窗口 */
		| WS_CLIPSIBLINGS;		/**< 排除同类窗口 */
	pWnd->hWnd = CreateWindowEx(dwExStyle,
		m_sAppName,
		title,
		dwStyle,
		rect.left,					/**< start x	*/
		rect.top,					/**< start y	*/
		rect.right - rect.left,	/**< width		*/
		rect.bottom - rect.top,	/**< height		*/
		NULL,		/**< parent window handle */
		NULL,		/**< window menu handle		*/
		m_hInstance,	/**< program instance handle	*/
		NULL);		/**< creation parameters	*/

	if (pWnd->hWnd == NULL)		/**< 如果创建失败? */
	{		
		free(pWnd);
		return FALSE;
	}

	if (!glmy_WndList_Add(pWnd))		/**< 增加到链表。如果失败,则可能是超出了容量或内存分配失败 */
	{
		free(pWnd);
		return NULL;
	}

	glmyEnableGL(pWnd);								/**< 启用OpenGL渲染环境 */

	glmyResize(pWnd, pWnd->nWidth, pWnd->nHeight);	/**< 手动触发Resize事件,使之能够调整GL环境 */

	ShowWindow(pWnd->hWnd, SW_SHOW);	/**< 显示窗口 */
	SetForegroundWindow(pWnd->hWnd);
	SetFocus(pWnd->hWnd);

	return pWnd;
}

/** 删除GL窗口 */
void GLMYENTRY glmyDestroyWindow(GLMY_WINDOW * pWnd)
{
	assert(pWnd != NULL);		/**< 调试时不允许错误,限制用户必须维护数据的正确 */
	if (pWnd == NULL)			/**< 防止用户传入空参数 */
	{
		return;
	}

	if (pWnd->bFullScreen)		/**< 如果是全屏,则要切换回桌面 */
	{
		glmyChangeScreenMode(FALSE, 0, 0, 0);
	}

	glmyDisableGL(pWnd);		/**< 先禁止OpenGL环境 */

	if (pWnd->hWnd != NULL)		/**< 销毁窗口 */
	{
		DestroyWindow(pWnd->hWnd);
		//SendMessage(pWnd->hWnd, WM_CLOSE, 0, 0);
		pWnd->hWnd = NULL;
	}
}

/** 设置全屏 */
void	GLMYENTRY glmyFullscreen(BOOL bEnable)
{


}

/** 设置刷新帧率 */
void	GLMYENTRY glmyRefreshRate(unsigned int nMs)
{
	int nRefreshRateMin = 1;		/**< 最快1ms刷新一次 */
	int nRefreshRateMax = 10000;	/**< 最慢10s刷新一次 */

	nMs = nMs < nRefreshRateMin ? nRefreshRateMin : nMs;
	nMs = nMs > nRefreshRateMax ? nRefreshRateMax : nMs;
	m_nRefreshRate = nMs;
}

/** 更改窗口大小 */
void	GLMYENTRY glmyResize(GLMY_WINDOW * pWnd, int nWidth, int nHeight)
{
	assert(pWnd != NULL);
	assert(pWnd->hWnd != NULL);
	assert(pWnd->hDC != NULL);
	assert(pWnd->hRC != NULL);

	SendMessage(pWnd->hWnd, WM_SIZE, 0, MAKELONG(nWidth, nHeight));	/**< 发送WM_SIZE消息 */
}

/** 设置定时器 */
void	GLMYENTRY glmyTimer(GLMY_WINDOW * pWnd, unsigned char nIndex, unsigned int nMs, BOOL bEnable)
{
	if (pWnd == NULL || pWnd->hWnd == NULL)
	{
		return;
	}

	nMs = nMs < 10 ? 10 : nMs;	/**< 定时器精度最大为10ms */

	if (bEnable)
	{
		SetTimer(pWnd->hWnd, nIndex, nMs, NULL);
	}
	else
	{
		KillTimer(pWnd->hWnd, nIndex);
	}
}

/** 设置 程序启动后的Hook */
void	GLMYENTRY glmySetPreHook(void (*pFunc)())
{
	m_PreHook = pFunc;
}
/** 设置 程序结束前的Hook */
void	GLMYENTRY glmySetPostHook(void (*pFunc)())
{
	m_PostHook = pFunc;
}
/** 设置 窗口尺寸改变后的Hook */
void	GLMYENTRY glmySetResizeHook(void (*pFunc)(GLMY_WINDOW * pWnd))
{
	m_ResizeHook = pFunc;
}
/** 设置 绘制的Hook */
void	GLMYENTRY glmySetDrawHook(void (*pFunc)())
{
	m_DrawHook = pFunc;
}
/** 设置 定时器触发的Hook */
void	GLMYENTRY glmySetTimerHook(void (*pFunc)(GLMY_WINDOW * pWnd, unsigned char nIndex))
{
	m_TimerHook = pFunc;
}
/** 设置 键盘触发的Hook */
void	GLMYENTRY glmySetKeyboardHook(void (*pFunc)(GLMY_WINDOW * pWnd, GLMY_KEYBOARD_KEY key))
{
	m_KeyboardHook = pFunc;
}
/** 设置 鼠标触发的Hook */
void	GLMYENTRY glmySetMouseHook(void (*pFunc)(GLMY_WINDOW * pWnd, GLMY_MOUSE_BUTTON button, GLMY_MOUSE_ACTION action))
{
	m_MouseHook = pFunc;
}

下面补充WndList的链表操作。

/** 窗口链表 - 增加 */
static BOOL glmy_WndList_Add( GLMY_WINDOW * pWnd )
{
	int count = 0;
	GLMY_WINDOW * pPrev = m_pWndListHead, * pNode;

	assert(pWnd != NULL);			//传入元素必不为空
	if (m_pWndListHead == NULL)		//如果链表为空
	{
		m_pWndListHead = pWnd;
		m_nWndListCount = 1;		/**< 数量为1 */
		return TRUE;
	}

	pNode = pPrev;
	while (pNode != NULL)			//找到第一个空节点
	{
		pPrev = pNode;
		pNode = pNode->pNext;
		count++;
		if (count >= GLMY_WINDOW_MAX)	//超出链表容量
		{
			return FALSE;
		}
	}

	pPrev->pNext = pWnd;
	m_nWndListCount++;			/**< 数量+1 */
	return TRUE;
}
/** 窗口链表 - 删除 */
static BOOL glmy_WndList_Delete( GLMY_WINDOW * pWnd )
{
	GLMY_WINDOW * pPrev = m_pWndListHead, * pNode = NULL;

	assert(pPrev != NULL);		//链表必不为空
	assert(pWnd != NULL);		//传入元素必不为空
	if (pWnd == m_pWndListHead)	//如果就是头结点
	{
		pNode = pWnd->pNext;	//先取得后一个元素

		glmyDestroyWindow(pWnd);	/**< 释放窗体资源 */
		free(pWnd);					/**< 释放GL窗体节点 */
		m_pWndListHead = pNode;		/**< 修改表头 */
		m_nWndListCount = 0;		/**< 数量为0 */
		return TRUE;
	}

	pNode = pPrev;
	while(pNode != NULL)
	{
		pPrev = pNode;
		pNode = pNode->pNext;
		if (pNode == pWnd)		//找到元素
		{
			pPrev->pNext = pNode->pNext;	/**< 指向下下个节点 */
			m_nWndListCount--;			/**< 数量-1 */
			glmyDestroyWindow(pWnd);	/**< 释放窗体资源 */
			free(pWnd);					/**< 释放GL窗体节点 */
			return TRUE;
		}
	}
	return FALSE;
}
/** 窗口链表 - 查找 */
static GLMY_WINDOW * glmy_WndList_Search(HWND hWnd)
{
	GLMY_WINDOW * pNode = NULL, *pHead;

	pHead = m_pWndListHead;
	while (pHead != NULL)
	{
		if (pHead->hWnd == hWnd)
		{
			pNode = pHead;
			break;
		}
		pHead = pHead->pNext;
	}

	//assert(pWnd != NULL);			/**< !此处不可避免的会失败,当CreateWindows()运行后自动发送WM_SIZE事件,但是还没有机会将新的hWND有关的GL_Wnd存储下来。
	return pNode;
}

/** 窗口链表 - 反向销毁所有的窗口对象 */
static void glmy_WndList_ReverseFreeAll()
{
	GLMY_WINDOW * pPrev, * pNode;

	while (TRUE)
	{
		pPrev = m_pWndListHead;
		if (pPrev == NULL)		//表头为空
		{
			return;
		}

		pNode = pPrev->pNext;
		if (pNode == NULL)		//只有一个元素
		{
			glmyDestroyWindow(pPrev);
			free(pPrev);
			m_pWndListHead = NULL;
			m_nWndListCount = 0;			/**< 数量为0 */
			return;
		}

		while (pNode != NULL)	//元素数量>=2。找到末尾节点。
		{
			if (pNode->pNext == NULL)
			{
				break;
			}
			else
			{
				pPrev = pNode;
				pNode = pNode->pNext;
			}
		}
		glmyDestroyWindow(pPrev->pNext);
		free(pPrev->pNext);		//销毁刚找到的末尾节点,使之数量减1
		pPrev->pNext = NULL;
		m_nWndListCount--;		/**< 数量-1 */
	}
	assert(m_nWndListCount == 0);		/**< 数量必为0 */
}

=====================================================================================

(华丽的结束线)

后记:当然,可能还有些许错误,也还没有做任何优化,期待我的库能够运行下去。欢迎各位指正。


版权声明:本文为openlibcn原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>