每个3D世界基本上可以看作是sector(区段)的集合。一个sector(区段)可以是一个房间、一个立方体、或者任意一个闭合的区间。在程序内部直接存储数据会让程序显得太过死板和无趣。从磁盘上载入世界资料,会给我们带来更多的弹性,可以让我们体验不同的世界,而不用被迫重新编译程序。另一个好处就是用户可以切换世界资料并修改它们而无需知道程序如何读入输出这些资料的。

 

数据文件中每个三角形都以如下形式声明:

X1 Y1 Z1 U1 V1
X2 Y2 Z2 U2 V2
X3 Y3 Z3 U3 V3

ContractedBlock.gifExpandedBlockStart.gif坐标数据文件

NUMPOLLIES 
36

// Floor 1
-3.0  0.0 -3.0 0.0 6.0
-3.0  0.0  3.0 0.0 0.0
 
3.0  0.0  3.0 6.0 0.0

-3.0  0.0 -3.0 0.0 6.0
 
3.0  0.0 -3.0 6.0 6.0
 
3.0  0.0  3.0 6.0 0.0

// Ceiling 1
-3.0  1.0 -3.0 0.0 6.0
-3.0  1.0  3.0 0.0 0.0
 
3.0  1.0  3.0 6.0 0.0
-3.0  1.0 -3.0 0.0 6.0
 
3.0  1.0 -3.0 6.0 6.0
 
3.0  1.0  3.0 6.0 0.0

// A1

-2.0  1.0  -2.0 0.0 1.0
-2.0  0.0  -2.0 0.0 0.0
-0.5  0.0  -2.0 1.5 0.0
-2.0  1.0  -2.0 0.0 1.0
-0.5  1.0  -2.0 1.5 1.0
-0.5  0.0  -2.0 1.5 0.0

// A2

 
2.0  1.0  -2.0 2.0 1.0
 
2.0  0.0  -2.0 2.0 0.0
 
0.5  0.0  -2.0 0.5 0.0
 
2.0  1.0  -2.0 2.0 1.0
 
0.5  1.0  -2.0 0.5 1.0
 
0.5  0.0  -2.0 0.5 0.0

// B1

-2.0  1.0  2.0 2.0  1.0
-2.0  0.0   2.0 2.0 0.0
-0.5  0.0   2.0 0.5 0.0
-2.0  1.0  2.0 2.0  1.0
-0.5  1.0  2.0 0.5  1.0
-0.5  0.0   2.0 0.5 0.0

// B2

 
2.0  1.0  2.0 2.0  1.0
 
2.0  0.0   2.0 2.0 0.0
 
0.5  0.0   2.0 0.5 0.0
 
2.0  1.0  2.0 2.0  1.0
 
0.5  1.0  2.0 0.5  1.0
 
0.5  0.0   2.0 0.5 0.0

// C1

-2.0  1.0  -2.0 0.0  1.0
-2.0  0.0   -2.0 0.0 0.0
-2.0  0.0   -0.5 1.5 0.0
-2.0  1.0  -2.0 0.0  1.0
-2.0  1.0  -0.5 1.5  1.0
-2.0  0.0   -0.5 1.5 0.0

// C2

-2.0  1.0   2.0 2.0 1.0
-2.0  0.0   2.0 2.0 0.0
-2.0  0.0   0.5 0.5 0.0
-2.0  1.0  2.0 2.0 1.0
-2.0  1.0  0.5 0.5 1.0
-2.0  0.0   0.5 0.5 0.0

// D1

2.0  1.0  -2.0 0.0 1.0
2.0  0.0   -2.0 0.0 0.0
2.0  0.0   -0.5 1.5 0.0
2.0  1.0  -2.0 0.0 1.0
2.0  1.0  -0.5 1.5 1.0
2.0  0.0   -0.5 1.5 0.0

// D2

2.0  1.0  2.0 2.0 1.0
2.0  0.0   2.0 2.0 0.0
2.0  0.0   0.5 0.5 0.0
2.0  1.0  2.0 2.0 1.0
2.0  1.0  0.5 0.5 1.0
2.0  0.0   0.5 0.5 0.0

// Upper hallway - L
-0.5  1.0  -3.0 0.0 1.0
-0.5  0.0   -3.0 0.0 0.0
-0.5  0.0   -2.0 1.0 0.0
-0.5  1.0  -3.0 0.0 1.0
-0.5  1.0  -2.0 1.0 1.0
-0.5  0.0   -2.0 1.0 0.0

// Upper hallway - R
0.5  1.0  -3.0 0.0 1.0
0.5  0.0   -3.0 0.0 0.0
0.5  0.0   -2.0 1.0 0.0
0.5  1.0  -3.0 0.0 1.0
0.5  1.0  -2.0 1.0 1.0
0.5  0.0   -2.0 1.0 0.0

// Lower hallway - L
-0.5  1.0  3.0 0.0 1.0
-0.5  0.0   3.0 0.0 0.0
-0.5  0.0   2.0 1.0 0.0
-0.5  1.0  3.0 0.0 1.0
-0.5  1.0  2.0 1.0 1.0
-0.5  0.0   2.0 1.0 0.0

// Lower hallway - R
0.5  1.0  3.0 0.0 1.0
0.5  0.0   3.0 0.0 0.0
0.5  0.0   2.0 1.0 0.0
0.5  1.0  3.0 0.0 1.0
0.5  1.0  2.0 1.0 1.0
0.5  0.0   2.0 1.0 0.0


// Left hallway - Lw

-3.0  1.0  0.5 1.0 1.0
-3.0  0.0   0.5 1.0 0.0
-2.0  0.0   0.5 0.0 0.0
-3.0  1.0  0.5 1.0 1.0
-2.0  1.0  0.5 0.0 1.0
-2.0  0.0   0.5 0.0 0.0

// Left hallway - Hi

-3.0  1.0  -0.5 1.0 1.0
-3.0  0.0   -0.5 1.0 0.0
-2.0  0.0   -0.5 0.0 0.0
-3.0  1.0  -0.5 1.0 1.0
-2.0  1.0  -0.5 0.0 1.0
-2.0  0.0   -0.5 0.0 0.0

// Right hallway - Lw

3.0  1.0  0.5 1.0 1.0
3.0  0.0   0.5 1.0 0.0
2.0  0.0   0.5 0.0 0.0
3.0  1.0  0.5 1.0 1.0
2.0  1.0  0.5 0.0 1.0
2.0  0.0   0.5 0.0 0.0

// Right hallway - Hi

3.0  1.0  -0.5 1.0 1.0
3.0  0.0   -0.5 1.0 0.0
2.0  0.0   -0.5 0.0 0.0
3.0  1.0  -0.5 1.0 1.0
2.0  1.0 -0.5 0.0 1.0
2.0  0.0   -0.5 0.0 0.0

到目前为止,我们所作过的都是些简单的旋转和平移。但我们的镜头始终位于原点(000)处。任何一个不错的3D引擎都会允许用户在这个世界中游走和遍 历,我们的这个也一样。实现这个功能的一种途径是直接移动镜头并绘制以镜头为中心的3D环境。这样做会很慢并且不易用代码实现。我们的解决方法如下:

1)根据用户的指令旋转并变换镜头位置。

2)围绕原点,以与镜头相反的旋转方向来旋转世界。(让人产生镜头旋转的错觉)

3)以与镜头平移方式相反的方式来平移世界(让人产生镜头移动的错觉)

实现很简单。当左右方向键按下后,旋转变量yrot相应增加或减少。当前后方向键按下后,我们使用sinecosine函数重新生成镜头位置。Piover180是一个很简单的折算因子用来折算度和弧度。

walkbias基本上就是当人行走时头部产生上下摆动的幅度。我们使用简单的sine正弦波来调节镜头的Y轴位置。如果不添加这个而只是前后移动的话,程序看起来就没这么棒了。

ContractedBlock.gifExpandedBlockStart.gifdemo10

#include 
<windows.h>        // Header File For Windows
#include <math.h>            // Math Library Header File
#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
bool    blend;                // Blending ON/OFF
bool    bp;                    // B Pressed?
bool    fp;                    // F Pressed?

const float piover180 = 0.0174532925f;
float heading;
float xpos;
float zpos;

GLfloat    yrot;                
// Y Rotation
GLfloat walkbias = 0;
GLfloat walkbiasangle 
= 0;
GLfloat lookupdown 
= 0.0f;
GLfloat    z
=0.0f;                // Depth Into The Screen

GLuint    filter;                
// Which Filter To Use
GLuint    texture[3];            // Storage For 3 Textures

typedef 
struct tagVERTEX
{
    
float x, y, z;//3D坐标
    float u, v;//纹理坐标
} VERTEX;

typedef 
struct tagTRIANGLE
{
//Triangle三角形结构
    VERTEX vertex[3];
} TRIANGLE;

typedef 
struct tagSECTOR
{
    
int numtriangles;// Sector中的三角形个数
    TRIANGLE* triangle;// 指向三角数组的指针
} SECTOR;

SECTOR sector1;                
// Our Model Goes Here:

LRESULT    CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);    
// Declaration For WndProc

void readstr(FILE *f,char *string)
{
//  读入一个字符串
    do
    {
        fgets(
string255, f);
    } 
while ((string[0== '/'|| (string[0== '\n'));
    
return;
}

void SetupWorld()
{
// 设置我们的世界
    float x, y, z, u, v;
    
int numtriangles;
    FILE 
*filein;
    
char oneline[255];
    filein 
= fopen("data/world.txt""rt");                // File To Load World Data From

    readstr(filein,oneline);
    sscanf(oneline, 
"NUMPOLLIES %d\n"&numtriangles);

    sector1.triangle 
= new TRIANGLE[numtriangles];
    sector1.numtriangles 
= numtriangles;
    
for (int loop = 0; loop < numtriangles; loop++)
    {
        
for (int vert = 0; vert < 3; vert++)
        {
            readstr(filein,oneline);
            sscanf(oneline, 
"%f %f %f %f %f"&x, &y, &z, &u, &v);
            sector1.triangle[loop].vertex[vert].x 
= x;
            sector1.triangle[loop].vertex[vert].y 
= y;
            sector1.triangle[loop].vertex[vert].z 
= z;
            sector1.triangle[loop].vertex[vert].u 
= u;
            sector1.triangle[loop].vertex[vert].v 
= v;
        }
    }
    fclose(filein);
    
return;
}

AUX_RGBImageRec 
*LoadBMP(char *Filename)                // Loads A Bitmap Image
{
        FILE 
*File=NULL;                                // File Handle

        
if (!Filename)                                  // Make Sure A Filename Was Given
        {
                
return NULL;                            // If Not Return NULL
        }

        File
=fopen(Filename,"r");                       // Check To See If The File Exists

        
if (File)                                       // Does The File Exist?
        {
                fclose(File);                           
// Close The Handle
                return auxDIBImageLoad(Filename);       // Load The Bitmap And Return A Pointer
        }
        
return NULL;                                    // If Load Failed Return NULL
}

int LoadGLTextures()                                    // Load Bitmaps And Convert To Textures
{
        
int Status=FALSE;                               // Status Indicator

        AUX_RGBImageRec 
*TextureImage[1];               // Create Storage Space For The Texture

        memset(TextureImage,
0,sizeof(void *)*1);        // Set The Pointer To NULL

        
// Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit
        if (TextureImage[0]=LoadBMP("Data/Mud.bmp"))
        {
                Status
=TRUE;                            // Set The Status To TRUE

                glGenTextures(
3&texture[0]);          // Create Three Textures

                
// Create Nearest Filtered Texture
                glBindTexture(GL_TEXTURE_2D, texture[0]);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
                glTexImage2D(GL_TEXTURE_2D, 
03, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);

                
// Create Linear Filtered Texture
                glBindTexture(GL_TEXTURE_2D, texture[1]);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
                glTexImage2D(GL_TEXTURE_2D, 
03, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);

                
// Create MipMapped Texture
                glBindTexture(GL_TEXTURE_2D, texture[2]);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
                glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
                gluBuild2DMipmaps(GL_TEXTURE_2D, 
3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
        }
        
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
}

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
}

int InitGL(GLvoid)                                        // All Setup For OpenGL Goes Here
{
    
if (!LoadGLTextures())                                // Jump To Texture Loading Routine
    {
        
return FALSE;                                    // If Texture Didn't Load Return FALSE
    }

    glEnable(GL_TEXTURE_2D);                            
// Enable Texture Mapping
    glBlendFunc(GL_SRC_ALPHA,GL_ONE);                    // Set The Blending Function For Translucency
    glClearColor(0.0f0.0f0.0f0.0f);                // This Will Clear The Background Color To Black
    glClearDepth(1.0);                                    // Enables Clearing Of The Depth Buffer
    glDepthFunc(GL_LESS);                                // The Type Of Depth Test To Do
    glEnable(GL_DEPTH_TEST);                            // Enables Depth Testing
    glShadeModel(GL_SMOOTH);                            // Enables Smooth Color Shading
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);    // Really Nice Perspective Calculations

    SetupWorld();
//建立世界

    
return TRUE;                                        // Initialization Went OK
}

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

    GLfloat x_m, y_m, z_m, u_m, v_m;
    GLfloat xtrans 
= -xpos;
    GLfloat ztrans 
= -zpos;
    GLfloat ytrans 
= -walkbias-0.25f;
    GLfloat sceneroty 
= 360.0f - yrot;

    
int numtriangles;

    glRotatef(lookupdown,
1.0f,0,0);
    glRotatef(sceneroty,
0,1.0f,0);
    
    glTranslatef(xtrans, ytrans, ztrans);
    glBindTexture(GL_TEXTURE_2D, texture[filter]);
    
    numtriangles 
= sector1.numtriangles;
    
    
// Process Each Triangle
    for (int loop_m = 0; loop_m < numtriangles; loop_m++)
    {
        glBegin(GL_TRIANGLES);
            glNormal3f( 
0.0f0.0f1.0f);
            x_m 
= sector1.triangle[loop_m].vertex[0].x;
            y_m 
= sector1.triangle[loop_m].vertex[0].y;
            z_m 
= sector1.triangle[loop_m].vertex[0].z;
            u_m 
= sector1.triangle[loop_m].vertex[0].u;
            v_m 
= sector1.triangle[loop_m].vertex[0].v;
            glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m);
            
            x_m 
= sector1.triangle[loop_m].vertex[1].x;
            y_m 
= sector1.triangle[loop_m].vertex[1].y;
            z_m 
= sector1.triangle[loop_m].vertex[1].z;
            u_m 
= sector1.triangle[loop_m].vertex[1].u;
            v_m 
= sector1.triangle[loop_m].vertex[1].v;
            glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m);
            
            x_m 
= sector1.triangle[loop_m].vertex[2].x;
            y_m 
= sector1.triangle[loop_m].vertex[2].y;
            z_m 
= sector1.triangle[loop_m].vertex[2].z;
            u_m 
= sector1.triangle[loop_m].vertex[2].u;
            v_m 
= sector1.triangle[loop_m].vertex[2].v;
            glTexCoord2f(u_m,v_m); glVertex3f(x_m,y_m,z_m);
        glEnd();
    }
    
return TRUE;                                        // Everything Went OK
}

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;                                            // 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;                                    // 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
                                00,                                // 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;                                // 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
        000000,                            // Color Bits Ignored
        0,                                            // No Alpha Buffer
        0,                                            // Shift Bit Ignored
        0,                                            // No Accumulation Buffer
        0000,                                    // 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
        000                                        // 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;                                // 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;                                // 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;                                // 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;                                // 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 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("Lionel Brits & NeHe's 3D World 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['B'&& !bp)
                {
                    bp
=TRUE;
                    blend
=!blend;
                    
if (!blend)
                    {
                        glDisable(GL_BLEND);
                        glEnable(GL_DEPTH_TEST);
                    }
                    
else
                    {
                        glEnable(GL_BLEND);
                        glDisable(GL_DEPTH_TEST);
                    }
                }
                
if (!keys['B'])
                {
                    bp
=FALSE;
                }

                
if (keys['F'&& !fp)
                {
                    fp
=TRUE;
                    filter
+=1;
                    
if (filter>2)
                    {
                        filter
=0;
                    }
                }
                
if (!keys['F'])
                {
                    fp
=FALSE;
                }

                
if (keys[VK_PRIOR])
                {
                    z
-=0.02f;
                }

                
if (keys[VK_NEXT])
                {
                    z
+=0.02f;
                }

                
if (keys[VK_UP])
                {

                    xpos 
-= (float)sin(heading*piover180) * 0.05f;
                    zpos 
-= (float)cos(heading*piover180) * 0.05f;
                    
if (walkbiasangle >= 359.0f)
                    {
                        walkbiasangle 
= 0.0f;
                    }
                    
else
                    {
                        walkbiasangle
+= 10;
                    }
                    walkbias 
= (float)sin(walkbiasangle * piover180)/20.0f;
                }

                
if (keys[VK_DOWN])
                {
                    xpos 
+= (float)sin(heading*piover180) * 0.05f;
                    zpos 
+= (float)cos(heading*piover180) * 0.05f;
                    
if (walkbiasangle <= 1.0f)
                    {
                        walkbiasangle 
= 359.0f;
                    }
                    
else
                    {
                        walkbiasangle
-= 10;
                    }
                    walkbias 
= (float)sin(walkbiasangle * piover180)/20.0f;
                }

                
if (keys[VK_RIGHT])
                {
                    heading 
-= 1.0f;
                    yrot 
= heading;
                }

                
if (keys[VK_LEFT])
                {
                    heading 
+= 1.0f;    
                    yrot 
= heading;
                }

                
if (keys[VK_PRIOR])
                {
                    lookupdown
-= 1.0f;
                }

                
if (keys[VK_NEXT])
                {
                    lookupdown
+= 1.0f;
                }

                
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("Lionel Brits & NeHe's 3D World Tutorial",640,480,16,fullscreen))
                    {
                        
return 0;                        // Quit If Window Was Not Created
                    }
                }
            }
        }
    }

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