Click here to Skip to main content
15,867,704 members
Articles / Desktop Programming / Win32

Native Win32 API OpenGL Tutorial - Part 2

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
17 Apr 2016CPOL3 min read 17.1K   3K   10   2
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.

Image 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:

C++
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.

C++
 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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Australia Australia
Experience hardware/software design engineer with specialization in the field of embedded controllers and software. Software credits include Free Pascal development RTL, Cipher Multitask engraving software and Symmetry Laser/Cutting software. Firmware/hardware credits include Cipher CNC controllers series 1-3, Vision series 1 engraver controller and I-Marc pet tagger controller.

It is about now you realize you have been doing this for far too long Smile | :)

contact: ldeboer@gateway.net.au

Comments and Discussions

 
QuestionMDI child windows loosing their OpenGL rendering Pin
Martin081520-Apr-16 1:16
professionalMartin081520-Apr-16 1:16 
AnswerRe: MDI child windows loosing their OpenGL rendering Pin
leon de boer20-Apr-16 5:14
leon de boer20-Apr-16 5:14 
If your window doesn't have a frame then its easy override the handler and just return zero (that tells windows you handled it). If you have no frame anyhow and your render takes up the whole window that is fine. It sounds like you work that out above but make sure you return zero from the override so windows knows you handled the WM_NCPAINT call.

See documentation: WM_NCPAINT message (Windows)[^]

If your window has a frame, or you want a frame and the WM_NCPAINT is causing a problem, slip a window without a frame as the render window into the frame window as a child. Never forget windows can be children of windows to fix behaviours. Just remember to stop the draws on the parent frame in the childrens area so it doesn't flash.

Funny as it would happen one of the questions I got was about putting two screen inside the same window a sample code I just did. It puts not one (like you are asking) but two children render windows inside a single parent window. That should give you the idea of running the render window as a child of another window. http://s000.tinyupload.com/?file_id=08701924383590736839. That code is much simpler than the above MDI code as its just a single normal window with two children both that happen to render but take up the whole client area of the framed parent.

So always remember you can always go around the framework you don't have to fight with it, we have to do the same thing down on the API level. That includes your licensed extension library.

So I guess that is my advice make your render window a blank featureless window with no border where possible, if that means inserting it as a child so be it and you can safely override the WM_NCPAINT message
In vino veritas


modified 20-Apr-16 11:49am.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.