-Writing an OpenGL Wrapper-

To make any decent OpenGL based programs, we must have some decent OpenGL tools. This tutorial takes you through the steps for writing a wrapper class around OpenGL, sorta like like DXDraw that comes with DelphiX (if you dont know what DelphiX is, what planet have you been on ?). Of course, it is perfectly viable to use a totally API based framework, but I find that VCL and class based code is a lot easier to understand, maintain and modify. So I'm going to stick with this paradigm (until I'm convinced otherwise :)

You will probably see a lot of missing things in the wrapper. This is due to two reasons.
A wrapper with all the frills would require a lot more code and explanation than one tut could accomodate.I find it best to learn when the necessity arises, that way, you are able to use what you have learned right away.
Another thing. This tut will not try to explain what a rendering context is, and stuff. All it will do is try to make a concrete class, one that can be used time and again (and extended). A basic working knowledge of OpenGL is assumed.
Anyway, here goes nothing.
The basic methods we would for initialization and cleanup are InitGL and CleanupGL. InitGL has to be called after the class is created, but CLeanUPGL automatically happens when the class is destroyed. Another method, InitGLFromHandle is useful when you want to render to some object other than the form's surface (mostly a TPanel). For fullscreen rendering (will work only when OpenGL is inited with a form.), we call ChangeDisplaySettingsA from User32.dll. This function can arbitrarily change the desktop resolution and colordepth. We will now walk through the declaration of our wrapper, and see what it's made of.
    TOpenGL=class(TObject)
            private
            //The device context
            fDC:HDC;
            //The OpenGL rendering context that we will init
            fHRC:HGLRC;
            //Palette in case the user needs color-index mode
            fPalette:HPALETTE;            //The color using which the screen will be cleared
            fClearColor:TColorf;            //Fullscreen? YES! YES!
            fFullScreen:boolean;
            //Resolution at which to go fullscreen
            fXRes,fYRes:integer;
            //If OpenGL inited using a form, a pointer to it
            fOwner:TForm;
            //Was OpenGL inited from just a handle?
            fFromHandle:boolean;
            //The handle from which OpenGL was inited
            fHandle:THandle;
            protected
            //Make our rendering context the current one.
            procedure MakeCurrent;            //Access methods for some properties
            procedure SetClearColor(aColor:TColorf);
            procedure SetFullScreen(v:boolean);            public
            //Sets up basic info.
            constructor Create(aOwner:TForm);
            //DUH!
            destructor Destroy;override;            //Initialize OpenGL with the given info
            procedure InitGL;
            //Initialize OpenGL with the given info
            procedure InitGLFromHandle(aHandle:THandle);
            //Clean up after OpenGL
            procedure CleanUpGL;            //Flip the backbuffer to the front buffer
            procedure Flip;
            //Clear all the inited buffer
            procedure ClearAll;
            //Clear the ZBuffer alone
            procedure ClearZBuffer;
            //Clear the Color buffer alone
            procedure ClearColorBuffer;
            //Clear the stencil buffer alone
            procedure ClearStencilBuffer;
            //The device context
            property DC:HDC read fDC;
            //The rendering context
            property RC:HGLRC read fHRC;
            //The palette
            property Palette:HPALETTE
                     read fPalette
                     write fPalette;
            //The clear color
            property ClearColor:TColorf
                     read fClearColor
                     write SetClearColor;
            //An alien from mars...
            //Just checking to see if you are really reading these silly
            //comments
            property XRes:integer
                     read fXRes
                     write fXRes
                     default 640;
            //Y resolution
            property YRes:integer
                     read fYRes
                     write fYRes
                     default 480;
            //FullScreen property
            property FullScreen:boolean
                     read fFullScreen
                     write SetFullScreen;
    end;
So at the end of all this, we can initialze OpenGL and shut it down. What good is that going to do? We need to render something, right. At least just to prove to someone that you dont simply goof of in front of the computer?
We need to set up some more stuff before we can actually draw some stuff to the screen. The most basic is a projection matrix. Instead of using gluPerspective all over the program, what we will do is write another small class that sets this up for us. A plus of making a class from this is that whenever we write a camera, we can declare a TViewVolume (that's what we're gonna call the class) instance inside a TCamera so that the internal details are all hidden. All we do is call Camera.SetView, and the camera takes care of the rest of the things. This will be especially useful when you have to render 2D text on top of a 3D scene. All we would have to do is,(I'm assuming we don't have the camera class, but the technique would essentially remain the same). Declare and create two instances of TViewVolume. Set up one for 2D rendering, andthe other for 3D . To render the 3D scene, call the appropriate instance of TViewVolume's SetMatrix method. Simple, isnt it? Instead of maintaining flags and such?!
Now for a walkthrough of the TViewVolume declaration. This is, again, a primitive version that supports only perspective viewing volumes.When we come around to rendering text, we will extend this class, as we will a great many others.
    //A **really** simple class, but goddamn helpful around the place
    TViewVolume=class
                     private
                     //The field-of-view
                     fFOV,
                     //distance to the near clipping plane
                     fHither,
                     //distance to the far clipping plane
                     fYon:double;
                     //Viewport information
                     fX,fY,
                     fWidth,
                     fHeight:integer;
                     public
                     //I really should stop commenting each line :)
                     constructor Create;
                     //Set the projection params
                     procedure SetMatrix;
                     //Properties for the private member vars
                     //described above
                     property FOV:double
                              read fFOV
                              write fFOV;
                     property Hither:double
                              read fHither
                              write fHither;
                     property Yon:double
                              read fYon
                              write fYon;
                     property X:integer
                              read fX
                              write fX;
                     property Y:integer
                              read fY
                              write fY;
                     property Width:integer
                              read fWidth
                              write fWidth;
                     property Height:integer
                              read fHeight
                              write fHeight;    end;
We are finally ready to render something onto the screen. All we will render is a GLUT teapot, Stop groaning, GLUT objects are an extremely easy way of testing rendering code. Or would you rather sit up another couple of hours and code a file format loader? (If you stick with me, I'll make you do that too). What I want you to note is that, the main rendering code will look really neat. All the messy details are tucked away inside classes. That's the way it should be. I'm presenting the code for a simple example here. The component names in the code should be self-descriptive.
unit Main;interfaceuses
    Windows,
    Messages,
    SysUtils,
    Classes,
    Graphics,
    Controls,
    Forms,
    Dialogs,
    GL,
    GLU,
    GLUT,
    GLClass,
    ExtCtrls;type
  TMainForm = class(TForm)
    Timer: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure TimerTimer(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
  private
    { Private declarations }
  public
    { Public declarations }
    //Declare an instance so we can use it
    GL:TOpenGL;
    //we need to set up a projection matrix, right?
    ViewVolume:TViewVolume;    //Some mouse movement to add spice to this drab tut
    MousePressed:boolean;
    xStart,xDelta,yStart,yDelta:integer;
    xRot,yRot:single;
  end;var
  MainForm: TMainForm;implementation{$R *.DFM}procedure TMainForm.FormCreate(Sender: TObject);
begin
     //Create it. We are going to render to the form.
     GL:=TOpenGL.Create(Self);
     //Check if the user wants to run fullscreen or not.
     GL.FullScreen:=Application.MessageBox('Would you like to run fullscreen?',
                                           'TOpenGL Demo',
                                           MB_YESNO or MB_ICONQUESTION)=ID_YES;
     //That's it. Cool, aint it?
     //Any changes you want to make will never muck up this code.
     GL.InitGL;     //Create it and set up the width and height of the window
     ViewVolume:=TViewVolume.Create;
     //we could also do this if we wanted to resize the window
     ViewVolume.Width:=ClientWidth;
     ViewVolume.Height:=ClientHeight;     MousePressed:=false;
     xRot:=0;
     yRot:=0;     //Let the flipping begin
     Timer.Enabled:=true;
end;procedure TMainForm.FormDestroy(Sender: TObject);
begin
     //Stop the rendering. We dont want to try and render
     //a destroyed object, right?!!
     Timer.Enabled:=false;     if assigned(GL) then GL.Destroy;
end;procedure TMainForm.TimerTimer(Sender: TObject);
begin
     //Clear contents of previous render
     GL.ClearColorBuffer;     //Set the projection matrix
     ViewVolume.SetMatrix;     //We will simply translate ourselves and watch a little teapot
     glTranslatef(0,0,-10);
     glRotatef(xRot,1,0,0);
     glRotatef(yRot,0,1,0);
     GLUTWireTeapot(1.0);     //Flip the screen
     GL.Flip;
end;procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
     case Key of
          //Now quit, we should...like any good app would :)
          VK_ESCAPE:Close;
     end;
end;procedure TMainForm.FormMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
     //Mouse button is being held down
     MousePressed:=true;
     //Note the starting position
     xStart:=X;
     yStart:=Y;
end;procedure TMainForm.FormMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
     //User released the mouse
     MousePressed:=false;
end;procedure TMainForm.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
     //Dragging is happening only if the mouse button is down
     if not MousePressed then exit;
     //Calculate how much the mouse has moved
     xDelta:=xStart-X;
     yDelta:=yStart-Y;     //Adjust rotation and scale so it is'nt too fast
     xRot:=xRot-yDelta/2;
     yrot:=yRot-xDelta/2;     //Next time, we start from here
     xStart:=X;
     yStart:=Y;
end;end.
If you notice, the only messy pieces of code are where we do the rotation and set up the viewing matrix. Why, you ask, didnt I write a camera as well, after making you sit through all this? The reason is that a camera is so integral to everything that we will write, so we will devote an entire tutorial to it. Coming soon.
>>Download the tutorial source
You'll also need, in case you dont have them already
>>Download the OpenGL 1.2 headers
>>Download the GLUT pack