outdated: 10.Loading And Moving Through A 3D World
这一节主要说的是camera的移动以及视角的旋转问题。
我在原本代码的基础上做了添加和修改,将移动方向的前、后、左、右改为W、S、A、D,并将视角旋转的向上、向下、向左和向右改为UP、DOWN、LEFT、RIGHT。最后还添加了下蹲为Ctrl键和跳跃SPACE键(空格),其实就是CS里的几个虚拟键的实现。
想想如果把世界坐标的数据存在于程序中是非常繁琐的,所以给出了读取世界数据信息的格式:
X1 Y1 Z1 U1 V1
X2 Y2 Z2 U2 V2
X3 Y3 Z3 U3 V3
...
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
对于camera的移动以及视角的旋转,我们并不是直接实现camera的移动以及视角的旋转,而是
- 以与镜头平移方向相反的方向来平移世界,进而让人产生镜头移动的错觉。
- 围绕原点,以镜头相反的方向旋转世界,进而让人产生镜头旋转的错觉。
实现起来还算简单。
下蹲:
跳跃:
正常:
代码如下,同样修改部分位于双行星号内。
1 #include <windows.h> 2 #include <stdio.h> 3 #include <math.h> 4 #include <gl/glew.h> 5 #include <gl/glut.h> 6 #include <GL/GLUAX.H> 7 #pragma comment(lib, "legacy_stdio_definitions.lib") 8 /* 9 * Every OpenGL program is linked to a Rendering Context. 10 * A Rendering Context is what links OpenGL calls to the Device Context. 11 * In order for your program to draw to a Window you need to create a Device Context. 12 * The DC connects the Window to the GDI (Graphics Device Interface). 13 */ 14 15 HGLRC hRC = NULL; // Permanent rendering context 16 HDC hDC = NULL; // Private GDI device context 17 HWND hWnd = NULL; // Holds our window handle 18 HINSTANCE hInstance; // Holds the instance of the application 19 20 /* 21 * It's important to make this global so that each procedure knows if 22 * the program is running in fullscreen mode or not. 23 */ 24 25 bool keys[256]; // Array used for the keyboard routine 26 bool active = TRUE; // Window active flag set to TRUE by default 27 bool fullscreen = TRUE; // Fullscreen flag set to fullscreen mode by default 28 29 BOOL light; // Lighting ON/OFF 30 BOOL blend; // Blend ON/OFF 31 BOOL twinkle; // Twinkling stars 32 BOOL tp; // T pressed 33 BOOL lp; // L pressed 34 BOOL fp; // F pressed 35 BOOL bp; // B pressed 36 37 GLfloat LightAmbient[] = {0.5f, 0.5f, 0.5f, 1.0f}; // Ambient light values 38 39 GLfloat LightDiffuse[] = {1.0f, 1.0f, 1.0f, 1.0f}; // Diffuse light values 40 41 GLfloat LightPosition[] = {0.0f, 0.0f, 2.0f, 1.0f}; // Light position 42 /******************************************************************************************************************************************/ 43 /******************************************************************************************************************************************/ 44 GLuint filter; 45 GLuint texture[3]; 46 // Piover180 is simply a conversion factor for converting between degrees and radians 47 const float piover180 = 0.0174532925f; 48 float heading; 49 float xpos; 50 float zpos; 51 float ytemp1; 52 float ytemp2; 53 54 GLfloat yrot; 55 GLfloat walkbias = 0.0f; 56 GLfloat walkbiasangle = 0.0f; 57 GLfloat lookupdown = 0.0f; 58 GLfloat z = 0.0f; 59 60 typedef struct tagVERTEX VERTEX; 61 struct tagVERTEX { 62 float x, y, z; // 3D Coordinates 63 float u, v; // Texture Coordinates 64 }; 65 66 typedef struct tagTRIANGLE TRIANGLE; 67 struct tagTRIANGLE { 68 VERTEX vertex[3]; // Array Of Three Vertices 69 }; 70 71 typedef struct tagSECTOR SECTOR; 72 struct tagSECTOR { // Build Our Sector Structure 73 int numtriangles; // Number Of Triangles In Sector 74 TRIANGLE* triangle; // Pointer To Array Of Triangles 75 }; 76 77 SECTOR sector1; 78 79 /* 80 * CreateGLWindow() has a reference to WndProc() but WndProc() comes after CreateGLWindow(). 81 */ 82 83 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration for WndProc 84 85 /* 86 * The job of the next section of code is to resize the OpenGL scene 87 * whenever the window (assuming you are using a Window rather than fullscreen mode) has been resized. 88 */ 89 void readstr(FILE* f, char* string) 90 { 91 do { 92 fgets(string, 255, f); 93 } while ((string[0] == '/') || (string[0] == '\n')); 94 return ; 95 } 96 97 void SetupWorld() 98 { 99 float x, y, z, u, v; 100 int numtriangles; 101 FILE* filein; 102 char oneline[255]; 103 filein = fopen("World.txt", "rt"); 104 105 readstr(filein, oneline); 106 sscanf(oneline, "NUMPOLLIES %d\n", &numtriangles); 107 108 sector1.triangle = new TRIANGLE[numtriangles]; 109 sector1.numtriangles = numtriangles; 110 111 for (int triloop = 0; triloop < numtriangles; ++triloop) { 112 for (int vertloop = 0; vertloop < 3; ++vertloop) { 113 readstr(filein, oneline); 114 sscanf(oneline, "%f %f %f %f %f", &x, &y, &z, &u, &v); 115 sector1.triangle[triloop].vertex[vertloop].x = x; 116 sector1.triangle[triloop].vertex[vertloop].y = y; 117 sector1.triangle[triloop].vertex[vertloop].z = z; 118 sector1.triangle[triloop].vertex[vertloop].u = u; 119 sector1.triangle[triloop].vertex[vertloop].v = v; 120 } 121 } 122 fclose(filein); 123 return ; 124 } 125 /******************************************************************************************************************************************/ 126 /******************************************************************************************************************************************/ 127 AUX_RGBImageRec* LoadBMP(char* Filename) // Loads a bitmap image 128 { 129 FILE* File = NULL; // File handle 130 131 if (!Filename) { // Make sure a filename was given 132 return NULL; // If not return NULL 133 } 134 135 File = fopen(Filename, "r"); // Check to see of the file exists 136 if (File) { 137 fclose(File); 138 return auxDIBImageLoad(Filename); // Load the bitmap and return a pointer 139 } 140 141 return NULL; 142 } 143 144 int LoadGLTextures() // Load bitmap and convert to texture 145 { 146 int Status = FALSE; // Status indicator 147 148 AUX_RGBImageRec* TextureImage[1]; // Create storage space for the texture 149 150 memset(TextureImage, 0, sizeof(void*)*1); // Set the pointer to NULL 151 152 // Load the bitmap, check for error, if bitmap's not found quit 153 if (TextureImage[0] = LoadBMP("1.bmp")) { 154 Status = TRUE; 155 156 glGenTextures(1, &texture[0]); // Create the texture 157 /******************************************************************************************************************************************/ 158 /******************************************************************************************************************************************/ 159 glBindTexture(GL_TEXTURE_2D, texture[0]); 160 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Linear filtering 161 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Linear filtering 162 glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, 163 GL_UNSIGNED_BYTE, TextureImage[0]->data); 164 165 glBindTexture(GL_TEXTURE_2D, texture[1]); 166 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Linear filtering 167 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Linear filtering 168 glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, 169 GL_UNSIGNED_BYTE, TextureImage[0]->data); 170 171 glBindTexture(GL_TEXTURE_2D, texture[2]); 172 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 173 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); 174 gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, GL_RGB, 175 GL_UNSIGNED_BYTE, TextureImage[0]->data); 176 } 177 /******************************************************************************************************************************************/ 178 /******************************************************************************************************************************************/ 179 if (TextureImage[0]) { 180 if (TextureImage[0]->data) { 181 free(TextureImage[0]->data); 182 } 183 free(TextureImage[0]); 184 } 185 return Status; 186 } 187 188 189 GLvoid ReSizeGLScene(GLsizei width, GLsizei height) // Resize and initialize the GL window 190 { 191 if (height == 0) { // Prevent a divide by zero by 192 height = 1; // Making height equal one 193 } 194 195 glViewport(0, 0, width, height); // Reset the current viewport 196 197 /* 198 * The following lines set the screen up for a perspective view. 199 * Meaning things in the distance get smaller. This creates a realistic looking scene. 200 * The perspective is calculated with a 45 degree viewing angle based on 201 * the windows width and height. The 0.1f, 100.0f is the starting point and 202 * ending point for how deep we can draw into the screen. 203 * 204 * The projection matrix is responsible for adding perspective to our scene. 205 * glLoadIdentity() restores the selected matrix to it's original state. 206 * The modelview matrix is where our object information is stored. 207 * Lastly we reset the modelview matrix. 208 */ 209 210 glMatrixMode(GL_PROJECTION); // Select the projection matrix 211 glLoadIdentity(); // Reset the projection matrix 212 213 // Calculate the aspect ratio of the window 214 gluPerspective(45.0f, (GLfloat)width / (GLfloat)height, 0.1f, 100.0f); 215 216 glMatrixMode(GL_MODELVIEW); // Seclet the modelview matrix 217 glLoadIdentity(); // Reset the modelview matrix 218 } 219 220 int InitGL(GLvoid) // All setup for OpenGL goes here 221 { 222 if (!LoadGLTextures()) { // Jump to texture loading routine 223 return FALSE; // If texture didn't load return false 224 } 225 glEnable(GL_TEXTURE_2D); // Enable texture mapping 226 /* 227 * Smooth shading blends colors nicely across a polygon, and smoothes out lighting. 228 */ 229 230 /******************************************************************************************************************************************/ 231 /******************************************************************************************************************************************/ 232 glBlendFunc(GL_SRC_ALPHA, GL_ONE); 233 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Black background 234 235 /* 236 * Think of the depth buffer as layers into the screen. 237 * The depth buffer keeps track of how deep objects are into the screen. 238 */ 239 240 glClearDepth(1.0f); // Depth buffer setup 241 /* 242 * Next we tell OpenGL we want the best perspective correction to be done. 243 * This causes a very tiny performance hit, but makes the perspective view look a bit better. 244 */ 245 glDepthFunc(GL_LESS); 246 glEnable(GL_DEPTH_TEST); 247 glShadeModel(GL_SMOOTH); // Enables smooth shading 248 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really nice perspective calculations 249 250 SetupWorld(); 251 return TRUE; 252 } 253 254 /* 255 * For now all we will do is clear the screen to the color we previously decided on, 256 * clear the depth buffer and reset the scene. We wont draw anything yet. 257 */ 258 int DrawGLScene(GLvoid) // Here's where we do all the drawing 259 { 260 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear the screen and the depth buffer 261 glLoadIdentity(); 262 263 GLfloat x_m, y_m, z_m, u_m, v_m; 264 GLfloat xtrans = -xpos; 265 GLfloat ztrans = -zpos; 266 GLfloat ytrans = -walkbias - 0.25f - ytemp1 - ytemp2; // Used for bouncing motion up and down 267 GLfloat sceneroty = 360.0f - yrot; // 360 degree angle for player direction 268 269 int numtriangles; 270 271 glRotatef(lookupdown, 1.0f, 0.0f, 0.0f); 272 glRotatef(sceneroty, 0.0f, 1.0f, 0.0f); 273 274 glTranslatef(xtrans, ytrans-0.2, ztrans); 275 glBindTexture(GL_TEXTURE_2D, texture[filter]); 276 277 numtriangles = sector1.numtriangles; 278 279 for (int loop_m = 0; loop_m < numtriangles; ++loop_m) { 280 glBegin(GL_TRIANGLES); 281 glNormal3f(0.0f, 0.0f, 1.0f); 282 x_m = sector1.triangle[loop_m].vertex[0].x; 283 y_m = sector1.triangle[loop_m].vertex[0].y; 284 z_m = sector1.triangle[loop_m].vertex[0].z; 285 u_m = sector1.triangle[loop_m].vertex[0].u; 286 v_m = sector1.triangle[loop_m].vertex[0].v; 287 glTexCoord2f(u_m, v_m); glVertex3f(x_m, y_m, z_m); 288 289 x_m = sector1.triangle[loop_m].vertex[1].x; 290 y_m = sector1.triangle[loop_m].vertex[1].y; 291 z_m = sector1.triangle[loop_m].vertex[1].z; 292 u_m = sector1.triangle[loop_m].vertex[1].u; 293 v_m = sector1.triangle[loop_m].vertex[1].v; 294 glTexCoord2f(u_m, v_m); glVertex3f(x_m, y_m, z_m); 295 296 x_m = sector1.triangle[loop_m].vertex[2].x; 297 y_m = sector1.triangle[loop_m].vertex[2].y; 298 z_m = sector1.triangle[loop_m].vertex[2].z; 299 u_m = sector1.triangle[loop_m].vertex[2].u; 300 v_m = sector1.triangle[loop_m].vertex[2].v; 301 glTexCoord2f(u_m, v_m); glVertex3f(x_m, y_m, z_m); 302 glEnd(); 303 } 304 return TRUE; // everthing went OK 305 } 306 /******************************************************************************************************************************************/ 307 /******************************************************************************************************************************************/ 308 /* 309 * The job of KillGLWindow() is to release the Rendering Context, 310 * the Device Context and finally the Window Handle. 311 */ 312 313 GLvoid KillGLWindow(GLvoid) // Properly kill the window 314 { 315 if (fullscreen) { // Are we in fullscreen mode 316 317 /* 318 * We use ChangeDisplaySettings(NULL,0) to return us to our original desktop. 319 * After we've switched back to the desktop we make the cursor visible again. 320 */ 321 322 ChangeDisplaySettings(NULL, 0); // if so switch back to the desktop 323 ShowCursor(TRUE); // Show mouse pointer 324 } 325 326 if (hRC) { // Do we have a rendering context 327 if (!wglMakeCurrent(NULL, NULL)) { // Are we able to release the DC and RC contexts 328 MessageBox(NULL, "Release of DC and RC failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); 329 } 330 331 if (!wglDeleteContext(hRC)) { // Are we able to delete the RC 332 MessageBox(NULL, "Release rendering context failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); 333 hRC = NULL; // Set RC to NULL 334 } 335 336 if (hDC && !ReleaseDC(hWnd, hDC)) { // Are we able to release the DC 337 MessageBox(NULL, "Release device context failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); 338 hDC = NULL; // Set DC to NULL 339 } 340 if (hWnd && !DestroyWindow(hWnd)) { // Are we able to destroy the window 341 MessageBox(NULL, "Could not release hWnd.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); 342 hWnd = NULL; // Set hWnd to NULL 343 } 344 345 if (!UnregisterClass("OpenGL", hInstance)) { // Are we able to unregister class 346 MessageBox(NULL, "Could not register class.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); 347 hInstance = NULL; // Set hInstance to NULL 348 } 349 } 350 } 351 352 /* 353 * The next section of code creates our OpenGL Window. 354 */ 355 356 BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag) 357 { 358 /* 359 * Find a pixel format that matches the one we want 360 */ 361 GLuint PixelFormat; // Holds the result after serching for a match 362 363 /* 364 * Before you create a window, you MUST register a Class for the window 365 */ 366 WNDCLASS wc; // Windows class structure 367 368 /* 369 * dwExStyle and dwStyle will store the Extended and normal Window Style Information. 370 */ 371 DWORD dwExStyle; // Window extend style 372 DWORD dwStyle; // Window style 373 374 RECT WindowRect; // Grabs rectangle upper left/lower right values 375 WindowRect.left = (long)0; // Set left value to 0 376 WindowRect.right = (long)width; // Set right value to requested width 377 WindowRect.top = (long)0; // Set top value to 0 378 WindowRect.bottom = (long)height; // Set bottom value to requested height 379 380 fullscreen = fullscreenflag; // Set the global fullscreen flag 381 382 /* 383 * The style CS_HREDRAW and CS_VREDRAW force the Window to redraw whenever it is resized. 384 * CS_OWNDC creates a private DC for the Window. Meaning the DC is not shared across applications. 385 * WndProc is the procedure that watches for messages in our program. 386 * No extra Window data is used so we zero the two fields. Then we set the instance. 387 * Next we set hIcon to NULL meaning we don't want an ICON in the Window, 388 * and for a mouse pointer we use the standard arrow. The background color doesn't matter 389 * (we set that in GL). We don't want a menu in this Window so we set it to NULL, 390 * and the class name can be any name you want. I'll use "OpenGL" for simplicity. 391 */ 392 hInstance = GetModuleHandle(NULL); // Grab an instance for our window 393 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraw on move, and own DC for window 394 wc.lpfnWndProc = (WNDPROC)WndProc; // WndProc handles message 395 wc.cbClsExtra = 0; // No extra window date 396 wc.cbWndExtra = 0; // No extra window date 397 wc.hInstance = hInstance; // set the instance 398 wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); // Load the default icon 399 wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Load the arrow pointer 400 wc.hbrBackground = NULL; // No background requried for GL 401 wc.lpszMenuName = NULL; // We don't want a menu 402 wc.lpszClassName = "OpenGL"; // set the class name 403 404 if (!RegisterClass(&wc)) { // Attempt to register the window class 405 MessageBox(NULL, "Failed to register the window class.", "ERROR", MB_OK | MB_ICONEXCLAMATION); 406 return FALSE; // Exit and return false 407 } 408 409 if (fullscreen) { // attempt fullsreen model 410 411 /* 412 T* here are a few very important things you should keep in mind when switching to full screen mode. 413 * Make sure the width and height that you use in fullscreen mode is the same as 414 * the width and height you plan to use for your window, and most importantly, 415 * set fullscreen mode BEFORE you create your window. 416 */ 417 DEVMODE dmScreenSettings; // Device mode 418 memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); // Make sure memory's cleared 419 dmScreenSettings.dmSize = sizeof(dmScreenSettings); // Size of devmode structure 420 dmScreenSettings.dmPelsWidth = width; // Select window width 421 dmScreenSettings.dmPelsHeight = height; // Select window height 422 dmScreenSettings.dmBitsPerPel = bits; // Select bits per pixel 423 dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; 424 425 /* 426 * In the line below ChangeDisplaySettings tries to switch to a mode that matches 427 * what we stored in dmScreenSettings. I use the parameter CDS_FULLSCREEN when switching modes, 428 * because it's supposed to remove the start bar at the bottom of the screen, 429 * plus it doesn't move or resize the windows on your desktop when you switch to 430 * fullscreen mode and back. 431 */ 432 //Try to set selected mode and get results. Note: CDS_FULLSCREEN gets rid of start bar 433 if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { 434 //If the mode fails, offer two options. Quit or run in a window 435 if (MessageBox(NULL, "The requested fullscreen mode is not supported by\n your video card. Use" 436 "windowed mode instead?", "GL", MB_YESNO | MB_ICONEXCLAMATION) == IDYES) 437 { 438 fullscreen = FALSE; // Select windowed mode (fullscreen=FLASE) 439 } 440 else { 441 // Pop up a message box letting user know the programe is closing. 442 MessageBox(NULL, "Program will now close.", "ERROR", MB_OK | MB_ICONSTOP); 443 return FALSE; // Exit and return FALSE 444 } 445 } 446 } 447 448 if (fullscreen) { // Are we still in fullscreen mode 449 450 /* 451 * If we are still in fullscreen mode we'll set the extended style to WS_EX_APPWINDOW, 452 * which force a top level window down to the taskbar once our window is visible. 453 * For the window style we'll create a WS_POPUP window. 454 * This type of window has no border around it, making it perfect for fullscreen mode. 455 456 * Finally, we disable the mouse pointer. If your program is not interactive, 457 * it's usually nice to disable the mouse pointer when in fullscreen mode. It's up to you though. 458 */ 459 dwExStyle = WS_EX_APPWINDOW; // Window extended style 460 dwStyle = WS_POPUP; // Window style 461 ShowCursor(FALSE); // Hide mosue pointer 462 } 463 else { 464 465 /* 466 * If we're using a window instead of fullscreen mode, 467 * we'll add WS_EX_WINDOWEDGE to the extended style. This gives the window a more 3D look. 468 * For style we'll use WS_OVERLAPPEDWINDOW instead of WS_POPUP. 469 * WS_OVERLAPPEDWINDOW creates a window with a title bar, sizing border, 470 * window menu, and minimize / maximize buttons. 471 */ 472 dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // Window extended style 473 dwStyle = WS_OVERLAPPEDWINDOW; // Window style 474 } 475 476 /* 477 * By using the AdjustWindowRectEx command none of our OpenGL scene will be covered up by the borders, 478 * instead, the window will be made larger to account for the pixels needed to draw the window border. 479 * In fullscreen mode, this command has no effect. 480 */ 481 AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); // Adjust window to true resqusted 482 483 /* 484 * WS_CLIPSIBLINGS and WS_CLIPCHILDREN are both REQUIRED for OpenGL to work properly. 485 * These styles prevent other windows from drawing over or into our OpenGL Window. 486 */ 487 if (!(hWnd = CreateWindowEx(dwExStyle, // Extended style for the window 488 "OpenGL", // Class name 489 title, // Window title 490 WS_CLIPSIBLINGS | // Requried window style 491 WS_CLIPCHILDREN | // Requried window style 492 dwStyle, // Select window style 493 0, 0, // Window position 494 WindowRect.right - WindowRect.left, // Calculate adjusted window width 495 WindowRect.bottom - WindowRect.top, // Calculate adjusted window height 496 NULL, // No parent window 497 NULL, // No menu 498 hInstance, // Instance 499 NULL))) // Don't pass anything to WM_CREATE 500 { 501 KillGLWindow(); //Reset the display 502 MessageBox(NULL, "Window creation error.", "ERROR", MB_OK | MB_ICONEXCLAMATION); 503 return FALSE; // Retrurn FALSE; 504 } 505 506 /* 507 * aside from the stencil buffer and the (slow) accumulation buffer 508 */ 509 static PIXELFORMATDESCRIPTOR pfd = // pfd tells windows how we want things to be 510 { 511 sizeof(PIXELFORMATDESCRIPTOR), // Size of this pixel format descriptor 512 1, // Version number 513 PFD_DRAW_TO_WINDOW | // Format must support window 514 PFD_SUPPORT_OPENGL | // Format must support OpenGL 515 PFD_DOUBLEBUFFER, // Must support double buffer 516 PFD_TYPE_RGBA, // Request an RGBA format 517 bits, // Select our color depth 518 0, 0, 0, 0, 0, 0, // Color bits ignored 519 0, // No alpha buffer 520 0, // shift bit ignored 521 0, // No accumulation buffer 522 0, 0, 0, 0, // Accumulation bits ignored 523 16, // 16Bits Z_Buffer (depth buffer) 524 0, // No stencil buffer 525 0, // No auxiliary buffer 526 PFD_MAIN_PLANE, // Main drawing layer 527 0, // Reserved 528 0, 0, 0 // Layer makes ignored 529 }; 530 531 if (!(hDC = GetDC(hWnd))) { // Did we get a device context 532 KillGLWindow(); // Reset the display 533 MessageBox(NULL, "Can't create a GL device context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); 534 return FALSE; // Return FALSE 535 } 536 537 if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd))) { // Did window find a matching pixel format 538 KillGLWindow(); // Reset the display 539 MessageBox(NULL, "Can't find a suitable pixelformat.", "ERROR", MB_OK | MB_ICONEXCLAMATION); 540 return FALSE; // Return FALSE; 541 } 542 543 if (!SetPixelFormat(hDC, PixelFormat, &pfd)) { // Are we able to set the pixel format 544 KillGLWindow(); // Reset the display 545 MessageBox(NULL, "Can't set the pixelformat.", "ERROR", MB_OK | MB_ICONEXCLAMATION); 546 return FALSE; // Return FALSE; 547 } 548 549 if (!(hRC = wglCreateContext(hDC))) { // Are we able to rendering context 550 KillGLWindow(); // Reset the display 551 MessageBox(NULL, "Can't create a GL rendering context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); 552 return FALSE; // Return FASLE; 553 } 554 555 if (!wglMakeCurrent(hDC, hRC)) { // Try to activate the rendering context 556 KillGLWindow(); // Reset the display 557 MessageBox(NULL, "Can't activate the GL rendering context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); 558 return FALSE; // Return FALSE 559 } 560 561 /* 562 * ReSizeGLScene passing the screen width and height to set up our perspective OpenGL screen. 563 */ 564 ShowWindow(hWnd, SW_SHOW); // Show the window 565 SetForegroundWindow(hWnd); // slightly higher priority 566 SetFocus(hWnd); // Sets keyboard focus to the window 567 ReSizeGLScene(width, height); // Set up our perspective GL screen 568 569 /* 570 * we can set up lighting, textures, and anything else that needs to be setup in InitGL(). 571 */ 572 if (!InitGL()) { // Initialize our newly created GL window 573 KillGLWindow(); // Reset the display 574 MessageBox(NULL, "Initialize Failed.", "ERROR", MB_OK | MB_ICONEXCLAMATION); 575 return FALSE; // Return FALSE 576 } 577 return TRUE; 578 } 579 580 LRESULT CALLBACK WndProc(HWND hWnd, // Handle for this window 581 UINT uMsg, // Message for this window 582 WPARAM wParam, // Additional message information 583 LPARAM lParam) // Additional message information 584 { 585 switch (uMsg) { // Check for window message 586 case WM_ACTIVATE: { // Check minimization state 587 if (!HIWORD(wParam)) { 588 active = TRUE; // Program is active 589 } 590 else { 591 active = FALSE; // Program is no longer active 592 } 593 return 0; // Return to the message loop 594 } 595 case WM_SYSCOMMAND: { // Intercept system commands 596 switch (wParam) { // Check system calls 597 case SC_SCREENSAVE: // Screensaver trying to start 598 case SC_MONITORPOWER: // Monitor trying to enter powersave 599 return 0; // Prevent form happening 600 } 601 break; // Exit 602 } 603 case WM_CLOSE: { // Did we receive a close message 604 PostQuitMessage(0); // Send a quit message 605 return 0; 606 } 607 case WM_KEYDOWN: { // Is a key being held down 608 keys[wParam] = TRUE; // if so, mark it as TRUE 609 return 0; // Jump back 610 } 611 case WM_KEYUP: { // Has a key been released 612 keys[wParam] = FALSE; // if so, mark it as FALSE 613 return 0; // Jump back 614 } 615 case WM_SIZE: { // Resize the OpenGL window 616 ReSizeGLScene(LOWORD(lParam), HIWORD(lParam)); // LoWord = width HiWord = height 617 return 0; // Jump back 618 } 619 } 620 return DefWindowProc(hWnd, uMsg, wParam, lParam); // Pass all unhandled message to DefWindwProc 621 } 622 623 int WINAPI WinMain(HINSTANCE hInstance, // Instance 624 HINSTANCE hPrevInstance, // Previous instance 625 LPSTR lpCmdLine, // Command line parameters 626 int nCmdShow) // Window show state 627 { 628 MSG msg; // Window message structure 629 BOOL done = FALSE; // Bool variable to exit loop 630 // Ask the user which screen mode they prefer 631 if (MessageBox(NULL, "Would you like to run in fullscreen mode?", 632 "Start fullscreen?", MB_YESNO | MB_ICONQUESTION) == IDNO) 633 { 634 fullscreen = FALSE; // Window mode 635 } 636 // Create our OpenGL window 637 if (!CreateGLWindow("3D Shapes", 640, 480, 16, fullscreen)) { // (Modified) 638 return 0; // Quit if window was not create 639 } 640 641 while (!done) { // Loop that runs until donw = TRUE 642 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // Is there a message wating 643 if (msg.message == WM_QUIT) { // Havw we received a quit message 644 done = TRUE; // if so done = TRUE 645 } 646 else { // If not, deal with window message 647 TranslateMessage(&msg); // Translate message 648 DispatchMessage(&msg); // Dispatch message 649 } 650 } 651 else { 652 // Draw the scene. Watch for ESC key and quit message from DrawGLScene() 653 if (active) { // Program active 654 if (keys[VK_ESCAPE]) { // Was ESC pressed 655 done = TRUE; // ESC signalled a quit 656 } 657 else { // Not time to quit, update screen 658 DrawGLScene(); // Draw scene 659 SwapBuffers(hDC); // Swap buffers (double buffering) 660 } 661 } 662 663 /* 664 * It allows us to press the F1 key to switch from fullscreen mode to 665 * windowed mode or windowed mode to fullscreen mode. 666 */ 667 668 if (keys['L'] && !lp) { // L key being pressed not held 669 lp = TRUE; // lp become TRUE 670 light = !light; // Toggle light TRUE/FALSE 671 672 if (!light) { 673 glDisable(GL_LIGHTING); // Disable light 674 } 675 else { 676 glEnable(GL_LIGHTING); // Enable light 677 } 678 } 679 if (!keys['L']) { 680 lp = FALSE; 681 } 682 683 if (keys['F'] && !fp) { 684 fp = TRUE; 685 filter += 1; 686 if (filter > 2) { 687 filter = 0; 688 } 689 } 690 if (!keys['F']) { 691 fp = FALSE; 692 } 693 /******************************************************************************************************************************************/ 694 /******************************************************************************************************************************************/ 695 if (keys['W']) { //VK_PRIOR 696 xpos -= (float)sin(heading*piover180) * 0.0005f; 697 zpos -= (float)cos(heading*piover180) * 0.0005f; 698 if (walkbiasangle >= 359.0f) { 699 walkbiasangle = 0.0f; 700 } 701 else { 702 walkbiasangle += 0.02f; 703 } 704 walkbias = (float)sin(walkbiasangle*piover180) / 20.0f; 705 } 706 if (keys['S']) { // VK_NEXT 707 xpos += (float)sin(heading*piover180) * 0.0005f; 708 zpos += (float)cos(heading*piover180) * 0.0005f; 709 if (walkbiasangle <= 1.0f) { 710 walkbiasangle = 359.0f; 711 } 712 else { 713 walkbiasangle -= 0.02f; 714 } 715 walkbias = (float)sin(walkbiasangle*piover180) / 20.0f; 716 } 717 718 if (keys['A']) { 719 zpos += (float)sin(heading * piover180) * 0.0005f; 720 xpos -= (float)cos(heading * piover180) * 0.0005f; 721 if (walkbiasangle >= 359.0f) { 722 walkbiasangle = 0.0f; 723 } 724 else { 725 walkbiasangle += 0.02f; 726 } 727 walkbias = (float)sin(walkbiasangle*piover180) / 20.0f; 728 } 729 if (keys['D']) { 730 zpos -= (float)sin(heading * piover180) * 0.0005f; 731 xpos += (float)cos(heading * piover180) * 0.0005f; 732 if (walkbiasangle <= 1.0f) { 733 walkbiasangle = 359.0f; 734 } 735 else { 736 walkbiasangle -= 0.02f; 737 } 738 walkbias = (float)sin(walkbiasangle*piover180) / 20.0f; 739 } 740 741 if (keys[VK_SPACE]) { 742 if (ytemp2 < 0.3) 743 ytemp2 += 0.0008; 744 } 745 else { 746 if (ytemp2 >= 0.08) 747 ytemp2 -= 0.0008; 748 } 749 750 if (keys[VK_CONTROL]) { 751 if (ytemp1 > -0.3) 752 ytemp1 -= 0.0008; 753 } 754 else { 755 if (ytemp1 <= 0.01) 756 ytemp1 += 0.0008; 757 } 758 759 if (keys['T'] && !tp) { 760 tp = TRUE; 761 twinkle = !twinkle; 762 } 763 if (!keys['T']) { 764 tp = FALSE; 765 } 766 if (keys[VK_UP]) { 767 z -= 0.05f; 768 lookupdown -= 0.05f; 769 } 770 if (keys[VK_DOWN]) { 771 z += 0.05f; 772 lookupdown += 0.05f; 773 } 774 775 if (keys[VK_RIGHT]) { 776 heading -= 0.05f; 777 yrot = heading; 778 } 779 780 if (keys[VK_LEFT]) { 781 heading += 0.05f; 782 yrot = heading; 783 } 784 785 /******************************************************************************************************************************************/ 786 /******************************************************************************************************************************************/ 787 if (keys['B'] && !bp) { 788 bp = TRUE; 789 blend = !blend; 790 if (blend) { 791 glEnable(GL_BLEND); 792 glDisable(GL_DEPTH_TEST); 793 } 794 else { 795 glDisable(GL_BLEND); 796 glEnable(GL_DEPTH_TEST); 797 } 798 } 799 if (keys['B']) { 800 bp = FALSE; 801 } 802 if (keys[VK_F1]) { // Is F1 being pressed 803 keys[VK_F1] = FALSE; // If so make key FASLE 804 KillGLWindow(); // Kill our current window 805 fullscreen = !fullscreen; // Toggle fullscreen / window mode 806 //Recreate our OpenGL window(modified) 807 if (!CreateGLWindow("3D Shapes", 640, 480, 16, fullscreen)) { 808 return 0; // Quit if window was not create 809 } 810 } 811 } 812 } 813 // Shutdown 814 KillGLWindow(); // Kill the window 815 return (msg.wParam); // Exit the program 816 }
Thanks for Nehe's tutorials, this is his home.