Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

Getting Started with OpenGL on Windows Mobile

There are a number of good posts on working with OpenGL on Windows Mobile via the .NET Compact Framework but one thing I’ve noticed is that they don’t really try to wrap the OpenGL functionality in a way that makes it reusable as an application/game framework. Mostly the logic for rendering was intermingled with windows forms logic, such as OnPaint, which was intermingled with logic for updating the current scene. If you look at say XNA you notice that the model is very simple – essentially they have a single run loop consisting of Update and Draw. I set out to update the OpenGL ES wrapper initially provided by Koushik Dutta and since extended with some great examples across at XDA Developers. Here’s what I came up with to start with.

If you grab the OpenGL ES wrapper you will notice that at its core is essentially a single Windows Form that handles the OnPaint method in order to draw using OpenGL primitives. Nearly all the computation and rendering is done in this method. In the constructor and subsequent initialization methods there are a number of OpenGL ES calls in order to setup the display, surface and context required in order for OpenGL to draw to the screen. In order to create a reusable framework that would be a starting point for any project undertaken in OpenGL all this code would have to be wrapped in a way that it doesn’t need to be duplicated for each project.

I decided to go down the path of creating an abstract form which I called the ApplicationForm. This form has three virtual methods that the overriding form needs to implement:

protected virtual void SetupScene(){}

protected virtual void UpdateScene(float secondsSinceLastUpdate){}

protected virtual void DrawScene(){}

As you can imagine these form the basis of the Draw-Update rendering loop. Of course SetupScene is called prior to the first iteration of this loop to ensure the scene is correctly setup before the first call to Draw. Unlike some implementations which rely on a For/While loop and DoEvents (to allow windows events to be processed) I went with the approach that works in conjunction with the existing windows message pump. When the form needs to be painted the OnPaint method is invoked:

protected override void OnPaint(PaintEventArgs e)
       {
           base.OnPaint(e);

           // Draw the current scene
           RunDrawScene();

           egl.SwapBuffers(_display, _surface);
           gl.Clear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT);

           // Update the current scene
           RunUpdateScene();
       }

As you can see this does a single pass of the rendering loop.  What’s interesting is the implementation of the RunUpdateScene method:

private void RunUpdateScene()
       {
           // Check for running instance - exit if already running
           if (Interlocked.CompareExchange(ref isUpdating, 1, 0) == 1)
           {
               return;
           }

           // Only instance running, so create the thread in which to
           // invoke the UpdateScene method
           ThreadPool.QueueUserWorkItem((async) =>
           {
               try
               {
                   // Calculate the time in seconds since last update
                   var seconds = (float)Environment.TickCount/1000.0f;
                   var diff = 0.0f;
                   if (this.lastUpdate > 0)
                   {
                       diff = seconds - this.lastUpdate;
                   }
                   this.lastUpdate = seconds;

                   // Invoke the virtual UpdateScene method
                   UpdateScene(diff);
               }
               finally
               {
                   // Make sure this method can be re-entered
                   Interlocked.Decrement(ref isUpdating);

                   // Invoke "Invalidate" on the control which will cause
                   // the control to be refreshed (ie OnPaint called) leading
                   // to another iteration of Draw and Update
                   this.BeginInvoke((Action)(() => { Invalidate(); }));
               }
           });
       }

Essentially this method wraps the actual call to UpdateScene for three reasons:

  1. To ensure only one instance of UpdateScene is being invoked at any time
  2. To push the call to UpdateScene onto a separate thread. This prevents the UI from blocking whilst it’s in running.
  3. To invoke Invalidate once UpdateScene has completed. This ensures that OnPaint will be invoked again and that the draw-update loop will be invoked again.

In the next post I’ll show you how to get started with this wrapper by overriding the ApplicationForm.  In the meantime, try it yourself….

Comments are closed