Dynamic display compass
OpenGL_WGL_v4.cs
// Copyright 2010 ESRI
// 
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
// 
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
// 
// See the use restrictions.
// 

// ############################################################################
// ############################################################################
// ############################################################################
// ############################################################################
//
// Namespace:     OpenGL
// Class:         WGL
// File Version:  4.0 (2003 Feb 14; Second Public Release)
//
// Description: C# Wrapper for miscellaneous Windows code to support OpenGL.
//
// WARNING: This class must be compiled using the /unsafe C# compiler switch
//          since many OpenGL functions involve pointers.
//
// Development Notes:
//
//   This file does not correspond to any particular C/C++ header file.
// I hand-picked a small set of functions necessary to support OpenGL 
// C# application development for Windows.
//
// Future Versions of this File:
//
//   This file is the first public release of a particular C# wrapper for
// Windows support for OpenGL applications.  Please visit the following web
// page for updates to this file:
//
//     http://www.colinfahey.com/opengl/csharp.htm
//
//   Send any bug reports or minor implementation enhancement suggestions
// to the following e-mail address:
//
//     cpfahey@earthlink.net      (Colin P. Fahey)
//
// ############################################################################
// ############################################################################
// ############################################################################
// ############################################################################

using System;
using System.Runtime.InteropServices; // Necessary for [DllImport(...)]
namespace OpenGL
{
[ComVisible(false)]
public class WGL
{

// ############################################################################
// ############################################################################
// ############################################################################
// ############################################################################

// ============================================================================
//  int MessageBox
//    (
//    HWND hWnd,          // handle to owner window
//    LPCTSTR lpText,     // text in message box
//    LPCTSTR lpCaption,  // message box title
//    UINT uType          // message box style
//    );
// ============================================================================
[DllImport("user32")]
public static extern unsafe
int MessageBox( uint hwnd, string text, string caption, uint type );

public const uint MB_OK = 0;

// ############################################################################
// ############################################################################
// ############################################################################
// ############################################################################

// ============================================================================
//  HDC GetDC
//    (
//    HWND hWnd   // handle to window
//    );
// ============================================================================
[DllImport("user32")]
public static extern unsafe
uint GetDC( uint hwnd );

// ############################################################################
// ############################################################################
// ############################################################################
// ############################################################################

// ============================================================================
//
//  typedef struct tagPIXELFORMATDESCRIPTOR { // pfd   
//    WORD  nSize; 
//    WORD  nVersion; 
//    DWORD dwFlags; 
//    BYTE  iPixelType; 
//    BYTE  cColorBits; 
//    BYTE  cRedBits; 
//    BYTE  cRedShift; 
//    BYTE  cGreenBits; 
//    BYTE  cGreenShift; 
//    BYTE  cBlueBits; 
//    BYTE  cBlueShift; 
//    BYTE  cAlphaBits; 
//    BYTE  cAlphaShift; 
//    BYTE  cAccumBits; 
//    BYTE  cAccumRedBits; 
//    BYTE  cAccumGreenBits; 
//    BYTE  cAccumBlueBits; 
//    BYTE  cAccumAlphaBits; 
//    BYTE  cDepthBits; 
//    BYTE  cStencilBits; 
//    BYTE  cAuxBuffers; 
//    BYTE  iLayerType; 
//    BYTE  bReserved; 
//    DWORD dwLayerMask; 
//    DWORD dwVisibleMask; 
//    DWORD dwDamageMask; 
//  } PIXELFORMATDESCRIPTOR; 
//  
// ============================================================================
[StructLayout(LayoutKind.Sequential)] 
public struct PIXELFORMATDESCRIPTOR 
{
public ushort  nSize; 
public ushort  nVersion; 
public uint    dwFlags; 
public byte    iPixelType; 
public byte    cColorBits; 
public byte    cRedBits; 
public byte    cRedShift; 
public byte    cGreenBits; 
public byte    cGreenShift; 
public byte    cBlueBits; 
public byte    cBlueShift; 
public byte    cAlphaBits; 
public byte    cAlphaShift; 
public byte    cAccumBits; 
public byte    cAccumRedBits; 
public byte    cAccumGreenBits; 
public byte    cAccumBlueBits; 
public byte    cAccumAlphaBits; 
public byte    cDepthBits; 
public byte    cStencilBits; 
public byte    cAuxBuffers; 
public byte    iLayerType; 
public byte    bReserved; 
public uint    dwLayerMask; 
public uint    dwVisibleMask; 
public uint    dwDamageMask; 
// 40 bytes total
}

/* pixel types */
public const uint  PFD_TYPE_RGBA        = 0;
public const uint  PFD_TYPE_COLORINDEX  = 1;

/* layer types */
public const uint  PFD_MAIN_PLANE       = 0;
public const uint  PFD_OVERLAY_PLANE    = 1;
public const uint  PFD_UNDERLAY_PLANE   = 0xff; // (-1)

/* PIXELFORMATDESCRIPTOR flags */
public const uint  PFD_DOUBLEBUFFER            = 0x00000001;
public const uint  PFD_STEREO                  = 0x00000002;
public const uint  PFD_DRAW_TO_WINDOW          = 0x00000004;
public const uint  PFD_DRAW_TO_BITMAP          = 0x00000008;
public const uint  PFD_SUPPORT_GDI             = 0x00000010;
public const uint  PFD_SUPPORT_OPENGL          = 0x00000020;
public const uint  PFD_GENERIC_FORMAT          = 0x00000040;
public const uint  PFD_NEED_PALETTE            = 0x00000080;
public const uint  PFD_NEED_SYSTEM_PALETTE     = 0x00000100;
public const uint  PFD_SWAP_EXCHANGE           = 0x00000200;
public const uint  PFD_SWAP_COPY               = 0x00000400;
public const uint  PFD_SWAP_LAYER_BUFFERS      = 0x00000800;
public const uint  PFD_GENERIC_ACCELERATED     = 0x00001000;
public const uint  PFD_SUPPORT_DIRECTDRAW      = 0x00002000;

/* PIXELFORMATDESCRIPTOR flags for use in ChoosePixelFormat only */
public const uint  PFD_DEPTH_DONTCARE          = 0x20000000;
public const uint  PFD_DOUBLEBUFFER_DONTCARE   = 0x40000000;
public const uint  PFD_STEREO_DONTCARE         = 0x80000000;

// ############################################################################
// ############################################################################
// ############################################################################
// ############################################################################

// ============================================================================
// int ChoosePixelFormat
//   (
//   HDC  hdc,  // device context to search for a best pixel format 
//              // match
//   CONST PIXELFORMATDESCRIPTOR *  ppfd 
//              // pixel format for which a best match is sought
//   );
// ============================================================================
[DllImport("gdi32")]
public static extern unsafe
int ChoosePixelFormat( uint hdc, PIXELFORMATDESCRIPTOR * p_pfd );

// ############################################################################
// ############################################################################
// ############################################################################
// ############################################################################

// ============================================================================
// BOOL SetPixelFormat
//   (
//   HDC  hdc,  // device context whose pixel format the function 
//              // attempts to set
//   int  iPixelFormat,
//              // pixel format index (one-based)
//   CONST PIXELFORMATDESCRIPTOR *  ppfd 
//              // pointer to logical pixel format specification
//   );
// ============================================================================
[DllImport("gdi32")]
public static extern unsafe
int SetPixelFormat( uint hdc, int iPixelFormat, PIXELFORMATDESCRIPTOR * p_pfd );

// ############################################################################
// ############################################################################
// ############################################################################
// ############################################################################

// ============================================================================
// HGLRC wglCreateContext
//   (
//   HDC  hdc   // device context of device that the rendering context 
//              // will be suitable for
//   );
// ============================================================================
[DllImport("opengl32")]
public static extern unsafe
uint wglCreateContext( uint hdc );


// ############################################################################
// ############################################################################
// ############################################################################
// ############################################################################

// ============================================================================
// BOOL wglMakeCurrent
//   (
//   HDC  hdc,      // device context of device that OpenGL calls are 
//                  // to be drawn on
//   HGLRC  hglrc   // OpenGL rendering context to be made the calling 
//                  // thread's current rendering context
//   );
// ============================================================================
[DllImport("opengl32")]
public static extern unsafe
int wglMakeCurrent( uint hdc, uint hglrc );

// ############################################################################
// ############################################################################
// ############################################################################
// ############################################################################

// ============================================================================
// BOOL wglDeleteContext
//   (
//   HGLRC  hglrc   // handle to the OpenGL rendering context to delete
//   );
// ============================================================================
[DllImport("opengl32")]
public static extern unsafe
int wglDeleteContext( uint hglrc );

// ############################################################################
// ############################################################################
// ############################################################################
// ############################################################################

// ============================================================================
// BOOL wglSwapBuffers
//   (
//   HDC  hdc  // device context whose buffers get swapped
//   );
// ============================================================================
[DllImport("opengl32")]
public static extern unsafe
uint wglSwapBuffers( uint hdc );

// ############################################################################
// ############################################################################
// ############################################################################
// ############################################################################

// ============================================================================
// DWORD GetLastError(VOID);
// ============================================================================
[DllImport("kernel32")]
public static extern unsafe
uint GetLastError( );
// DWORD GetLastError(VOID);

// ############################################################################
// ############################################################################
// ############################################################################
// ############################################################################

// ============================================================================
// The following function, DemoCreateRenderingContext(ref_uint_DC,ref_uint_RC),
// can be used as a simple way to create an OpenGL "Rendering Context" (RC).
// **** DO NOT CALL DemoCreateRenderingContext() DIRECTLY IF YOU CHOOSE TO
// CALL DemoInitOpenGL() (below) TO ESTABLISH OPENGL. ****
// ============================================================================
public static unsafe void DemoCreateRenderingContext
  (
  ref uint  ref_uint_DC,
  ref uint  ref_uint_RC
  ) 
{
  ref_uint_RC = 0;

  PIXELFORMATDESCRIPTOR pfd = new PIXELFORMATDESCRIPTOR();

  // --------------------------------------------------------------------------
  pfd.nSize           = 40; // sizeof(PIXELFORMATDESCRIPTOR); 
  pfd.nVersion        = 1; 
  pfd.dwFlags         = (PFD_DRAW_TO_WINDOW |  PFD_SUPPORT_OPENGL |  PFD_DOUBLEBUFFER); 
  pfd.iPixelType      = (byte)(PFD_TYPE_RGBA);
  pfd.cColorBits      = 32; 
  pfd.cRedBits        = 0; 
  pfd.cRedShift       = 0; 
  pfd.cGreenBits      = 0; 
  pfd.cGreenShift     = 0; 
  pfd.cBlueBits       = 0; 
  pfd.cBlueShift      = 0; 
  pfd.cAlphaBits      = 0; 
  pfd.cAlphaShift     = 0; 
  pfd.cAccumBits      = 0; 
  pfd.cAccumRedBits   = 0; 
  pfd.cAccumGreenBits = 0;
  pfd.cAccumBlueBits  = 0; 
  pfd.cAccumAlphaBits = 0;
  pfd.cDepthBits      = 32; 
  pfd.cStencilBits    = 0; 
  pfd.cAuxBuffers     = 0; 
  pfd.iLayerType      = (byte)(PFD_MAIN_PLANE);
  pfd.bReserved       = 0; 
  pfd.dwLayerMask     = 0; 
  pfd.dwVisibleMask   = 0; 
  pfd.dwDamageMask    = 0; 
  // --------------------------------------------------------------------------



  // --------------------------------------------------------------------------
  // Choose Pixel Format
  // --------------------------------------------------------------------------
  int  iPixelFormat = 0;

  PIXELFORMATDESCRIPTOR* _pfd = &pfd;
  iPixelFormat = WGL.ChoosePixelFormat(ref_uint_DC, _pfd);

  if (0 == iPixelFormat)
    {
    uint   uint_LastError = WGL.GetLastError();
    string string_Message = "ChoosePixelFormat() FAILED:  Error: " + uint_LastError;
    WGL.MessageBox( 0, string_Message, "WGL.DemoGetRenderingContext() : ERROR", MB_OK );
    return;
    }
  // --------------------------------------------------------------------------


  // --------------------------------------------------------------------------
  // Set Pixel Format
  // --------------------------------------------------------------------------
  int int_Result_SPF = 0;

  int_Result_SPF = WGL.SetPixelFormat(ref_uint_DC, iPixelFormat, _pfd);

  if (0 == int_Result_SPF)
    {
    uint   uint_LastError = WGL.GetLastError();
    string string_Message = "SetPixelFormat() FAILED.  Error: " + uint_LastError;
    WGL.MessageBox( 0, string_Message, "WGL.DemoGetRenderingContext() : ERROR", MB_OK );
    return;
    }
  // --------------------------------------------------------------------------



  // --------------------------------------------------------------------------
  // Create Rendering Context (RC)
  // NOTE: You will get the following error:
  //             126 : ERROR_MOD_NOT_FOUND
  // if you attempt to create a render context too soon after creating a
  // window and getting its Device Context (DC).
  // See the comments for WGL.DemoInitOpenGL() on how to use a call to
  // WGL.wglSwapBuffers( ref_uint_DC ) before attempting to create the RC.
  // --------------------------------------------------------------------------
  ref_uint_RC = WGL.wglCreateContext( ref_uint_DC );

  if (0 == ref_uint_RC)
    {    
    uint   uint_LastError = WGL.GetLastError();
    string string_Message = "wglCreateContext() FAILED.  Error: " + uint_LastError;
    WGL.MessageBox( 0, string_Message, "WGL.DemoGetRenderingContext() : ERROR", MB_OK );
    return;
    }
  // --------------------------------------------------------------------------


  // --------------------------------------------------------------------------
  // Make the new Render Context (RC) the current Render Context (RC)
  // --------------------------------------------------------------------------
  int int_Result_MC = 0;

  int_Result_MC = WGL.wglMakeCurrent( ref_uint_DC, ref_uint_RC );

  if (0 == int_Result_MC)
    {
    uint   uint_LastError = WGL.GetLastError();
    string string_Message = "wglMakeCurrent() FAILED.  Error: " + uint_LastError;
    WGL.MessageBox( 0, string_Message, "WGL.DemoGetRenderingContext() : ERROR", MB_OK );
    // ***************************
    WGL.wglDeleteContext( ref_uint_RC );
    ref_uint_RC = 0;
    // ***************************
    return;
    }
  // --------------------------------------------------------------------------        
}

// ############################################################################
// ############################################################################
// ############################################################################
// ############################################################################

// ============================================================================
// The following can be used as an example of how to initialize OpenGL 
// rendering.  A System.Windows.Forms object can use a window handle acquired
// from (uint)((this.Handle).ToInt32()) as the "HWND" parameter.
//
// Here is a crude illustration of the use of WGL.DemoInitOpenGL() by a Form:
//
//    // ----------------------------------------------------------------------
//    public static uint            m_uint_HWND = 0;
//    public static uint            m_uint_DC   = 0;
//    public static uint            m_uint_RC   = 0;
//    
//    protected override void OnPaintBackground( PaintEventArgs e )
//    { 
//    // This overrides the System.Windows.Forms.Control protected method
//    // "OnPaintBackground()" so that we don't clear the client area of
//    // this form window -- so the OpenGL doesn't flicker on each frame.
//    }
//    
//    protected override void OnPaint( System.Windows.Forms.PaintEventArgs e )
//    {
//    if (0 == m_uint_RC)
//      {
//      m_uint_HWND = (uint)((this.Handle).ToInt32());
//      WGL.DemoInitOpenGL( m_uint_HWND, ref m_uint_DC, ref m_uint_RC );
//      }
//    if (0 != m_uint_RC)
//      {
//      WGL.DemoOpenGLDraw( this.Size.Width, this.Size.Height,  m_uint_DC );
//      }
//    System.Threading.Thread.Sleep( 10 ); // 10 msec --> 100 frames per second, max.
//    Invalidate(false); // Force OnPaint() to get called again.
//    }
//    // ----------------------------------------------------------------------
//
// ============================================================================
public static void DemoInitOpenGL
  (
  uint         uint_HWND,  // in
  ref uint ref_uint_DC,    // out
  ref uint ref_uint_RC     // out
  )
{
  ref_uint_DC   = WGL.GetDC( uint_HWND );

  // CAUTION: Not doing the following WGL.wglSwapBuffers() on the DC will
  // result in a failure to subsequently create the RC.
  WGL.wglSwapBuffers( ref_uint_DC );

  WGL.DemoCreateRenderingContext( ref ref_uint_DC, ref ref_uint_RC );
  
  if (0 == ref_uint_RC) 
    {
    WGL.MessageBox( 0, "Failed to create OpenGL Rendering Context (RC)", 
                "WGL.DemoInitOpenGL() : ERROR", MB_OK );
    return;
    }
}

// ############################################################################
// ############################################################################
// ############################################################################
// ############################################################################


// ============================================================================
// The following is an example of OpenGL rendering code, complete with
// buffer swapping.  This function can be called by a Form's "OnPaint()"
// method if a previous WGL.DemoInitOpenGL() call (for example) has
// already successfully established a valid Render Context (RC).
// ============================================================================

public static void DemoOpenGLDraw
  (
  int      int_WindowWidth,
  int      int_WindowHeight,
  uint     uint_DC
  )
{
  int int_Phase = (int)(System.Environment.TickCount % 120000);
  float float_Phase = (float)(0.3f * (int_Phase));

  if (int_WindowWidth  <= 0)  int_WindowWidth  = 1;
  if (int_WindowHeight <= 0)  int_WindowHeight = 1;

  GL.glViewport( 0, 0, int_WindowWidth, int_WindowHeight );

  GL.glEnable     ( GL.GL_DEPTH_TEST );
  GL.glDepthFunc  ( GL.GL_LEQUAL     );
  GL.glEnable     ( GL.GL_CULL_FACE  );
  GL.glCullFace   ( GL.GL_BACK       );
  GL.glClearColor ( 0.0f, 0.0f, 0.0f, 0.0f );
  GL.glClear      ( GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT );
  GL.glMatrixMode ( GL.GL_PROJECTION );
  GL.glLoadIdentity();

  //GL.glOrtho( 0.0f, (float)(int_WindowWidth), 0.0f, (float)(int_WindowHeight), -1.0f, 1.0f );
  GLU.gluPerspective
      ( 
      60.0,    // Field of view angle (Y angle; degrees)
      ((double)(int_WindowWidth) / (double)(int_WindowHeight)), 
      1.0,     // Near plane
      1000.0   // Far  plane
      );

  GL.glMatrixMode ( GL.GL_MODELVIEW );
  GL.glLoadIdentity();

  // Translating the camera to +600.0f Z is essentially
  // adding -600.0f to all drawing commands.  
  GL.glTranslatef( 0.0f, 0.0f, -600.0f );

  GL.glRotatef( (0.11f * float_Phase), 1.0f, 0.0f, 0.0f );
  GL.glRotatef( (0.31f * float_Phase), 0.0f, 1.0f, 0.0f );
  GL.glRotatef( (0.19f * float_Phase), 0.0f, 0.0f, 1.0f );

  float[][] vert_xyz = new float[8][]
  {
  new float[] { -100.0f, -100.0f, -100.0f },  // 0
  new float[] { -100.0f, -100.0f,  100.0f },  // 1
  new float[] { -100.0f,  100.0f, -100.0f },  // 2
  new float[] { -100.0f,  100.0f,  100.0f },  // 3
  new float[] {  100.0f, -100.0f, -100.0f },  // 4
  new float[] {  100.0f, -100.0f,  100.0f },  // 5
  new float[] {  100.0f,  100.0f, -100.0f },  // 6
  new float[] {  100.0f,  100.0f,  100.0f }   // 7
  };
  int [][] tri_abc = new int [12][]
  {
  new int[] {0,2,4}, new int[] {4,2,6}, // Back
  new int[] {0,4,1}, new int[] {1,4,5}, // Bottom
  new int[] {0,1,2}, new int[] {2,1,3}, // Left
  new int[] {4,6,5}, new int[] {5,6,7}, // Right
  new int[] {2,3,6}, new int[] {6,3,7}, // Top
  new int[] {1,5,3}, new int[] {3,5,7}  // Front
  };
  float[][] colors_rgb = new float[12][]
  {
  new float[] {0.5f,0.1f,0.1f }, new float[] {1.0f,0.1f,0.1f }, // Red
  new float[] {0.5f,0.5f,0.1f }, new float[] {1.0f,1.0f,0.1f }, // Yellow
  new float[] {0.1f,0.5f,0.1f }, new float[] {0.1f,1.0f,0.1f }, // Green
  new float[] {0.1f,0.5f,0.5f }, new float[] {0.1f,1.0f,1.0f }, // Cyan
  new float[] {0.1f,0.1f,0.5f }, new float[] {0.1f,0.1f,1.0f }, // Blue
  new float[] {0.5f,0.1f,0.5f }, new float[] {1.0f,0.1f,1.0f }  // Magenta
  };

  int iTriTotal = 12;
  int iTriIndex = 0;
  GL.glBegin( GL.GL_TRIANGLES );
  for ( iTriIndex = 0; iTriIndex < iTriTotal; iTriIndex++ )
    {
    GL.glColor3fv( colors_rgb[iTriIndex] );
    GL.glVertex3fv( vert_xyz[tri_abc[iTriIndex][0]] );

    GL.glColor3fv( colors_rgb[iTriIndex] );
    GL.glVertex3fv( vert_xyz[tri_abc[iTriIndex][1]] );

    GL.glColor3fv( colors_rgb[iTriIndex] );
    GL.glVertex3fv( vert_xyz[tri_abc[iTriIndex][2]] );
    }
  GL.glEnd();

  WGL.wglSwapBuffers( uint_DC );
}


// ############################################################################
// ############################################################################
// ############################################################################
// ############################################################################
//
//
} // public class WGL
} // namespace OpenGL
//
//
// ############################################################################
// ############################################################################
// ############################################################################
// ############################################################################