OpenGL:Tutorials:Tutorial Framework:Base

From GPWiki
Jump to: navigation, search

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.


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();
  }
}