Switching Between First/Third Person View?

I’m trying to implement a simple 3D environment that a robot can walk around. I want the user to be able to switch between first and third person views for the robot.

I have implemented the robot, and a simple area with a few other objects. Currently, the view is 3rd person. I’ve been using the gluPerspective function to try to implement a first person that turns when the head turns (head turn already implemented), but it seems like no matter the parameters I use, I can’t get the toggling to work. It always stays the same. Is there a simpler way to achieve this effect than what I’ve tried so far? Is what I’m doing not working because of my approach?

The following code suffices to set the perspective to first person, however it does not follow the head of the figure as I expect. I can initialize it on top of him, but I’m not sure how to make the robot move with the camera, as the landscape effectively moves around him. I was thinking I could sort of ‘fake’ this by removing the robot while in first person. Thoughts?:

glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    double atx = xPos + cos(lookAngle);
    double atz = zPos + sin(lookAngle);
    double atHeight = eyeHeight + eyeIncline;
    gluLookAt(xPos, eyeHeight, zPos, atx, atHeight, atz, 0.0, 1.0, 0.0);

I have tried calling a switch statement in a keyboard function to switch to 3rd person. Keyboard call looks something like this:

void key(unsigned char k, int x, int y)
    {
    if ( k == 27 ) /* Escape */
      exit(0);
    else if ( k == 86 ) { /* 'v' for view change */
	   glMatrixMode(GL_MODELVIEW);
	   glLoadIdentity();
	   gluLookAt(0, 0, -20, 0, 0, -1, 0, 1, 0); //Look down z-axis

    }
    glutPostRedisplay();
    }

But it doesn’t work. No toggling is done.

Full code below:



#define GLUT_DISABLE_ATEXIT_HACK

#include <GL/glut.h>
#include <GL/gl.h>
#include <math.h>

#define TORSO_HEIGHT 5.0
#define UPPER_ARM_HEIGHT 3.0
#define LOWER_ARM_HEIGHT 2.0
#define UPPER_LEG_RADIUS  0.5
#define LOWER_LEG_RADIUS  0.5
#define LOWER_LEG_HEIGHT 2.0
#define UPPER_LEG_HEIGHT 3.0
#define UPPER_LEG_RADIUS  0.5
#define TORSO_RADIUS 1.0
#define UPPER_ARM_RADIUS  0.5
#define LOWER_ARM_RADIUS  0.5
#define FINGER_1_RADIUS 0.25
#define FINGER_1_HEIGHT 0.1
#define HEAD_HEIGHT 1.5
#define HEAD_RADIUS 1.0

double cubeX[] = { 5.0, 25.0, 55.0, 65.0};
double cubeY[] = { 3.0, 6.0, 9.0, 12.0};
double cubeZ[] = { 0.0, 25.0, 75.0, 49.0};
GLfloat cubeR[] = { 1.0, 0.0, 0.0, 0.0 };
GLfloat cubeG[] = { 0.0, 1.0, 0.0, 1.0 };
GLfloat cubeB[] = { 0.0, 0.0, 1.0, 1.0 };
GLfloat cubeP[] = { 1.0, 0.0, 1.0, 1.0 };

double xPos = 2.0, zPos = 5.0; //x = 2 z = 5
double eyeHeight = 4.5;
double eyeIncline = -0.5;
double lookAngle = 0.0; //In rads

double posIncr = 0.25;
double thetaIncr = 0.1;

typedef float point[3];

static GLfloat theta[11] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,
            180.0,0.0,180.0,0.0}; /* initial joint angles */
static GLint angle = 2;

GLUquadricObj *t, *h, *lua, *lla, *rua, *rla, *lll, *rll, *rul, *lul, *lf1, *lf2;

double size=1.0;

void torso()
{
   glPushMatrix();
   glRotatef(-90.0, 1.0, 0.0, 0.0);
   gluCylinder(t,TORSO_RADIUS, TORSO_RADIUS, TORSO_HEIGHT,10,10);
   glPopMatrix();
}

void head()
{
   glPushMatrix();
   glTranslatef(0.0, 0.5*HEAD_HEIGHT,0.0);
   glScalef(HEAD_RADIUS, HEAD_HEIGHT, HEAD_RADIUS);
   gluSphere(h,1.0,10,10);
   glPopMatrix();
}

void left_upper_arm()
{
   glPushMatrix();
   glRotatef(-90.0, 1.0, 0.0, 0.0);
   gluCylinder(lua,UPPER_ARM_RADIUS, UPPER_ARM_RADIUS, UPPER_ARM_HEIGHT,10,10);
   glPopMatrix();
}

void left_lower_arm()
{
   glPushMatrix();
   glRotatef(-90.0, 1.0, 0.0, 0.0);
   gluCylinder(lla,LOWER_ARM_RADIUS, LOWER_ARM_RADIUS, LOWER_ARM_HEIGHT,10,10);
   glPopMatrix();
}

void left_finger1()
{
   glPushMatrix();
   glRotatef(-90.0, 1.0, 0.0, 0.0);
   gluCylinder(lf1,FINGER_1_RADIUS, FINGER_1_RADIUS, FINGER_1_HEIGHT,10,10);
   glPopMatrix();
}

void left_finger2()
{
   glPushMatrix();
   glRotatef(-90.0, 1.0, 0.0, 0.0);
   gluCylinder(lla,LOWER_ARM_RADIUS, LOWER_ARM_RADIUS, LOWER_ARM_HEIGHT,10,10);
   glPopMatrix();
}

void right_upper_arm()
{
   glPushMatrix();
   glRotatef(-90.0, 1.0, 0.0, 0.0);
   gluCylinder(rua,UPPER_ARM_RADIUS, UPPER_ARM_RADIUS, UPPER_ARM_HEIGHT,10,10);
   glPopMatrix();
}

void right_lower_arm()
{
   glPushMatrix();
   glRotatef(-90.0, 1.0, 0.0, 0.0);
   gluCylinder(rla,LOWER_ARM_RADIUS, LOWER_ARM_RADIUS, LOWER_ARM_HEIGHT,10,10);
   glPopMatrix();
}

void left_upper_leg()
{
   glPushMatrix();
   glRotatef(-90.0, 1.0, 0.0, 0.0);
   gluCylinder(lul,UPPER_LEG_RADIUS, UPPER_LEG_RADIUS, UPPER_LEG_HEIGHT,10,10);
   glPopMatrix();
}

void left_lower_leg()
{
   glPushMatrix();
   glRotatef(-90.0, 1.0, 0.0, 0.0);
   gluCylinder(lll,LOWER_LEG_RADIUS, LOWER_LEG_RADIUS, LOWER_LEG_HEIGHT,10,10);
   glPopMatrix();
}

void right_upper_leg()
{
   glPushMatrix();
   glRotatef(-90.0, 1.0, 0.0, 0.0);
   gluCylinder(rul,UPPER_LEG_RADIUS, UPPER_LEG_RADIUS, UPPER_LEG_HEIGHT,10,10);
   glPopMatrix();
}

void right_lower_leg()
{
   glPushMatrix();
   glRotatef(-90.0, 1.0, 0.0, 0.0);
   gluCylinder(rll,LOWER_LEG_RADIUS, LOWER_LEG_RADIUS, LOWER_LEG_HEIGHT,10,10);
   glPopMatrix();
}

void drawCube( int i )
{
   double x = cubeX[i];
   double y = cubeY[i];
   double z = cubeZ[i];

   //RED
   glPushMatrix();
   glTranslatef(x,y,z);
   glutSolidCube(5);
   glPopMatrix();

   //BLUE
   glPushMatrix();
   glTranslatef(x,y,z);
   glutSolidCube(10);
   glPopMatrix();

   //GREEN
   glPushMatrix();
   glTranslatef(x,y,z);
   glutSolidCube(15);
   glPopMatrix();

  // glColor3i(0, 1, 1); //Purple Cube
   glPushMatrix();
   glTranslatef(x,y,z);
   glutSolidCube(10);
   glPopMatrix();

}

void drawCubes(void)
{
   glPushMatrix();
   glLoadName(1);
   glTranslatef(0.0, -10.0, 0.0);
   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, cubeR);
   drawCube(0);
   glPopMatrix();

   glPushMatrix();
   glLoadName(2);
   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, cubeB);
   glTranslatef(0.0, -10.0, 0.0);
   drawCube(1);
   glPopMatrix();

   glPushMatrix();
   glLoadName(3);
   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, cubeG);
   glTranslatef(0.0, -10.0, 0.0);
   drawCube(2);
   glPopMatrix();

   glPushMatrix();
   glLoadName(4);
   glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, cubeP);
   glTranslatef(0.0, -10.0, 0.0);
   drawCube(3);
   glPopMatrix();

 /* Set name back to '0' to indicate background. */
   glLoadName(0);
}

void drawFloor(void)
{
   GLfloat mat_diffuse[] = { 0.5, 0.5, 0.5, 1.0 };
   glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
   glPushMatrix();
   glTranslatef(0.0, -10.0, 0.0);
   glBegin(GL_POLYGON);
      glNormal3f(0.0, 1.0, 0.0);
      glVertex3f(-100.0, 0.0, -100.0);
      glVertex3f(100.0, 0.0, -100.0);
      glVertex3f(100.0, 0.0,  100.0);
      glVertex3f(-100.0, 0.0,  100.0);
   glEnd();
   glPopMatrix();
}

void doDrawing(void)
{
	GLfloat mat_diffuse[] = { 0.8, 0.8, 0.8, 1.0 };

   drawCubes();

   glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
   drawFloor();

 /* Everything else is a lighter shade of grey. */

   glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
}

void drawEnviro(void)
{
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   double atx = xPos + cos(lookAngle);
   double atz = zPos + sin(lookAngle);
   double atHeight = eyeHeight + eyeIncline;
   gluLookAt(xPos, eyeHeight, zPos, atx, atHeight, atz, 0.0, 1.0, 0.0);

   glPushMatrix();
   doDrawing();
   glPopMatrix();
}

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    drawEnviro();
    glLoadIdentity();
    gluLookAt(0, 0, -20, 0, 0, -1, 0, 1, 0); //Look down z-axis
    glColor3f(0.0, 0.0, 0.0);

    glRotatef(theta[0], 0.0, 1.0, 0.0);
    torso();
    glPushMatrix();

    glTranslatef(0.0, TORSO_HEIGHT+0.5*HEAD_HEIGHT, 0.0);
    glRotatef(theta[1], 1.0, 0.0, 0.0);
    glRotatef(theta[2], 0.0, 1.0, 0.0);
    glTranslatef(0.0, -0.5*HEAD_HEIGHT, 0.0);
    head();

    glPopMatrix();
    glPushMatrix();
    glTranslatef(-(TORSO_RADIUS + UPPER_ARM_RADIUS), 0.9 * TORSO_HEIGHT, 0.0);
    glRotatef(theta[3], 1.0, 0.0, 0.0);
    left_upper_arm();

    glTranslatef(0.0, UPPER_ARM_HEIGHT, 0.0);
    glRotatef(theta[4], 1.0, 0.0, 0.0);
    left_lower_arm();

    glTranslatef(0.0, LOWER_ARM_HEIGHT, 0.0);
    glRotatef(theta[11], 1.0, 0.0, 0.0);
    left_finger1();

    glPopMatrix();
    glPushMatrix();
    glTranslatef(TORSO_RADIUS+UPPER_ARM_RADIUS, 0.9*TORSO_HEIGHT, 0.0);
    glRotatef(theta[5], 1.0, 0.0, 0.0);
    right_upper_arm();

    glTranslatef(0.0, UPPER_ARM_HEIGHT, 0.0);
    glRotatef(theta[6], 1.0, 0.0, 0.0);
    right_lower_arm();

    glPopMatrix();
    glPushMatrix();
    glTranslatef(-(TORSO_RADIUS+UPPER_LEG_RADIUS), 0.1*UPPER_LEG_HEIGHT, 0.0);
    glRotatef(theta[7], 1.0, 0.0, 0.0);
    left_upper_leg();

    glTranslatef(0.0, UPPER_LEG_HEIGHT, 0.0);
    glRotatef(theta[8], 1.0, 0.0, 0.0);
    left_lower_leg();

    glPopMatrix();
    glPushMatrix();
    glTranslatef(TORSO_RADIUS+UPPER_LEG_RADIUS, 0.1*UPPER_LEG_HEIGHT, 0.0);
    glRotatef(theta[9], 1.0, 0.0, 0.0);
    right_upper_leg();

    glTranslatef(0.0, UPPER_LEG_HEIGHT, 0.0);
    glRotatef(theta[10], 1.0, 0.0, 0.0);
    right_lower_leg();

    glPopMatrix();

    glFlush();
    glutSwapBuffers();
}

void mouse(int btn, int state, int x, int y)
{
      if(btn==GLUT_LEFT_BUTTON && state == GLUT_DOWN)
        {
        theta[angle] += 5.0;
        if( theta[angle] > 360.0 ) theta[angle] -= 360.0;
        }
      if(btn==GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
        {
        theta[angle] -= 5.0;
        if( theta[angle] < 360.0 ) theta[angle] += 360.0;
        }
        display();
}

void menu(int id)
{
   if (id < 11 ) angle = id;
   if (id == 11 ) exit(0);
}

void specialKey(int k, int x, int y)
{
 /* Accept commands to move the viewpoint. */
   switch (k) {
      case GLUT_KEY_UP: //Move forward
         xPos += posIncr * cos(lookAngle);
         zPos += posIncr * sin(lookAngle);
         break;
      case GLUT_KEY_DOWN: //Move backward
         xPos -= posIncr * cos(lookAngle);
         zPos -= posIncr * sin(lookAngle);
         break;
      case GLUT_KEY_LEFT: //Spin left
         lookAngle -= thetaIncr;
         break;
      case GLUT_KEY_RIGHT: //Spin right
         lookAngle += thetaIncr;
         break;
      case GLUT_KEY_PAGE_UP: //Look up
         eyeIncline += 0.5;
         break;
      case GLUT_KEY_PAGE_DOWN: //Look down
         eyeIncline -= 0.5;
         break;
      case GLUT_KEY_HOME: //Rise up
         eyeHeight += 0.5;
         break;
      case GLUT_KEY_END: //Sink down
         eyeHeight -= 0.5;
         break;
      default:
         return;
   }
   glutPostRedisplay();
}

void reshape(int w, int h)
{
  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective (65.0, w/h, 1, 1000.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

void init()
{
        GLfloat mat_specular[]={1.0, 1.0, 1.0, 1.0};
        GLfloat mat_diffuse[]={1.0, 1.0, 1.0, 1.0};
        GLfloat mat_ambient[]={1.0, 1.0, 1.0, 1.0};
        GLfloat mat_shininess={100.0};
        GLfloat light_ambient[]={0.0, 0.0, 0.0, 1.0};
        GLfloat light_diffuse[]={1.0, 1.0, 1.0, 1.0};
        GLfloat light_specular[]={1.0, 1.0, 1.0, 1.0};
        GLfloat light_position[]={10.0, 10.0, 10.0, 0.0};

        glLightfv(GL_LIGHT0, GL_POSITION, light_position);
        glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
        glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
        glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);

        glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
        glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
        glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
        glMaterialf(GL_FRONT, GL_SHININESS, mat_shininess);

        glShadeModel(GL_SMOOTH);
        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);
        glDepthFunc(GL_LEQUAL);
        glEnable(GL_DEPTH_TEST);

        glClearColor(0.0, 0.0, 0.0, 0.0);
        glMatrixMode(GL_PROJECTION);

        //Fill the body parts of the robot
        h=gluNewQuadric();
        gluQuadricDrawStyle(h, GLU_FILL);
        t=gluNewQuadric();
        gluQuadricDrawStyle(t, GLU_FILL);
        lua=gluNewQuadric();
        gluQuadricDrawStyle(lua, GLU_FILL);
        lla=gluNewQuadric();
        gluQuadricDrawStyle(lla, GLU_FILL);
        rua=gluNewQuadric();
        gluQuadricDrawStyle(rua, GLU_FILL);
        rla=gluNewQuadric();
        gluQuadricDrawStyle(rla, GLU_FILL);
        lul=gluNewQuadric();
        gluQuadricDrawStyle(lul, GLU_FILL);
        lll=gluNewQuadric();
        gluQuadricDrawStyle(lll, GLU_FILL);
        rul=gluNewQuadric();
        gluQuadricDrawStyle(rul, GLU_FILL);
        rll=gluNewQuadric();
        gluQuadricDrawStyle(rll, GLU_FILL);
        lf1=gluNewQuadric();
        gluQuadricDrawStyle(lf1, GLU_FILL);

}

void key(unsigned char k, int x, int y)
{
   if ( k == 27 ) /* Escape */
      exit(0);
   else if ( k == 86 ) { /* 'v' for view change */
	   glMatrixMode(GL_MODELVIEW);
	   glLoadIdentity();
	   gluLookAt(0, 0, -20, 0, 0, -1, 0, 1, 0); //Look down z-axis

   }
   glutPostRedisplay();
}

int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(500, 500);
    glutCreateWindow("robot");
    init();
    glutReshapeFunc(reshape);
    glutDisplayFunc(display);
    glutKeyboardFunc(key);
    glutMouseFunc(mouse);
    glutSpecialFunc(specialKey);

    glutCreateMenu(menu);
    glutAddMenuEntry("torso", 0);
    glutAddMenuEntry("head1", 1);
    glutAddMenuEntry("head2", 2);
    glutAddMenuEntry("right_upper_arm", 3);
    glutAddMenuEntry("right_lower_arm", 4);
    glutAddMenuEntry("left_upper_arm", 5);
    glutAddMenuEntry("left_lower_arm", 6);
    glutAddMenuEntry("right_upper_leg", 7);
    glutAddMenuEntry("right_lower_leg", 8);
    glutAddMenuEntry("left_upper_leg", 9);
    glutAddMenuEntry("left_lower_leg", 10);
    glutAddMenuEntry("quit", 11);
    glutAttachMenu(GLUT_MIDDLE_BUTTON);

    glutMainLoop();

    return 0;
}

The first thing you do after clearing the framebuffer is to call drawEnviro(), which starts by setting the modelview matrix. The matrix you set in key() will never be used.

Instead, you should have key() simply toggle a variable which indicates which view you wish to use, then have drawEnviro() set the correct matrix according to that variable.

[QUOTE=GClements;1262359]The first thing you do after clearing the framebuffer is to call drawEnviro(), which starts by setting the modelview matrix. The matrix you set in key() will never be used.

Instead, you should have key() simply toggle a variable which indicates which view you wish to use, then have drawEnviro() set the correct matrix according to that variable.[/QUOTE]

That actually didn’t work for me either. I had ‘v’ set a boolean flag ‘fpview’ to true (where previously it was false - fpview is global). Then I did a simple conditional wrapping like so in drawEnviro:

void drawEnviro(void)
{
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   [b]if (fpsview == true)
   {
   double atx = xPos + cos(lookAngle);
   double atz = zPos + sin(lookAngle);
   double atHeight = eyeHeight + eyeIncline;
   gluLookAt(xPos, eyeHeight, zPos, atx, atHeight, atz, 0.0, 1.0, 0.0);
   }

   else
   {

	gluLookAt(0, 0, -20, 0, 0, -1, 0, 1, 0); //Look down z-axis

   }[/b]

   glPushMatrix();
   doDrawing();
   glPopMatrix();
}

Nothing happens when I press v. In fact, the program is frozen in some weird state.