Friday, July 8, 2022

Implementation - Initializing OpenGL Context

Overview
As discussed earlier, OpenGL is a drawing specification that requires a context contained in a hosting window to draw upon. 
Creating OpenGL context in windows OS is not a trivial task. It's mainly because opengl.dll and  opengl.lib supplied by Windows only supports OpenGL specification 1.1. 
The display card providers such as Nvidia or Intel or AMD actually implement the latest and greatest OpenGL specification, including additional functionality as extensions and supply it as OpenGL Installable Client Driver or ICD. For example, nvogl.dll, atiogl.dll, or iglicd.dll etc.
These ICDs are registered and their locations are stored in the windows registry.
The OpenGL.dll supplied with windows acts as a wrapper and forwards any OpenGL API calls made by the client to the ICD.
This post discusses creating Hosting Window, initializing OpenGL context and handling Keyboard/Mouse events.

Details
There are many third party utilities such as FreeGLUT or GLFW to create an Application Window, create OpenGL context in it, provide Window management and mouse and Keyboard Inputs. 
As discussed earlier, VedaGraphicsLib uses Windows SDK and VC++ supplied libraries to implement this functionality.
The classes OGLAppWindow, BaseScene and BaseCamera  implement this functionality. 

System  class diagram

OGLAppWindow
OGLAppWindow is derived from CWindowImpl class. It creates a hosting window and loads OpenGL context. It's an abstract class. BaseScene is derived from this class. 

As discussed earlier, OpenGL.dll works as a wrapper forwarding OpenGL API calls to the ICD.  First time when it's loaded, it looks up the registry and loads the registered ICD. It directly forwards any OpenGL 1.1 related API Calls to the ICD.  However for the higher specification, it patches the call thru an internal API wglGetProcAddress. This is achieved thru a "dedicated function". This function is internally implemented by the Glad\GL.h and\WGL.h header files discussed earlier. The prototype of this function looks as below.

void* GalogenGetProcAddress(const char *name) 
{
  static HMODULE opengl32module = NULL;
  static PROC(WINAPI *wgl_get_proc_address)(LPCSTR name) = NULL;
  if (!wgl_get_proc_address) {
    if (!opengl32module) {
      opengl32module = LoadLibraryA("opengl32.dll");
    }
    wgl_get_proc_address = (PROC(WINAPI*)(LPCSTR))GetProcAddress(opengl32module, "wglGetProcAddress");
    assert(wgl_get_proc_address);
  }
  void *ptr = (void *)wgl_get_proc_address(name);
  if(ptr == 0 || (ptr == (void*)1) || (ptr == (void*)2) || (ptr == (void*)3) ||
     (ptr == (void*)-1) ) {
    if (opengl32module == NULL) {
      opengl32module = LoadLibraryA("opengl32.dll");
      assert(opengl32module);
    }
    ptr = (void *)GetProcAddress(opengl32module, name);
  }
  return ptr;
}

For example, glViewport is defined as  
static void  GL_APIENTRY _impl_glViewport (GLint x, GLint y, GLsizei width, GLsizei height) {
  _glptr_glViewport = (PFN_glViewport)GalogenGetProcAddress("glViewport");
   _glptr_glViewport(x, y, width, height);
}

Output
Members
NameDescription
glrcThis stores modern OpenGL context. It's destroyed along with the hosting window.
GL_MAJOR_VERConstant containing  OpenGL major version. Currently it's set to 4.
GL_MINOR_VERConstant containing  OpenGL minor version. Currently it's set to 0.
WGL_MAJOR_VERConstant containing  Windows Wiggle API major version. Currently it's set to 1.
WGL_MINOR_VERConstant containing  Windows Wiggle API minor version. Currently it's set to 0.

Methods
NameDescription
init_opengl_extensions


In order to load any OpenGL context a Display context or DC is required. init_opengl_extensions method first creates a dummy window and initializes its DC with OpenGL 1.1  Context.
Later LoadWGLExtensions() is called to load the OpenGL extensions. 
In the end, the dummy window is destroyed as it contains OpenGL 1.1 context.
LoadWGLExtensionsLoadWGLExtensions method loads modern OpenGL extension. It does this by internally calling gladLoaderLoadGL() and gladLoaderLoadWGL() functions.
init_openglInitializes the host window with specific modern OpenGL  context as set by GL_MAJOR_VER and GL_MINOR_VER.
DestroyWindow
Deletes the OpenGL context and then destroys the hosting window.

BaseScene  
BaseScene  is derived from OGLAppWindow class. BaseScene Implements rendering, resizing, mouse and keyboard events. The BaseScene class is abstract. All the scene classes should derive from BaseScene class and must override DrawScene and Cleanup methods. 
Members
NameDescription
cameraHandles Mouse and Keyboard inputs. This is a pointer to an instance of BaseCamera class or its derivative. It can be a nullptr meaning, Mouse and Keyboard inputs except Escape key press are ignored.

Methods
NameDescription
DestroyWindowCalled when the application window is closed. This internally calls Cleanup() and OGLAppWindow::DestroyWindow().
Keyboard and Mouse inputs are used for various visual operations such as panning, rotation, zoom in/out etc. BaseScene class captures' keyboard and mouse inputs and passes it to the scene's  Keyboard and mouse input handler camera for further processing when it's assigned. The scene's  Keyboard and mouse input handler is  implemented in BaseCamera class. The BaseCamera class itself can be overridden for further specialization.
OnKeyDownThis method handles Keyboard input. It's called in response to WM_KEYDOWN. Delegates to camera if assigned. By default, closes application window if Escape key is pressed.
OnMouseBtnDownThis method handles Mouse button clicks. It's called in response to WM_LBUTTONDOWN and WM_RBUTTONDOWN. Delegates to camera if assigned. The parameter button represents the left, right or middle button. x and y represents the coordinates of the mouse.
OnMouseMoveThis method handles Mouse moves. It's called in response to WM_MOUSEMOVE. Delegates to camera if assigned. The parameter button represents the left, right or middle button. x and y represents the coordinates of the mouse.
OnMouseWheelThis method handles Mouse scroll wheel inputs. It's called in response to WM_MOUSEWHEEL.Mouse scroll wheel handler. Delegates to camera  if assigned. This method updates FOV.
HandleResize
When the window is resized, WM_SIZE event is raised. OnSize method in BaseScene class captures' this event. This method resizes the viewport,and internally calls this method.
This passes the input to BaseCamera::UpdateWH()  for aspect ratio  calculations and repaints the scene.
Init
When the scene is created Init method is called. Note that the Init method should be overridden by all scene classes derived from  BaseScene class. Internally it calls OGLAppWindow Init methods to create the hosting window and the OpenGL context.
Following lists the steps 
  1. Create a dummy window with CS_OWNDC style.
  2. Create OpenGL context  as per OpenGL 1.1 specification
  3. Load the OpenGL extensions using wglGetProcAddress API. This will setup and forward any future OpenGL API calls to OpenGL ICD from the vendor
  4. Once the extensions are loaded the the OpenGL context and dummy window should be discarded  because the pixel format  of the OpenGL specification 1.1  and 3.3 are different and cannot be changed once set.
  5. Create a new window that will render 3D Graphics with CS_OWNDC style. 
  6. Create OpenGL context compatible with OpenGL specification 3.3.
OnPaintThe scene will be rendered when WM_PAINT  message is received. The OnPaint method captures it and internally calls DrawScene to render the scene using OpenGL APIs. Note that the DrawScene method should be overridden by all scene classes derived from  BaseScene class.

The BaseCamera class will be discussed in an upcoming post.
In the next post we will discuss creation of  the host window and initializing it with OpenGL Context.

No comments:

Post a Comment