Back to July Calendar

Previous Entry Next Entry→

November 1, 2005

Here's a word that isn't what it appears to be: undecimal (UHN-des-uh-muhl) adjective, meaning "Based on the number eleven." [From Latin undecim (eleven).]

Here's massive program that does texture mapping to the surface of a cube.  Criminations, this really illustrates the impossibility of grasping windows programs in a finite amount of time.  The commentary at http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=06 is somewhat enlightening.  Much of the bulk here is the Windows overhead...my interest recedes rapidly after the glEnd() function statement.

/*
 *	This Code Was Created By Jeff Molofee 2000
 *	A HUGE Thanks To Fredric Echols For Cleaning Up
 *	And Optimizing The Base Code, Making It More Flexible!
 *	If You've Found This Code Useful, Please Let Me Know.
 *	Visit My Site At nehe.gamedev.net
 */
#include <stdlib.h>
#include <windows.h>		// Header File For Windows
#include <stdio.h>		// Header File For Standard Input/Output
#include <gl\gl.h>		// Header File For The OpenGL32 Library
#include <gl\glu.h>		// Header File For The GLu32 Library
#include <gl\glaux.h>		// Header File For The Glaux Library

HDC		hDC=NULL;	// Private GDI Device Context
HGLRC		hRC=NULL;	// Permanent Rendering Context
HWND		hWnd=NULL;	// Holds Our Window Handle
HINSTANCE	hInstance;	// Holds The Instance Of The Application

bool	keys[256];		// Array Used For The Keyboard Routine
bool	active=TRUE;		// Window Active Flag Set To TRUE By Default
bool	fullscreen=TRUE;	// Fullscreen Flag Set To Fullscreen Mode By Default

GLfloat	xrot;		// X Rotation ( NEW )
GLfloat	yrot;		// Y Rotation ( NEW )
GLfloat	zrot;		// Z Rotation ( NEW )

GLuint	texture[1];	// Storage For One Texture ( NEW )

LRESULT	CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);	// Declaration For WndProc
/* Now immediately after the above code, and before ReSizeGLScene(), we want to add the following section of code. 
The job of this code is to load in a bitmap file. If the file doesn't exist NULL is sent back meaning the texture 
couldn't be loaded. Before I start explaining the code there are a few VERY important things you need to know 
about the images you plan to use as textures. The image height and width MUST be a power of 2. The width and 
height must be at least 64 pixels, and for compatibility reasons, shouldn't be more than 256 pixels. 
If the image you want to use is not 64, 128 or 256 pixels on the width or height, resize it in an art program. 
There are ways around this limitation, but for now we'll just stick to standard texture sizes. 

First thing we do is create a file handle. A handle is a value used to identify a resource so that our program 
can access it. We set the handle to NULL to start off. */

AUX_RGBImageRec *LoadBMP(char *Filename)	// Loads A Bitmap Image
{
	FILE *File=NULL 			// File Handle
/* Next we check to make sure that a filename was actually given. The person may have used LoadBMP() without 
specifying the file to load, so we have to check for this. We don't want to try loading nothing :) 
*/
	if (!Filename)	 			// Make Sure A Filename Was Given
	{
		return NULL;			// If Not Return NULL
	}
// If a filename was given, we check to see if the file exists. The line below tries to open the file. 
	File=fopen(Filename,"r");		// Check To See If The File Exists
// If we were able to open the file it obviously exists. We close the file with fclose(File) then we return the image 
// data. auxDIBImageLoad(Filename) reads in the data. 
	if (File)				// Does The File Exist?
	{
		fclose(File);				// Close The Handle
		return auxDIBImageLoad(Filename);	// Load The Bitmap And Return A Pointer
	}
// If we were unable to open the file we'll return NULL. which means the file couldn't be loaded. 
// Later we'll check to see if the file was loaded and quit the program with an error message if it wasn't. 
	return NULL;					// If Load Failed Return NULL
}
// This is the section of code that loads the bitmap (calling the code above) and converts it into a texture. 
int LoadGLTextures()	// Load Bitmaps And Convert To Textures
{
// We'll set up a variable called Status. We'll use this variable to keep track of whether or not we were able to load 
// the bitmap and build a texture. We set Status to FALSE (meaning nothing has been loaded or built) by default.
	int Status=FALSE;			// Status Indicator
// Now we create an image record that we can store our bitmap in. The record will hold the bitmap width, height, and data. 
	AUX_RGBImageRec *TextureImage[1];	// Create Storage Space For The Texture
// We clear the image record just to make sure it's empty. 
	memset(TextureImage,0,sizeof(void *)*1);       	// Set The Pointer To NULL
// Now we load the bitmap and convert it to a texture. TextureImage[0]=LoadBMP("Data/NeHe.bmp") will jump to our 
// LoadBMP() code. The file named NeHe.bmp in the Data directory will be loaded. If everything goes well, the image data 
// is stored in TextureImage[0], Status is set to TRUE, and we start to build our texture. 
	// Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit
	if (TextureImage[0]=LoadBMP("Data/NeHe.bmp"))
	{
		Status=TRUE;				// Set The Status To TRUE
/* Now that we've loaded the image data into TextureImage[0], we will build a texture using this data. The first line 
glGenTextures(1, &texture[0]) tells OpenGL we want to generate one texture name (increase the number if you load more 
than one texture). Remember at the very beginning of this tutorial we created room for one texture with the line 
GLuint texture[1]. Although you'd think the first texture would be stored at &texture[1] instead of &texture[0], 
it's not. The first actual storage area is 0. If we wanted two textures we would use GLuint texture[2] and the second 
texture would be stored at texture[1]. 

The second line glBindTexture(GL_TEXTURE_2D, texture[0]) tells OpenGL to bind the named texture texture[0] to a texture 
target. 2D textures have both height (on the Y axes) and width (on the X axes). The main function of glBindTexture is 
to assign a texture name to texture data. In this case we're telling OpenGL there is memory available at &texture[0]. 
When we create the texture, it will be stored in the memory that &texture[0] references. */
		glGenTextures(1, &texture[0]);		// Create The Texture

		// Typical Texture Generation Using Data From The Bitmap
		glBindTexture(GL_TEXTURE_2D, texture[0]);
/* Next we create the actual texture. The following line tells OpenGL the texture will be a 2D texture (GL_TEXTURE_2D). 
Zero represents the images level of detail, this is usually left at zero. Three is the number of data components. 
Because the image is made up of red data, green data and blue data, there are three components. 
TextureImage[0]->sizeX is the width of the texture. If you know the width, you can put it here, but it's easier to let 
the computer figure it out for you. TextureImage[0]->sizey is the height of the texture. zero is the border. 
It's usually left at zero. GL_RGB tells OpenGL the image data we are using is made up of red, green and blue data in 
that order. GL_UNSIGNED_BYTE means the data that makes up the image is made up of unsigned bytes, 
and finally... TextureImage[0]->data tells OpenGL where to get the texture data from. In this case it points to the 
data stored in the TextureImage[0] record. */
		glTexImage2D(GL_TEXTURE_2D, 0, 3, 
			     TextureImage[0]->sizeX, 
			     TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
/* The next two lines tell OpenGL what type of filtering to use when the image is larger (GL_TEXTURE_MAG_FILTER) or 
stretched on the screen than the original texture, or when it's smaller (GL_TEXTURE_MIN_FILTER) on the screen than the 
actual texture. I usually use GL_LINEAR for both. This makes the texture look smooth way in the distance, and when it's 
up close to the screen. Using GL_LINEAR requires alot of work from the processor/video card, so if your system is slow, 
you might want to use GL_NEAREST. A texture that's filtered with GL_NEAREST will appear blocky when it's stretched. 
You can also try a combination of both. Make it filter things up close, but not things in the distance.  */
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	}
/* Now we free up any ram that we may have used to store the bitmap data. We check to see if the bitmap data was stored 
in TextureImage[0]. If it was we check to see if the data has been stored. If data was stored, we erase it. Then we 
free the image structure making sure any used memory is freed up. */
	if (TextureImage[0])				// If Texture Exists
	{	if (TextureImage[0]->data)		// If Texture Image Exists
		{
		///////////////////////////////////////////////////////////	////////////////////////////////	
   			free(TextureImage[0]->data);     // Free The Texture Image Memory 
		}
		free(TextureImage[0]);								// Free The Image Structure
	}
	return Status;										// Return The Status
}
// Finally we return the status. If everything went OK, the variable Status will be TRUE. If anything went wrong, 
// Status will be FALSE. 
GLvoid ReSizeGLScene(GLsizei width, GLsizei height)		// Resize And Initialize The GL Window
{
	if (height==0)			// Prevent A Divide By Zero By
	{
		height=1;		// Making Height Equal One
	}
	glViewport(0,0,width,height);	// Reset The Current Viewport

	glMatrixMode(GL_PROJECTION);	// Select The Projection Matrix
	glLoadIdentity();		// Reset The Projection Matrix

	// Calculate The Aspect Ratio Of The Window
	gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);

	glMatrixMode(GL_MODELVIEW);	// Select The Modelview Matrix
	glLoadIdentity();		// Reset The Modelview Matrix
}
/* I've added a few lines of code to InitGL. I'll repost the entire section of code, so it's easy to see the lines that 
I've added, and where they go in the code. The first line if (!LoadGLTextures()) jumps to the routine above which loads 
the bitmap and makes a texture from it. If LoadGLTextures() fails for any reason, the next line of code will return FALSE. 
If everything went OK, and the texture was created, we enable 2D texture mapping. If you forget to enable texture mapping 
your object will usually appear solid white, which is definitely not good. */
int InitGL(GLvoid)			// All Setup For OpenGL Goes Here
{
	if (!LoadGLTextures())		// Jump To Texture Loading Routine ( NEW )
	{
		return FALSE;		// If Texture Didn't Load Return FALSE
	}

	glEnable(GL_TEXTURE_2D);	// Enable Texture Mapping ( NEW )
	glShadeModel(GL_SMOOTH);		// Enable Smooth Shading
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);	// Black Background
	glClearDepth(1.0f);			// Depth Buffer Setup
	glEnable(GL_DEPTH_TEST);		// Enables Depth Testing
	glDepthFunc(GL_LEQUAL);			// The Type Of Depth Testing To Do
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Really Nice Perspective Calculations
	return TRUE;						// Initialization Went OK
}
/* Now we draw the textured cube. You can replace the DrawGLScene code with the code below, or you can add the new code to
 the original lesson one code. This section will be heavily commented so it's easy to understand. The first two lines of 
code glClear() and glLoadIdentity() are in the original lesson one code. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 
will clear the screen to the color we selected in InitGL(). In this case, the screen will be cleared to black. The depth 
buffer will also be cleared. The view will then be reset with glLoadIdentity(). */
int DrawGLScene(GLvoid)		// Here's Where We Do All The Drawing
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear The Screen And The Depth Buffer
	glLoadIdentity();					// Reset The View
	glTranslatef(0.0f,0.0f,-5.0f);
/* The following three lines of code will rotate the cube on the x axis, then the y axis, and finally the z axis. 
   How much it rotates on each axis will depend on the value stored in xrot, yrot and zrot. */
	glRotatef(xrot,1.0f,0.0f,0.0f);
	glRotatef(yrot,0.0f,1.0f,0.0f);
	glRotatef(zrot,0.0f,0.0f,1.0f);
/* The next line of code selects which texture we want to use. If there was more than one texture you wanted to use 
   in your scene, you would select the texture using glBindTexture(GL_TEXTURE_2D, texture[number of texture to use]). 
   If you wanted to change textures, you would bind to the new texture. One thing to note is that you can NOT bind a 
   texture inside glBegin() and glEnd(), you have to do it before or after glBegin(). Notice how we use glBindTextures 
   to specify which texture to create and to select a specific texture. */
	glBindTexture(GL_TEXTURE_2D, texture[0]);
/* To properly map a texture onto a quad, you have to make sure the top right of the texture is mapped to the top right 
of the quad. The top left of the texture is mapped to the top left of the quad, the bottom right of the texture is mapped 
to the bottom right of the quad, and finally, the bottom left of the texture is mapped to the bottom left of the quad. 
If the corners of the texture do not match the same corners of the quad, the image may appear upside down, sideways, 
or not at all. 

The first value of glTexCoord2f is the X coordinate. 0.0f is the left side of the texture. 0.5f is the middle of the 
texture, and 1.0f is the right side of the texture. The second value of glTexCoord2f is the Y coordinate. 0.0f is the 
bottom of the texture. 0.5f is the middle of the texture, and 1.0f is the top of the texture. 

So now we know the top left coordinate of a texture is 0.0f on X and 1.0f on Y, and the top left vertex of a quad is 
-1.0f on X, and 1.0f on Y. Now all you have to do is match the other three texture coordinates up with the remaining 
three corners of the quad. 

Try playing around with the x and y values of glTexCoord2f. Changing 1.0f to 0.5f will only draw the left half of a 
texture from 0.0f (left) to 0.5f (middle of the texture). Changing 0.0f to 0.5f will only draw the right half of a 
texture from 0.5f (middle) to 1.0f (right). */
	glBegin(GL_QUADS);
		// Front Face
		glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
		glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
		glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
		glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
		// Back Face
		glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
		glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);
		glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);
		glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
		// Top Face
		glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);
		glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
		glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
		glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);
		// Bottom Face
		glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
		glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
		glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
		glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
		// Right face
		glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
		glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);
		glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);
		glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);
		// Left Face
		glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
		glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);
		glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);
		glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);
	glEnd();
/* Now we increase the value of xrot, yrot and zrot. Try changing the number each variable increases by to make the 
cube spin faster or slower, or try changing a + to a - to make the cube spin the other direction. */
	xrot+=0.3f;
	yrot+=0.2f;
	zrot+=0.4f;
	return TRUE;			// Keep Going
}
GLvoid KillGLWindow(GLvoid)		/ Properly Kill The Window
{
	if (fullscreen)			/ Are We In Fullscreen Mode?
	{
		ChangeDisplaySettings(NULL,0);		// If So Switch Back To The Desktop
		ShowCursor(TRUE);			// Show Mouse Pointer
	}

	if (hRC)					// Do We Have A Rendering Context?
	{
		if (!wglMakeCurrent(NULL,NULL))		// Are We Able To Release The DC And RC Contexts?
		{
			MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		}

		if (!wglDeleteContext(hRC))	 	// Are We Able To Delete The RC?
		{
			MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		}
		hRC=NULL;				// Set RC To NULL
	}

	if (hDC && !ReleaseDC(hWnd,hDC))		// Are We Able To Release The DC
	{
		MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hDC=NULL;				// Set DC To NULL
	}

	if (hWnd && !DestroyWindow(hWnd))		// Are We Able To Destroy The Window?
	{
		MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hWnd=NULL;				// Set hWnd To NULL
	}

	if (!UnregisterClass("OpenGL",hInstance))	// Are We Able To Unregister Class
	{
		MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		hInstance=NULL;				// Set hInstance To NULL
	}
}

/*	This Code Creates Our OpenGL Window.  Parameters Are:					*
 *	title			- Title To Appear At The Top Of The Window				*
 *	width			- Width Of The GL Window Or Fullscreen Mode				*
 *	height			- Height Of The GL Window Or Fullscreen Mode			*
 *	bits			- Number Of Bits To Use For Color (8/16/24/32)			*
 *	fullscreenflag	- Use Fullscreen Mode (TRUE) Or Windowed Mode (FALSE)	*/
 
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
	GLuint		PixelFormat;		// Holds The Results After Searching For A Match
	WNDCLASS	wc;			// Windows Class Structure
	DWORD		dwExStyle;		// Window Extended Style
	DWORD		dwStyle;		// Window Style
	RECT		WindowRect;		// Grabs Rectangle Upper Left / Lower Right Values
	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

	fullscreen=fullscreenflag;		// Set The Global Fullscreen Flag

	hInstance		= GetModuleHandle(NULL);		// Grab An Instance For Our Window
	wc.style		= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;	// Redraw On Size, And Own DC For Window.
	wc.lpfnWndProc		= (WNDPROC) WndProc;			// WndProc Handles Messages
	wc.cbClsExtra		= 0;					// No Extra Window Data
	wc.cbWndExtra		= 0;					// No Extra Window Data
	wc.hInstance		= hInstance;				// Set The Instance
	wc.hIcon		= LoadIcon(NULL, IDI_WINLOGO);		// Load The Default Icon
	wc.hCursor		= LoadCursor(NULL, IDC_ARROW);		// Load The Arrow Pointer
	wc.hbrBackground	= NULL;					// No Background Required For GL
	wc.lpszMenuName		= NULL;					// We Don't Want A Menu
	wc.lpszClassName	= "OpenGL";				// Set The Class Name

	if (!RegisterClass(&wc))	// Attempt To Register The Window Class
	{
		MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;		
	}
	
	if (fullscreen)			// Attempt Fullscreen Mode?
	{
		DEVMODE dmScreenSettings;	// Device Mode
		memset(&dmScreenSettings,0,sizeof(dmScreenSettings));	// Makes Sure Memory's Cleared
		dmScreenSettings.dmSize=sizeof(dmScreenSettings);	// Size Of The Devmode Structure
		dmScreenSettings.dmPelsWidth	= width;		// Selected Screen Width
		dmScreenSettings.dmPelsHeight	= height;		// Selected Screen Height
		dmScreenSettings.dmBitsPerPel	= bits;			// Selected Bits Per Pixel
		dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

		// Try To Set Selected Mode And Get Results.  NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar.
		if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
		{
			// If The Mode Fails, Offer Two Options.  Quit Or Use Windowed Mode.
			if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?","NeHe GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
			{
				fullscreen=FALSE;		// Windowed Mode Selected.  Fullscreen = FALSE
			}
			else
			{
				// Pop Up A Message Box Letting User Know The Program Is Closing.
				MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
				return FALSE;
			}
		}
	}

	if (fullscreen)		// Are We Still In Fullscreen Mode?
	{
		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);	// Adjust Window To True Requested Size

	// Create The Window
	if (!(hWnd=CreateWindowEx( 	dwExStyle,	// Extended Style For The Window
					"OpenGL",							// Class Name
					title,		// Window Title
					dwStyle |	// Defined Window Style
					WS_CLIPSIBLINGS |	// Required Window Style
					WS_CLIPCHILDREN,	// Required Window Style
					0, 0,			// Window Position
					WindowRect.right-WindowRect.left,  // Calculate Window Width
					WindowRect.bottom-WindowRect.top,  // Calculate Window Height
					NULL,				   // No Parent Window
					NULL,				// No Menu
					hInstance,		// Instance
					NULL)))			// Dont Pass Anything To WM_CREATE
	{
		KillGLWindow();		// Reset The Display
		MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;
	}

	static	PIXELFORMATDESCRIPTOR pfd=	// pfd Tells Windows How We Want Things To Be
	{
		sizeof(PIXELFORMATDESCRIPTOR),		// Size Of This Pixel Format Descriptor
		1,					// Version Number
		PFD_DRAW_TO_WINDOW |			// Format Must Support Window
		PFD_SUPPORT_OPENGL |			// Format Must Support OpenGL
		PFD_DOUBLEBUFFER,			// Must Support Double Buffering
		PFD_TYPE_RGBA,				// Request An RGBA Format
		bits,					// Select Our Color Depth
		0, 0, 0, 0, 0, 0,			// Color Bits Ignored
		0,					// No Alpha Buffer
		0,					// Shift Bit Ignored
		0,					// No Accumulation Buffer
		0, 0, 0, 0,				// Accumulation Bits Ignored
		16,					// 16Bit Z-Buffer (Depth Buffer)  
		0,					// No Stencil Buffer
		0,					// No Auxiliary Buffer
		PFD_MAIN_PLANE,				// Main Drawing Layer
		0,					// Reserved
		0, 0, 0					// Layer Masks Ignored
	};	
	if (!(hDC=GetDC(hWnd)))			// Did We Get A Device Context?
	{
		KillGLWindow();			// Reset The Display
		MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;
	}

	if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))	// Did Windows Find A Matching Pixel Format?
	{
		KillGLWindow();						// Reset The Display
		MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;
	}

	if(!SetPixelFormat(hDC,PixelFormat,&pfd))	// Are We Able To Set The Pixel Format?
	{
		KillGLWindow();				// Reset The Display
		MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;				// Return FALSE
	}

	if (!(hRC=wglCreateContext(hDC)))		// Are We Able To Get A Rendering Context?
	{
		KillGLWindow();				// Reset The Display
		MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;
	}

	if(!wglMakeCurrent(hDC,hRC))		// Try To Activate The Rendering Context
	{
		KillGLWindow();			// Reset The Display
		MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;
	}

	ShowWindow(hWnd,SW_SHOW);		// Show The Window
	SetForegroundWindow(hWnd);		// Slightly Higher Priority
	SetFocus(hWnd);				// Sets Keyboard Focus To The Window
	ReSizeGLScene(width, height);		// Set Up Our Perspective GL Screen

	if (!InitGL())				// Initialize Our Newly Created GL Window
	{
		KillGLWindow();			// Reset The Display
		MessageBox(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION);
		return FALSE;
	}

	return TRUE;				// Success
}

LRESULT CALLBACK WndProc(	HWND	hWnd,	// Handle For This Window
				UINT	uMsg,	// Message For This Window
				WPARAM	wParam,	// Additional Message Information
				LPARAM	lParam)	// Additional Message Information
{
	switch (uMsg)		// Check For Windows Messages
	{
		case WM_ACTIVATE:	// Watch For Window Activate Message
		{
			if (!HIWORD(wParam))	// Check Minimization State
			{
				active=TRUE;	// Program Is Active
			}
			else
			{
				active=FALSE;	// Program Is No Longer Active
			}

			return 0;		// Return To The Message Loop
		}

		case WM_SYSCOMMAND:		// Intercept System Commands
		{
			switch (wParam)		// Check System Calls
			{
				case SC_SCREENSAVE:	// Screensaver Trying To Start?
				case SC_MONITORPOWER:	// Monitor Trying To Enter Powersave?
				return 0;		// Prevent From Happening
			}
			break;				// Exit
		}

		case WM_CLOSE:			// Did We Receive A Close Message?
		{
			PostQuitMessage(0);	// Send A Quit Message
			return 0;		// Jump Back
		}

		case WM_KEYDOWN:		// Is A Key Being Held Down?
		{
			keys[wParam] = TRUE;	// If So, Mark It As TRUE
			return 0;		// Jump Back
		}

		case WM_KEYUP:			// Has A Key Been Released?
		{
			keys[wParam] = FALSE;	// If So, Mark It As FALSE
			return 0;		// Jump Back
		}

		case WM_SIZE:			// Resize The OpenGL Window
		{
			ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));  // LoWord=Width, HiWord=Height
			return 0;					// Jump Back
		}
	}

	// Pass All Unhandled Messages To DefWindowProc
	return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

int WINAPI WinMain(HINSTANCE	hInstance,		// Instance
		   HINSTANCE	hPrevInstance,		// Previous Instance
		   LPSTR	lpCmdLine,		// Command Line Parameters
		   int		nCmdShow)		// Window Show State
{
	MSG	msg;		// Windows Message Structure
	BOOL	done=FALSE;	// Bool Variable To Exit Loop

	// Ask The User Which Screen Mode They Prefer
	if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
	{
		fullscreen=FALSE;	// Windowed Mode
	}

	// Create Our OpenGL Window
	if (!CreateGLWindow("NeHe's Texture Mapping Tutorial",640,480,16,fullscreen))
	{
		return 0;		// Quit If Window Was Not Created
	}

	while(!done)			// Loop That Runs While done=FALSE
	{
		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))	// Is There A Message Waiting?
		{
			if (msg.message==WM_QUIT)		// Have We Received A Quit Message?
			{
				done=TRUE;			// If So done=TRUE
			}
			else					// If Not, Deal With Window Messages
			{
				TranslateMessage(&msg);		// Translate The Message
				DispatchMessage(&msg);		// Dispatch The Message
			}
		}
		else			// If There Are No Messages
		{
			// Draw The Scene.  Watch For ESC Key And Quit Messages From DrawGLScene()
			if ((active && !DrawGLScene()) || keys[VK_ESCAPE])	// Active?  Was There A Quit Received?
			{
				done=TRUE;					// ESC or DrawGLScene Signalled A Quit
			}
			else							// Not Time To Quit, Update Screen
			{
				SwapBuffers(hDC);				// Swap Buffers (Double Buffering)
			}

			if (keys[VK_F1])				// Is F1 Being Pressed?
			{
				keys[VK_F1]=FALSE;			// If So Make Key FALSE
				KillGLWindow();				// Kill Our Current Window
				fullscreen=!fullscreen;			// Toggle Fullscreen / Windowed Mode
				// Recreate Our OpenGL Window
				if (!CreateGLWindow("NeHe's Texture Mapping Tutorial",640,480,16,fullscreen))
				{
					return 0;			// Quit If Window Was Not Created
				}
			}
		}
	}

	// Shutdown
	KillGLWindow();			// Kill The Window
	return (msg.wParam);		// Exit The Program
}