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 */
}

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

(華麗的結束線)

後記:當然,可能還有些許錯誤,也還沒有做任何優化,期待我的庫能夠運行下去。歡迎各位指正。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章