I needed a very very simple OpenGL interface for drawing graphics on Win32 windows. My aim was/is to simply put a pixel on a window. If you are able to put a pixel, you can draw anything, isn't it? For this purpose, I wrote the class below: GLWindow.h Code: #ifndef GLWINDOW_H #define GLWINDOW_H #include <Windows.h> #include <gl/GL.h> class GLWindow { public: GLWindow(HWND hWindowHandle, UINT uiPixelWidth, UINT uiPixelHeight); ~GLWindow(){}; // Thrown exception values and their meanings: // 1: Failed: GetDC() // 2: Failed: ChoosePixelFormat() // 3: Failed: SetPixelFormat() // 4: Failed: wglCreateContext() // 5: Failed: wglMakeCurrent() void Initialize() throw(int); // Thrown exception values and their meanings: // 1: Failed: wglMakeCurrent() // 2: Failed: wglDeleteContext() void Destroy() throw(int); void SetColor(GLubyte ubRed, GLubyte ubGreen, GLubyte ubBlue) const; void PutPixel(UINT x, UINT y) const; void ClearScreen() const; // Thrown exception values and their meanings: // 1: Failed: SwapBuffers() void SwapBuffers() const throw(int); protected: double m_dbWidth; double m_dbHeight; HWND m_hWnd; HDC m_hDC; HGLRC m_hGLRC; }; #endif GLWindow.cpp Code: #include "GLWindow.h" #include <gl/GL.h> GLWindow::GLWindow(HWND hWindowHandle, UINT uiPixelWidth, UINT uiPixelHeight) { m_hWnd = hWindowHandle; m_dbWidth = static_cast<double>(uiPixelWidth); m_dbHeight = static_cast<double>(uiPixelHeight); } void GLWindow::Initialize() throw(int) { m_hDC = GetDC(m_hWnd); if (m_hDC == NULL) throw(1); 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 = 24; Pfd.cDepthBits = 16; Pfd.iLayerType = PFD_MAIN_PLANE; int iFormat = ChoosePixelFormat(m_hDC, &Pfd); if (iFormat == 0) throw(2); if (SetPixelFormat(m_hDC, iFormat, &Pfd) == FALSE) throw(3); m_hGLRC = wglCreateContext(m_hDC); if (m_hGLRC == NULL) throw(4); if (wglMakeCurrent(m_hDC, m_hGLRC) == FALSE) throw(5); //////////////// glEnable(GL_DEPTH_TEST); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glPushMatrix(); glLoadIdentity(); glScaled(2.0 / m_dbWidth, 2.0 / m_dbHeight, 1.0); glTranslated(-m_dbWidth / 2.0, -m_dbHeight / 2.0, 0.0); glBegin(GL_POINTS); } void GLWindow::Destroy() throw(int) { glEnd(); glPopMatrix(); glFlush(); if (wglMakeCurrent(m_hDC, NULL) == FALSE) throw(1); if (wglDeleteContext(m_hGLRC) == FALSE) throw(2); } void GLWindow::SetColor(GLubyte ubRed, GLubyte ubGreen, GLubyte ubBlue) const { glColor3ub(ubRed, ubGreen, ubBlue); } void GLWindow::PutPixel(UINT x, UINT y) const { glVertex3i(static_cast<GLint>(x), static_cast<GLint>(y), 0); } void GLWindow::ClearScreen() const { glEnd(); glClearColor(0.0f, 0.0f, 0.0f, 1.0f ); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glBegin(GL_POINTS); } void GLWindow::SwapBuffers() const throw(int) { glEnd(); if (::SwapBuffers(m_hDC) == FALSE) throw(1); glBegin(GL_POINTS); } Yes, this class is only for printing a custom colored pixel on a window, and it does nothing else. I know how vast the capabilities of OpenGL is, but my requirement was as simple as this. The below is my code that uses this class: main.cpp Code: #include <Windows.h> #include <string> #include <gl/GL.h> #include <math.h> #include "GLWindow.h" HWND g_hWnd; const UINT WIN_WIDTH = 600; const UINT WIN_HEIGHT = 400; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { std::string ClassName("MyWindowClass"); WNDCLASSEX wcx; wcx.cbSize = sizeof(wcx); wcx.style = 0; wcx.lpfnWndProc = DefWindowProc; wcx.cbClsExtra = 0; wcx.cbWndExtra = 0; wcx.hInstance = hInstance; wcx.hIcon = NULL; wcx.hCursor = LoadCursor(NULL, IDC_ARROW); wcx.hbrBackground = (HBRUSH) 2; wcx.lpszMenuName = NULL; wcx.lpszClassName = ClassName.c_str(); wcx.hIconSm = NULL; ATOM Atom = RegisterClassEx(&wcx); std::string WindowName("GLWindow Test"); g_hWnd = CreateWindowEx(0, ClassName.c_str(), WindowName.c_str(), WS_OVERLAPPED | WS_VISIBLE | WS_TILEDWINDOW, 1300, 650, WIN_WIDTH, WIN_HEIGHT, HWND_DESKTOP, (HMENU) NULL, hInstance, (LPVOID) NULL); GLWindow glWnd(g_hWnd, WIN_WIDTH, WIN_HEIGHT); glWnd.Initialize(); /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// //////////////// MY CODE STARTS HERE ////////////////////////////////// // Draw a diagonal line for (UINT x=0, y; x<WIN_WIDTH; x++) { glWnd.SetColor(255, (GLbyte) (255.0 * x / (double) WIN_WIDTH), (GLbyte) (255.0 * x / (double) WIN_WIDTH)); y = ((double) WIN_HEIGHT / WIN_WIDTH) * x; glWnd.PutPixel(x, y); } // Display it on the screen glWnd.SwapBuffers(); // Draw a quarter of a circle UINT x, y; for (double teta=0.0; teta<1.5708 /* PI/2 */; teta+=0.001) { x = WIN_HEIGHT * cos(teta); y = WIN_HEIGHT * sin(teta); glWnd.SetColor( (GLbyte) (150.0 * teta), (GLbyte) (256.0 - 150.0 * teta), 128.0); glWnd.PutPixel(x, y); } // Switch between line and circle several times for (UINT i=0; i<10; i++) { glWnd.SwapBuffers(); Sleep(1000); } // Close OpenGL engine glWnd.Destroy(); // Quit the program PostQuitMessage(0); /////////////// MY CODE ENDS HERE ///////////////////////////////////// /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// MSG Msg; const UINT wMsgFilterMin = 0; const UINT wMsgFilterMax = 0; while (GetMessage(&Msg, (HWND) NULL, wMsgFilterMin, wMsgFilterMax) > 0) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return 0; } My code above (which is in the main.cpp file) works very fine. Try it yourself if you have time. Now, I want two things from you guys. Criticize my code. Do you see any flaw? Would it work in all cases (except for the problem I stated in (2))? I feel that this code won't work when I use two objects of this class under the same process. And I feel that every GLWindow object must check if they are the "current", if not, they must make themselves the "current" by somehow using the function wglMakeCurrent(). But I don't know how to do it, because I don't know much about OpenGL at all. How do I do it?
Dudes, I did that! I kept an ID for each class instance, and before calling any OpenGL function, I check if the GLWindow object is the "current" object, if not I made it current with the wglMakeCurrent() function. GLWindow.h Code: #ifndef GLWINDOW_H #define GLWINDOW_H #include <Windows.h> #include <gl/GL.h> class GLWindow { public: GLWindow(HWND hWindowHandle, UINT uiPixelWidth, UINT uiPixelHeight); ~GLWindow(){}; // Thrown exception values and their meanings: // 1: Failed: GetDC() // 2: Failed: ChoosePixelFormat() // 3: Failed: SetPixelFormat() // 4: Failed: wglCreateContext() // 5: Failed: wglMakeCurrent() void Initialize() throw(int); // Thrown exception values and their meanings: // 1: Failed: wglMakeCurrent() // 2: Failed: wglDeleteContext() void Destroy() throw(int); void SetColor(GLubyte ubRed, GLubyte ubGreen, GLubyte ubBlue) const; void PutPixel(UINT x, UINT y) const; void ClearScreen() const; // Thrown exception values and their meanings: // 1: Failed: SwapBuffers() void SwapBuffers() const throw(int); protected: double m_dbWidth; double m_dbHeight; HWND m_hWnd; HDC m_hDC; HGLRC m_hGLRC; static unsigned int LAST_OBJECT_ID; static unsigned int ACTIVE_OBJECT_ID; unsigned int m_uiObjectId; bool MakeCurrent() const; }; #endif GLWindow.cpp Code: #include "GLWindow.h" #include <gl/GL.h> unsigned int GLWindow::LAST_OBJECT_ID = 0; unsigned int GLWindow::ACTIVE_OBJECT_ID = 0; GLWindow::GLWindow(HWND hWindowHandle, UINT uiPixelWidth, UINT uiPixelHeight) { m_hWnd = hWindowHandle; m_dbWidth = static_cast<double>(uiPixelWidth); m_dbHeight = static_cast<double>(uiPixelHeight); LAST_OBJECT_ID++; m_uiObjectId = LAST_OBJECT_ID; } void GLWindow::Initialize() throw(int) { m_hDC = GetDC(m_hWnd); if (m_hDC == NULL) throw(1); 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 = 24; Pfd.cDepthBits = 16; Pfd.iLayerType = PFD_MAIN_PLANE; int iFormat = ChoosePixelFormat(m_hDC, &Pfd); if (iFormat == 0) throw(2); if (SetPixelFormat(m_hDC, iFormat, &Pfd) == FALSE) throw(3); m_hGLRC = wglCreateContext(m_hDC); if (m_hGLRC == NULL) throw(4); if (this->MakeCurrent() == FALSE) throw(5); //////////////// glEnable(GL_DEPTH_TEST); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glPushMatrix(); glLoadIdentity(); glScaled(2.0 / m_dbWidth, 2.0 / m_dbHeight, 1.0); glTranslated(-m_dbWidth / 2.0, -m_dbHeight / 2.0, 0.0); glBegin(GL_POINTS); } void GLWindow::Destroy() throw(int) { glEnd(); glPopMatrix(); glFlush(); if (wglMakeCurrent(m_hDC, NULL) == FALSE) throw(1); ACTIVE_OBJECT_ID = 0; if (wglDeleteContext(m_hGLRC) == FALSE) throw(2); } void GLWindow::SetColor(GLubyte ubRed, GLubyte ubGreen, GLubyte ubBlue) const { this->MakeCurrent(); glColor3ub(ubRed, ubGreen, ubBlue); } void GLWindow::PutPixel(UINT x, UINT y) const { this->MakeCurrent(); glVertex3i(static_cast<GLint>(x), static_cast<GLint>(y), 0); } void GLWindow::ClearScreen() const { this->MakeCurrent(); glEnd(); glClearColor(0.0f, 0.0f, 0.0f, 1.0f ); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glBegin(GL_POINTS); } void GLWindow::SwapBuffers() const throw(int) { this->MakeCurrent(); glEnd(); if (::SwapBuffers(m_hDC) == FALSE) throw(1); glBegin(GL_POINTS); } bool GLWindow::MakeCurrent() const { bool bReturn; if (ACTIVE_OBJECT_ID == m_uiObjectId) return true; bReturn = wglMakeCurrent(m_hDC, m_hGLRC); if (bReturn) ACTIVE_OBJECT_ID = m_uiObjectId; return bReturn; } main.cpp Code: #include <Windows.h> #include <string> #include <gl/GL.h> #include <math.h> #include "GLWindow.h" HWND g_hWnd; const UINT WIN_WIDTH = 600; const UINT WIN_HEIGHT = 400; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { std::string ClassName("MyWindowClass"); WNDCLASSEX wcx; wcx.cbSize = sizeof(wcx); wcx.style = 0; wcx.lpfnWndProc = DefWindowProc; wcx.cbClsExtra = 0; wcx.cbWndExtra = 0; wcx.hInstance = hInstance; wcx.hIcon = NULL; wcx.hCursor = LoadCursor(NULL, IDC_ARROW); wcx.hbrBackground = (HBRUSH) 1; wcx.lpszMenuName = NULL; wcx.lpszClassName = ClassName.c_str(); wcx.hIconSm = NULL; ATOM Atom = RegisterClassEx(&wcx); std::string WindowName("GLWindow Test"); g_hWnd = CreateWindowEx(0, ClassName.c_str(), WindowName.c_str(), WS_OVERLAPPED | WS_VISIBLE | WS_TILEDWINDOW, 1300, 650, WIN_WIDTH, WIN_HEIGHT, HWND_DESKTOP, (HMENU) NULL, hInstance, (LPVOID) NULL); const UINT STATIC1_WIDTH = WIN_WIDTH / 2; const UINT STATIC1_HEIGHT = WIN_HEIGHT / 2; HWND hWndStatic1 = CreateWindowEx( 0, "STATIC", "My Static #1", WS_VISIBLE | WS_CHILD, 0, 0, WIN_WIDTH/2, WIN_HEIGHT/2, g_hWnd, (HMENU) NULL, hInstance, (LPVOID) NULL); GLWindow glWnd1(hWndStatic1, WIN_WIDTH/2, WIN_HEIGHT/2); glWnd1.Initialize(); const UINT STATIC2_WIDTH = WIN_WIDTH / 2; const UINT STATIC2_HEIGHT = WIN_HEIGHT / 2; HWND hWndStatic2 = CreateWindowEx( 0, "STATIC", "My Static #2", WS_VISIBLE | WS_CHILD, WIN_WIDTH/2, WIN_HEIGHT/2, WIN_WIDTH/2, WIN_HEIGHT/2, g_hWnd, (HMENU) NULL, hInstance, (LPVOID) NULL); GLWindow glWnd2(hWndStatic2, WIN_WIDTH/2, WIN_HEIGHT/2); glWnd2.Initialize(); /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// //////////////// MY CODE STARTS HERE ////////////////////////////////// //////////////// STATIC CONTROL #1 //////////////////////////////////// // Draw a diagonal line for (UINT x=0, y; x<STATIC1_WIDTH; x++) { glWnd1.SetColor(255, (GLbyte) (255.0 * x / (double) STATIC1_WIDTH), (GLbyte) (255.0 * x / (double) STATIC1_WIDTH)); y = ((double) STATIC1_HEIGHT / STATIC1_WIDTH) * x; glWnd1.PutPixel(x, y); } // Display it on the screen glWnd1.SwapBuffers(); // Draw a quarter of a circle UINT x, y; for (double teta=0.0; teta<1.5708 /* PI/2 */; teta+=0.001) { x = STATIC1_HEIGHT * cos(teta); y = STATIC1_HEIGHT * sin(teta); glWnd1.SetColor((GLbyte) (150.0 * teta), (GLbyte) (256.0 - 150.0 * teta), 128.0); glWnd1.PutPixel(x, y); } //////////////// STATIC CONTROL #2 //////////////////////////////////// // Draw a rectangle for (UINT x=STATIC2_WIDTH/4; x<3*STATIC2_WIDTH/4; x++) { glWnd2.SetColor(255, 128, 0); glWnd2.PutPixel(x, STATIC2_HEIGHT/4); glWnd2.SetColor(0, 128, 255); glWnd2.PutPixel(x, 3*STATIC2_HEIGHT/4); } for (UINT y=STATIC2_HEIGHT/4; y<3*STATIC2_HEIGHT/4; y++) { glWnd2.SetColor(192, 128, 0); glWnd2.PutPixel( STATIC2_WIDTH/4, y); glWnd2.SetColor(0, 128, 192); glWnd2.PutPixel(3*STATIC2_WIDTH/4, y); } // Display it on the screen glWnd2.SwapBuffers(); // Draw a filled rectangle for (UINT x=STATIC2_WIDTH/4; x<3*STATIC2_WIDTH/4; x++) { for (UINT y=STATIC2_HEIGHT/4; y<3*STATIC2_HEIGHT/4; y++) { glWnd2.SetColor(rand() % 256, rand() % 256, rand() % 256); glWnd2.PutPixel(x, y); } } /////////////// FINALIZE ////////////////////////////////////////////// // Switch between line and circle several times for (UINT i=0; i<10; i++) { glWnd1.SwapBuffers(); glWnd2.SwapBuffers(); Sleep(1000); } // Close OpenGL engines glWnd1.Destroy(); glWnd2.Destroy(); // Quit the program PostQuitMessage(0); /////////////// MY CODE ENDS HERE ///////////////////////////////////// /////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// MSG Msg; const UINT wMsgFilterMin = 0; const UINT wMsgFilterMax = 0; while (GetMessage(&Msg, (HWND) NULL, wMsgFilterMin, wMsgFilterMax) > 0) { TranslateMessage(&Msg); DispatchMessage(&Msg); } return 0; } In this code, I'm running OpenGL on two independent STATIC windows. The code and its exe file are attached.