每个3D世界基本上可以看作是sector(区段)的集合。一个sector(区段)可以是一个房间、一个立方体、或者任意一个闭合的区间。在程序内部直接存储数据会让程序显得太过死板和无趣。从磁盘上载入世界资料,会给我们带来更多的弹性,可以让我们体验不同的世界,而不用被迫重新编译程序。另一个好处就是用户可以切换世界资料并修改它们而无需知道程序如何读入输出这些资料的。
数据文件中每个三角形都以如下形式声明:
X1 Y1 Z1 U1 V1
X2 Y2 Z2 U2 V2
X3 Y3 Z3 U3 V3


<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->
NUMPOLLIES36
//Floor1
-3.00.0-3.00.06.0
-3.00.03.00.00.0
3.00.03.06.00.0
-3.00.0-3.00.06.0
3.00.0-3.06.06.0
3.00.03.06.00.0
//Ceiling1
-3.01.0-3.00.06.0
-3.01.03.00.00.0
3.01.03.06.00.0
-3.01.0-3.00.06.0
3.01.0-3.06.06.0
3.01.03.06.00.0
//A1
-2.01.0-2.00.01.0
-2.00.0-2.00.00.0
-0.50.0-2.01.50.0
-2.01.0-2.00.01.0
-0.51.0-2.01.51.0
-0.50.0-2.01.50.0
//A2
2.01.0-2.02.01.0
2.00.0-2.02.00.0
0.50.0-2.00.50.0
2.01.0-2.02.01.0
0.51.0-2.00.51.0
0.50.0-2.00.50.0
//B1
-2.01.02.02.01.0
-2.00.02.02.00.0
-0.50.02.00.50.0
-2.01.02.02.01.0
-0.51.02.00.51.0
-0.50.02.00.50.0
//B2
2.01.02.02.01.0
2.00.02.02.00.0
0.50.02.00.50.0
2.01.02.02.01.0
0.51.02.00.51.0
0.50.02.00.50.0
//C1
-2.01.0-2.00.01.0
-2.00.0-2.00.00.0
-2.00.0-0.51.50.0
-2.01.0-2.00.01.0
-2.01.0-0.51.51.0
-2.00.0-0.51.50.0
//C2
-2.01.02.02.01.0
-2.00.02.02.00.0
-2.00.00.50.50.0
-2.01.02.02.01.0
-2.01.00.50.51.0
-2.00.00.50.50.0
//D1
2.01.0-2.00.01.0
2.00.0-2.00.00.0
2.00.0-0.51.50.0
2.01.0-2.00.01.0
2.01.0-0.51.51.0
2.00.0-0.51.50.0
//D2
2.01.02.02.01.0
2.00.02.02.00.0
2.00.00.50.50.0
2.01.02.02.01.0
2.01.00.50.51.0
2.00.00.50.50.0
//Upperhallway-L
-0.51.0-3.00.01.0
-0.50.0-3.00.00.0
-0.50.0-2.01.00.0
-0.51.0-3.00.01.0
-0.51.0-2.01.01.0
-0.50.0-2.01.00.0
//Upperhallway-R
0.51.0-3.00.01.0
0.50.0-3.00.00.0
0.50.0-2.01.00.0
0.51.0-3.00.01.0
0.51.0-2.01.01.0
0.50.0-2.01.00.0
//Lowerhallway-L
-0.51.03.00.01.0
-0.50.03.00.00.0
-0.50.02.01.00.0
-0.51.03.00.01.0
-0.51.02.01.01.0
-0.50.02.01.00.0
//Lowerhallway-R
0.51.03.00.01.0
0.50.03.00.00.0
0.50.02.01.00.0
0.51.03.00.01.0
0.51.02.01.01.0
0.50.02.01.00.0
//Lefthallway-Lw
-3.01.00.51.01.0
-3.00.00.51.00.0
-2.00.00.50.00.0
-3.01.00.51.01.0
-2.01.00.50.01.0
-2.00.00.50.00.0
//Lefthallway-Hi
-3.01.0-0.51.01.0
-3.00.0-0.51.00.0
-2.00.0-0.50.00.0
-3.01.0-0.51.01.0
-2.01.0-0.50.01.0
-2.00.0-0.50.00.0
//Righthallway-Lw
3.01.00.51.01.0
3.00.00.51.00.0
2.00.00.50.00.0
3.01.00.51.01.0
2.01.00.50.01.0
2.00.00.50.00.0
//Righthallway-Hi
3.01.0-0.51.01.0
3.00.0-0.51.00.0
2.00.0-0.50.00.0
3.01.0-0.51.01.0
2.01.0-0.50.01.0
2.00.0-0.50.00.0
到目前为止,我们所作过的都是些简单的旋转和平移。但我们的镜头始终位于原点(0,0,0)处。任何一个不错的3D引擎都会允许用户在这个世界中游走和遍 历,我们的这个也一样。实现这个功能的一种途径是直接移动镜头并绘制以镜头为中心的3D环境。这样做会很慢并且不易用代码实现。我们的解决方法如下:
1)根据用户的指令旋转并变换镜头位置。
2)围绕原点,以与镜头相反的旋转方向来旋转世界。(让人产生镜头旋转的错觉)
3)以与镜头平移方式相反的方式来平移世界(让人产生镜头移动的错觉)。
实现很简单。当左右方向键按下后,旋转变量yrot相应增加或减少。当前后方向键按下后,我们使用sine和cosine函数重新生成镜头位置。Piover180是一个很简单的折算因子用来折算度和弧度。
walkbias基本上就是当人行走时头部产生上下摆动的幅度。我们使用简单的sine正弦波来调节镜头的Y轴位置。如果不添加这个而只是前后移动的话,程序看起来就没这么棒了。


<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->
#include<windows.h>//HeaderFileForWindows
#include<math.h>//MathLibraryHeaderFile
#include<stdio.h>//HeaderFileForStandardInput/Output
#include<gl/gl.h>//HeaderFileForTheOpenGL32Library
#include<gl/glu.h>//HeaderFileForTheGLu32Library
#include<gl/glaux.h>//HeaderFileForTheGlauxLibrary
HDChDC=NULL;//PrivateGDIDeviceContext
HGLRChRC=NULL;//PermanentRenderingContext
HWNDhWnd=NULL;//HoldsOurWindowHandle
HINSTANCEhInstance;//HoldsTheInstanceOfTheApplication
boolkeys[256];//ArrayUsedForTheKeyboardRoutine
boolactive=TRUE;//WindowActiveFlagSetToTRUEByDefault
boolfullscreen=TRUE;//FullscreenFlagSetToFullscreenModeByDefault
boolblend;//BlendingON/OFF
boolbp;//BPressed?
boolfp;//FPressed?
constfloatpiover180=0.0174532925f;
floatheading;
floatxpos;
floatzpos;
GLfloatyrot;//YRotation
GLfloatwalkbias=0;
GLfloatwalkbiasangle=0;
GLfloatlookupdown=0.0f;
GLfloatz=0.0f;//DepthIntoTheScreen
GLuintfilter;//WhichFilterToUse
GLuinttexture[3];//StorageFor3Textures
typedefstructtagVERTEX
{
floatx,y,z;//3D坐标
floatu,v;//纹理坐标
}VERTEX;
typedefstructtagTRIANGLE
{//Triangle三角形结构
VERTEXvertex[3];
}TRIANGLE;
typedefstructtagSECTOR
{
intnumtriangles;//Sector中的三角形个数
TRIANGLE*triangle;//指向三角数组的指针
}SECTOR;
SECTORsector1;//OurModelGoesHere:
LRESULTCALLBACKWndProc(HWND,UINT,WPARAM,LPARAM);//DeclarationForWndProc
voidreadstr(FILE*f,char*string)
{//读入一个字符串
do
{
fgets(string,255,f);
}while((string[0]=='/')||(string[0]=='/n'));
return;
}
voidSetupWorld()
{//设置我们的世界
floatx,y,z,u,v;
intnumtriangles;
FILE*filein;
charoneline[255];
filein=fopen("data/world.txt","rt");//FileToLoadWorldDataFrom
readstr(filein,oneline);
sscanf(oneline,"NUMPOLLIES%d/n",&numtriangles);
sector1.triangle=newTRIANGLE[numtriangles];
sector1.numtriangles=numtriangles;
for(intloop=0;loop<numtriangles;loop++)
{
for(intvert=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)//LoadsABitmapImage
{
FILE*File=NULL;//FileHandle
if(!Filename)//MakeSureAFilenameWasGiven
{
returnNULL;//IfNotReturnNULL
}
File=fopen(Filename,"r");//CheckToSeeIfTheFileExists
if(File)//DoesTheFileExist?
{
fclose(File);//CloseTheHandle
returnauxDIBImageLoad(Filename);//LoadTheBitmapAndReturnAPointer
}
returnNULL;//IfLoadFailedReturnNULL
}
intLoadGLTextures()//LoadBitmapsAndConvertToTextures
{
intStatus=FALSE;//StatusIndicator
AUX_RGBImageRec*TextureImage[1];//CreateStorageSpaceForTheTexture
memset(TextureImage,0,sizeof(void*)*1);//SetThePointerToNULL
//LoadTheBitmap,CheckForErrors,IfBitmap'sNotFoundQuit
if(TextureImage[0]=LoadBMP("Data/Mud.bmp"))
{
Status=TRUE;//SetTheStatusToTRUE
glGenTextures(3,&texture[0]);//CreateThreeTextures
//CreateNearestFilteredTexture
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,0,3,TextureImage[0]->sizeX,TextureImage[0]->sizeY,0,GL_RGB,GL_UNSIGNED_BYTE,TextureImage[0]->data);
//CreateLinearFilteredTexture
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,0,3,TextureImage[0]->sizeX,TextureImage[0]->sizeY,0,GL_RGB,GL_UNSIGNED_BYTE,TextureImage[0]->data);
//CreateMipMappedTexture
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])//IfTextureExists
{
if(TextureImage[0]->data)//IfTextureImageExists
{
free(TextureImage[0]->data);//FreeTheTextureImageMemory
}
free(TextureImage[0]);//FreeTheImageStructure
}
returnStatus;//ReturnTheStatus
}
GLvoidReSizeGLScene(GLsizeiwidth,GLsizeiheight)//ResizeAndInitializeTheGLWindow
{
if(height==0)//PreventADivideByZeroBy
{
height=1;//MakingHeightEqualOne
}
glViewport(0,0,width,height);//ResetTheCurrentViewport
glMatrixMode(GL_PROJECTION);//SelectTheProjectionMatrix
glLoadIdentity();//ResetTheProjectionMatrix
//CalculateTheAspectRatioOfTheWindow
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
glMatrixMode(GL_MODELVIEW);//SelectTheModelviewMatrix
glLoadIdentity();//ResetTheModelviewMatrix
}
intInitGL(GLvoid)//AllSetupForOpenGLGoesHere
{
if(!LoadGLTextures())//JumpToTextureLoadingRoutine
{
returnFALSE;//IfTextureDidn'tLoadReturnFALSE
}
glEnable(GL_TEXTURE_2D);//EnableTextureMapping
glBlendFunc(GL_SRC_ALPHA,GL_ONE);//SetTheBlendingFunctionForTranslucency
glClearColor(0.0f,0.0f,0.0f,0.0f);//ThisWillClearTheBackgroundColorToBlack
glClearDepth(1.0);//EnablesClearingOfTheDepthBuffer
glDepthFunc(GL_LESS);//TheTypeOfDepthTestToDo
glEnable(GL_DEPTH_TEST);//EnablesDepthTesting
glShadeModel(GL_SMOOTH);//EnablesSmoothColorShading
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);//ReallyNicePerspectiveCalculations
SetupWorld();//建立世界
returnTRUE;//InitializationWentOK
}
intDrawGLScene(GLvoid)//Here'sWhereWeDoAllTheDrawing
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);//ClearTheScreenAndTheDepthBuffer
glLoadIdentity();//ResetTheView
GLfloatx_m,y_m,z_m,u_m,v_m;
GLfloatxtrans=-xpos;
GLfloatztrans=-zpos;
GLfloatytrans=-walkbias-0.25f;
GLfloatsceneroty=360.0f-yrot;
intnumtriangles;
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;
//ProcessEachTriangle
for(intloop_m=0;loop_m<numtriangles;loop_m++)
{
glBegin(GL_TRIANGLES);
glNormal3f(0.0f,0.0f,1.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();
}
returnTRUE;//EverythingWentOK
}
GLvoidKillGLWindow(GLvoid)//ProperlyKillTheWindow
{
if(fullscreen)//AreWeInFullscreenMode?
{
ChangeDisplaySettings(NULL,0);//IfSoSwitchBackToTheDesktop
ShowCursor(TRUE);//ShowMousePointer
}
if(hRC)//DoWeHaveARenderingContext?
{
if(!wglMakeCurrent(NULL,NULL))//AreWeAbleToReleaseTheDCAndRCContexts?
{
MessageBox(NULL,"ReleaseOfDCAndRCFailed.","SHUTDOWNERROR",MB_OK|MB_ICONINFORMATION);
}
if(!wglDeleteContext(hRC))//AreWeAbleToDeleteTheRC?
{
MessageBox(NULL,"ReleaseRenderingContextFailed.","SHUTDOWNERROR",MB_OK|MB_ICONINFORMATION);
}
hRC=NULL;//SetRCToNULL
}
if(hDC&&!ReleaseDC(hWnd,hDC))//AreWeAbleToReleaseTheDC
{
MessageBox(NULL,"ReleaseDeviceContextFailed.","SHUTDOWNERROR",MB_OK|MB_ICONINFORMATION);
hDC=NULL;//SetDCToNULL
}
if(hWnd&&!DestroyWindow(hWnd))//AreWeAbleToDestroyTheWindow?
{
MessageBox(NULL,"CouldNotReleasehWnd.","SHUTDOWNERROR",MB_OK|MB_ICONINFORMATION);
hWnd=NULL;//SethWndToNULL
}
if(!UnregisterClass("OpenGL",hInstance))//AreWeAbleToUnregisterClass
{
MessageBox(NULL,"CouldNotUnregisterClass.","SHUTDOWNERROR",MB_OK|MB_ICONINFORMATION);
hInstance=NULL;//SethInstanceToNULL
}
}
/*ThisCodeCreatesOurOpenGLWindow.ParametersAre:*
*title-TitleToAppearAtTheTopOfTheWindow*
*width-WidthOfTheGLWindowOrFullscreenMode*
*height-HeightOfTheGLWindowOrFullscreenMode*
*bits-NumberOfBitsToUseForColor(8/16/24/32)*
*fullscreenflag-UseFullscreenMode(TRUE)OrWindowedMode(FALSE)*/
BOOLCreateGLWindow(char*title,intwidth,intheight,intbits,boolfullscreenflag)
{
GLuintPixelFormat;//HoldsTheResultsAfterSearchingForAMatch
WNDCLASSwc;//WindowsClassStructure
DWORDdwExStyle;//WindowExtendedStyle
DWORDdwStyle;//WindowStyle
RECTWindowRect;//GrabsRectangleUpperLeft/LowerRightValues
WindowRect.left=(long)0;//SetLeftValueTo0
WindowRect.right=(long)width;//SetRightValueToRequestedWidth
WindowRect.top=(long)0;//SetTopValueTo0
WindowRect.bottom=(long)height;//SetBottomValueToRequestedHeight
fullscreen=fullscreenflag;//SetTheGlobalFullscreenFlag
hInstance=GetModuleHandle(NULL);//GrabAnInstanceForOurWindow
wc.style=CS_HREDRAW|CS_VREDRAW|CS_OWNDC;//RedrawOnSize,AndOwnDCForWindow.
wc.lpfnWndProc=(WNDPROC)WndProc;//WndProcHandlesMessages
wc.cbClsExtra=0;//NoExtraWindowData
wc.cbWndExtra=0;//NoExtraWindowData
wc.hInstance=hInstance;//SetTheInstance
wc.hIcon=LoadIcon(NULL,IDI_WINLOGO);//LoadTheDefaultIcon
wc.hCursor=LoadCursor(NULL,IDC_ARROW);//LoadTheArrowPointer
wc.hbrBackground=NULL;//NoBackgroundRequiredForGL
wc.lpszMenuName=NULL;//WeDon'tWantAMenu
wc.lpszClassName="OpenGL";//SetTheClassName
if(!RegisterClass(&wc))//AttemptToRegisterTheWindowClass
{
MessageBox(NULL,"FailedToRegisterTheWindowClass.","ERROR",MB_OK|MB_ICONEXCLAMATION);
returnFALSE;//ReturnFALSE
}
if(fullscreen)//AttemptFullscreenMode?
{
DEVMODEdmScreenSettings;//DeviceMode
memset(&dmScreenSettings,0,sizeof(dmScreenSettings));//MakesSureMemory'sCleared
dmScreenSettings.dmSize=sizeof(dmScreenSettings);//SizeOfTheDevmodeStructure
dmScreenSettings.dmPelsWidth=width;//SelectedScreenWidth
dmScreenSettings.dmPelsHeight=height;//SelectedScreenHeight
dmScreenSettings.dmBitsPerPel=bits;//SelectedBitsPerPixel
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
//TryToSetSelectedModeAndGetResults.NOTE:CDS_FULLSCREENGetsRidOfStartBar.
if(ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
{
//IfTheModeFails,OfferTwoOptions.QuitOrUseWindowedMode.
if(MessageBox(NULL,"TheRequestedFullscreenModeIsNotSupportedBy/nYourVideoCard.UseWindowedModeInstead?","NeHeGL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
{
fullscreen=FALSE;//WindowedModeSelected.Fullscreen=FALSE
}
else
{
//PopUpAMessageBoxLettingUserKnowTheProgramIsClosing.
MessageBox(NULL,"ProgramWillNowClose.","ERROR",MB_OK|MB_ICONSTOP);
returnFALSE;//ReturnFALSE
}
}
}
if(fullscreen)//AreWeStillInFullscreenMode?
{
dwExStyle=WS_EX_APPWINDOW;//WindowExtendedStyle
dwStyle=WS_POPUP;//WindowsStyle
ShowCursor(FALSE);//HideMousePointer
}
else
{
dwExStyle=WS_EX_APPWINDOW|WS_EX_WINDOWEDGE;//WindowExtendedStyle
dwStyle=WS_OVERLAPPEDWINDOW;//WindowsStyle
}
AdjustWindowRectEx(&WindowRect,dwStyle,FALSE,dwExStyle);//AdjustWindowToTrueRequestedSize
//CreateTheWindow
if(!(hWnd=CreateWindowEx(dwExStyle,//ExtendedStyleForTheWindow
"OpenGL",//ClassName
title,//WindowTitle
dwStyle|//DefinedWindowStyle
WS_CLIPSIBLINGS|//RequiredWindowStyle
WS_CLIPCHILDREN,//RequiredWindowStyle
0,0,//WindowPosition
WindowRect.right-WindowRect.left,//CalculateWindowWidth
WindowRect.bottom-WindowRect.top,//CalculateWindowHeight
NULL,//NoParentWindow
NULL,//NoMenu
hInstance,//Instance
NULL)))//DontPassAnythingToWM_CREATE
{
KillGLWindow();//ResetTheDisplay
MessageBox(NULL,"WindowCreationError.","ERROR",MB_OK|MB_ICONEXCLAMATION);
returnFALSE;//ReturnFALSE
}
staticPIXELFORMATDESCRIPTORpfd=//pfdTellsWindowsHowWeWantThingsToBe
{
sizeof(PIXELFORMATDESCRIPTOR),//SizeOfThisPixelFormatDescriptor
1,//VersionNumber
PFD_DRAW_TO_WINDOW|//FormatMustSupportWindow
PFD_SUPPORT_OPENGL|//FormatMustSupportOpenGL
PFD_DOUBLEBUFFER,//MustSupportDoubleBuffering
PFD_TYPE_RGBA,//RequestAnRGBAFormat
bits,//SelectOurColorDepth
0,0,0,0,0,0,//ColorBitsIgnored
0,//NoAlphaBuffer
0,//ShiftBitIgnored
0,//NoAccumulationBuffer
0,0,0,0,//AccumulationBitsIgnored
16,//16BitZ-Buffer(DepthBuffer)
0,//NoStencilBuffer
0,//NoAuxiliaryBuffer
PFD_MAIN_PLANE,//MainDrawingLayer
0,//Reserved
0,0,0//LayerMasksIgnored
};
if(!(hDC=GetDC(hWnd)))//DidWeGetADeviceContext?
{
KillGLWindow();//ResetTheDisplay
MessageBox(NULL,"Can'tCreateAGLDeviceContext.","ERROR",MB_OK|MB_ICONEXCLAMATION);
returnFALSE;//ReturnFALSE
}
if(!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))//DidWindowsFindAMatchingPixelFormat?
{
KillGLWindow();//ResetTheDisplay
MessageBox(NULL,"Can'tFindASuitablePixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
returnFALSE;//ReturnFALSE
}
if(!SetPixelFormat(hDC,PixelFormat,&pfd))//AreWeAbleToSetThePixelFormat?
{
KillGLWindow();//ResetTheDisplay
MessageBox(NULL,"Can'tSetThePixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
returnFALSE;//ReturnFALSE
}
if(!(hRC=wglCreateContext(hDC)))//AreWeAbleToGetARenderingContext?
{
KillGLWindow();//ResetTheDisplay
MessageBox(NULL,"Can'tCreateAGLRenderingContext.","ERROR",MB_OK|MB_ICONEXCLAMATION);
returnFALSE;//ReturnFALSE
}
if(!wglMakeCurrent(hDC,hRC))//TryToActivateTheRenderingContext
{
KillGLWindow();//ResetTheDisplay
MessageBox(NULL,"Can'tActivateTheGLRenderingContext.","ERROR",MB_OK|MB_ICONEXCLAMATION);
returnFALSE;//ReturnFALSE
}
ShowWindow(hWnd,SW_SHOW);//ShowTheWindow
SetForegroundWindow(hWnd);//SlightlyHigherPriority
SetFocus(hWnd);//SetsKeyboardFocusToTheWindow
ReSizeGLScene(width,height);//SetUpOurPerspectiveGLScreen
if(!InitGL())//InitializeOurNewlyCreatedGLWindow
{
KillGLWindow();//ResetTheDisplay
MessageBox(NULL,"InitializationFailed.","ERROR",MB_OK|MB_ICONEXCLAMATION);
returnFALSE;//ReturnFALSE
}
returnTRUE;//Success
}
LRESULTCALLBACKWndProc(HWNDhWnd,//HandleForThisWindow
UINTuMsg,//MessageForThisWindow
WPARAMwParam,//AdditionalMessageInformation
LPARAMlParam)//AdditionalMessageInformation
{
switch(uMsg)//CheckForWindowsMessages
{
caseWM_ACTIVATE://WatchForWindowActivateMessage
{
if(!HIWORD(wParam))//CheckMinimizationState
{
active=TRUE;//ProgramIsActive
}
else
{
active=FALSE;//ProgramIsNoLongerActive
}
return0;//ReturnToTheMessageLoop
}
caseWM_SYSCOMMAND://InterceptSystemCommands
{
switch(wParam)//CheckSystemCalls
{
caseSC_SCREENSAVE://ScreensaverTryingToStart?
caseSC_MONITORPOWER://MonitorTryingToEnterPowersave?
return0;//PreventFromHappening
}
break;//Exit
}
caseWM_CLOSE://DidWeReceiveACloseMessage?
{
PostQuitMessage(0);//SendAQuitMessage
return0;//JumpBack
}
caseWM_KEYDOWN://IsAKeyBeingHeldDown?
{
keys[wParam]=TRUE;//IfSo,MarkItAsTRUE
return0;//JumpBack
}
caseWM_KEYUP://HasAKeyBeenReleased?
{
keys[wParam]=FALSE;//IfSo,MarkItAsFALSE
return0;//JumpBack
}
caseWM_SIZE://ResizeTheOpenGLWindow
{
ReSizeGLScene(LOWORD(lParam),HIWORD(lParam));//LoWord=Width,HiWord=Height
return0;//JumpBack
}
}
//PassAllUnhandledMessagesToDefWindowProc
returnDefWindowProc(hWnd,uMsg,wParam,lParam);
}
intWINAPIWinMain(HINSTANCEhInstance,//Instance
HINSTANCEhPrevInstance,//PreviousInstance
LPSTRlpCmdLine,//CommandLineParameters
intnCmdShow)//WindowShowState
{
MSGmsg;//WindowsMessageStructure
BOOLdone=FALSE;//BoolVariableToExitLoop
//AskTheUserWhichScreenModeTheyPrefer
if(MessageBox(NULL,"WouldYouLikeToRunInFullscreenMode?","StartFullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
{
fullscreen=FALSE;//WindowedMode
}
//CreateOurOpenGLWindow
if(!CreateGLWindow("LionelBrits&NeHe's3DWorldTutorial",640,480,16,fullscreen))
{
return0;//QuitIfWindowWasNotCreated
}
while(!done)//LoopThatRunsWhiledone=FALSE
{
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))//IsThereAMessageWaiting?
{
if(msg.message==WM_QUIT)//HaveWeReceivedAQuitMessage?
{
done=TRUE;//IfSodone=TRUE
}
else//IfNot,DealWithWindowMessages
{
TranslateMessage(&msg);//TranslateTheMessage
DispatchMessage(&msg);//DispatchTheMessage
}
}
else//IfThereAreNoMessages
{
//DrawTheScene.WatchForESCKeyAndQuitMessagesFromDrawGLScene()
if((active&&!DrawGLScene())||keys[VK_ESCAPE])//Active?WasThereAQuitReceived?
{
done=TRUE;//ESCorDrawGLSceneSignalledAQuit
}
else//NotTimeToQuit,UpdateScreen
{
SwapBuffers(hDC);//SwapBuffers(DoubleBuffering)
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])//IsF1BeingPressed?
{
keys[VK_F1]=FALSE;//IfSoMakeKeyFALSE
KillGLWindow();//KillOurCurrentWindow
fullscreen=!fullscreen;//ToggleFullscreen/WindowedMode
//RecreateOurOpenGLWindow
if(!CreateGLWindow("LionelBrits&NeHe's3DWorldTutorial",640,480,16,fullscreen))
{
return0;//QuitIfWindowWasNotCreated
}
}
}
}
}
//Shutdown
KillGLWindow();//KillTheWindow
return(msg.wParam);//ExitTheProgram
}