René Nyffenegger's collection of things on the web
René Nyffenegger on Oracle - Most wanted - Feedback -
 

Open GL for Windows

The OpenGlWin class

At the heart is the OpenGlWin class. It is an Abstract Base Class whose Draw Method must be overridden. Download the project here.

OpenGlWin.h

#ifndef __OPEN_GL_H__
#define __OPEN_GL_H__

#include <windows.h>

#include <gl/gl.h> 
#include <gl/glu.h>

#include "ChildWindow.h"

class OpenGlWin : public ChildWindow {
  public:
    OpenGlWin(Wnd& parent, int x, int y, int width, int height, int bits=24);
   
    virtual ~OpenGlWin();

    virtual void Draw() = 0;

    void MakeCurrent(bool b=true);

    void SwapBuffers();

    virtual void Move(int x, int y, int width, int height, bool repaint=true);

  private:
    void SetPixelFormat(int bits);

    void Error();

    HDC     hdc_  ;   // Private GDI Device Context
    HGLRC   hglrc_;   // Permanent Rendering Context
};

#endif

OpenGlWin.cpp

#include "OpenGlWin.h"

/*

  Performs hidden surface removal using the Depth Test:

glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);

  */


LRESULT CALLBACK OpenGlWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {

  OpenGlWin* glwin=reinterpret_cast<OpenGlWin*>(::GetWindowLong(hWnd, GWL_USERDATA));

  switch (uMsg) {
    case WM_PAINT: {
      glwin->Draw();
      break;
    }
    case WM_ERASEBKGND:
      return 1; // Do not erase background between frames
    break;
  }

  return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

OpenGlWin::OpenGlWin(Wnd& parent, int x, int y, int width, int height, int bits) :
  ChildWindow(parent), hdc_(0) , hglrc_(0) {

  WNDCLASSEXW  wc;

  wc.cbSize         = sizeof(wc);
  wc.style          = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  wc.lpfnWndProc    = OpenGlWndProc;
  wc.cbClsExtra     = 0; 
  wc.cbWndExtra     = 0; 
  wc.hInstance      = 0;
  wc.hIcon          = 0; // LoadIcon  (0, IDI_WINLOGO);
  wc.hCursor        = LoadCursor(0, IDC_ARROW);
  wc.hbrBackground  = 0; // No Background Required For GL
  wc.lpszMenuName   = 0; // No Menu
  wc.lpszClassName  = L"OpenGlWnd";
  wc.hIconSm        = 0;

  if (!RegisterClassExW(&wc)) {
    MessageBoxA(0,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    return;
  }

  Create(L"OpenGlWnd", x, y, width, height, WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
  Show();

  hdc_ = GetDC(hwnd_);

  SetPixelFormat(bits);

  if (!(hglrc_= ::wglCreateContext(hdc_)))  {
    Error();
    throw "wglCreateContext";
  }
}

OpenGlWin::~OpenGlWin() {
  ::wglDeleteContext(hglrc_);
  MakeCurrent(false);
}


void OpenGlWin::SetPixelFormat(int bits) {
  PIXELFORMATDESCRIPTOR pfd;
  
  ::ZeroMemory(&pfd,sizeof(pfd));
  pfd.nSize      = sizeof(pfd);
  pfd.nVersion   = 1;
  pfd.dwFlags    = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |  PFD_DOUBLEBUFFER;
  pfd.iPixelType = PFD_TYPE_RGBA;
  pfd.cColorBits = bits;
  pfd.cDepthBits = 32;
  pfd.iLayerType = PFD_MAIN_PLANE;
  
  GLuint pixelFormat;
  if (! (pixelFormat = ::ChoosePixelFormat(hdc_,&pfd))) {
    throw "ChoosePixelFormat";
  }
  if (! ::SetPixelFormat(hdc_,pixelFormat,&pfd)) {
    throw "SetPixelFormat";
  }
}


void OpenGlWin::MakeCurrent(bool b) {
  if (b) ::wglMakeCurrent(hdc_,hglrc_);
  else   ::wglMakeCurrent(0,0);
}

void OpenGlWin::Error() {
  GLenum errorno = glGetError() ;
  if (errorno != GL_NO_ERROR)
    MessageBoxA(0,(const char*)gluErrorString(errorno),0,0) ;
}

/* A double buffered DC is used. anything drawn to the DC actually goes to 
   a non-visible space in memory. This behaviour is set with the PFD_DOUBLEBUFFER 
   flag (see SetPixelFormat) */ 
void OpenGlWin::SwapBuffers() {
  ::SwapBuffers(hdc_);
}

void OpenGlWin::Move(int x, int y, int width, int height, bool repaint) {
  ChildWindow::Move(x, y, width, height, false);

  MakeCurrent();

  glViewport(0,0,width,height);		// Reset The Current Viewport

	glMatrixMode(GL_PROJECTION);   // Select The Projection Matrix
	glLoadIdentity();							// Reset The Projection Matrix
  //glOrtho2f(-1,1,-1,1);

	// Calculate The Aspect Ratio Of The Window
	gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glLoadIdentity();						

  MakeCurrent(false);
}

The Main Program

Main.cpp

#include "OverlappedWindow.h"

#include "WndApplication.h"
#include "OpenGlWin_1.h"

OpenGlWin_1* oglw = 0;

LRESULT CALLBACK WndProc(
    HWND   hWnd,
    UINT   msg,
    WPARAM wParam,
    LPARAM lParam ) {

  switch( msg ) {
    case WM_DESTROY:
      PostQuitMessage(0);
    break;

    case WM_SIZE: {
      int width = LOWORD(lParam);
      int height= HIWORD(lParam);

      oglw->Move(20,20,width-40,height-40);
      break;
    }

    default:
      return DefWindowProc( hWnd, msg, wParam, lParam);
  } 
  return 0;
}


int WINAPI WinMain(
             HINSTANCE hInstance, HINSTANCE hPrevInstance,
             LPSTR lpCmdLine, int nCmdShow) {


  OverlappedWindow ow(WndProc, L"Open GL", 50, 100, 300, 300, 
    0);//WS_CLIPCHILDREN);// | WS_CLIPSIBLINGS);

  oglw = new OpenGlWin_1(ow,20,20,200,200);

  ow.Show();

  return EnterMsgLoop();
  delete oglw;  // NEVER REACHED!
}

The derived class

>OpenGlWin_1.h

#ifndef __OPEN_GL_WIN_1__
#define __OPEN_GL_WIN_1__

#include "OpenGlWin.h"


class OpenGlWin_1 : public OpenGlWin {
public:
  OpenGlWin_1(Wnd& parent, int x, int y, int width, int height);
  virtual void Draw();
};


#endif

OpenGlWin_1.cpp

#include "OpenGlWin_1.h"

OpenGlWin_1::OpenGlWin_1(Wnd& parent, int x, int y, int width, int height) :
  OpenGlWin(parent, x, y, width, height) {
}


void OpenGlWin_1::Draw() {
  MakeCurrent();

  glClearColor(0.0f, 0.0f, 0.0f, 0.0f) ;
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer

  glColor3f(1.0, 1.0, 1.0);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
 //   glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);

    glBegin(GL_POLYGON);
            glVertex2f(-0.5, -0.5);
            glVertex2f(-0.5, 0.5);
            glVertex2f(0.5, 0.5);
            glVertex2f(0.5, -0.5);
    glEnd();
    glFlush();

  SwapBuffers();
  MakeCurrent(false);
}