来自:http://www.codeproject.com/Articles/27219/TetroGL-An-OpenGL-Game-Tutorial-in-C-for-Win32-Pla
main.cpp
#include <Windows.h>
#include "Exception.h"
#include "Application.h"
int WINAPI WinMain(HINSTANCE Instance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT)
{
try
{
// Create the application class,
// parse the command line and
// start the app.
CApplication theApp(Instance);
theApp.ParseCmdLine(lpCmdLine);
theApp.Run();
}
catch(CException& e)
{
MessageBox(NULL,e.what(),"Error",MB_OK|MB_ICONEXCLAMATION);
}
return 0;
}
Application.h
#ifndef _APPLICATION_H_
#define _APPLICATION_H_
#include <windows.h>
// The application class, which simply wraps the message queue and process
// the command line.
class CApplication
{
public:
CApplication(HINSTANCE hInstance);
~CApplication();
// Parses the command line to see if the application
// should be in fullscreen mode.
void ParseCmdLine(LPSTR lpCmdLine);
// Creates the main window and start the message loop.
void Run();
private:
static const int FRAME_TIME = 75;
HINSTANCE m_hInstance;
// Specifies if the application has to be started in fullscreen
// mode. This option is supplied through the command line
// ("-fullscreen" option).
bool m_bFullScreen;
};
#endif // _APPLICATION_H_
#include "Application.h"
#include "MainWindow.h"
#include <string>
// Disable the warnings for deprecated functions (strtok and stricmp)
#pragma warning(disable:4996)
CApplication::CApplication(HINSTANCE hInstance)
: m_hInstance(hInstance), m_bFullScreen(false)
{
}
CApplication::~CApplication()
{
}
void CApplication::ParseCmdLine(LPSTR lpCmdLine)
{
LPSTR lpArgument = strtok(lpCmdLine," ");
while (lpArgument)
{
if (stricmp(lpArgument,"-fullscreen") == 0)
m_bFullScreen = true;
lpArgument = strtok(NULL," ");
}
}
void CApplication::Run()
{
// Create the main window first
CMainWindow mainWindow(800,600,m_bFullScreen);
MSG Message;
Message.message = ~WM_QUIT;
DWORD dwNextDeadLine = GetTickCount() + FRAME_TIME;
DWORD dwSleep = FRAME_TIME;
bool bUpdate = false;
// Loop until a WM_QUIT message is received
while (Message.message != WM_QUIT)
{
// Wait until a message comes in or until the timeout expires. The
// timeout is recalculated so that this function will return at
// least every FRAME_TIME msec.
DWORD dwResult = MsgWaitForMultipleObjectsEx(0,NULL,dwSleep,QS_ALLEVENTS,0);
if (dwResult != WAIT_TIMEOUT)
{
// If the function returned with no timeout, it means that at
// least one message has been received, so process all of them.
while (PeekMessage(&Message, NULL, 0, 0, PM_REMOVE))
{
// If a message was waiting in the message queue, process it
TranslateMessage(&Message);
DispatchMessage(&Message);
}
// If the current time is close (or past) to the
// deadline, the application should be processed.
if (GetTickCount() >= dwNextDeadLine)
bUpdate = true;
else
bUpdate = false;
}
else
// On a timeout, the application should be processed.
bUpdate = true;
// Check if the application should be processed
if (bUpdate)
{
DWORD dwCurrentTime = GetTickCount();
// Update the main window
mainWindow.Update(dwCurrentTime);
// Draw the main window
mainWindow.Draw();
dwNextDeadLine = dwNextDeadLine + FRAME_TIME;
}
// Process the sleep time, which is the difference
// between the current time and the next deadline.
dwSleep = dwNextDeadLine - GetCurrentTime();
// If the sleep time is larger than the frame time,
// it probably means that the processing was stopped
// (e.g. the window was being moved,...), so recalculate
// the next deadline.
if (dwSleep>FRAME_TIME)
{
dwSleep = FRAME_TIME;
dwNextDeadLine = GetCurrentTime() + FRAME_TIME;
}
}
}
MainWindow.h
#ifndef _MAINWINDOW_H_
#define _MAINWINDOW_H_
#include <Windows.h>
#include "GL/gl.h"
// The main window class. It wraps the HANDLE of the window and initializes the
// openGL rendering context. It is also in charge of processing the different
// event messages.
class CMainWindow
{
public:
CMainWindow(int iWidth, int iHeight, bool bFullScreen);
~CMainWindow();
// Called by the application class to update the game logic
void Update(DWORD dwCurrentTime);
// Called by the application class when the window need to be redrawn.
void Draw();
private:
// Register the window class with the correct window procedure (OnEvent)
void RegisterWindowClass();
// Create the rendering context used by OpenGL
void CreateContext();
// Initialize openGL
void InitGL();
// Called when a WM_SIZE message is received
void OnSize(GLsizei width, GLsizei height);
// Static function which will be the window procedure callback
static LRESULT CALLBACK OnEvent(HWND Handle, UINT Message, WPARAM wParam, LPARAM lParam);
// Processes the messages that were received in OnEvent.
void ProcessEvent(UINT Message, WPARAM wParam, LPARAM lParam);
// The window handle
HWND m_hWindow;
// The window device context
HDC m_hDeviceContext;
// The openGL context.
HGLRC m_hGLContext;
// Specifies if the window is fullscreen.
bool m_bFullScreen;
};
#endif // _MAINWINDOW_H_
MainWindow.cpp
#include "MainWindow.h"
#include "Exception.h"
#define WINDOW_CLASSNAME "Tutorial1" // Window class name
CMainWindow::CMainWindow(int iWidth, int iHeight, bool bFullScreen)
: m_hWindow(NULL), m_hDeviceContext(NULL), m_hGLContext(NULL),
m_bFullScreen(bFullScreen)
{
RegisterWindowClass();
RECT WindowRect;
WindowRect.top = WindowRect.left = 0;
WindowRect.right = iWidth;
WindowRect.bottom = iHeight;
// Window Extended Style
DWORD dwExStyle = 0;
// Windows Style
DWORD dwStyle = 0;
if (m_bFullScreen)
{
DEVMODE dmScreenSettings;
memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmPelsWidth = iWidth;
dmScreenSettings.dmPelsHeight = iHeight;
dmScreenSettings.dmBitsPerPel = 32;
dmScreenSettings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
// Change the display settings to fullscreen. On error, throw
// an exception.
if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)
!= DISP_CHANGE_SUCCESSFUL)
{
throw CException("Unable to swith to fullscreen mode");
}
dwExStyle = WS_EX_APPWINDOW;
dwStyle = WS_POPUP;
// In fullscreen mode, we hide the cursor.
ShowCursor(FALSE);
}
else
{
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
dwStyle = WS_OVERLAPPEDWINDOW;
}
// Adjust the window to the true requested size
AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);
// Now create the main window
m_hWindow = CreateWindowEx(dwExStyle,TEXT(WINDOW_CLASSNAME),
TEXT("Tutorial1"),
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dwStyle,
0, 0, WindowRect.right-WindowRect.left,
WindowRect.bottom-WindowRect.top,
NULL, NULL,
GetModuleHandle(NULL),
this);
if (m_hWindow==NULL)
throw CException("Cannot create the main window");
CreateContext();
InitGL();
ShowWindow(m_hWindow,SW_SHOW);
// Call OnSize manually because in fullscreen mode it will be
// called only when the window is created (which is too early
// because OpenGL is not initialized yet).
OnSize(iWidth,iHeight);
}
CMainWindow::~CMainWindow()
{
if (m_bFullScreen)
{
// Remove the full screen setting
ChangeDisplaySettings(NULL,0);
ShowCursor(TRUE);
}
if (m_hGLContext)
{
// Make the rendering context not current
wglMakeCurrent(NULL,NULL);
// Delete the OpenGL rendering context
wglDeleteContext(m_hGLContext);
m_hGLContext = NULL;
}
if (m_hDeviceContext)
{
// Release the device context
ReleaseDC(m_hWindow,m_hDeviceContext);
m_hDeviceContext = NULL;
}
// Finally, destroy our main window and unregister the
// window class.
DestroyWindow(m_hWindow);
UnregisterClass(TEXT(WINDOW_CLASSNAME), GetModuleHandle(NULL));
}
LRESULT CMainWindow::OnEvent(HWND Handle, UINT Message, WPARAM wParam, LPARAM lParam)
{
if (Message == WM_NCCREATE)
{
// Get the creation parameters.
CREATESTRUCT* pCreateStruct = reinterpret_cast<CREATESTRUCT*>(lParam);
// Set as the "user data" parameter of the window
SetWindowLongPtr(Handle, GWLP_USERDATA,
reinterpret_cast<long>(pCreateStruct->lpCreateParams));
}
// Get the CMainWindow instance corresponding to the window handle
CMainWindow* pWindow = reinterpret_cast<CMainWindow*>
(GetWindowLongPtr(Handle, GWLP_USERDATA));
if (pWindow)
pWindow->ProcessEvent(Message,wParam,lParam);
return DefWindowProc(Handle, Message, wParam, lParam);
}
void CMainWindow::ProcessEvent(UINT Message, WPARAM wParam, LPARAM lParam)
{
switch (Message)
{
// Quit when we close the main window
case WM_CLOSE :
PostQuitMessage(0);
break;
case WM_SIZE:
OnSize(LOWORD(lParam),HIWORD(lParam));
break;
case WM_KEYDOWN :
break;
case WM_KEYUP :
break;
}
}
void CMainWindow::RegisterWindowClass()
{
WNDCLASS WindowClass;
WindowClass.style = 0;
WindowClass.lpfnWndProc = &CMainWindow::OnEvent;
WindowClass.cbClsExtra = 0;
WindowClass.cbWndExtra = 0;
WindowClass.hInstance = GetModuleHandle(NULL);
WindowClass.hIcon = NULL;
WindowClass.hCursor = 0;
WindowClass.hbrBackground = 0;
WindowClass.lpszMenuName = NULL;
WindowClass.lpszClassName = WINDOW_CLASSNAME;
RegisterClass(&WindowClass);
}
void CMainWindow::CreateContext()
{
// Describes the pixel format of the drawing surface
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1; // Version Number
pfd.dwFlags = PFD_DRAW_TO_WINDOW | // Draws to a window
PFD_SUPPORT_OPENGL | // The format must support OpenGL
PFD_DOUBLEBUFFER; // Support for double buffering
pfd.iPixelType = PFD_TYPE_RGBA; // Uses an RGBA pixel format
pfd.cColorBits = 32; // 32 bits colors
if (!(m_hDeviceContext=GetDC(m_hWindow)))
throw CException("Unable to create rendering context");
int PixelFormat;
// Do Windows find a matching pixel format ?
if (!(PixelFormat=ChoosePixelFormat(m_hDeviceContext,&pfd)))
throw CException("Unable to create rendering context");
// Set the new pixel format
if(!SetPixelFormat(m_hDeviceContext,PixelFormat,&pfd))
throw CException("Unable to create rendering context");
// Create the OpenGL rendering context
if (!(m_hGLContext=wglCreateContext(m_hDeviceContext)))
throw CException("Unable to create rendering context");
// Activate the rendering context
if(!wglMakeCurrent(m_hDeviceContext,m_hGLContext))
throw CException("Unable to create rendering context");
}
void CMainWindow::InitGL()
{
// Enable 2D texturing
glEnable(GL_TEXTURE_2D);
// Choose a smooth shading model
glShadeModel(GL_SMOOTH);
// Set the clear color to black
glClearColor(0.0, 0.0, 0.0, 0.0);
// Enable the alpha test. This is needed
// to be able to have images with transparent
// parts.
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.0f);
}
void CMainWindow::OnSize(GLsizei width, GLsizei height)
{
// Sets the size of the OpenGL viewport
glViewport(0,0,width,height);
// Select the projection stack and apply
// an orthographic projection
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0,width,height,0.0,-1.0,1.0);
glMatrixMode(GL_MODELVIEW);
}
void CMainWindow::Update(DWORD dwCurrentTime)
{
}
void CMainWindow::Draw()
{
// Clear the buffer
glClear(GL_COLOR_BUFFER_BIT);
// Here goes the drawing code
glBegin(GL_QUADS);
glColor3f(1.0,0.0,0.0); glVertex3i(50,200,0);
glColor3f(0.0,1.0,0.0); glVertex3i(250,200,0);
glColor3f(0.0,0.0,1.0); glVertex3i(250,350,0);
glColor3f(1.0,1.0,1.0); glVertex3i(50,350,0);
glEnd();
glBegin(GL_TRIANGLES);
glColor3f(1.0,0.0,0.0); glVertex3i(400,350,0);
glColor3f(0.0,1.0,0.0); glVertex3i(500,200,0);
glColor3f(0.0,0.0,1.0); glVertex3i(600,350,0);
glEnd();
SwapBuffers(m_hDeviceContext);
}