Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Native Win32 API OpenGL Tutorial - Part 2

0.00/5 (No votes)
17 Apr 2016 1  
In this second part, we will create an OpenGL MDI application

The source and files are in Visual Studio 2013 format.

Introduction

In this second article, we will expand the simple OpenGL application from Part 1 into a standard MDI interface application. Here, each MDICHILD window will contain the fuctionality of the single application from part 1.

Background

In part one, we had a single render context which belonged to the only application window. What we will now do is move much of the functionality away from the application window and onto the MDICHILD window. So we will have multiple render contexts operating at once and the organization we undertook in lesson 1 becomes the key. In later lessons, we will add threads and the drawing complexity will go up another level.

The key part to understand this lesson is understanding the MDI Application and behaviour. MDI Applications are based around an invisible or transparent Window class that exists in the area that is drawn in a normal application window. This special Window class is called an MDIClient and although invisible, it is involved in controlling the behaviours of the MDIChildren windows that are inserted into it.

The MDIClient is responsible for all those MDI special things like the minimize and maximize behaviours, tiling and cascading and a wealth of other unique features. They are worth reading some articles on if you want to use this sort of application. For our MDI application, we will make our OpenGL windows and insert them into the MDIClient and allow the standard behaviours to work.

Using the Code

Our pseudo code from lesson 1 remains unchanged except it is moved from the application window and onto the MDIChild window and we add a cleanup step when closing. Thus, each MDIChild runs the sequence:

1.) Initialize OpenGL for window (Called Once)
2.) Scale the OpenGL viewPort (Initial Call)
repeat
3.) Draw the scene
4.) Transfer the scene to screen
until window closes
5.) Window closing cleanup OpenGL memory and stuff used

** Note Item 2) the scale process also gets called if the window changes size

We use the same structure to hold our data as per Lesson 1, only this time each MDIChild creates a structure and it is attached to each MDI child. The initialization of step 1 returns a render context specific to the MDIChild and thus each MDIChild has its own render context.

The MDIChild handler becomes the site of all the OpenGL calls. The create of the MDI child will cause creation of a new render context which will be stored in its own database structure on the window itself. Each MDIChild will therefore have its own data structure and messages which create operations on the windows becomes unique to the data structure attached to each MDIChild. So each MDI child can be doing different things without having to track any complexity for the OpenGL system itself.

What seemed like a slightly complex data holding arrangement in lesson 1 makes data management obvious and simple in our MDI. There are other faster ways to attach data to Windows but they are more complex than SetProp/GetProp but this is the easiest for our target audience of beginners. Later, as we move into games and faster render situations, we will discuss other methods to deal with this.

For this application, the MDIChild shown below is where all the OpenGL is called and controlled from and it is worth looking at the process and equating it back up to the pseudocode above.

 static LRESULT CALLBACK OpenGLMDIChildHandler (HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) 
{
    switch (Msg){ 
        case WM_CREATE:    {                             // WM_CREATE MESSAGE
                GLDATABASE* db = (GLDATABASE*) malloc(sizeof(GLDATABASE)); // Allocate structure
                db->Rc = InitGL(Wnd);                    // Initialize OpenGL & get render context
                db->glTexture = 0;                       // Zero the texture
                db->xrot = 0.0f;                         // Zero x rotation
                db->yrot = 0.0f;                         // Zero y rotation
                SetProp (Wnd, DATABASE_PROPERTY, (HANDLE) db);  // Data structure hold as property
                ReSizeGLScene (Wnd);                     // Rescale the OpenGL window
            }
            break;
         case WM_DESTROY: {                             // WM_DESTROY MESSAGE
                wglMakeCurrent(NULL, NULL);             // Make the rendering context not current
                GLDATABASE* db = (GLDATABASE*) GetProp(Wnd, DATABASE_PROPERTY);// Get data struct
                if (db != 0) {
                    if (db->Rc != 0) wglDeleteContext(db->Rc); // If valid delete context
                    if (db->glTexture != 0)
                        glDeleteTextures(1, &db->glTexture);   // If valid delete the texture
                    free(db);                           // Release the data structure memory
                }
            }
            break;
         case WM_PAINT: {                                // WM_PAINT MESSAGE
                PAINTSTRUCT Ps;
                GLDATABASE* db = (GLDATABASE*) GetProp(Wnd, DATABASE_PROPERTY);// Get data struct
                BeginPaint (Wnd, &Ps);                  // Begin paint
                DrawGLScene(db, Ps.hdc);                // Draw the OpenGL scene
                SwapBuffers(Ps.hdc);                    // Swap buffers
                EndPaint(Wnd, &Ps);                     // End paint
                return 0;
            }
            break;  
         case WM_TIMER: {                               // WM_TIMER MESSAGE
                GLDATABASE* db = (GLDATABASE*) GetProp(Wnd, DATABASE_PROPERTY);// Get data struct
                db->xrot += 1.0f;                       // Inc x rotation
                db->yrot += 1.0f;                       // Inc y rotation
                InvalidateRect(Wnd, 0, TRUE);           // Redraw now so invalidate us            
            }
            break; 
         case WM_WINDOWPOSCHANGED:                      // WM_WINDOWPOSCHANGED
            // Check if window size has changed .. window move doesnt change aspect ratio
            if ((lParam == 0) || ((((PWINDOWPOS) lParam)->flags & SWP_NOSIZE) == 0)){
                ReSizeGLScene(Wnd);                     // Rescale the GL window                  
                InvalidateRect(Wnd, 0, TRUE);           // We need a redraw now so invalidate us
            }
            break;
         case WM_ERASEBKGND:                            // WM_ERASEBKGND MESSAGE
            return (FALSE);
    } 
     return DefMDIChildProc(Wnd, Msg, wParam, lParam);  // Unprocessed messages to DefMDIChildProc
}

History

  • Version 1.00 - Initial release
  • Version 1.10 - Small bug changing wrong textures on MDIChild fixed, MDI DragDrop functionality added.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here