OpenGL:Tutorials:Tutorial Framework:Base
From GPWiki
Both of our OpenGL frameworks consist of a source file and a header file. Lesson material should be placed in another platform neutral source file and compiled together with the framework source.
Contents |
Windows Framework
The 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.
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 volatile RunLevel; extern bool Keys[256]; // Key monitor extern MouseInfo Mouse; // Mouse monitor
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 volatile 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); }
GLFW Framework
This framework requires the GLFW library and should run on most contemporary platforms.
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;
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; }
Render Template
Here's a template for the Render.cpp file. Code in this file should be platform independent.
This one just displays a black screen till 'Esc' is pressed.
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(); } }