OpenGL Tutorial Framework
From GPWikiBecause OpenGL is platform independent it is hard to write a single framework for tutorials. Here we present two frameworks: one using the MS Windows API, and and using the platform independent GLFW wrapper. Pieces of code shown in other parts of these tutorials can easily be plugged into these frameworks. Lesson material should be placed in another source file and compiled together with the framework source. Each framework consists of a source file and a header file.
[edit] Windows FrameworkThe Windows framework runs two threads, one manages the Windows message pump, the other runs the 'Render()' function. Please tweak the code if you find a bug, but try to keep it simple, the idea here is not to be bombproof, but easy to play and learn with. [edit] Framework.h
#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>
// Functions
void FlipBuffers();
void Render();
// A struct to hold mouse data
struct MouseInfo
{
int Mx,My;
bool Mleft, Mright;
};
// Globals
extern int RunLevel;
extern bool Keys[256]; // Key monitor
extern MouseInfo Mouse; // Mouse monitor
[edit] OpenGLBase.cpp
// OpenGLBase.cpp - A multi-threaded OpenGL base application for GPWiki tutorials.
// Windows version.
#include "Framework.h"
#pragma comment (lib , "opengl32.lib") // Makes VC link the GL libs,
#pragma comment (lib , "glu32.lib") // other compliers will have to do it manually
// Globals
HINSTANCE gInst;
HWND hGLWin;
HDC GLDC;
int RunLevel = 1;
bool Keys[256]; // Key monitor
MouseInfo Mouse; // Mouse monitor
bool RegisterWin();
bool StartGL(int ScrX, int ScrY, int BPP);
bool RenderProc(LPVOID lpParam);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
DEVMODE ScrRes;
HANDLE hThr;
DWORD Res;
// Set the global instance
gInst=hInstance;
// Store the current screen resolution
EnumDisplaySettings(NULL,ENUM_CURRENT_SETTINGS,&ScrRes);
// Register our Window Class
if(!RegisterWin())
{
MessageBox(NULL,"Register Window Class Failed!","Error",MB_OK | MB_ICONERROR);
return 0;
}
// Start GL Window
if(!StartGL(800,600,32))
{
MessageBox(NULL,"GL Startup Failed!","Error",MB_OK | MB_ICONERROR);
return 0;
}
// Launch rendering thread
hThr=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)RenderProc,0,0,NULL);
if(hThr)
{
RunLevel=1;
// Main message loop:
while(RunLevel)
{
GetMessage(&msg, NULL, 0, 0);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Shutdown and cleanup
// Wait for thread to stop
Res=STILL_ACTIVE;
while(Res==STILL_ACTIVE)
GetExitCodeThread(hThr,&Res);
// Close window
if(hGLWin)
DestroyWindow(hGLWin);
UnregisterClass("OpenGLBaseWin",gInst);
// Restore Original Screen Mode
ChangeDisplaySettings(&ScrRes,CDS_RESET);
return (int) msg.wParam;
}
// Message Handler for our Window
LRESULT CALLBACK GLWinProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch(Msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
// Grab inputs
case WM_KEYDOWN:
Keys[wParam] = TRUE;
return 0;
case WM_KEYUP:
Keys[wParam] = FALSE;
return 0;
case WM_MOUSEMOVE:
Mouse.Mx=LOWORD(lParam);
Mouse.My=HIWORD(lParam);
return 0;
case WM_LBUTTONDOWN:
Mouse.Mleft=TRUE;
return 0;
case WM_LBUTTONUP:
Mouse.Mleft=FALSE;
return 0;
case WM_RBUTTONDOWN:
Mouse.Mright=TRUE;
return 0;
case WM_RBUTTONUP:
Mouse.Mright=FALSE;
return 0;
default:
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
return 0;
}
// Register a Window Class
bool RegisterWin()
{
WNDCLASSEX glWin;
glWin.cbSize=sizeof(WNDCLASSEX);
glWin.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC; // Window has it's own context
glWin.lpfnWndProc = GLWinProc;
glWin.cbClsExtra = 0;
glWin.cbWndExtra = 0;
glWin.hInstance = gInst;
glWin.hIcon = LoadIcon(NULL, IDI_WINLOGO); // Default icon
glWin.hCursor = LoadCursor(NULL, IDC_ARROW); // Default pointer
glWin.hbrBackground = NULL;
glWin.lpszMenuName = NULL;
glWin.lpszClassName = "OpenGLBaseWin";
glWin.hIconSm=NULL;
if(RegisterClassEx(&glWin))
return TRUE;
else
return FALSE;
}
bool StartGL(int ScrX, int ScrY, int BPP)
{
DEVMODE ScrMode;
PIXELFORMATDESCRIPTOR PixFmtReq;
int PixFmt;
HGLRC GLRC;
// Set the screen mode
ZeroMemory(&ScrMode,sizeof(DEVMODE));
ScrMode.dmSize=sizeof(DEVMODE);
ScrMode.dmPelsWidth=ScrX;
ScrMode.dmPelsHeight=ScrY;
ScrMode.dmBitsPerPel=BPP;
ScrMode.dmFields=DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
if(ChangeDisplaySettings(&ScrMode,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
return FALSE;
// Create our window
hGLWin=CreateWindowEx(WS_EX_LEFT,
"OpenGLBaseWin",
"OpenGL Test",
WS_POPUP | WS_VISIBLE,
0,0,ScrX,ScrY,
NULL,NULL,gInst,NULL);
if(hGLWin==NULL)
return FALSE;
// Define pixel format for our window
ZeroMemory(&PixFmtReq,sizeof(PIXELFORMATDESCRIPTOR));
PixFmtReq.nSize=sizeof (PIXELFORMATDESCRIPTOR);
PixFmtReq.nVersion=1;
PixFmtReq.dwFlags= PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
PixFmtReq.iPixelType=PFD_TYPE_RGBA;
PixFmtReq.cColorBits=BPP; // Color depth as specified in arguments
PixFmtReq.cDepthBits=16;
PixFmtReq.iLayerType=PFD_MAIN_PLANE;
// Get the device context
GLDC=GetDC(hGLWin);
if(!GLDC)
return FALSE;
// Match our specified pixel format to device
PixFmt=ChoosePixelFormat(GLDC,&PixFmtReq);
if(PixFmt==0)
return FALSE;
// Set pixel format
if(!SetPixelFormat(GLDC,PixFmt,&PixFmtReq))
return FALSE;
// Create the OpenGL render context and bind to this thread current
GLRC=wglCreateContext(GLDC);
if(!GLRC)
return FALSE;
if(!wglMakeCurrent(GLDC,GLRC))
return FALSE;
// Clear to black
glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT);
SwapBuffers(GLDC);
return TRUE;
}
// The render thread start point
bool RenderProc(LPVOID lpParam)
{
HGLRC glRC;
// Re-aquire the context as we are in a different thread
glRC=wglCreateContext(GLDC);
wglMakeCurrent(GLDC,glRC);
// Here's were we bring in the Render funtion
Render();
RunLevel=0; // Make sure the app stops
return 0;
}
void FlipBuffers()
{
SwapBuffers(GLDC);
}
[edit] GLFW FrameworkThis framework requires the GLFW library and should run on most contemporary platforms. [edit] Framework.h
#include <GL/glfw.h>
// Functions
void FlipBuffers();
void Render();
// A struct to hold mouse data
struct MouseInfo
{
int Mx,My;
bool Mleft, Mright;
};
// Globals
extern int RunLevel;
extern bool Keys[GLFW_KEY_LAST]; // Key monitor
extern MouseInfo Mouse; // Mouse monitor
// Definitions of some windows key codes in terms of GLFW keys (Use
// uppercase characters for characer keys - 'X' is the x key for
// example.
const unsigned int
VK_ESCAPE = GLFW_KEY_ESC,
VK_RETURN = GLFW_KEY_ENTER,
VK_SPACE = GLFW_KEY_SPACE,
VK_UP = GLFW_KEY_UP,
VK_DOWN = GLFW_KEY_DOWN,
VK_RIGHT = GLFW_KEY_RIGHT,
VK_LEFT = GLFW_KEY_LEFT,
VK_HOME = GLFW_KEY_HOME,
VK_END = GLFW_KEY_END,
VK_INSERT = GLFW_KEY_INSERT,
VK_DELETE = GLFW_KEY_DEL;
[edit] GLFWBase.cpp
// GLFWBase.cpp - platform independent version of the gpwiki.org
// OpenGL framework.
#include <fstream>
#include "Framework.h"
// Globals
int RunLevel = 1;
bool Keys[GLFW_KEY_LAST] = {false}; // Key monitor
MouseInfo Mouse; // Mouse monitor
// Initializationa
void InitWindow(int ScrX, int ScrY, int BPP);
// Event callback functions
void KeyCallback(int key, int action);
void MouseButtonCallback(int button, int action);
void MousePosCallback(int x, int y);
int main()
{
int retval = 0;
try
{
// Initialize the window
InitWindow(800, 600, 32);
// Pass control to the render function
Render();
}
catch (const char* error)
{
// Report an error
std::ofstream error_file("GL_ERROR.txt");
error_file << "Caught exception:\n " << error << '\n';
retval = 1;
}
// Shut down GLFW
glfwTerminate();
// Return the appropriate value
return retval;
}
// Initialize the window, can throw if something goes wrong.
void InitWindow(int ScrX, int ScrY, int BPP)
{
// Initialize the GLFW library
if (glfwInit() != GL_TRUE)
throw "Failed to initialize GLFW.";
// Create a window (8-bit depth-buffer, no alpha and stencil buffers, windowed)
if (glfwOpenWindow(ScrX, ScrY, BPP/3, BPP/3, BPP/3, 0, 8, 0, GLFW_WINDOW) != GL_TRUE)
throw "Failed to open window.";
// Give the window a title
glfwSetWindowTitle("GPWiki OpenGL Tutorial");
// Register event callbacks
glfwSetKeyCallback(KeyCallback);
glfwSetMouseButtonCallback(MouseButtonCallback);
glfwSetMousePosCallback(MousePosCallback);
// Set the projection matrix to a normal frustum with a max depth of 500
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float aspect_ratio = ((float)ScrX) / ScrY;
glFrustum(.5, -.5, -.5 * aspect_ratio, .5 * aspect_ratio, 1, 500);
glMatrixMode(GL_MODELVIEW);
}
// Wrapper for buffer swapping
void FlipBuffers()
{
glfwSwapBuffers();
// glfwSwapBuffers also automatically polls for input
// If the window was closed we quit
if (glfwGetWindowParam(GLFW_OPENED) != GL_TRUE)
RunLevel = 0;
}
// Handle keys - updates the Keys array
void KeyCallback(int key, int action)
{
Keys[key] = (action == GLFW_PRESS);
if (Keys[GLFW_KEY_ESC])
RunLevel = 0;
}
// Handle mouse button events - updates the Mouse structure
void MouseButtonCallback(int button, int action)
{
if (button == GLFW_MOUSE_BUTTON_LEFT)
Mouse.Mleft = (action == GLFW_PRESS);
else if (button == GLFW_MOUSE_BUTTON_RIGHT)
Mouse.Mright = (action == GLFW_PRESS);
}
// Handle mouse motion - updates the Mouse structure
void MousePosCallback(int x, int y)
{
Mouse.Mx = x;
Mouse.My = y;
}
[edit] Render TemplateHere's a template for the Render.cpp file. Code in this file should be platform independent. [edit] Render.cpp
// This code will be executed by the OpenGL base app's render thread
#include "Framework.h"
void Render(void)
{
// Set the background to black
glClearColor(0,0,0,0);
glClear(GL_COLOR_BUFFER_BIT);
// This loop will run until Esc is pressed
while(RunLevel)
{
if(Keys[VK_ESCAPE]) // Esc Key
RunLevel=0;
// Do OpenGL stuff here
// We're using double buffers, so we need to swap to see our stuff
FlipBuffers();
}
}
Categories: Tutorial | C | OpenGL |


