Click here to Skip to main content
Click here to Skip to main content

2D Drawing with an OpenGL Control

, 8 Feb 2010
Rate this:
Please Sign up or sign in to vote.
An article explaning how to create a user control for 2D shape drawing with OpenGL
Sample Image - GLView.png

Introduction

I had recently written some code to display lots of simple 2D shapes for a finite element program. My first thought was to use GDI. However, since I had to display thousands of shapes on the screen, GDI was too slow for me. The solution came up as an OpenGL control. In this article, I will try to explain how I created a control utilizing OpenGL for a 2D shape drawing.

Using the Code

The source code includes a control named GLCanvas2D based on System.Windows.Forms.UserControl. OpenGL specific initialization code is as follows:

GLCanvas2D::GLCanvas2D()
{
    /* User control initialization code goes here
    ....
    .... */

    // Get a handle to the device context
    mhDC = GetDC((HWND)this->Handle.ToPointer());

    // Choose a pixel format
    PIXELFORMATDESCRIPTOR pfd = {
        sizeof(PIXELFORMATDESCRIPTOR),     // size of the structure
        1,                                 // version
        PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
                        // flags
        PFD_TYPE_RGBA,                     // pixel type
        32,                                // color bits
        0, 0, 0, 0, 0, 0, 0, 0,            // RGBA bits and shifts
        0,                                 // accumulation buffer bits
        0, 0, 0, 0,                        // accumulation buffer RGBA bits
        32,                                // depth bits
        24,                                // stencil bits
        0,                                 // aux bits
        PFD_MAIN_PLANE,                    // layer type
        0,                                 // reserved
        0, 0, 0                            // layer masks
    };

    // Set the pixel format
    int iPixelFormat = ChoosePixelFormat(mhDC, &pfd);
    SetPixelFormat(mhDC, iPixelFormat, &pfd);

    // Create the render context
    mhGLRC = wglCreateContext(mhDC);
    wglMakeCurrent(mhDC, mhGLRC);
}

This code is called in the constructor of GLCanvas2D. The destructor deletes the render context and releases the device context.

GLCanvas2D::~GLCanvas2D()
{
    wglMakeCurrent(NULL, NULL);
    wglDeleteContext(mhGLRC);
    ReleaseDC((HWND)this->Handle.ToPointer(), mhDC);
}

I have two variables used for panning and zooming the view: a point structure containing a camera position and a zoom factor. In each paint event, the projection matrix is setup using these two variables.

// Set an orthogonal projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(mCameraPosition.X -
        ((float)ClientRectangle.Width) * mZoomFactor / 2, // Left
    mCameraPosition.X +
        ((float)ClientRectangle.Width) * mZoomFactor / 2, // Right
    mCameraPosition.Y -
        ((float)ClientRectangle.Height) * mZoomFactor / 2, // Bottom
    mCameraPosition.Y +
        ((float)ClientRectangle.Height) * mZoomFactor / 2, // Top
    -1.0f,     // Near plane
    1.0f);     // Far plane

Camera position and zoom factor are calculated when the user pans (by holding down the mouse wheel) or zooms (by scrolling the mouse wheel). I also needed some method of transforming coordinates from screen to model coordinates and back.

/// <summary>
/// Converts the given point from world coordinates to screen coordinates.
/// </summary>
Drawing::Point WorldToScreen(float x, float y)
{
    // Move the given point to the origin, divide by the zoom factor and
    // add the screen coordinates of the center point
    return Drawing::Point(
        (int)((x - mCameraPosition.X) / mZoomFactor) +
            ClientRectangle.Width / 2,
        -(int)((y - mCameraPosition.Y) / mZoomFactor) +
            ClientRectangle.Height / 2);
}

/// <summary>
/// Converts the given point from screen coordinates to world coordinates.
/// </summary>
Drawing::PointF ScreenToWorld(int x, int y)
{
    // Move the given point to the origin, multiply by the zoom factor and
    // add the model coordinates of the center point (camera position)
    return Drawing::PointF(
        (float)(x - ClientRectangle.Width / 2) * mZoomFactor +
            mCameraPosition.X,
        -(float)(y - ClientRectangle.Height / 2) * mZoomFactor +
            mCameraPosition.Y);
}

GLCanvas2D exposes a Render event which is typically overridden in implementations. The control overrides the OnPaint event of the base class and raises the Render event passing a GLGraphics object as an argument. GLGraphics is similar to the System.Drawing.Graphics class. Drawing is done using the methods of the GLGraphics object.

void MyCanvas2D::Render(System::Object ^ sender, GLView::GLGraphics ^ Graphics)
{
    // Draw a filled rectangle
    Graphics->FillRectangle(0.0f, 0.0f, 100.0f, 50.0f, Drawing::Color::Red);
}

Implementations can also use native OpenGL calls within the Render event.

void MyCanvas2D::Render(System::Object ^ sender, GLView::GLGraphics ^ Graphics)
{
    // Draw a filled rectangle
    glBegin(GL_QUADS);
    glColor3f(1.0f, 0.0f, 0.0f);
    glVertex2f(0.0f, 0.0f);
    glVertex2f(100.0f, 0.0f);
    glVertex2f(100.0f, 50.0f);
    glVertex2f(0.0f, 50.0f);
    glEnd();
}

Points of Interest

If an implementation places more than one instance of GLCanvas2D on a Form, we have to make sure that OpenGL calls are directed to the correct render context. We achieve this by calling wglMakeCurrent with the handle of our render context each time the OnPaint event is fired.

void GLCanvas2D::OnPaint(System::Windows::Forms::PaintEventArgs^ e)
{
    // Save previous context and make our context current
    HDC mhOldDC = wglGetCurrentDC();
    HGLRC mhOldGLRC = wglGetCurrentContext();
    wglMakeCurrent(mhDC, mhGLRC);

    /* Drawing code goes here
    ....
    .... */

    // Restore previous context
    wglMakeCurrent(mhOldDC, mhOldGLRC);
}

GLView uses OpenGL vertex arrays to speed up drawing. Vertex arrays are handled internally by a custom GLVertexArray class. The GLGraphics class collects vertex information using two GLVertexArray's. One GLVertexArray is used for filled shapes. Each filled shape is converted to triangles and stored in the vertex array. A second GLVertexArray collects lines. No drawing is actually performed until the Render event ends.

System::Void Render()
{
    // Create the vertex arrays on the fly
    float * vp = new float[mVertices->Count * 3];
    float * cp = new float[mVertices->Count * 4];

    // Fill in the vertex arrays. mVertices is an internal list for collecting
    // vertex position and color.
    for (int j = 0; j < mVertices->Count; j++)
    {
        vp[j * 3] = mVertices[j].x;
        vp[j * 3 + 1] = mVertices[j].y;
        vp[j * 3 + 2] = mVertices[j].z;
        cp[j * 4] = mVertices[j].r;
        cp[j * 4 + 1] = mVertices[j].g;
        cp[j * 4 + 2] = mVertices[j].b;
        cp[j * 4 + 3] = mVertices[j].a;
    }

    // Set OpenGL vertex and color pointers to our vertex arrays
    glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), vp);
    glColorPointer(4, GL_FLOAT, 4 * sizeof(float), cp);

    // Draw the arrays
    glDrawArrays(mType, 0, mVertices->Count);

    // Clean up
    delete[] vp;
    vp = 0;
    delete[] cp;
    cp = 0;
}

History

  • 25.01.2007 - First release
  • 26.04.2007 - Various bug fixes (Thanks to Jac for bug reports and suggestions)
  • 05.02.2010 - Fixed a memory leak in GLGraphics::Render

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Ozgur Ozcitak

Turkey Turkey
No Biography provided

Comments and Discussions

 
GeneralMy vote of 5 Pinmembermanoj kumar choubey9-Feb-12 21:52 
Bug[My vote of 2] It does work on my computer,initialization error Pinmemberhuangzhai12712-Nov-11 4:48 
GeneralRe: [My vote of 2] It does work on my computer,initialization error PinmemberOzgur Ozcitak12-Nov-11 12:13 
GeneralRe: [My vote of 2] It does work on my computer,initialization error Pinmemberhuangzhai12720-Nov-11 15:20 
Generalmemory leak problem Pinmembercuatro0044-Feb-10 9:52 
GeneralRe: memory leak problem PinmemberOzgur Ozcitak4-Feb-10 23:01 
GeneralRe: memory leak problem Pinmemberfetti7-Feb-10 0:48 
GeneralRe: memory leak problem PinmemberOzgur Ozcitak7-Feb-10 2:33 
GeneralRe: memory leak problem [modified] Pinmemberfetti7-Feb-10 4:47 
GeneralRe: memory leak problem PinmemberOzgur Ozcitak7-Feb-10 11:32 
GeneralRe: memory leak problem Pinmemberfetti7-Feb-10 11:39 
GeneralRe: memory leak problem PinmemberOzgur Ozcitak7-Feb-10 13:22 
GeneralRe: memory leak problem Pinmemberfetti7-Feb-10 15:55 
GeneralRe: memory leak problem PinmemberOzgur Ozcitak7-Feb-10 22:09 
GeneralRe: memory leak problem PinmemberOzgur Ozcitak8-Feb-10 0:01 
GeneralRe: memory leak problem PinmemberOzgur Ozcitak8-Feb-10 1:45 
Generalcode may result in opengl problem Pinmembergeorg waechter11-Mar-08 9:16 
GeneralRe: code may result in opengl problem Pinmembernewspicy12-Jun-08 0:46 
GeneralRe: code may result in opengl problem PinmemberOzgur Ozcitak24-Jun-08 3:30 
GeneralThank you Pinmemberjoemc0414-Dec-07 12:36 
GeneralRe: Thank you Pinmemberjoemc0414-Dec-07 13:21 
GeneralRe: Thank you Pinmemberjoemc0414-Dec-07 16:24 
GeneralRe: Thank you PinmemberOzgur Ozcitak28-Apr-08 0:46 
GeneralPrinting PinmemberJac31 Balzer28-Apr-07 16:21 
GeneralRe: Printing PinmemberOzgur Ozcitak3-May-07 0:26 
GeneralRe: Printing PinmemberJac31 Balzer4-May-07 22:01 
GeneralCool article PinmemberJim Crafton26-Apr-07 9:03 
GeneralRe: Cool article PinmemberRick York26-Apr-07 14:17 
GeneralRe: Cool article PinmemberOzgur Ozcitak26-Apr-07 22:40 
GeneralMultiple Controls in the same App PinmemberJac31 Balzer26-Apr-07 1:30 
GeneralRe: Multiple Controls in the same App PinmemberOzgur Ozcitak26-Apr-07 22:16 
GeneralRe: Multiple Controls in the same App PinmemberJac31 Balzer27-Apr-07 2:48 
GeneralAnother bug ? (creating the font display list). PinmemberJac31 Balzer25-Apr-07 12:21 
GeneralRe: Another bug ? (creating the font display list). PinmemberOzgur Ozcitak25-Apr-07 22:01 
GeneralRe: Another bug ? (creating the font display list). PinmemberJac31 Balzer25-Apr-07 22:40 
GeneralMissing call to the base class PinmemberJac31 Balzer26-Apr-07 4:57 
GeneralRe: Missing call to the base class PinmemberOzgur Ozcitak26-Apr-07 23:53 
GeneralA bug in GLGraphics::Render PinmemberJac31 Balzer24-Apr-07 15:23 
GeneralRe: A bug in GLGraphics::Render Pinmemberozgurozcitak24-Apr-07 23:25 
GeneralWOW ! Thanks PinmemberJac31 Balzer22-Apr-07 12:25 
GeneralRe: WOW ! Thanks Pinmemberozgurozcitak24-Apr-07 23:13 

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

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

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 8 Feb 2010
Article Copyright 2007 by Ozgur Ozcitak
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid