Open GL Super Bible

Previous Table of Contents Next


Handling Window Resizing

When the window size changes, the WM_SIZE message is posted to the window. We added a handler for this message with ClassExpert, and call the external function GLResize(), passing the new width and height of the window. The rendering context must be made current before calling this function, or the OpenGL function calls in GLResize will have no effect on the rendering context for this window. This code is in Listing 22-3.

Listing 22-3 WM_SIZE handler that adjusts the OpenGL viewport

// Handles WM_SIZE message
void TOwlglWindowView::EvSize(uint sizeType, TSize& size)
{
        TWindowView::EvSize(sizeType, size);

        // Make the rendering context current, and call function
        // to make adjustments to OpenGL viewport
        wglMakeCurrent(m_hDC,m_hRC);
        GLResize(size.cx, size.cy);
        wglMakeCurrent(m_hDC,NULL);
}

Rendering the Scene

Now we are ready to add the code that actually draws the OpenGL scene. The member function EvPaint was added by ClassExpert and is called whenever the window receives a WM_PAINT message. Here we make the rendering context current and call the GLRenderScene function, which contains only OpenGL function calls. The code for our EvPaint() function is in Listing 22-4.

Note that since we earlier requested a double-buffered window, we have to call SwapBuffers() afterward. Also, any WM_PAINT handler needs to validate the window so that Windows knows you are finished drawing in it. If you don’t do this, Windows will continually post WM_PAINT messages to your window.

Listing 22-4 Code for handling WM_PAINT for our OWL-based OpenGL sample

// Handles WM_PAINT message
void TOwlglWindowView::EvPaint()
{
        // Make the rendering context current, and call OpenGL Rendering
        code wglMakeCurrent(m_hDC,m_hRC);
        GLRenderScene(NULL);
        wglMakeCurrent(NULL,m_hRC);

        // Finally swap buffers since this rendering context is double
        buffered SwapBuffers(m_hDC);

        // Validate the window
        Validate();
}

No Flickering Allowed

Whenever the window is resized or invalidated, Windows will erase the window background before repainting. Since our OpenGL background is black, this erasing (which sets the window to white) will cause a flicker every time EvPaint is called. Even if that weren’t so, we are using SwapBuffer() to get our image in the window, which updates the entire client region anyway.

To keep the window from flickering, we override the default handling of WM_ERASEBACKGROUND. Usually, the window is erased before being repainted after a resize. If we return FALSE from this function, however, the window will never be erased before a repaint, and there won’t be any flicker. Usually this function returns TWindowView::EvEraseBkgnd(dc), but you can just return FALSE to get this behavior. See Listing 22-5.

Listing 22-5 Preventing the window from being erased everytime it is redrawn

// Handles WM_ERASEBACKGROUND message
bool TOwlglWindowView::EvEraseBkgnd(HDC dc)
        {
        return FALSE;// Do not erase background
        }

Keep It Moving

Though certainly not a requirement, the example for this chapter uses a timer to invalidate the window every 200 milliseconds (thus forcing a repaint from our OpenGL code). The code in glcode.c rotates a figure every time it is called. This has the effect of displaying a smoothly rotating set of objects—in this case, three particular 3D letters. Implementing a timer is simple: You set a timer in the EvCreate() function, add a handler for WM_TIMER, and then kill the timer in the EvDestroy handler. This is standard Windows programming, and the pertinent code is shown in Listing 22-6.

The output from our program thus far is shown in Figure 22-5.


Figure 22-5  Animated output from the OWL-based OpenGL program

Listing 22-6 Code that creates/destroys a timer to do some animation

// Handles WM_CREATE message
int TOwlglWindowView::EvCreate(CREATESTRUCT far& createStruct)
        {
        ...
        ...

        // Set a timer for 200 milliseconds
        SetTimer(200,101,NULL);
        ...
        ...

// Handles WM_TIMER message
void TOwlglWindowView::EvTimer(uint timerId)
        {
        TWindowView::EvTimer(timerId);

        // Force a repaint
        Invalidate();
        }

// Handles WM_DESTROY message
void TOwlglWindowView::EvDestroy()
        {
        // Kill the timer
        KillTimer(101);
        ...
        ...


Previous Table of Contents Next