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 |