Alright, we have seen an OpenGL in C/C++ using GLUT but it is rarely used. Most of the people prefer Win32 to write an OpenGL application, mostly games.
Win32 is still the best game development platform for PC rather than other languages, such as .NET. But to write games using only Win32 is a lot of overhead, so you can use Simple DirectMedia Layer (SDL) library.
To get started with OpenGL using GLUT, read this article.
See the Wikipedia page of Win32 API also: https://en.wikipedia.org/wiki/Windows_API
To get started with Win32, go through these articles -
First, start Visual Studio and create Win32 Project from a Visual C++ list and give a name to the project. I have created OpenGL_Win32.
Once you create a project, you can see some predefined code in the editor of the file (OpenGL_Win32.cpp).
- #include "stdafx.h"
- #include "OpenGL_Win32.h"
-
- #define MAX_LOADSTRING 100
-
-
- HINSTANCE hInst;
- TCHAR szTitle[MAX_LOADSTRING];
- TCHAR szWindowClass[MAX_LOADSTRING];
-
-
- ATOM MyRegisterClass(HINSTANCE hInstance);
- BOOL InitInstance(HINSTANCE, int);
- LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
- INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
-
- int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
- _In_opt_ HINSTANCE hPrevInstance,
- _In_ LPTSTR lpCmdLine,
- _In_ int nCmdShow)
- {
- UNREFERENCED_PARAMETER(hPrevInstance);
- UNREFERENCED_PARAMETER(lpCmdLine);
-
-
- MSG msg;
- HACCEL hAccelTable;
-
-
- LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
- LoadString(hInstance, IDC_WIN32PROJECT1, szWindowClass, MAX_LOADSTRING);
- MyRegisterClass(hInstance);
-
-
- if (!InitInstance (hInstance, nCmdShow))
- {
- return FALSE;
- }
-
- hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32PROJECT1));
-
-
- while (GetMessage(&msg, NULL, 0, 0))
- {
- if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
-
- return (int) msg.wParam;
- }
-
-
-
-
-
-
-
-
- ATOM MyRegisterClass(HINSTANCE hInstance)
- {
- WNDCLASSEX wcex;
-
- wcex.cbSize = sizeof(WNDCLASSEX);
-
- wcex.style = CS_HREDRAW | CS_VREDRAW;
- wcex.lpfnWndProc = WndProc;
- wcex.cbClsExtra = 0;
- wcex.cbWndExtra = 0;
- wcex.hInstance = hInstance;
- wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32PROJECT1));
- wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
- wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
- wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32PROJECT1);
- wcex.lpszClassName = szWindowClass;
- wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
-
- return RegisterClassEx(&wcex);
- }
-
-
-
-
-
-
-
-
-
-
-
- BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
- {
- HWND hWnd;
-
- hInst = hInstance;
-
- hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
- CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
-
- if (!hWnd)
- {
- return FALSE;
- }
-
- ShowWindow(hWnd, nCmdShow);
- UpdateWindow(hWnd);
-
- return TRUE;
- }
-
-
-
-
-
-
-
-
-
-
-
- LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
- {
- int wmId, wmEvent;
- PAINTSTRUCT ps;
- HDC hdc;
-
- switch (message)
- {
- case WM_COMMAND:
- wmId = LOWORD(wParam);
- wmEvent = HIWORD(wParam);
-
- switch (wmId)
- {
- case IDM_ABOUT:
- DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
- break;
- case IDM_EXIT:
- DestroyWindow(hWnd);
- break;
- default:
- return DefWindowProc(hWnd, message, wParam, lParam);
- }
- break;
- case WM_PAINT:
- hdc = BeginPaint(hWnd, &ps);
-
- EndPaint(hWnd, &ps);
- break;
- case WM_DESTROY:
- PostQuitMessage(0);
- break;
- default:
- return DefWindowProc(hWnd, message, wParam, lParam);
- }
- return 0;
- }
-
-
- INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
- {
- UNREFERENCED_PARAMETER(lParam);
- switch (message)
- {
- case WM_INITDIALOG:
- return (INT_PTR)TRUE;
-
- case WM_COMMAND:
- if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
- {
- EndDialog(hDlg, LOWORD(wParam));
- return (INT_PTR)TRUE;
- }
- break;
- }
- return (INT_PTR)FALSE;
- }
We need the above code so we don't need to write a code for creating a window.
First add the OpenGL headers.
- #include<stdio.h>
- #include<GL/gl.h>
- #include<GL/glu.h>
Then, we need to tell the compiler to link the OpenGL libraries.
- #pragma comment(lib, "opengl32.lib")
- #pragma comment(lib, "glu32.lib")
I'm also using the following variables to rotate the OpenGL objects.
- float angle = 0.0f;
- float X_angle = 1.0f, Y_angle = 0.0f, Z_angle = 0.0f;
Now, we have a window, so we need to create an OpenGL drawing surface on that window.
To do this, we need to set some pixel formats to that window.The pixel format will allow to enable the drawing of an OpenGL elements on it. First, declare the variable of PIXELFORMATDESCRIPTOR and set its fields. It has 26 fields. Most of the bits you can keep 0, but the flags, type, and size must be initialized.
Following is the function that returns the pixel format.
-
- PIXELFORMATDESCRIPTOR GetPixelFormatDescriptor()
- {
- PIXELFORMATDESCRIPTOR pfd;
-
- pfd.bReserved = 0;
- pfd.cAccumAlphaBits = 0;
- pfd.cAccumBits = 0;
- pfd.cAccumBlueBits = 0;
- pfd.cAccumGreenBits = 0;
- pfd.cAccumRedBits = 0;
- pfd.cAlphaBits = 0;
- pfd.cAlphaShift = 0;
- pfd.cAuxBuffers = 0;
- pfd.cBlueBits = 0;
- pfd.cBlueShift = 0;
- pfd.cColorBits = 32;
- pfd.cDepthBits = 32;
- pfd.cGreenBits = 0;
- pfd.cGreenShift = 0;
- pfd.cRedBits = 0;
- pfd.cRedShift = 0;
- pfd.cStencilBits = 0;
- pfd.dwDamageMask = 0;
- pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
- pfd.dwLayerMask = 0;
- pfd.dwVisibleMask = 0;
- pfd.iLayerType = 0;
- pfd.iPixelType = PFD_TYPE_RGBA;
- pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
- pfd.nVersion = 1;
-
- return pfd;
- }
The
dwFlags are set to
PFD_SUPPORT_OPENGL, to support OpenGL,
PFD_DRAW_TO_WINDOW, drawing directly on a window rather than any other static control.
The iPixelType is of type RGBA.
Once we get our pixel format, we need to initialize our OpenGL on current window handler. To do this, we need the device context of the current window handler, same as we did in
Basic Drawing.
Once you get the device context, get the pixel format descriptor by calling the above function. Choose the pixel format whether it supports the current device context or not by calling ChoosePixelFormat(HDC, PIXELFORMATDESCRIPTOR*) function.
If it supports, then set that pixel format using SetPixelFormat(HDC, int, PIXELFORMATDESCRIPTOR*) function to current device context by getting the pixel format by above function.
Now, you have a window that supports RGBA pixel formats. To create an OpenGL context surface for current device, call the wglCreateContext(HDC) function. wglCreateContext(HDC) returns the handle to the graphics library resource(HGLRC) from the provided device context.
To enable the current device to support OpenGL, call wglMakeCurrent(HDC, HGLRC) function; it returns true or false values. Following is the function that initializes an OpenGL on current HWND.
-
- BOOL InitOpenGL(HWND hWnd)
- {
- PIXELFORMATDESCRIPTOR pfd;
- int pixelFormat;
- HGLRC glrc;
- HDC hdc;
-
- hdc = GetDC(hWnd);
-
- if (hdc == NULL) {
- MessageBox(hWnd, TEXT("Error: Can't Get Device Context for Window"), TEXT("Error"),
- MB_OK | MB_ICONERROR);
- return FALSE;
- }
-
- pfd = GetPixelFormatDescriptor();
-
-
- pixelFormat = ChoosePixelFormat(hdc, &pfd);
-
- if (pixelFormat == 0) {
- MessageBox(hWnd, TEXT("Error: Can't Choose Pixel Format"), TEXT("Error"),
- MB_OK | MB_ICONERROR);
- ReleaseDC(hWnd, hdc);
- return FALSE;
- }
-
-
- pixelFormat = SetPixelFormat(hdc, pixelFormat, &pfd);
-
- if (pixelFormat == 0) {
- MessageBox(hWnd, TEXT("Error: Can't Set The Pixel Format"), TEXT("Error"),
- MB_OK | MB_ICONERROR);
- ReleaseDC(hWnd, hdc);
- return FALSE;
- }
-
-
- glrc = wglCreateContext(hdc);
-
- if (glrc == NULL) {
- MessageBox(hWnd, TEXT("Error: Can't Create GL Context"), TEXT("Error"),
- MB_OK | MB_ICONERROR);
- ReleaseDC(hWnd, hdc);
- return FALSE;
- }
-
-
- if (!wglMakeCurrent(hdc, glrc)) {
- MessageBox(hWnd, TEXT("Error: Can't Make Current GL Context"), TEXT("Error"),
- MB_OK | MB_ICONERROR);
- wglDeleteContext(glrc);
- ReleaseDC(hWnd, hdc);
- return FALSE;
- }
- }
To update the screen, we have used Reshape() function in GLUT. It's same as here, you just need to get the size of the window.
-
- void GLResizeWindow(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
- {
- RECT rect;
- GetClientRect(hwnd, &rect);
-
- GLsizei width = rect.right - rect.left;
- GLsizei height = rect.bottom - rect.top;
-
- glViewport(0, 0, width, height);
- gluPerspective(45, 1.0*(width/height), 1.0, 1000);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- }
We also used a texture in our OpenGL progams.
-
- GLuint LoadTextureImageFile(const char * filename)
- {
- GLuint texture = 0;
- int width, height;
- BYTE * data = NULL;
- FILE * file;
-
- fopen_s(&file, filename, "rb");
-
- if (&file == NULL) return 0;
-
- width = 256;
- height = 256;
- data = (BYTE*)malloc(width * height * 3);
- fread(data, width * height * 3, 1, file);
- fclose(file);
- glGenTextures(1, &texture);
- glBindTexture(GL_TEXTURE_2D, texture);
- gluBuild2DMipmaps(GL_TEXTURE_2D, GL_BGRA_EXT, width, height, GL_BGR_EXT, GL_UNSIGNED_BYTE, data);
- glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_TEXTURE_ENV_COLOR);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- free(data);
- return texture;
- }
-
- void FreeCreatedTexture(GLuint texture)
- {
- glDeleteTextures(1, &texture);
- }
Here's the function that draws a simple component with textures,
- void DrawScene(HDC hdc)
- {
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
- glLoadIdentity();
-
- glPushMatrix();
- glTranslatef(0.0f, 0.2f, 0.0f);
- glRotatef(angle, X_angle, Y_angle, Z_angle);
- glEnable(GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, LoadTextureImageFile("textures/blocks.bmp"));
-
- glBegin(GL_QUADS);
- glTexCoord2f(0.0, 0.0);
- glVertex3f(-0.25f, 0.10f, 0.0f);
- glTexCoord2f(0.0, 1.0);
- glVertex3f(0.05f, 0.10f, 0.0f);
- glTexCoord2f(1.0, 1.0);
- glVertex3f(0.05f, -0.40f, 0.0f);
- glTexCoord2f(1.0, 0.0);
- glVertex3f(-0.25f, -0.40f, 0.0f);
- glEnd();
-
- glBegin(GL_QUADS);
- glColor3f(0.8f, 0.8f, 0.8f);
- glTexCoord2f(0.0, 0.0);
- glVertex3f(-0.25f, 0.10f, 0.0f);
- glTexCoord2f(0.0, 1.0);
- glVertex3f(-0.25f, 0.10f, 0.30f);
- glTexCoord2f(1.0, 1.0);
- glVertex3f(-0.25f, -0.40f, 0.30f);
- glTexCoord2f(1.0, 0.0);
- glVertex3f(-0.25f, -0.40f, 0.0f);
- glEnd();
-
- glBegin(GL_QUADS);
- glTexCoord2f(0.0, 0.0);
- glVertex3f(0.05f, 0.10f, 0.0f);
- glTexCoord2f(0.0, 1.0);
- glVertex3f(0.05f, 0.10f, 0.30f);
- glTexCoord2f(1.0, 1.0);
- glVertex3f(0.05f, -0.40f, 0.30f);
- glTexCoord2f(1.0, 0.0);
- glVertex3f(0.05f, -0.40f, 0.0f);
- glEnd();
-
- glBegin(GL_QUADS);
- glTexCoord2f(0.0, 0.0);
- glVertex3f(-0.25f, 0.10f, 0.0f);
- glTexCoord2f(0.0, 1.0);
- glVertex3f(0.05f, 0.10f, 0.0f);
- glTexCoord2f(1.0, 1.0);
- glVertex3f(0.05f, 0.10f, 0.30f);
- glTexCoord2f(1.0, 0.0);
- glVertex3f(-0.25f, 0.10f, 0.30f);
- glEnd();
-
- glBegin(GL_QUADS);
- glTexCoord2f(0.0, 0.0);
- glVertex3f(-0.25f, -0.40f, 0.0f);
- glTexCoord2f(0.0, 1.0);
- glVertex3f(0.05f, -0.40f, 0.0f);
- glTexCoord2f(1.0, 1.0);
- glVertex3f(0.05f, -0.40f, 0.30f);
- glTexCoord2f(1.0, 0.0);
- glVertex3f(-0.25f, -0.40f, 0.30f);
- glEnd();
-
- glPopMatrix();
- glDisable(GL_TEXTURE_2D);
-
- glEnable(GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, LoadTextureImageFile("textures/rocks.bmp"));
- glPushMatrix();
- glTranslatef(-0.5f, 0.4f, 0.0f);
- glRotatef(angle, X_angle, Y_angle, Z_angle);
- glBegin(GL_TRIANGLES);
- glColor3f(1.0f, 1.0f, 1.0f);
- glTexCoord2f(0.0, 0.0);
- glVertex3f(0.0f, 0.3f, 0.0f);
- glTexCoord2f(0.0, 1.0);
- glVertex3f(0.15f, -0.1f, 0.0f);
- glTexCoord2f(1.0, 1.0);
- glVertex3f(-0.15f, -0.1f, 0.0f);
- glEnd();
-
- glPopMatrix();
- glDisable(GL_TEXTURE_2D);
-
- GLUquadric *quad = gluNewQuadric();
- glEnable(GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, LoadTextureImageFile("textures/blocks5.bmp"));
- gluQuadricTexture(quad, 1);
- glPushMatrix();
- glTranslatef(0.4f, 0.5f, 0.0f);
- glRotatef(angle, X_angle, Y_angle, Z_angle);
- glColor3f(1.0f, 1.0f, 1.0f);
- gluCylinder(quad, 0.1f, 0.1f, 0.6f, 40, 40);
- glPopMatrix();
- glDisable(GL_TEXTURE_2D);
-
- GLUquadric *quad2 = gluNewQuadric();
- glEnable(GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, LoadTextureImageFile("textures/blocks8.bmp"));
- gluQuadricTexture(quad2, 1);
- glPushMatrix();
- glTranslatef(0.4f, -0.5f, 0.0f);
- glRotatef(angle, X_angle, Y_angle, Z_angle);
- glColor3f(1.0f, 1.0f, 1.0f);
- gluCylinder(quad2, 0.1f, 0.1f, 0.6f, 40, 40);
- glPopMatrix();
- glDisable(GL_TEXTURE_2D);
-
- SwapBuffers(hdc);
- }
In _tWinMain() function, call the DrawScene() function in message loop.
-
- while (GetMessage(&msg, NULL, 0, 0))
- {
- if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
- {
-
- DrawScene(GetDC(mainHWND));
-
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
- }
To initialize OpenGL for current window, create a WM_CREATE case statement in the function WndProc() and call the above InitOpenGL() function.
You can also do this by calling in WM_PAINT and not in WM_CREATE. Also create WM_SIZE case and call GLResizeWindow() function in it.
- LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
- {
- int wmId, wmEvent;
- PAINTSTRUCT ps;
- HDC hdc;
-
- switch (message)
- {
- case WM_CREATE :
- if (!InitOpenGL(hWnd)){
- MessageBox(hWnd, TEXT("Error: Cannot initialize OpenGL"), TEXT("ERROR"),
- MB_OK | MB_ICONERROR);
- }
- break;
-
- case WM_COMMAND:
- wmId = LOWORD(wParam);
- wmEvent = HIWORD(wParam);
-
- switch (wmId)
- {
- case IDM_ABOUT:
- DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
- break;
- case IDM_EXIT:
- DestroyWindow(hWnd);
- break;
- default:
- return DefWindowProc(hWnd, message, wParam, lParam);
- }
- break;
-
- case WM_KEYDOWN:
- switch (wParam)
- {
- case VK_DOWN :
- angle += 3.0f;
- X_angle = 1.0f;
- Y_angle = 0.0f;
- Z_angle = 0.0f;
- break;
- case VK_RIGHT :
- angle += 3.0f;
- X_angle = 0.0f;
- Y_angle = 1.0f;
- Z_angle = 0.0f;
- break;
- case VK_LEFT:
- angle += 3.0f;
- X_angle = 0.0f;
- Y_angle = 0.0f;
- Z_angle = 1.0f;
- break;
- case VK_UP:
- angle += 3.0f;
- X_angle = 1.0f;
- Y_angle = 1.0f;
- Z_angle = 1.0f;
- break;
- }
- break;
-
- case WM_SIZE:
- GLResizeWindow(hWnd, message, wParam, lParam);
- break;
-
- case WM_PAINT:
- hdc = BeginPaint(hWnd, &ps);
-
- EndPaint(hWnd, &ps);
- break;
- case WM_DESTROY:
- PostQuitMessage(0);
- break;
- default:
- return DefWindowProc(hWnd, message, wParam, lParam);
- }
- return 0;
- }