I am trying to implement user input to navigate around a generated terrain. I am using SDL to create the window, and manage keystroke events. All of the drawing, rendering etc. is in OpenGL. My problem is this:
Instead of using gluLookAt(), I have written custom routines as indicated in the "red book" (Chapter 3 - Viewing Advanced try this section in 'Viewing Transformations' section of that chapter.) My glTranslatef() functions move the POV based on user input, BUT when you 'pivot' you point of view (Rotate about the y-axis) The view rotates around the POV. But - as soon as the keyup event occurs, the viewpoint "Snaps" back to wherever it was before the last tranformation occurred. So basically, it's like walking around, but whenever you spin, you are immediately forced back into the heading you had before you attempted to spin.
I am somewhat new to OpenGL but feel fairly comfortable with most of the concepts. I have attempted a wide variety of fixes to troubleshoot this to no avail. The following is my code, commented to try to illustrate what I'm trying to do. This code is merely proto-type for learning purposes and won't resemble anything I would do in production so any comments concerning optimization, design suggestions are certainly welcome, but I would really appreciate some help concerning this.
//following values are incremented / decremented per event keystrokefloat Ang = 0.0;float x_pos = 0.0;float y_pos = 0.0;float z_pos = 0.0;bool x_rot = false;bool y_rot = false;float x_input = 0.0;float y_input = 4.85;float z_input = 0.0; // This function actually builds the terrainvoid buildTerrain(){ // Here, an 80X80 array is generated with height values to use as vertices for my GL_QUADS //implementing glList since these values are static. glNewList(1, GL_COMPILE); // Terrain rendering routine goes here glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, myTexture);//texture mapping for terrain glBegin(GL_QUADS);//draws the polygons based the values obtained by the array generated above (or described rather.) for(int x = 0; x<tWidth-1; x++){ for(int y = 0; y<tHeight-1; y++){ glTexCoord2f(0.0f, 0.0f); glVertex3f(x+0, terrain[x+0][y+1], y+1); glTexCoord2f(1.0f, 0.0f); glVertex3f(x+1, terrain[x+1][y+1], y+1); glTexCoord2f(1.0f, 1.0f); glVertex3f(x+1, terrain[x+1][y+0], y+0); glTexCoord2f(0.0f, 1.0f); glVertex3f(x+0, terrain[x+0][y+0], y+0); } } glEnd(); glEndList(); }//Here is where I attempt to perform all of my transformations:void mainLoop() { while(true) { processEvents();//y,x,z *_input are incremented based on keypresses (the events are mapped in code below.) y_input += y_pos; x_input += x_pos; z_input += z_pos; // Graphical commands...//We are in the GL_MODELVIEW matrix mode here. This was the last matrix mode set in the function: setupOpenGL(). //I didn't provide that code since I'm fairly certain it's not relavant. It sets the perspective, lighting, loads textures, and fog. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity();//x_rot set to "true" if the right arrow key is depressed, when true,the polygons above rotate counter clockwise. if (x_rot) { Ang += .5; glRotatef(Ang, 0.0, 1.0, 0.0); } // if x_rot is false, no rotation occurs but the viewing plans "snaps //back to the world coordinates it had before the rotate call (but after the translate calls.) //y_rot set to "true" if the left arrow key is depressed, when true,the polygons above rotate clockwise. if (y_rot) { Ang += .5; glRotatef(Ang, 0.0, -1.0, 0.0); }//This translate function sets the user at eye level with the rendered terrain. removing this does not affect the rotation bevavior glTranslatef(-tWidth/2, -12, -tHeight/2);//This translation works as intended glTranslatef(x_input, y_input, z_input); glCallList(1); // <--(Render terrain from list) SDL_GL_SwapBuffers(); }}// Handle SDL keypressesvoid handleKeys(SDL_keysym* keysym, bool state) {//the state variable is true if a key is pressed down, false when the key is released. if (state) { switch(keysym->sym) { case SDLK_ESCAPE: endProgram(0); break; case SDLK_UP: y_pos -= .10; break; case SDLK_DOWN: y_pos += .10; break;//x and z transformations are set similarly.//tell main() that a rotation is occuring. case SDLK_RIGHT: x_rot = true; break; case SDLK_LEFT: y_rot = true; break; } } else { switch(keysym->sym) { case SDLK_ESCAPE: endProgram(0); break; case SDLK_UP: y_pos = 0.0; break; case SDLK_DOWN : y_pos = 0.0; break; //tell main() to stop rotating. Reset the Angle about which to rotate to zero. case SDLK_RIGHT: Ang = 0; x_rot = false; break; case SDLK_LEFT: Ang = 0; y_rot = false; break; } } } // Process SDL eventsvoid processEvents() { SDL_Event event; while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_KEYDOWN: handleKeys(&event.key.keysym, true ); break; case SDL_KEYUP: handleKeys(&event.key.keysym, false); break; case SDL_QUIT : endProgram(0); break; } } }
Sorry for the amount of code I posted. This issue is really nagging me and I have tried the following means to correct it:
use push/pop matrix to retain the rotated view. As anticipated, this achieved the opposite effect.
Change the Matrix mode to GL_PROJECTION for the rotation transformations only. This worked but the x y and z coords for my translation (even though I switched back to GL_MODELVIEW and called glLoadIdentity(). I was skeptical of this however since the red book and all other sources I've read concerning viewing transformations make absolutely no mention of changing the matrix mode when using rotate as a viewing transformation.
Whew. Sorry for the diatribe but any ideas would help. Thanks!