Graphics Programming

Introduction to C++ OpenGL Programming

    Welcome to another fine lesson in C++! Today you'll be introduced to the wonderful world of OpenGL. OpenGL is a fairly straight forward -- although at many times confusing -- concept. OpenGL gives the programmer an interface with the graphics hardware. OpenGL is a low-level, widely supported modeling and rendering software package, available on all platforms. It can be used in a range of graphics applications, such as games, CAD design, or modeling (to name a few).
OpenGL is the core graphics rendering option for many 3D games, such as Quake 3. The providing of only low-level rendering routines is fully intentional because this gives the programmer a great control and flexibility in his applications. These routines can easily be used to build high-level rendering and modeling libraries. The OpenGL Utility Library (GLU) does exactly this, and is included in most OpenGL distributions!
Unlike DirectX, OpenGL is only a graphics API; it doesn't include support for functionality such as sound, input, or networking (or anything not related to graphics). That's ok, however, because in future tutorials I will teach you how you how to use DirectX for these things.
OpenGL was originally developed in 1992 by Silicon Graphics, Inc, (SGI) as a multi-purpose, platform independent graphics API. Since 1992 all of the development of OpenGL has been headed by the OpenGL Architecture Review Board (ARB). This exclusive board is composed of the major graphics vendors and industry leaders. Some of these are Intel, IBM, NVIDIA, Microsoft, and Silicon Graphics.
OpenGL is a collection of several hundred functions that provide access to all of the features that your graphics hardware has to offer. Internally it acts like a state machine-a collection of states that tell OpenGL what to do. Using the API you can set various aspects of this state machine, including current color, blending, lighting effect, etc.
This is a very general introduction to OpenGL, and you may find other in-depth introductions elsewhere. There is a reason, however, to my generalized introduction. I don't want to slam you with specifics, but give you an idea as to what OpenGL is so that you may decide for yourself if this set of lessons is for you. As always, happy coding, and see you next lesson (I hope)!

OpenGL vs. DirectX: A Comparison


    The competition between OpenGL and DirectX is possibly as well known as the wars waged between AMD and Intel enthusiasts. This topic has sparked the fires of many flame wars throughout the years, and I don't anticipate that changing anytime soon. I won't preach why I prefer OpenGL over DirectX, but rather lay out the facts and let you make that decision. So let's dive in!
Perhaps the most obvious difference is that DirectX, as opposed to OpenGL, is more than just a graphics API. DirectX contains tools to deal with such components of a game as sound, music, input, networking, and multimedia. On the other hand, OpenGL is strictly a graphics API. So what aspect of OpenGL sets it apart from the DirectX graphics component?

Well, first things first: both APIs rely on the use of the traditional graphics pipeline. This is the same pipeline that has been used in computer games since the early days of computer graphics. Although it has been modified in slight ways to adapt with advancements in hardware, the basic idea remains intact.

Both OpenGL and DirectX describe vertices as a set of data consisting of coordinates in space that define the vertex location and any other vertex related data. Graphics primitives, such as points, lines, and triangles, are defined as an ordered set of vertices. There is a difference in how each API handles how vertices are combined to form primitives.

There are a bunch of differences in the DirectX and OpenGL APIs, so I will list a few of those for you. This chart is based off the book, OpenGL Game Programming and a few of these may now be incorrect as new DirectX versions are released. If you wish to correct me, please do via one of the ways listed at the end of this tutorial.

Table 1.1:
Feature:OpenGLDirectX
Vertex BlendingN/AYes
Multiple Operating SystemsYesNo
Extension MechanismYesYes
DevelopmentMultiple member BoardMicrosoft
Thorough SpecificationYesNo
Two-sided lightingYesNo
Volume TexturesYesNo
Hardware independent Z-buffersYesNo
Accumulation buffersYesNo
Full-screen AntialiasingYesYes
Motion BlurYesYes
Depth of fieldYesYes
Stereo RenderingYesNo
Point-size/line-width attributesYesNo
PickingYesNo
Parametric curves and surfacesYesNo
Cache geometryDisplay ListsVertex Buffers
System emulationHardware not presentLet app determine
InterfaceProcedure callsCOM
UpdatesYearlyYearly
Source CodeSampleSDK Implementation

So now you know what separates DirectX and OpenGL, and hopefully you have chosen based on facts which you would prefer, not on myths or opinions. I hope you chose OpenGL, as that's what I will be teaching. If you chose DirectX my tutorials won't help you very much. In the next lesson we will begin with our first OpenGL program.

Introduction to Windows Programming and OpenGL


   At this time we will take a look into the vast world of Windows programming. Since this tutorial is based entirely on programming done within the Windows environment, it will be required of you to have access to a Windows machine and compiler such as Code Blocks. 


For the programs we will be creating you will need a base understanding of the mechanics and structuring of the Windows operating system. Not to worry, however, because I am going to teach this to you!

Microsoft Windows is a multi-tasking operating system that allows multiple applications, referred to here on out as processes. Every process in Windows is given a certain amount of time, called a time slice, where the application is given the right to control the system without being interrupted by the other processes. The runtime priority and the amount of time allocated to a process are determined by the scheduler.

The scheduler is, simply put, the manager of this multi-tasking operating system, ensuring that each process is given the time and the priority it needs depending on the current state of the system.

When it comes to game developers, you will find a large common interest in multithreading. Processes can be broken down into threads, where each thread can execute its own code to allow for multitasking within a single application. The scheduling for threads is the same as that for processes, except that threads are what make up the process.

This means that within your games you can have multiple threads running that can perform multiple calculations at once and thus provide your game with multitasking within itself. To go a step farther we can do a quick explanation of fibers. In newer versions of Windows (Windows 98+) there is an even lower level execution object exists, called a fiber. Each thread in your process has the ability to house multiple fibers that can perform multiple operations at once, all in a single thread.

Windows is what is known as an event-driven operating system. What this means is that each process is executed based on the events they receive from the operating system. For instance, an application may sit at idle and wait for the user to press a key. When that key is pressed Windows will register an event to the application that the key is down.

Windows programming is not difficult at all in most cases, and to start we'll use the most basic program ever created. Hello World is used in almost every class when you begin programming in any language, and that won't change here. So, shall we have a look at Hello World in Windows style? Sure!
#include <windows.h>
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
 	
	MessageBox(NULL, "\tHello World!", "My first windows app", NULL);
	return 0;
}
As you can see, it's pretty straight forward. Don't pay attention to the things that don't make sense, as in the next lesson we will go in-depth to explain WinMain () and other functions, as well as show how this and OpenGL work together!

WinMain() and the Windows Procedu

      Every Windows programming needs a main entry point. That being said, you may be wondering what this entry point is. The main entry point for any Windows program is called WinMain. The function prototype for WinMain is a little confusing at first, but as we continue to work with it you'll notice it becomes much easier to understand. Well. we've told you what it is; now were going to show you! Here's the prototype for WinMain:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd);


As you may have already noticed, the return type for WinMain is, and always will be, int. All 32-bit Windows operating system applications use the calling convention WINAPI. This calling convention MUST be used to distinguish the function as the entry point. Now for the parameters. As you can see, WinMain uses four parameters when the program starts. Let's have a look at them, shall we?
hInstance - is a handle to your applications instance, where an instance 
can be considered to be a single run of your application. The instance 
is used by windows as a reference to your application for event handling,
message processing, and various other duties.
hPrevInstance - is always NULL. 
lpCmdLine - is a pointer string that is used to hold any command-line
arguments that may have been specified when the application began.  
For example, if the user opened the Run application and typed myapp.exe
myparameter 1, then lpCmdLine would be myparameter 1.
nShowCMD - is the parameter that determines how your application's window
will be displayed once it begins executing.
Pretty simple, right? Don't worry if it's confusing, it will make sense soon enough! The Windows program you create is going to need some way to handle the messages that Windows will send to it. A few examples of these messages are: WM_CREATE, WM_SIZE, and WM_MOVE. There are TONS of these messages, and we'll show you how to handle a large number of them. To allow Windows to communicate with your application, we'll create a dandy little function called a Windows procedure. The most common name for this function is WndProc. This function MUST be created and used to determine how your application will respond to various events. The Windows procedure may also be called the event handler because, well, it responds to Windows events! So let's have a quick look at the prototype:
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
This function is declared with a return type of LRESULT CALLBACK. The LRESULT type is used by Windows to declare a long integer, and CALLBACK is a calling convention used with functions that are called by Windows. The Windows Procedure is a function pointer, which allows you to call it whatever you want because the function's address will be assigned as a function pointer upon creation of the window class.
hwnd - Only important if you have several windows of the same class open
at one time. This is used to determine which window hwnd pointed to before 
deciding on an action.
message - The actual message identifier that WndProc will be handling.
wParam and lParam - Extensions of the message parameter. Used to give 
more information and point to specifics that message cannot convey on its own.
Well now you should have a better understanding of these two topics. You may be wondering when you get to see some openGL usage, and the answer is soon. We first need to cover the basics, let's remember, we're here to learn! 

First Windows Application


   The following code is a build up on the basic "hello world" program I showed you earlier. Take a minute to review it, maybe even key it into your compiler and run it, and then we will break it down so that it is easier to understand what is actually going on.
/*	Trim fat from windows*/
#define WIN32_LEAN_AND_MEAN	
#pragma comment(linker, "/subsystem:windows")
/*	Pre-processor directives*/
#include "stdafx.h"
#include <windows.h>
/*	Windows Procedure Event Handler*/
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT paintStruct;
	/*	Device Context*/
	HDC hDC; 
	/*	Text for display*/
	char string[] = "Hello, World!"; 
	/*	Switch message, condition that is met will execute*/
	switch(message)
	{
		/*	Window is being created*/
		case WM_CREATE: 
			return 0;
			break;
		/*	Window is closing*/
		case WM_CLOSE: 
			PostQuitMessage(0);
			return 0;
			break;
		/*	Window needs update*/
		case WM_PAINT: 
			hDC = BeginPaint(hwnd,&paintStruct);
			/*	Set txt color to blue*/
			SetTextColor(hDC, COLORREF(0x00FF0000));
			/*	Display text in middle of window*/
			TextOut(hDC,150,150,string,sizeof(string)-1);
			EndPaint(hwnd, &paintStruct);
			return 0;
			break;
		default:
			break;
	}
	return (DefWindowProc(hwnd,message,wParam,lParam));
}
/*	Main function*/
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
	WNDCLASSEX  windowClass;		//window class
	HWND		hwnd;				//window handle
	MSG			msg;				//message
	bool		done;				//flag saying when app is complete
	/*	Fill out the window class structure*/
	windowClass.cbSize = sizeof(WNDCLASSEX);
	windowClass.style = CS_HREDRAW | CS_VREDRAW;
	windowClass.lpfnWndProc = WndProc;
	windowClass.cbClsExtra = 0;
	windowClass.cbWndExtra = 0;
	windowClass.hInstance = hInstance;
	windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	windowClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	windowClass.lpszMenuName = NULL;
	windowClass.lpszClassName = "MyClass";
	windowClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
	/*	Register window class*/
	if (!RegisterClassEx(&windowClass))
	{
		return 0;
	}
	/*	Class registerd, so now create window*/
	hwnd = CreateWindowEx(NULL,		//extended style
		"MyClass",			//class name
		"A Real Win App",		//app name
		WS_OVERLAPPEDWINDOW |		//window style
		WS_VISIBLE |
		WS_SYSMENU,
		100,100,			//x/y coords
		400,400,			//width,height
		NULL,				//handle to parent
		NULL,				//handle to menu
		hInstance,			//application instance
		NULL);				//no extra parameter's
	/*	Check if window creation failed*/
	if (!hwnd)
		return 0;
	done = false; //initialize loop condition variable
	/*	main message loop*/
	while(!done)
	{
		PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE);
		if (msg.message == WM_QUIT) //check for a quit message
		{
			done = true; //if found, quit app
		}
		else
		{
			/*	Translate and dispatch to event queue*/
			TranslateMessage(&msg); 
			DispatchMessage(&msg);
		}
	}
	return msg.wParam;
}
Well isn't this a whole mess of code! The first thing that you may have noticed is #define WIN32_LEAN_AND_MEAN. This syntax prevents Visual C++ from linking modules that you aren't going to need in your application.

Moving on we come to the include line #include <windows.h>. This includes all the headers you need in this application. Sometimes you may want to include <windowsx.h>, which will give you a few useful macros to use in Windows development.

The first function we arrive at is the WndProc function. We've discussed this before, so I am just going to highlight two lines I have added here.
HDC	hDC;				//device context
Char string[] = "Hello World!";	//display text
These two lines are essential as they declare the device context that we are going to use to output to the window we create, and the string that we will display. As we continue on in the code we arrive at the switch statement. This switch is used to determine the message being passed to the windows procedure. In this particular instance we want to take a closer look at the WM_PAINT block.
case WM_PAINT: 
			hDC = BeginPaint(hwnd,&paintStruct);
			/*	Set txt color to blue*/
			SetTextColor(hDC, COLORREF(0x00FF0000));
			/*	Display text in middle of window*/
			TextOut(hDC,150,150,string,sizeof(string)-1);
			EndPaint(hwnd, &paintStruct);
			return 0;
			break;
When the window is moved, resized, or is otherwise changed the window needs to be updated. The first noticeable change here is the use of the hDC device context. The win32 function BeginPaint() returns the graphics device context for the hwnd passed to it. You can then use this hDC to set the text color with SetTextColor() and follow up with the TextOut() function to display the text. If you want to know more about these functions, check out MSDN.

Next function up is the WinMain() function. Most of the WinMain() content is pretty straight forward, but were going to review a few parts of it for good measure. The msg variable holds the message received by PeekMessage() from the queue, and will be sent to TranslateMessage() and DispatchMessage(). The variable done is a Boolean value used by your message loop and will not equal true until a WM_QUIT message has been received from the queue to indicate that the application is about to be closed.

For easier understanding we will break the order of each setup task into a list.
1.	Window-class setup
2.	Window-class registration
3.	Window Creation
4.	Message loop with event handler
We now have a fully working Windows application! I encourage you to toy around with the code and alter it to your liking. Don't be discouraged by errors or problems, for it is these things that make us better.

WGL and the Wiggle Functions in C++


  The simple fact that OpenGL is only a graphics API means that any user interaction, or things like screen and window issues needs to be handled by the operating system. Most operating systems come with a set of extensions that tie OpenGL together with user interaction and window management. This tends to make our lives a little bit easier, but only if we know how to take advantage of this option. In Windows this set of functions are called the wiggle functions. Each of these functions is prefixed with wgl.
To maintain the portability of OpenGL, each operating system must supply functionality for specifying the rendering window before OpenGL can use it. In Windows the Graphics Device Interface uses a device context to remember settings about drawing modes and commands, but in OpenGL the rendering context is used to remember these things. You need to remember, however, that the device context is not a rendering context. The device context MUST be specified in a GDI call, unlike a rendering context, which is specified in an OpenGL call.

If you wish to render more than one window at once, multiple rendering contexts are allowed. You must ensure, however, that the proper rendering context is being used on the proper window. Another thing to remember is that OpenGL is thread-safe. You can have multiple threads rendering to the same device context at one time.

As I mentioned to you earlier, wiggle functions bring Windows API support into OpenGL. Well let's take this time to have a look at a few common wiggle functions:
  • wglCreateContext();
  • wglDeleteContext();
  • wglMakeCurrent();
The wglCreateContext() function creates a handle to the OpenGL rendering context while being passed like a handle to a GDI device context. The correct prototype for this function is:
	HGLRC	wglCreateContext(HDC hDC);
This function should only be called after the pixel format for the device context has been set. Don't worry, pixel format is coming into teaching shortly.

Keep in mind that as with a device context, a rendering context must be deleted after you are finished with it. This is where the wglDeleteContext() function comes into play. Let's have a look at the prototype for good measure:
	BOOL		wglDeleteContext(HGLRC hRC);
The name wglMakeCurrent() is highly accurate to what the function does. The function makes the rendering context passed to it the current rendering context. Makes a lot of sense, doesn't it? The device context used must have the same pixel format characteristics as the device context that was used to create the rendering context. This means that the device context used to create the rendering context does not need to be the same as the device context assigned to the wglMakeCurrent() function. Without further delay, let's see the prototype!
	BOOL		wglMakeCurrent(HDC hDC, HGLRC hRC);
You need to ensure that the device context and the rendering context passed to the function have the same pixel format, or the function will not work. To deselect the rendering context you can simply pass NULL for the hRC parameter, or simply pass another rendering context.

Both the wglCreateContext() and the wglMakeCurrent() functions should be called upon window creation. Let's look at a code snippet for an example of these in use:
LRESULT CALLBACK WndProc (HWND hwnd, UNIT message, WPARAM wParam, LPARAM lParam)
{
	static HGLRC	hRC;	//rendering context
	static HDC	hDC;	//device context
	switch (message)
	{
	case WM_CREATE:
	hDC = GetDC(hwnd);	//get the device context for window
	hRC = wglCreateContext(hDC);	//create rendering context
	wglMakeCurrent(hDC,hRC);	//make rendering context current
	break;
	case WM_DESTROY:
	wglMakeCurrent(hDC,NULL);	//deselect rendering context
	wglDeleteContext(hRC);		//delete rendering context
	PostQuitMessage(0);		//send wm_quit
	break;
	} } 
This code creates and destroys your OpenGL window. Before you actually do this, however, we must first discuss the PIXELFORMATDESCRIPTOR, which we will do in lesson 5. 

Sample OpenGL Program in C or C++


     In this lesson I shall introduce several functions and show you actual OpenGL rendering in a program. Prior to showing you the code, however, I want to go over a few things with you. This will give you a better understanding of what is going on when you do see the code, so you don't stare at the screen wondering what you're looking at. So, on with the show.


Transformations in OpenGL rely on the matrix for all mathematical computations. No, not the movie. Concentrate grasshopper. OpenGL has what is known as a matrix stack, which comes in handy for constructing models composed of many simple objects.

The modelview matrix defines the coordinate system that is being used to place and orient objects. It is a 4x4 matrix that is multiplied by vertices and transformations to create a new matrix that reflects the result of any transformations that have been applied to the vertices. When we want to modify the modelview matrix we use the command glMatrixMode(). We define this as
void glMatrixMode(GLenum mode);
Before you call any transformation commands you MUST specify whether you want to modify the modelview matrix or the projection matrix. The argument for modifying the modelview matrix is GL_MODELVIEW. So the complete line would appear as:
void glMatrixMode(GL_MODELVIEW);
Now we will look at translation. Translation allows you to move an object from one location to another within a 3D environment. The functions for this in OpenGL are glTranslatef() and glTranslated(). Here are their descriptions:
void glTranslatef(GLfloat x, GLfloat y, GLfloat z);
void glTranslated(GLdouble x, GLdouble y, GLdouble z);
Note that you must pass float types to glTranslatef() and double types to glTranslated(). X, Y, and Z represent the amount of translation on that axis.

Rotation in OpenGL is accomplished through the glRotate*() function, which is defined as
void  glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
void glRotated(GLdouble angle, GLdouble x, GLdouble y, GLdouble z);
Now let's take a look at these, and a few others, functions mentioned in a program. The following code is taken from OpenGL Game Programming and is commented by myself. If you have any problems building and using this code, feel free to contact me.
/*      Steven Billington
        January 13, 2003
        May 26, 2003 - UPDATE
        RobotOGL.cpp
        rod@cprogramming.com

        The following program creates a window and then
        uses multiple OpenGL functions to display a
        animated robot constructed of different size
        cubes. To compile this code you must make the
        proper library links in project--->settings.

        I apologize for any amount of scattered code or
        mis-formatting that printing this may occur. Please
        feel free to email me at the address above for the .cpp
        file.
*/

/*      These are what we refer to as Pre-processor
        Directives. In order for certain functions in
        C++ to operate you must include certain header
        files. Each header file below contains different
        functions needed throughout this program.
*/

#pragma comment(linker, "/subsystem:windows")

#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glaux.h>

/*      Here we find a few global variables. While
        i don't really like to use global variables,
        i found them very handy for this particular
        program. These variables will control angles,
        fullscreen, and the global device context.
*/

HDC g_HDC;
float angle = 0.0f;
float legAngle[2] = {0.0f, 0.0f};
float armAngle[2] = {0.0f, 0.0f};
bool fullScreen = false;

/*      Function:       DrawCube
        Purpose:        As the name would suggest, this is
                                the function for drawing the cubes.
*/

void DrawCube(float xPos, float yPos, float zPos)
{
        glPushMatrix();
        glBegin(GL_POLYGON);

                /*      This is the top face*/
                glVertex3f(0.0f, 0.0f, 0.0f);
                glVertex3f(0.0f, 0.0f, -1.0f);
                glVertex3f(-1.0f, 0.0f, -1.0f);
                glVertex3f(-1.0f, 0.0f, 0.0f);

                /*      This is the front face*/
                glVertex3f(0.0f, 0.0f, 0.0f);
                glVertex3f(-1.0f, 0.0f, 0.0f);
                glVertex3f(-1.0f, -1.0f, 0.0f);
                glVertex3f(0.0f, -1.0f, 0.0f);

                /*      This is the right face*/
                glVertex3f(0.0f, 0.0f, 0.0f);
                glVertex3f(0.0f, -1.0f, 0.0f);
                glVertex3f(0.0f, -1.0f, -1.0f);
                glVertex3f(0.0f, 0.0f, -1.0f);

                /*      This is the left face*/
                glVertex3f(-1.0f, 0.0f, 0.0f);
                glVertex3f(-1.0f, 0.0f, -1.0f);
                glVertex3f(-1.0f, -1.0f, -1.0f);
                glVertex3f(-1.0f, -1.0f, 0.0f);

                /*      This is the bottom face*/
                glVertex3f(0.0f, 0.0f, 0.0f);
                glVertex3f(0.0f, -1.0f, -1.0f);
                glVertex3f(-1.0f, -1.0f, -1.0f);
                glVertex3f(-1.0f, -1.0f, 0.0f);

                /*      This is the back face*/
                glVertex3f(0.0f, 0.0f, 0.0f);
                glVertex3f(-1.0f, 0.0f, -1.0f);
                glVertex3f(-1.0f, -1.0f, -1.0f);
                glVertex3f(0.0f, -1.0f, -1.0f);

        glEnd();
        glPopMatrix();
}

/*      Function:       DrawArm
        Purpose:        This function draws the arm
                                for the robot.
*/

void DrawArm(float xPos, float yPos, float zPos)
{
        glPushMatrix();

                /*      Sets color to red*/
                glColor3f(1.0f, 0.0f, 0.0f);
                glTranslatef(xPos, yPos, zPos);

                /*      Creates 1 x 4 x 1 cube*/
                glScalef(1.0f, 4.0f, 1.0f);
                DrawCube(0.0f, 0.0f, 0.0f);

        glPopMatrix();
}

/*      Function:       DrawHead
        Purpose:        This function will create the
                                head for the robot.
*/

void DrawHead(float xPos, float yPos, float zPos)
{
        glPushMatrix();

                /*      Sets color to white*/
                glColor3f(1.0f, 1.0f, 1.0f);
                glTranslatef(xPos, yPos, zPos);

                /*      Creates 2 x 2 x 2 cube*/
                glScalef(2.0f, 2.0f, 2.0f);
                DrawCube(0.0f, 0.0f, 0.0f);

        glPopMatrix();
}

/*      Function:       DrawTorso
        Purpose:        Function will do as suggested
                                and draw a torso for our robot.
*/

void DrawTorso(float xPos, float yPos, float zPos)
{
        glPushMatrix();

                /*      Sets color to blue*/
                glColor3f(0.0f, 0.0f, 1.0f);
                glTranslatef(xPos, yPos, zPos);

                /*      Creates 3 x 5 x 1 cube*/
                glScalef(3.0f, 5.0f, 1.0f);
                DrawCube(0.0f, 0.0f, 0.0f);

        glPopMatrix();
}

/*      Function:       DrawLeg
        Purpose:        Not to sound repetitve, but as suggested
                                this function will draw our robots legs.
*/

void DrawLeg(float xPos, float yPos, float zPos)
{
        glPushMatrix();

                /*      Sets color to yellow*/
                glColor3f(1.0f, 1.0f, 0.0f);
                glTranslatef(xPos, yPos, zPos);

                /*      Creates 1 x 5 x 1 cube*/
                glScalef(1.0f, 5.0f, 1.0f);
                DrawCube(0.0f, 0.0f, 0.0f);

        glPopMatrix();
}

/*      Function:       DrawRobot
        Purpose:        Function to draw our entire robot
*/

void DrawRobot(float xPos, float yPos, float zPos)
{
        /*      Variables for state of robots legs. True
                means the leg is forward, and False means
                the leg is back. The same applies to the
                robots arm states.
        */
        static bool leg1 = true;
        static bool leg2 = false;
        static bool arm1 = true;
        static bool arm2 = false;

        glPushMatrix();

                /*      This will draw our robot at the
                        desired coordinates.
                */
                glTranslatef(xPos, yPos, zPos);

                /*      These three lines will draw the
                        various components of our robot.
                */
                DrawHead(1.0f, 2.0f, 0.0f);
                DrawTorso(1.5f, 0.0f, 0.0f);
                glPushMatrix();


                /*      If the arm is moving forward we will increase
                        the angle; otherwise, we will decrease the
                        angle.
                */
                if (arm1)
                {
                        armAngle[0] = armAngle[0] + 1.0f;
                }
                else
                {
                        armAngle[0] = armAngle[0] - 1.0f;
                }

                /*      Once the arm has reached its max angle
                        in one direction, we want it to reverse
                        and change direction.
                */
                if (armAngle[0] >= 15.0f)
                {
                        arm1 = false;
                }
                if (armAngle[0] <= 15.0f)
                {
                        arm1 = true;
                }


                /*      Here we are going to move the arm away
                        from the torso and rotate. This will
                        create a walking effect.
                */
                glTranslatef(0.0f, -0.5f, 0.0f);
                glRotatef(armAngle[0], 1.0f, 0.0f, 0.0f);
                DrawArm(2.5f, 0.0f, -0.5f);

        glPopMatrix();

        glPushMatrix();


                /*      If the arm is moving forward we will increase
                        the angle, otherwise we will decrease the
                        angle
                */
                if (arm2)
                {
                        armAngle[1] = armAngle[1] + 1.0f;
                }
                else
                {
                        armAngle[1] = armAngle[1] - 1.0f;
                }

                /*      Here we are going to move the arm away
                        from the torso and rotate. This will
                        create a walking effect.
                */
                glTranslatef(0.0f, -0.5f, 0.0f);
                glRotatef(armAngle[1], 1.0f, 0.0f, 0.0f);
                DrawArm(-1.5f, 0.0f, -0.5f);

        glPopMatrix();

        /*      Now its time to rotate the legs relative to the
                robots position in the world, this is the first
                leg, ie the right one.
        */
        glPushMatrix();

                /*      If the leg is moving forward we will increase
                        the angle; otherwise, we will decrease the
                        angle.
                */
                if (leg1)
                {
                        legAngle[0] = legAngle[0] + 1.0f;
                }
                else
                {
                        legAngle[0] = legAngle[0] - 1.0f;
                }

                /*      Once the leg has reached its max angle
                        in one direction, we want it to reverse
                        and change direction.
                */
                if (legAngle[0] >= 15.0f)
                {
                        leg1 = false;
                }
                if (legAngle[0] <= -15.0f)
                {
                        leg1 = true;
                }


                /*      Here we are going to move the leg away
                        from the torso and rotate. This will
                        create a walking effect.
                */
                glTranslatef(0.0f, -0.5f, 0.0f);
                glRotatef(legAngle[0], 1.0f, 0.0f, 0.0f);


                /*      Time to draw the leg.
                */
                DrawLeg(-0.5f, -5.0f, -0.5f);

        glPopMatrix();

        /*      Same as above, for the left leg.
        */
        glPushMatrix();

                /*      If the leg is moving forward we will increase
                        the angle, otherwise we will decrease the
                        angle
                */
                if (leg2)
                {
                        legAngle[1] = legAngle[1] + 1.0f;
                }
                else
                {
                        legAngle[1] = legAngle[1] - 1.0f;
                }

                /*      Once the leg has reached its max angle
                        in one direction, we want it to reverse
                        and change direction.
                */
                if (legAngle[1] >= 15.0f)
                {
                        leg2 = false;
                }
                if (legAngle[1] <= -15.0f)
                {
                        leg2 = true;
                }

                /*      Here we are going to move the leg away
                        from the torso and rotate. This will
                        create a walking effect.
                */
                glTranslatef(0.0f, -0.5f, 0.0f);
                glRotatef(legAngle[1], 1.0f, 0.0f, 0.0f);
                DrawLeg(1.5f, -5.0f, -0.5f);

        glPopMatrix();

        glPopMatrix();

}

/*      Function:       Render
        Purpose:        This function will be responsible
                                for the rendering, got to love my
                                descriptive function names : )
*/
void Render()
{
        /*      Enable depth testing
        */
        glEnable(GL_DEPTH_TEST);

        /*      Heres our rendering. Clears the screen
                to black, clear the color and depth
                buffers, and reset our modelview matrix.
        */
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glLoadIdentity();

        /*      Increase rotation angle counter
        */
        angle = angle + 1.0f;

        /*      Reset after we have completed a circle
        */
        if (angle >= 360.0f)
        {
                angle = 0.0f;
        }

        glPushMatrix();
                glLoadIdentity();

                /*      Move to 0,0,-30 , rotate the robot on
                        its y axis, draw the robot, and dispose
                        of the current matrix.
                */
                glTranslatef(0.0f, 0.0f, -30.0f);
                glRotatef(angle, 0.0f, 1.0f, 0.0f);
                DrawRobot(0.0f, 0.0f, 0.0f);
        glPopMatrix();

        glFlush();

        /*      Bring back buffer to foreground
        */
        SwapBuffers(g_HDC);
}

//function to set the pixel format for the device context
/*      Function:       SetupPixelFormat
        Purpose:        This function will be responsible
                                for setting the pixel format for the
                                device context.
*/
void SetupPixelFormat(HDC hDC)
{
        /*      Pixel format index
        */
        int nPixelFormat;

        static PIXELFORMATDESCRIPTOR pfd = {
                sizeof(PIXELFORMATDESCRIPTOR),          //size of structure
                1,                                      //default version
                PFD_DRAW_TO_WINDOW |                    //window drawing support
                PFD_SUPPORT_OPENGL |                    //opengl support
                PFD_DOUBLEBUFFER,                       //double buffering support
                PFD_TYPE_RGBA,                          //RGBA color mode
                32,                                     //32 bit color mode
                0, 0, 0, 0, 0, 0,                       //ignore color bits
                0,                                      //no alpha buffer
                0,                                      //ignore shift bit
                0,                                      //no accumulation buffer
                0, 0, 0, 0,                             //ignore accumulation bits
                16,                                     //16 bit z-buffer size
                0,                                      //no stencil buffer
                0,                                      //no aux buffer
                PFD_MAIN_PLANE,                         //main drawing plane
                0,                                      //reserved
                0, 0, 0 };                              //layer masks ignored

                /*      Choose best matching format*/
                nPixelFormat = ChoosePixelFormat(hDC, &pfd);

                /*      Set the pixel format to the device context*/
                SetPixelFormat(hDC, nPixelFormat, &pfd);
}

/*      Windows Event Procedure Handler
*/
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        /*      Rendering and Device Context
                variables are declared here.
        */
        static HGLRC hRC;
        static HDC hDC;

        /*      Width and Height for the
                window our robot is to be
                displayed in.
        */
        int width, height;

        switch(message)
        {
                case WM_CREATE: //window being created

                        hDC = GetDC(hwnd);  //get current windows device context
                        g_HDC = hDC;
                        SetupPixelFormat(hDC); //call our pixel format setup function

                        /*      Create rendering context and make it current
                        */
                        hRC = wglCreateContext(hDC);
                        wglMakeCurrent(hDC, hRC);

                        return 0;
                        break;

                case WM_CLOSE:  //window is closing

                        /*      Deselect rendering context and delete it*/
                        wglMakeCurrent(hDC, NULL);
                        wglDeleteContext(hRC);

                        /*      Send quit message to queue*/
                        PostQuitMessage(0);

                        return 0;
                        break;

                case WM_SIZE:

                        /*      Retrieve width and height*/
                        height = HIWORD(lParam);
                        width = LOWORD(lParam);

                        /*      Don't want a divide by 0*/
                        if (height == 0)
                        {
                                height = 1;
                        }

                        /*      Reset the viewport to new dimensions*/
                        glViewport(0, 0, width, height);

                        /*      Set current Matrix to projection*/
                        glMatrixMode(GL_PROJECTION);
                        glLoadIdentity(); //reset projection matrix

                        /*      Time to calculate aspect ratio of
                                our window.
                        */
                        gluPerspective(54.0f, (GLfloat)width/(GLfloat)height, 1.0f, 1000.0f);

                        glMatrixMode(GL_MODELVIEW); //set modelview matrix
                        glLoadIdentity(); //reset modelview matrix

                        return 0;
                        break;

                default:

                        break;
        }

        return (DefWindowProc(hwnd, message, wParam, lParam));
}

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
        WNDCLASSEX windowClass; //window class
        HWND    hwnd;                   //window handle
        MSG             msg;                    //message
        bool    done;                   //flag for completion of app
        DWORD   dwExStyle;              //window extended style
        DWORD   dwStyle;                //window style
        RECT    windowRect;

        /*      Screen/display attributes*/
        int width = 800;
        int height = 600;
        int bits = 32;

        windowRect.left =(long)0;               //set left value to 0
        windowRect.right =(long)width;  //set right value to requested width
        windowRect.top =(long)0;                //set top value to 0
        windowRect.bottom =(long)height;//set bottom value to requested height

        /*      Fill out the window class structure*/
        windowClass.cbSize                      = sizeof(WNDCLASSEX);
        windowClass.style                       = CS_HREDRAW | CS_VREDRAW;
        windowClass.lpfnWndProc         = WndProc;
        windowClass.cbClsExtra          = 0;
        windowClass.cbWndExtra          = 0;
        windowClass.hInstance           = hInstance;
        windowClass.hIcon                       = LoadIcon(NULL, IDI_APPLICATION);
        windowClass.hCursor                     = LoadCursor(NULL, IDC_ARROW);
        windowClass.hbrBackground       = NULL;
        windowClass.lpszMenuName        = NULL;
        windowClass.lpszClassName       = "MyClass";
        windowClass.hIconSm                     = LoadIcon(NULL, IDI_WINLOGO);

        /*      Register window class*/
        if (!RegisterClassEx(&windowClass))
        {
                return 0;
        }

        /*      Check if fullscreen is on*/
        if (fullScreen)
        {
                DEVMODE dmScreenSettings;
                memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
                dmScreenSettings.dmSize = sizeof(dmScreenSettings);
                dmScreenSettings.dmPelsWidth = width;   //screen width
                dmScreenSettings.dmPelsHeight = height; //screen height
                dmScreenSettings.dmBitsPerPel = bits;   //bits per pixel
                dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

                if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN !=
                                                                  DISP_CHANGE_SUCCESSFUL))
                {
                        /*      Setting display mode failed, switch to windowed*/
                        MessageBox(NULL, "Display mode failed", NULL, MB_OK);
                        fullScreen = false;
                }
        }

        /*      Check if fullscreen is still on*/
        if (fullScreen)
        {
                dwExStyle = WS_EX_APPWINDOW;    //window extended style
                dwStyle = WS_POPUP;                             //windows style
                ShowCursor(FALSE);                              //hide mouse pointer
        }

        else
        {
                dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; //window extended style
                dwStyle = WS_OVERLAPPEDWINDOW;                                  //windows style
        }

        AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);

        /*      Class registerd, so now create our window*/
        hwnd = CreateWindowEx(NULL, "MyClass",  //class name
                                                  "OpenGL Robot",       //app name
                                                  dwStyle |
                                                  WS_CLIPCHILDREN |
                                                  WS_CLIPSIBLINGS,
                                                  0, 0,                         //x and y coords
                                                  windowRect.right - windowRect.left,
                                                  windowRect.bottom - windowRect.top,//width, height
                                                  NULL,                 //handle to parent
                                                  NULL,                 //handle to menu
                                                  hInstance,    //application instance
                                                  NULL);                //no xtra params

        /*      Check if window creation failed (hwnd = null ?)*/
        if (!hwnd)
        {
                return 0;
        }

        ShowWindow(hwnd, SW_SHOW);      //display window
        UpdateWindow(hwnd);                     //update window

        done = false;   //initialize loop condition variable

        /*      Main message loop*/
        while (!done)
        {
                PeekMessage(&msg, hwnd, NULL, NULL, PM_REMOVE);

                        if (msg.message == WM_QUIT)     //did we receive a quit message?
                        {
                                done = true;
                        }

                        else
                        {
                                Render();
                                TranslateMessage(&msg);
                                DispatchMessage(&msg);
                        }
        }

        if (fullScreen)
        {
                ChangeDisplaySettings(NULL, 0);
                ShowCursor(TRUE);
        }

        return msg.wParam;

}
That's a lot of code! Spend some time studying the example, practice a bit, and then we will proceed to lesson 7, where more of this code will be explained. Also, in lesson 7 we will cover Projections. 

Orthographic and Perspective Projections in OpenGL


   Note: All code beginning with the next lesson has been created and compiled using Microsoft Visual Studio .NET Enterprise Architect, not Visual Studio 6.0

Welcome to the seventh lesson in my OpenGL series! In this lesson we will learn about Projections and even put them to some simple use to see them in action. Some of you may have realized that we've used projection transformations in code before, and you're right, we have. The difference is now we will discuss how they work, and then demonstrate these concepts. 




OpenGL consists of two general classes of projection transformations: orthographic (parallel) and perspective. So let's get into detail on both of these!

Orthographic Projections

Orthographic, or parallel, projections consist of those that involve no perspective correction. There is no adjustment for distance from the camera made in these projections, meaning objects on the screen will appear the same size no matter how close or far away they are.

Traditionally this type of projection was included in OpenGL for uses in CAD, or Computer Aided Design. Some uses of orthographic projections are making 2D games, or for creating isometric games. To setup this type of projection we use the OpenGL provided glOrtho() function.
glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);
left and right specify the x-coordinate clipping planes, bottom and top specify the y-coordinate clipping planes, and near and far specify the distance to the z-coordinate clipping planes. Together these coordinates provide a box shaped viewing volume.

Since orthographic projections are commonly used in 2D scenes the Utility Library provides an additional routine to set them up for scenes that won't be using the z-coordinate.
gluOrtho2D(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top);

Perspective Projections

Although orthographic projections can be interesting, perspective projections create more realistic looking scenes, so that's what you will most likely be using most often. In perspective projections, as an object gets farther from the viewer it will appear smaller on the screen- an effect often referred to as foreshortening. The viewing volume for a perspective projection is a frustum, which looks like a pyramid with the top cut off, with the narrow end toward the user.

There is a few different ways you can setup the view frustum, and thus the perspective projection. The first we will look at is as follows:
void glFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);
Using glFrustum enables you to specify an asymmetrical frustum, which can be very useful in some instances, but isn't what you typically want to do. For a different solution we again turn to the Utility Library:
void gluPerspective(GLdouble fov, GLdouble aspect, GLdouble near, GLdouble far);
fov specifies, in degrees, the angle in the y direction that is visible to the user; aspect is the aspect ratio of the scene, which is width divided by the height. This will determine the field of view in the x direction.

Ok, so let's look at some code. This code is taken right from OpenGL Game Programming. Take the time to study and manipulate the code, then you will be ready for lesson 8!
// OpenGLProjectionExample.cpp : Defines the entry point for the application.
//

#define WIN32_LEAN_AND_MEAN

#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "glaux.lib")
#pragma comment(linker, "/subsystem:windows")

#include "stdafx.h"
#include <windows.h>                                    // standard Windows app include
#include <winuser.h>          // Windows constants
#include <gl/gl.h>                                              // standard OpenGL include
#include <gl/glu.h>                                             // OpenGL utilties
#include <glut.h>                                       // OpenGL utilties

#define WND_CLASS_NAME  "OpenGL Window Class"


/*************************** Constants and Macros ***************************/
const int   SCREEN_WIDTH    = 500;
const int   SCREEN_HEIGHT   = 500;
const int   SCREEN_BPP      = 32;
const bool  USE_FULLSCREEN  = false;
const char  *APP_TITLE      = "Projections";


/********************************* Globals **********************************/
HDC       g_hdc;                                                                  // global device context
HGLRC     g_hrc;                  // global rendering context
BOOL      g_isFullscreen = TRUE;  // toggles fullscreen and windowed display
BOOL      g_isActive = TRUE;      // false if window is minimized
HWND      g_hwnd = NULL;          // handle of our window
HINSTANCE g_hInstance;            // application instance


/******************************** Prototypes ********************************/
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

BOOL    SetupWindow(const char *title, int width, int height, int bits, bool isFullscreen);
BOOL    KillWindow();

GLvoid  ResizeScene(GLsizei width, GLsizei height);
BOOL    InitializeScene();
BOOL    DisplayScene();
BOOL    Cleanup();

void    UpdateProjection(GLboolean toggle = GL_FALSE);


/*****************************************************************************
 WinMain()

 Windows entry point
*****************************************************************************/
int WINAPI WinMain(HINSTANCE g_hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
  MSG   msg;       // message
  BOOL  isDone;    // flag indicating when the app is done

  // if the window is set up correctly, we can proceed with the message loop
  if (SetupWindow(APP_TITLE, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, USE_FULLSCREEN))
    isDone = FALSE;
  // otherwise, we need to never enter the loop and proceed to exit
  else
    isDone = TRUE;

  // main message loop
  while (!isDone)
  {
    if(PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))
    {
      if (msg.message == WM_QUIT)   // do we receive a WM_QUIT message?
      {
        isDone = TRUE;              // if so, time to quit the application
      }
      else
      {
        TranslateMessage(&msg);     // translate and dispatch to event queue
        DispatchMessage(&msg);
      }
    }

    // don't update the scene if the app is minimized
    if (g_isActive)
    {
      // update the scene every time through the loop
      DisplayScene();
      // switch the front and back buffers to display the updated scene
      SwapBuffers(g_hdc);
    }
  }

  Cleanup();
  KillWindow();

  return msg.wParam;
} // end WinMain()


/*****************************************************************************
 WndProc()

 Windows message handler
*****************************************************************************/
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch(message)
  {
  case WM_ACTIVATE:  // watch for the window being minimized and restored
    {
      if (!HIWORD(wParam))
      {
        // program was restored or maximized
        g_isActive = TRUE;
      }
      else
      {
        // program was minimized
        g_isActive=FALSE;
      }

      return 0;
    }

  case WM_SYSCOMMAND:  // look for screensavers and powersave mode
    {
      switch (wParam)
      {
      case SC_SCREENSAVE:     // screensaver trying to start
      case SC_MONITORPOWER:   // monitor going to powersave mode
        // returning 0 prevents either from happening
        return 0;
      default:
        break;
      }
    } break;

  case WM_CLOSE:    // window is being closed
    {
      // send WM_QUIT to message queue
      PostQuitMessage(0);

      return 0;
    }

  case WM_SIZE:
    {
      // update perspective with new width and height
      ResizeScene(LOWORD(lParam), HIWORD(lParam));
      return 0;
    }

  case WM_CHAR:
    {
      switch (toupper(wParam))
      {
      case VK_SPACE:
        {
          UpdateProjection(GL_TRUE);
          return 0;
        }
      case VK_ESCAPE:
        {
          // send WM_QUIT to message queue
          PostQuitMessage(0);
          return 0;
        }
      default:
        break;
      };
    } break;

  default:
    break;
  }

  return (DefWindowProc(hwnd, message, wParam, lParam));
} // end WndProc()


/*****************************************************************************
 SetupWindow()

 Create the window and everything else we need, including the device and
 rendering context. If a fullscreen window has been requested but can't be
 created, the user will be prompted to attempt windowed mode. Finally,
 InitializeScene is called for application-specific setup.

 Returns TRUE if everything goes well, or FALSE if an unrecoverable error
 occurs. Note that if this is called twice within a program, KillWindow needs
 to be called before subsequent calls to SetupWindow.
*****************************************************************************/
BOOL SetupWindow(const char *title, int width, int height, int bits, bool isFullscreen)
{
  // set the global flag
  g_isFullscreen = isFullscreen;

  // get our instance handle
  g_hInstance = GetModuleHandle(NULL);

  WNDCLASSEX  wc;    // window class

  // fill out the window class structure
  wc.cbSize         = sizeof(WNDCLASSEX);
  wc.style          = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  wc.lpfnWndProc    = WndProc;
  wc.cbClsExtra     = 0;
  wc.cbWndExtra     = 0;
  wc.hInstance      = g_hInstance;
  wc.hIcon          = LoadIcon(NULL, IDI_APPLICATION);  // default icon
  wc.hIconSm        = LoadIcon(NULL, IDI_WINLOGO);      // windows logo small icon
  wc.hCursor        = LoadCursor(NULL, IDC_ARROW);      // default arrow
  wc.hbrBackground  = NULL;     // no background needed
  wc.lpszMenuName   = NULL;     // no menu
  wc.lpszClassName  = WND_CLASS_NAME;

  // register the windows class
  if (!RegisterClassEx(&wc))
  {
    MessageBox(NULL,"Unable to register the window class", "Error", MB_OK | MB_ICONEXCLAMATION);

    // exit and return FALSE
    return FALSE;
  }

  // if we're in fullscreen mode, set the display up for it
  if (g_isFullscreen)
  {
    // set up the device mode structure
    DEVMODE screenSettings;
    memset(&screenSettings,0,sizeof(screenSettings));

    screenSettings.dmSize       = sizeof(screenSettings);
    screenSettings.dmPelsWidth  = width;    // screen width
    screenSettings.dmPelsHeight = height;   // screen height
    screenSettings.dmBitsPerPel = bits;     // bits per pixel
    screenSettings.dmFields     = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

    // attempt to switch to the resolution and bit depth we've selected
    if (ChangeDisplaySettings(&screenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
    {
      // if we can't get fullscreen, let them choose to quit or try windowed mode
      if (MessageBox(NULL, "Cannot run in the fullscreen mode at the selected resolution\n"
                           "on your video card. Try windowed mode instead?",
                           "OpenGL Game Programming",
                           MB_YESNO | MB_ICONEXCLAMATION) == IDYES)
      {
        g_isFullscreen = FALSE;
      }
      else
      {
        return FALSE;
      }
    }
  }

  DWORD dwExStyle;
  DWORD dwStyle;

  // set the window style appropriately, depending on whether we're in fullscreen mode
  if (g_isFullscreen)
  {
    dwExStyle = WS_EX_APPWINDOW;
    dwStyle = WS_POPUP;           // simple window with no borders or title bar
    ShowCursor(FALSE);            // hide the cursor for now
  }
  else
  {
    dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
    dwStyle = WS_OVERLAPPEDWINDOW;
  }

  // set up the window we're rendering to so that the top left corner is at (0,0)
  // and the bottom right corner is (height,width)
  RECT  windowRect;
  windowRect.left = 0;
  windowRect.right = (LONG) width;
  windowRect.top = 0;
  windowRect.bottom = (LONG) height;

  // change the size of the rect to account for borders, etc. set by the style
  AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle);

  // class registered, so now create our window
  g_hwnd = CreateWindowEx(dwExStyle,          // extended style
                          WND_CLASS_NAME,     // class name
                          title,              // app name
                          dwStyle |           // window style
                          WS_CLIPCHILDREN |   // required for
                          WS_CLIPSIBLINGS,    // using OpenGL
                          0, 0,               // x,y coordinate
                          windowRect.right - windowRect.left, // width
                          windowRect.bottom - windowRect.top, // height
                          NULL,               // handle to parent
                          NULL,               // handle to menu
                          g_hInstance,        // application instance
                          NULL);              // no extra params

  // see if our window handle is valid
  if (!g_hwnd)
  {
    MessageBox(NULL, "Unable to create window", "Error", MB_OK | MB_ICONEXCLAMATION);
    return FALSE;
  }

  // get a device context
  if (!(g_hdc = GetDC(g_hwnd)))
  {
    MessageBox(NULL,"Unable to create device context", "Error", MB_OK | MB_ICONEXCLAMATION);
    return FALSE;
  }

  // set the pixel format we want
  PIXELFORMATDESCRIPTOR pfd = {
    sizeof(PIXELFORMATDESCRIPTOR),  // size of structure
    1,                              // default version
    PFD_DRAW_TO_WINDOW |            // window drawing support
    PFD_SUPPORT_OPENGL |            // OpenGL support
    PFD_DOUBLEBUFFER,               // double buffering support
    PFD_TYPE_RGBA,                  // RGBA color mode
    bits,                           // 32 bit color mode
    0, 0, 0, 0, 0, 0,               // ignore color bits, non-palettized mode
    0,                              // no alpha buffer
    0,                              // ignore shift bit
    0,                              // no accumulation buffer
    0, 0, 0, 0,                     // ignore accumulation bits
    16,                             // 16 bit z-buffer size
    8,                              // no stencil buffer
    0,                              // no auxiliary buffer
    PFD_MAIN_PLANE,                 // main drawing plane
    0,                              // reserved
    0, 0, 0 };                      // layer masks ignored

  GLuint  pixelFormat;

  // choose best matching pixel format
  if (!(pixelFormat = ChoosePixelFormat(g_hdc, &pfd)))
  {
    MessageBox(NULL, "Can't find an appropriate pixel format", "Error", MB_OK | MB_ICONEXCLAMATION);
    return FALSE;
  }

  // set pixel format to device context
  if(!SetPixelFormat(g_hdc, pixelFormat,&pfd))
  {
    MessageBox(NULL, "Unable to set pixel format", "Error", MB_OK | MB_ICONEXCLAMATION);
    return FALSE;
  }

  // create the OpenGL rendering context
  if (!(g_hrc = wglCreateContext(g_hdc)))
  {
    MessageBox(NULL, "Unable to create OpenGL rendering context", "Error",MB_OK | MB_ICONEXCLAMATION);
    return FALSE;
  }

  // now make the rendering context the active one
  if(!wglMakeCurrent(g_hdc, g_hrc))
  {
    MessageBox(NULL,"Unable to activate OpenGL rendering context", "ERROR", MB_OK | MB_ICONEXCLAMATION);
    return FALSE;
  }

  // show the window in the forground, and set the keyboard focus to it
  ShowWindow(g_hwnd, SW_SHOW);
  SetForegroundWindow(g_hwnd);
  SetFocus(g_hwnd);

  // set up the perspective for the current screen size
  ResizeScene(width, height);

  // do one-time initialization
  if (!InitializeScene())
  {
    MessageBox(NULL, "Initialization failed", "Error", MB_OK | MB_ICONEXCLAMATION);
    return FALSE;
  }

  return TRUE;
} // end SetupWindow()


/*****************************************************************************
 KillWindow()

 Deletes the DC, RC, and Window, and restores the original display.
*****************************************************************************/
BOOL KillWindow()
{
  // restore the original display if we're in fullscreen mode
  if (g_isFullscreen)
  {
    ChangeDisplaySettings(NULL, 0);
    ShowCursor(TRUE);
  }

  // if we have an RC, release it
  if (g_hrc)
  {
    // release the RC
    if (!wglMakeCurrent(NULL,NULL))
    {
      MessageBox(NULL, "Unable to release rendering context", "Error", MB_OK | MB_ICONINFORMATION);
    }

    // delete the RC
    if (!wglDeleteContext(g_hrc))
    {
      MessageBox(NULL, "Unable to delete rendering context", "Error", MB_OK | MB_ICONINFORMATION);
    }

    g_hrc = NULL;
  }

  // release the DC if we have one
  if (g_hdc && !ReleaseDC(g_hwnd, g_hdc))
  {
    MessageBox(NULL, "Unable to release device context", "Error", MB_OK | MB_ICONINFORMATION);
    g_hdc = NULL;
  }

  // destroy the window if we have a valid handle
  if (g_hwnd && !DestroyWindow(g_hwnd))
  {
    MessageBox(NULL, "Unable to destroy window", "Error", MB_OK | MB_ICONINFORMATION);
    g_hwnd = NULL;
  }

  // unregister our class so we can create a new one if we need to
  if (!UnregisterClass(WND_CLASS_NAME, g_hInstance))
  {
    MessageBox(NULL, "Unable to unregister window class", "Error", MB_OK | MB_ICONINFORMATION);
    g_hInstance = NULL;
  }

  return TRUE;
} // end KillWindow()


/*****************************************************************************
 ResizeScene()

 Called once when the application starts and again every time the window is
 resized by the user.
*****************************************************************************/
GLvoid ResizeScene(GLsizei width, GLsizei height)
{
  // avoid divide by zero
  if (height==0)
  {
    height=1;
  }

  // reset the viewport to the new dimensions
  glViewport(0, 0, width, height);

  // set up the projection, without toggling the projection mode
  UpdateProjection();
} // end ResizeScene()


/*****************************************************************************
 InitializeScene()

 Performs one-time application-specific setup. Returns FALSE on any failure.
*****************************************************************************/
BOOL InitializeScene()
{
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  glShadeModel(GL_SMOOTH);
  glEnable(GL_DEPTH_TEST);

  return TRUE;
} // end InitializeScene()


/*****************************************************************************
 DisplayScene()

 The work of the application is done here. This is called every frame, and
 handles the actual rendering of the scene.
*****************************************************************************/
BOOL DisplayScene()
{
  GLfloat yellow[4] = { 1.0f, 1.0f, 0.2f, 1.0f };
  GLfloat blue[4] = { 0.2f, 0.2f, 1.0f, 1.0f };
  GLfloat green[4] = { 0.2f, 1.0f, 0.2f, 1.0f };

  glLoadIdentity();
  gluLookAt(-0.5, 1.0, 7.0,
            0.0, 0.0, 0.0,
            0.0, 1.0, 0.0);

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, yellow);
  glPushMatrix();
  glTranslatef(0.3, 0.0, 1.0);
  glutSolidCube(0.5);
  glPopMatrix();

  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, blue);
  glPushMatrix();
  glutSolidCube(0.5);
  glPopMatrix();

  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, green);
  glPushMatrix();
  glTranslatef(-0.3, 0.0, -1.0);
  glutSolidCube(0.5);
  glPopMatrix();

  return TRUE;
} // end DisplayScene()


/*****************************************************************************
 Cleanup()

 Called at the end of successful program execution.
*****************************************************************************/
BOOL Cleanup()
{
  return TRUE;
} // end Cleanup()


/****************************************************************************
 UpdateProjection()

 Sets the current projection mode. If toggle is set to GL_TRUE, then the
 projection will be toggled between perspective and orthograpic. Otherwise,
 the previous selection will be used again.
*****************************************************************************/
void UpdateProjection(GLboolean toggle)
{
  static GLboolean s_usePerspective = GL_TRUE;

  // toggle the control variable if appropriate
  if (toggle)
    s_usePerspective = !s_usePerspective;

  // select the projection matrix and clear it out
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  // choose the appropriate projection based on the currently toggled mode
  if (s_usePerspective)
  {
    // set the perspective with the appropriate aspect ratio
    glFrustum(-1.0, 1.0, -1.0, 1.0, 5, 100);
  }
  else
  {
    // set up an orthographic projection with the same near clip plane
    glOrtho(-1.0, 1.0, -1.0, 1.0, 5, 100);
  }

  // select modelview matrix and clear it out
  glMatrixMode(GL_MODELVIEW);
} // end UpdateProjection
So now you have learned about Projections, and use them in code. In lesson 9 we will move on to Matrices.



Aucun commentaire:

Enregistrer un commentaire