// pensoc2.c  --  simple animated penguin soccer
//
// This is an improved version of pensoc.c, an honours year (1999) project,
// which demonstrates various GL features.
//
// Keys:  space  move forward
//        b      move backward
//        w      toggle wireframe display
//        s      stop/start
//        S      toggle shade mode (flat/smooth)
// Holding down the left mouse button and moving the pointer rotates the
// viewport around the Y axis.
//
// Changes to the original include:
//
//   - somewhat cleaner code
//   - much better normal calculations
//   - corrections to the corner spotlights
//   - two new general lights
//   - simulation of a cone of light from the roving spotlight
//   - drawing of large rectangles as many smaller rectangles, for
//     more realistic lighting.
//
// GL features used:
//
//   - drawing, directly and via GLUT
//   - rotating and translating
//   - lighting, including spotlights
//   - texture mapping
//   - blending
//   - animation
//   - fog
//   - all three polygon modes and both shade models
//   - display lists
//   - line antialiasing.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <sys/time.h>
#include <GL/gl.h>
#include <GL/glut.h>

#define TIMED 0
#define FOG 1

#define HEIGHT 5
#define FALSE 0

//#define tracef(args...) printf (args)
#define tracef(args...)

#define CL_LIGHTS 1
#define CL_BOX 2
#define CL_SPHERES 3
#define CL_PENGUIN_GREEN 4
#define CL_PENGUIN_ORANGE 5

#if TIMED
static struct timeval last_tv;
static struct timeval tv;
#endif

typedef enum {CHASING, WAITING} penguinstate;

int stopped = 0;

float xangle = 0.0;
float posx = 0.0;
float posy = -40.0;
float posz = 0.0;

int numvertex;
int pnumface;
int snumface;
int bnumface;
int rnumface;
int numedge;

int distance = 0;
int distancechange = 0;

int firstx = 0;
int forward = FALSE;
int backward = FALSE;
int right = FALSE;
int left = FALSE;
int ftime = 0.0;

float ballx = 0.0;
float ballz = 0.0;

GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 };

typedef struct vertex
{
  float x;
  float y;
  float z;
} Vertex;

typedef int Triangle [3];

Vertex * pvertices;
Triangle * ptriangles;
Vertex * pnormals;

Vertex * svertices;
Triangle * striangles;

Vertex * bvertices;
Triangle * btriangles;

Vertex * rvertices;
Triangle * rtriangles;
Vertex * rnormals;


static GLuint texNames[2];
GLubyte * image;
int imagewidth;
int imageheight;

GLubyte * image2;
int image2width;
int image2height;

void set_normal (Vertex *normal, Vertex v1, Vertex v2, Vertex v3)
{
  normal->x = ((v1.y - v2.y) * (v2.z - v3.z)) - ((v1.z - v2.z) * (v2.y - v3.y));
  normal->y = ((v1.z - v2.z) * (v2.x - v3.x)) - ((v1.x - v2.x) * (v2.z - v3.z));
  normal->z = ((v1.x - v2.x) * (v2.y - v3.y)) - ((v1.y - v2.y) * (v2.x - v3.x));
}

void set_negated_normal (Vertex *normal, Vertex v1, Vertex v2, Vertex v3)
{
  normal->x = ((v1.z - v2.z) * (v2.y - v3.y)) - ((v1.y - v2.y) * (v2.z - v3.z));
  normal->y = ((v1.x - v2.x) * (v2.z - v3.z)) - ((v1.z - v2.z) * (v2.x - v3.x));
  normal->z = ((v1.y - v2.y) * (v2.x - v3.x)) - ((v1.x - v2.x) * (v2.y - v3.y));
}

void pReadOff (char * filename)
{
  int i;

  FILE * fl = fopen (filename, "r");
  fscanf (fl, "%d %d %d\n", &numvertex, &pnumface, &numedge);

  pvertices = (Vertex *) malloc (sizeof (Vertex) * numvertex);
  ptriangles = (Triangle *) malloc (sizeof (Triangle) * pnumface);
  pnormals = (Vertex *) malloc (sizeof (Vertex) * pnumface);

  for (i = 0; i < numvertex; i++)
    fscanf (fl, "%f %f %f\n",
	    &(pvertices[i].x),
	    &(pvertices[i].y),
	    &(pvertices[i].z));
  for (i = 0; i < pnumface; i++)
    fscanf (fl, "3 %d %d %d\n",
	    &(ptriangles[i][0]),
	    &(ptriangles[i][1]),
	    &(ptriangles[i][2]));

  fclose (fl);

  for (i = 0; i < pnumface; i++)
    {
      set_normal (&pnormals[i],
		  pvertices[ptriangles[i][0]],
		  pvertices[ptriangles[i][1]],
		  pvertices[ptriangles[i][2]]);
    }
}

void rReadOff (char * filename)
// should do both readoff's with one func maybe time later
{
  int i;

  FILE * fl = fopen (filename, "r");
  fscanf (fl, "%d %d %d\n", &numvertex, &rnumface, &numedge);

  rvertices = (Vertex *) malloc (sizeof (Vertex) * numvertex);
  rtriangles = (Triangle *) malloc (sizeof (Triangle) * rnumface);
  rnormals = (Vertex *) malloc (sizeof (Vertex) * rnumface);
  for (i = 0; i < numvertex; i++)
    fscanf (fl, "%f %f %f\n",
			&(rvertices[i].x),
			&(rvertices[i].y),
			&(rvertices[i].z));
  for (i = 0; i < rnumface; i++)
    fscanf (fl, "3 %d %d %d\n",
			&(rtriangles[i][0]),
			&(rtriangles[i][1]),
			&(rtriangles[i][2]));

  for (i = 0; i < rnumface; i++)
    {
      set_negated_normal (&rnormals[i],
			  rvertices[rtriangles[i][0]],
			  rvertices[rtriangles[i][1]],
			  rvertices[rtriangles[i][2]]);
    }

  fclose (fl);
}

void readimage ()
/* read in an image from a PPM file, without any comments. The header
   should be of the form: P6 - X Y - 255.
 */
{
  FILE * ppmfile = fopen ("scoreboard.ppm", "r");
  fscanf (ppmfile, "P6\n%d %d\n255\n", &imagewidth, &imageheight);
  image = (GLubyte *) malloc (sizeof (GLubyte) * imagewidth * imageheight * 3);
  fread (image, imagewidth * 3, imageheight, ppmfile);
  fclose (ppmfile);
}

void readimage2 ()
/* read in an image from a PPM file, without any comments. The header
   should be of the form: P6 - X Y - 255.
 */
{
  FILE * ppmfile2 = fopen ("scene.ppm", "r");
  fscanf (ppmfile2, "P6\n%d %d\n255\n", &image2width, &image2height);
  image2 = (GLubyte *) malloc (sizeof (GLubyte) * image2width * image2height * 3);
  fread (image2, image2width * 3, image2height, ppmfile2);
  fclose (ppmfile2);
}

void Key (unsigned char key, int x, int y)
{
  switch (key)
    {
      case '+' :
        //angle -= 1.0;
	break;
      case '-' :
        //angle += 1.0;
	break;

      case 's' :
        {
	  if (stopped)
	    stopped = 0;
	  else
	    stopped = 1;
	  break;
	}
      case 'S' :
        {
	  GLint i;
	  glGetIntegerv (GL_SHADE_MODEL, &i);
	  if (i == GL_SMOOTH)
	    glShadeModel (GL_FLAT);
	  else
	    glShadeModel (GL_SMOOTH);
	  break;
        }
      case 'w' :
        {
	  GLint i;
	  glGetIntegerv (GL_POLYGON_MODE, &i);
	  if (i == GL_POINT)
	    glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
	  else if (i == GL_LINE)
	    glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
	  else if (i == GL_FILL)
	    glPolygonMode (GL_FRONT_AND_BACK, GL_POINT);
	  break;
        }
      case ' ' :
        posx -= 0.5 * sin (xangle * M_PI / 180.0);
	posy += 0.5 * cos (xangle * M_PI / 180.0);
	break;
      case 'b' :
      case 'B' :
        posx += 0.5 * sin (xangle * M_PI / 180.0);
	posy -= 0.5 * cos (xangle * M_PI / 180.0);
	break;

      case 'q' :
      case 'Q' :
        exit (EXIT_SUCCESS);
	break;
    }
}

void mouseButton (int button, int state, int x, int y)
{
  switch (button)
    {
      case GLUT_LEFT_BUTTON:
        //angle -= 1.0;
	if (state == GLUT_DOWN)
	    break;
	firstx = x;
	distance = 0;
	distancechange = 0;
	break;
      case GLUT_RIGHT_BUTTON:
        //angle += 1.0;
	if (state == GLUT_DOWN)
	    break;
	distance = 0;
	distancechange = 0;
	break;
      case GLUT_MIDDLE_BUTTON:
        exit (EXIT_SUCCESS);
	break;
    }
}

void mouseMove (int x, int y)
{
  distancechange = x - firstx - distance;
  distance = x - firstx;
}

void drawRectangle (GLfloat x_min, GLfloat x_max,
		    GLfloat y_min, GLfloat y_max)
{
  GLfloat x = x_min;
  while (x < x_max)
    {
      GLfloat y = y_min;
      GLfloat last_x = x;
      x++;
      while (y < y_max)
	{
	  glRectf (last_x, y, x, y + 1);
	  y++;
	}
    }
}

void drawYRectangle (GLfloat x_min, GLfloat x_max,
		     GLfloat z_min, GLfloat z_max,
		     GLfloat y, int single)
{
  if (single)
    {
      glVertex3f (x_min, y, z_min);
      glVertex3f (x_max, y, z_min);
      glVertex3f (x_max, y, z_max);
      glVertex3f (x_min, y, z_max);
    }
  else
    {
      GLfloat x = x_min;
      while (x++ < x_max)
	{
	  GLfloat z = z_min;
	  while (z < z_max)
	    {
	      glVertex3f (x - 1, y, z);
	      glVertex3f (x, y, z);
	      glVertex3f (x, y, ++z);
	      glVertex3f (x - 1, y, z);
	    }
	}
    }
}

void drawNet ()
{
  glColor3f (1.0, 1.0, 1.0);
  glBegin (GL_LINE_STRIP);
  glVertex3f (-5.0, 0.0, 0.01);
  glVertex3f (-5.0, 5.0, 0.01);
  glVertex3f (5.0, 5.0, 0.01);
  glVertex3f (5.0, 0.0, 0.01);
  glEnd ();
}

void drawField ()
{
  glBindTexture (GL_TEXTURE_2D, texNames[0]);

  // scoreboard
  glEnable (GL_TEXTURE_2D);
  glBegin (GL_QUADS);
  {
    glColor3f (1.0, 1.0, 1.0);
    glNormal3f (0.0, 0.0, 1.0);
    glTexCoord2f (1.0, 1.0);
    glVertex3f (5.0, 10.0, -99.5);
    glTexCoord2f (0.0, 1.0);
    glVertex3f (-5.0, 10.0, -99.5);
    glTexCoord2f (0.0, 0.0);
    glVertex3f (-5.0, 20.0, -99.5);
    glTexCoord2f (1.0, 0.0);
    glVertex3f (5.0, 20.0, -99.5);
  }
  glEnd ();
  glDisable (GL_TEXTURE_2D);

  glColor3f (0.527, 0.371, 0.063);
  // nearwall
  glNormal3f (0.0, 0.0, -1.0);
  drawRectangle (-20.0, 30.0, 0.0, 25.0);

  // near goal
  glColor3f (0.2, 0.2, 0.2);
  glPushMatrix ();
  glTranslatef (0.0, 0.0, -0.5);
  drawRectangle (-5.0, 5.0, 0.0, 5.0);
  glRotatef (180.0, 0.0, 1.0, 0.0);
  glNormal3f (0.0, 0.0, 1.0);
  drawNet ();
  glPopMatrix ();

  // backwall (near rocket)
  glColor3f (0.527, 0.371, 0.063);
  glPushMatrix ();
  glTranslatef (0.0, 0.0, -100.0);
  glNormal3f (0.0, 0.0, 1.0);
  drawRectangle (-20.0, 30.0, 0.0, 25.0);
  // far goal
  glColor3f (0.2, 0.2, 0.2);
  glTranslatef (0.0, 0.0, 0.5);
  drawRectangle (-5.0, 5.0, 0.0, 5.0);
  drawNet ();
  glPopMatrix ();

  // leftwall (closest to origin)
  glPushMatrix ();
  glColor3f (0.555, 0.043, 0.043);
  glRotatef (90.0, 0.0, 1.0, 0.0);
  glTranslatef (0.0, 0.0, -20.0);
  glNormal3f (0.0, 0.0, 1.0);
  drawRectangle (0.0, 100.0, 0.0, 25.0);
  glPopMatrix ();

  glBegin (GL_QUADS);
  {
    glNormal3f (0.0, 1.0, 0.0);

    // court
    glColor3f (0.3, 0.6, 0.3);
    drawYRectangle (-20.0, 20.0, -100.1, 0.0, 0.0, 0);

    glColor3f (1.0, 1.0, 1.0);

    glVertex3f (20.0, 0.1, -49.75);
    glVertex3f (-20.0, 0.1, -49.75);
    glVertex3f (-20.0, 0.1, -50.75);
    glVertex3f (20.0, 0.1, -50.75); 	// middle line

    glVertex3f (19.5, 0.1, 0.0);
    glVertex3f (20.5, 0.1, 0.0);
    glVertex3f (20.5, 0.1, -100.0);
    glVertex3f (19.5, 0.1, -100.0); 	// right line (on the line is in)

    glVertex3f (10.0, 0.1, 0.0);
    glVertex3f (9.5, 0.1, 0.0);
    glVertex3f (9.5, 0.1, -10.0);
    glVertex3f (10.0, 0.1, -10.0);      // near right box line

    glVertex3f (10.0, 0.1, -10.0);
    glVertex3f (10.0, 0.1, -9.5);
    glVertex3f (-10.0, 0.1, -9.5);
    glVertex3f (-10.0, 0.1, -10.0);	// near front box line

    glVertex3f (-10.0, 0.1, -10.0);
    glVertex3f (-9.5, 0.1, -10.0);
    glVertex3f (-9.5, 0.1, 0.0);
    glVertex3f (-10.0, 0.1, 0.0);	// near left box line

    glVertex3f (10.0, 0.1, -100.0);
    glVertex3f (9.5, 0.1, -100.0);
    glVertex3f (9.5, 0.1, -90.0);
    glVertex3f (10.0, 0.1, -90.0);      // far right box line

    glVertex3f (10.0, 0.1, -90.0);
    glVertex3f (10.0, 0.1, -90.5);
    glVertex3f (-10.0, 0.1, -90.5);
    glVertex3f (-10.0, 0.1, -90.0);	// far front box line

    glVertex3f (-10.0, 0.1, -90.0);
    glVertex3f (-9.5, 0.1, -90.0);
    glVertex3f (-9.5, 0.1, -100.0);
    glVertex3f (-10.0, 0.1, -100.0);	// far left box line

    // roof
    glColor3f (0.6, 0.6, 0.3);
    glNormal3f (0.0, -1.0, 0.0);
    drawYRectangle (-20.0, 30.0, -100.1, 0.1, 25.0, 0);
  }
  glEnd ();
}

void drawOrangePenguin ()
{
  int i;

  glBegin (GL_TRIANGLES);
  {
    for (i = 0; i < pnumface; i++)
      {
	glColor3f (0.7, 0.7, 0.7);
	if (i<13) { glColor3f (0.0, 0.0, 0.0);  }  	    // feet
	if (i>209 && i<215) { glColor3f (0.0, 0.0, 0.0); }  // left wing incl line to feet
	if (i>199 && i<209) { glColor3f (0.0, 0.0, 0.0); }  // right wing incl line b/w wings
	if (i>226) { glColor3f (0.95, 0.38, 0.05); }   	    // beak

	glNormal3f (pnormals[i].x, pnormals[i].y, pnormals[i].z);
        glVertex3f (pvertices[ptriangles[i][0]].x,
                    pvertices[ptriangles[i][0]].y,
                    pvertices[ptriangles[i][0]].z);
        glVertex3f (pvertices[ptriangles[i][1]].x,
                    pvertices[ptriangles[i][1]].y,
                    pvertices[ptriangles[i][1]].z);
        glVertex3f (pvertices[ptriangles[i][2]].x,
                    pvertices[ptriangles[i][2]].y,
                    pvertices[ptriangles[i][2]].z);
      }
  }
  glEnd ();
}

void drawGreenPenguin ()
{
  int i;

  glBegin (GL_TRIANGLES);
  {
    for (i = 0; i < pnumface; i++)
      {
	glColor3f (0.7, 0.7, 0.7);
	if (i<13) { glColor3f (0.0, 0.0, 0.0);  }  	    // feet
        if (i>209 && i<215) { glColor3f (0.0, 0.0, 0.0); }  // left wing incl line to feet
	if (i>199 && i<209) { glColor3f (0.0, 0.0, 0.0); }  // right wing incl line b/w wings
	if (i>226) { glColor3f (0.0, 1.0, 0.0); }   	    // beak

	glNormal3f (pnormals[i].x, pnormals[i].y, pnormals[i].z);
        glVertex3f (pvertices[ptriangles[i][0]].x,
                    pvertices[ptriangles[i][0]].y,
                    pvertices[ptriangles[i][0]].z);
        glVertex3f (pvertices[ptriangles[i][1]].x,
                    pvertices[ptriangles[i][1]].y,
                    pvertices[ptriangles[i][1]].z);
        glVertex3f (pvertices[ptriangles[i][2]].x,
                    pvertices[ptriangles[i][2]].y,
                    pvertices[ptriangles[i][2]].z);
      }
  }
  glEnd ();
}

void drawRocket ()
{
  int i;

  glBegin (GL_TRIANGLES);
  {
    for (i = 0; i < rnumface; i++)
      {
	glColor3f (0.9, 0.3, 0.3);

	glNormal3f (rnormals[i].x, rnormals[i].y, rnormals[i].z);
        glVertex3f (rvertices[rtriangles[i][0]].x,
                    rvertices[rtriangles[i][0]].y,
                    rvertices[rtriangles[i][0]].z);
        glVertex3f (rvertices[rtriangles[i][1]].x,
                    rvertices[rtriangles[i][1]].y,
                    rvertices[rtriangles[i][1]].z);
        glVertex3f (rvertices[rtriangles[i][2]].x,
                    rvertices[rtriangles[i][2]].y,
                    rvertices[rtriangles[i][2]].z);
      }
  }
  glEnd ();
}

void lights ()
{
  GLfloat APosition[] = { 19.0, 23.0, -50.0, 1.0 };
  GLfloat ADirection[] = { -0.25, -1.0, 0.55, 1.0 };
  GLfloat BPosition[] = { -19.0, 23.0, -50.0, 1.0 };
  GLfloat BDirection[] = { 0.25, -1.0, 0.55, 1.0 };
  GLfloat CPosition[] = { -19.0, 23.0, 50.0, 1.0 };
  GLfloat CDirection[] = { 0.25, -1.0, -0.55, 1.0 };
  GLfloat DPosition[] = { 19.0, 23.0, 50.0, 1.0 };
  GLfloat DDirection[] = { -0.25, -1.0, -0.55, 1.0 };
  GLfloat FPosition[] = { 23.0, 23.0, 25.0, 1.0 };
  GLfloat FDirection[] = { -1.0, -1.0, 0.0, 1.0 };
  GLfloat GPosition[] = { 23.0, 23.0, -25.0, 1.0 };

  // Corner spots.
  glLightfv (GL_LIGHT0, GL_POSITION, APosition);
  glLightfv (GL_LIGHT0, GL_SPOT_DIRECTION, ADirection);
  glLightfv (GL_LIGHT1, GL_POSITION, BPosition);
  glLightfv (GL_LIGHT1, GL_SPOT_DIRECTION, BDirection);
  glLightfv (GL_LIGHT2, GL_POSITION, CPosition);
  glLightfv (GL_LIGHT2, GL_SPOT_DIRECTION, CDirection);
  glLightfv (GL_LIGHT3, GL_POSITION, DPosition);
  glLightfv (GL_LIGHT3, GL_SPOT_DIRECTION, DDirection);
  // General.
  glLightfv (GL_LIGHT5, GL_POSITION, FPosition);
  glLightfv (GL_LIGHT5, GL_SPOT_DIRECTION, FDirection);
  glLightfv (GL_LIGHT6, GL_POSITION, GPosition);
  glLightfv (GL_LIGHT6, GL_SPOT_DIRECTION, FDirection);
}

void initLights ()
{
#define AMBIENCE 0.4
  GLfloat Ambience[] = { AMBIENCE, AMBIENCE, AMBIENCE, 1.0 };

  // FIX Kills lights.
  // Set showing backsides
  //glLightModeli (GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);

  // Set local viewpoint
  glLightModelf (GL_LIGHT_MODEL_LOCAL_VIEWER, 1.0);

  // Set ambient levels of entire scene.
  glLightModelfv (GL_LIGHT_MODEL_AMBIENT, Ambience);

  // Turn on the lights.
  glEnable (GL_LIGHTING);

  // Turn on each light.
  glEnable (GL_LIGHT0);
  glEnable (GL_LIGHT1);
  glEnable (GL_LIGHT2);
  glEnable (GL_LIGHT3);
  glEnable (GL_LIGHT4);	// Rover.
  glEnable (GL_LIGHT5);
  glEnable (GL_LIGHT6);

  // Create spot lights.

#define INTENSITY 10.0
#define ANGLE 25.0

  glLightfv (GL_LIGHT0, GL_DIFFUSE, white);
  glLightf (GL_LIGHT0, GL_SPOT_EXPONENT, INTENSITY);
  glLightf (GL_LIGHT0, GL_SPOT_CUTOFF, ANGLE);
  glLightfv (GL_LIGHT0, GL_SPECULAR, white);

  glLightfv (GL_LIGHT1, GL_DIFFUSE, white);
  glLightf (GL_LIGHT1, GL_SPOT_EXPONENT, INTENSITY);
  glLightf (GL_LIGHT1, GL_SPOT_CUTOFF, ANGLE);
  glLightfv (GL_LIGHT1, GL_SPECULAR, white);

  glLightfv (GL_LIGHT2, GL_DIFFUSE, white);
  glLightf (GL_LIGHT2, GL_SPOT_EXPONENT, INTENSITY);
  glLightf (GL_LIGHT2, GL_SPOT_CUTOFF, ANGLE);
  glLightfv (GL_LIGHT2, GL_SPECULAR, white);

  glLightfv (GL_LIGHT3, GL_DIFFUSE, white);
  glLightf (GL_LIGHT3, GL_SPOT_EXPONENT, INTENSITY);
  glLightf (GL_LIGHT3, GL_SPOT_CUTOFF, ANGLE);
  glLightfv (GL_LIGHT3, GL_SPECULAR, white);

  glLightfv (GL_LIGHT4, GL_DIFFUSE, white);
  glLightf (GL_LIGHT4, GL_SPOT_EXPONENT, INTENSITY);
  glLightf (GL_LIGHT4, GL_SPOT_CUTOFF, 10.0);
  glLightfv (GL_LIGHT4, GL_SPECULAR, white);

  glLightfv (GL_LIGHT5, GL_DIFFUSE, Ambience);
  glLightfv (GL_LIGHT5, GL_SPECULAR, Ambience);

  glLightfv (GL_LIGHT6, GL_DIFFUSE, Ambience);
  glLightfv (GL_LIGHT6, GL_SPECULAR, Ambience);

#if 0
  // Normal-testing spot.
  {
    glEnable (GL_LIGHT7);
    glLightfv (GL_LIGHT7, GL_DIFFUSE, white);
    glLightf (GL_LIGHT7, GL_SPOT_EXPONENT, INTENSITY);
    glLightf (GL_LIGHT7, GL_SPOT_CUTOFF, 15.0);
    glLightfv (GL_LIGHT7, GL_SPECULAR, white);
    GLfloat HPosition[] = { 0.0, 0.0, 0.0, 1.0 };
    GLfloat HDirection[] = { 0.0, 0.0, -1.0, 1.0 };
    glLightfv (GL_LIGHT7, GL_POSITION, HPosition);
    glLightfv (GL_LIGHT7, GL_SPOT_DIRECTION, HDirection);
  }
#endif

#undef ANGLE
}

void lightMaterial ()
{
  GLfloat material_ambient[] = { 1.0, 1.0, 0.0, 1.0 };
  GLfloat material_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
  GLfloat material_specular[] = { 1.0, 1.0, 1.0, 1.0 };
  GLfloat material_emission[] = { 1.0, 1.0, 1.0, 1.0 };

  glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION, material_emission);
  glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, 128.0);
  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient);
  glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, material_diffuse);
  glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, material_specular);
}

void offMaterial ()
{
  GLfloat material_emission[] = { 0.0, 0.0, 0.0, 1.0 };
  GLfloat material_ambient[] = { 1.0, 1.0, 1.0, 1.0 };
  GLfloat material_diffuse[] = { 0.75, 0.75, 0.75, 1.0 };
  GLfloat material_specular[] = { 0.1, 0.1, 0.1, 1.0 };

  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient);
  glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, material_diffuse);
  glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, material_specular);
  glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION, material_emission);
  glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, 0.0);
}

void lightConeMaterial ()
{
  GLfloat material_emission[] = { 0.0, 0.0, 0.0, 0.2 };
  GLfloat material_ambient[] = { 1.0, 1.0, 1.0, 0.2 };
  GLfloat material_diffuse[] = { 0.75, 0.75, 0.75, 0.2 };
  GLfloat material_specular[] = { 0.1, 0.1, 0.1, 0.2 };

  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient);
  glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, material_diffuse);
  glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, material_specular);
  glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION, material_emission);
  glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, 0.0);
}

void initMaterial ()
{
  GLfloat material_emission[] = { 0.0, 0.0, 0.0, 1.0 };
  GLfloat material_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
  GLfloat material_diffuse[] = { 0.0, 0.0, 0.0, 1.0 };
  GLfloat material_specular[] = { 0.0, 0.0, 0.0, 1.0 };

  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient);
  glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, material_diffuse);
  glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, material_specular);
  glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION, material_emission);
  glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, 0.0);
}

void mapMaterial ()
{
  GLfloat material_emission[] = { 0.0, 0.0, 0.0, 1.0 };
  GLfloat material_ambient[] = { 1.0, 1.0, 1.0, 0.6 };
  GLfloat material_diffuse[] = { 0.75, 0.75, 0.75, 0.6 };
  GLfloat material_specular[] = { 0.1, 0.1, 0.1, 0.6 };

  glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient);
  glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, material_diffuse);
  glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, material_specular);
  glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION, material_emission);
  glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, 0.0);
}

void drawBox ()
{
  glBindTexture (GL_TEXTURE_2D, texNames[1]);

  mapMaterial ();

  glEnable (GL_TEXTURE_2D);
  glBegin (GL_QUADS);
  {
    glColor3f (1.0, 0.77, 1.0);
    glNormal3f (-1.0, 0.0, 0.0);
    glTexCoord2f (0.0, 1.0);
    glVertex3f (29.5, 8.0, -60.0);

    glTexCoord2f (0.0, 0.0);
    glVertex3f (29.5, 18.0, -60.0);

    glTexCoord2f (1.0, 0.0);
    glVertex3f (29.5, 18.0, -20.0);

    glTexCoord2f (1.0, 1.0);
    glVertex3f (29.5, 8.0, -20.0);
  }
  glEnd ();
  glDisable (GL_TEXTURE_2D);

  offMaterial ();

  // Rightwall (nearer rocket).
  glEnd ();
  glPushMatrix ();
  glColor3f (0.555, 0.043, 0.043);
  glRotatef (-90.0, 0.0, 1.0, 0.0);
  glTranslatef (0.0, 0.0, -30.0);
  glTranslatef (-100.0, 0.0, 0.0);
  glNormal3f (0.0, 0.0, 1.0);
  drawRectangle (0.0, 100.0, 0.0, 25.0);
  glPopMatrix ();

  glBegin (GL_QUADS);
  {
    glNormal3f (0.0, 1.0, 0.0);
    glColor3f (0.3, 0.6, 0.3);
    // Side floor.
    drawYRectangle (20.0, 30.0, -100.1, 0.0, 0.0, 0);
  }
  glEnd ();

  glColor3f (0.3, 0.3, 0.8);
}

void drawLightSpheres ()
{
  // Spot spheres.
  glPushMatrix ();
  {
    glTranslatef (19.0, 23.0, 50.0);
    glutSolidSphere (0.5, 10.0, 10.0);

    glTranslatef (-38.0, 0.0, 0.0);
    glutSolidSphere (0.5, 10.0, 10.0);

    glTranslatef (0.0, 0.0, -99.0);
    glutSolidSphere (0.5, 10.0, 10.0);

    glTranslatef (2.0, 0.0, 0.0);

    glTranslatef (38.0, 0.0, 0.0);
    glutSolidSphere (0.5, 10.0, 10.0);
  }
  glPopMatrix ();
}


static float rocket_time = 0.0;

static float ballmovez = 0.0;
static float ballmovex = 0.0;
static float ballxgoal = 0.0;
static float ballzgoal = 0.0;
static float xaccel = 0.0;
static float zaccel = 0.0;

static float p1x = -0.75;
static float p1z = 0.0;

static float p2x = 0.0;
static float p2z = 15.0;
static penguinstate p2state = WAITING;
static penguinstate p1state = WAITING;

static float degrees;

void display ()
{
#if TRACE
  float diffx;
  float diffz;
  char * str;
#endif

  glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  //glMatrixMode (GL_MODELVIEW);

  glLoadIdentity ();

  // Position

  glRotatef (xangle, 0.0, 1.0, 0.0);

  glTranslatef (posx, -HEIGHT, posy);

  // Lights.

  {
    // Rover.
    GLfloat EPosition[] = { 19.0, 23.0, ballz, 1.0 };
    GLfloat EDirection[] = { (ballx - 20.0), -23.0, 0.0, 1.0 };
    glLightfv (GL_LIGHT4, GL_POSITION, EPosition);
    glLightfv (GL_LIGHT4, GL_SPOT_DIRECTION, EDirection);

    glCallList (CL_LIGHTS);
  }

  // draw the field, goals, lines, wall, roof
  glCallList (CL_BOX);

  // ball

  glPushMatrix ();
  {
    glTranslatef (ballx, 0.25, ballz);

    glColor3f (0.0, 0.0, 0.7);
    glutSolidSphere (0.25, 10.0, 10.0);	// draw ball

    glPopMatrix ();
  }

  glPushMatrix ();
  glTranslatef (p1x, 0.0, p1z);

  glCallList (CL_PENGUIN_ORANGE);

  glPopMatrix ();

  glPushMatrix ();
  glTranslatef (p2x, 0.0, p2z);
  glRotatef (-180.0, 0.0, 1.0, 0.0);

  glCallList (CL_PENGUIN_GREEN);

  glPopMatrix ();

  // Rocket.

  glPushMatrix ();
  if (rocket_time < 30)
    {
      glTranslatef (25.0, 30.0 - rocket_time, -45.0);
      rocket_time = rocket_time + 0.4;
    }
  else
    glTranslatef (25.0, 0.0, -45.0);
  drawRocket ();
  glPopMatrix ();

  // Light spheres.

  // Stationary.
  glCallList (CL_SPHERES);

  // Rover.
  glPushMatrix ();
  {
    glTranslatef (20.0, 23.0, ballz);
    glutSolidSphere (0.5, 10.0, 10.0);
  }
  glPopMatrix ();

  // Rover cone, last for transparency.
  glPushMatrix ();
  {
    glTranslatef (ballx, 0.0, ballz);

    glColor4f (1.0, 1.0, 1.0, 0.2);
    lightConeMaterial ();
#define CONE_HEIGHT 65.0
    glRotatef (90.0, 0.0, 1.0, 0.0);
    glRotatef (degrees, 1.0, 0.0, 0.0);
    glTranslatef (0.0, 0.0, degrees / 90.0 * 23.0 - 15.0);
    glutSolidCone (9.0, CONE_HEIGHT, 20.0, 0.0);
#undef CONE_HEIGHT
    glColor3f (1.0, 1.0, 1.0);
  }
  glPopMatrix ();

  glFlush ();

  glutSwapBuffers ();
}

void animate ()
{
  float r;

  // Angle between ball and roving light (in radians)
  //   = arctan (opposite / adjacent)
  //   = atan (20.0 / (ballx + 20.0))
  degrees = - atan (23.0 / (20.0 - ballx)) / M_PI * 180.0;

#if TIMED
  if (gettimeofday (&tv, NULL)
      && last_tv.tv_sec == tv.tv_sec
      && (last_tv.tv_usec - last_tv.tv_usec < 40))
    {
      return;
    }
  last_tv = tv;
#endif

  // Penguin 1 kick ball if it's close.
  if (ballx - p1x < 1.0 && ballx -p1x > -1.0
      && p2state == WAITING
      && ballz - p1z < 1.0 && ballz - p1z > -1.0)
    {
      // random z kick amount
      ballmovez = rand ()% (int) (-ballz + 50.0);

      // random x kick amount
      r = 10.0 * rand () / (RAND_MAX+1.0);
      tracef ("\n%f", r);
      if (r < 5.0)   // kick to left
        {
	  tracef ("\nShould go left");
	  ballmovex = -rand () % (int) (20.0 + ballx);
	}
      else		             // kick to right
        ballmovex = rand () % (int) (-ballx + 20.0);

      ballxgoal = ballx + ballmovex;
      ballzgoal = ballz + ballmovez;
      xaccel = (ballxgoal - p1x) / 40.0;
      zaccel = (ballzgoal - p1z) / 40.0;
      p1state = WAITING;
      p2state = CHASING;
    }

  // Penguin 2 kick ball if it's close.
  if (ballx - p2x < 1.0 && ballx - p2x > -1.0
      && p1state == WAITING
      && ballz - p2z < 1.0 && ballz - p2z > -1.0)
    {
      // Random z kick amount.
      ballmovez = -rand () % (int) (ballz + 50.0);

      // Random x kick amount.
      // r = rand ()%10;
      r = 10.0 * rand () / (RAND_MAX + 1.0);
      tracef ("\n%f", r);
      if (r < 5.0)   // kick to left
        {
	  tracef ("\nShould go left");
	  ballmovex = -rand () % (int) (20.0 + ballx);
	}
      else		             // kick to right
        ballmovex = rand () % (int) (-ballx + 20.0);

      tracef ("\nPenguin 2:\n ballmovex: %f\n ballmovez: %f\n",
	      ballmovex,
	      ballmovez);
      ballxgoal = ballx + ballmovex;
      ballzgoal = ballz + ballmovez;
      xaccel = (ballxgoal - p2x) / 40.0;   // this needs neg check
      zaccel = (ballzgoal - p2z) / 40.0;
      p2state = WAITING;
      p1state = CHASING;
      tracef (" ballxgoal: %f\n ballzgoal: %f\n", ballxgoal, ballzgoal);
    }

#if TRACE
  tracef ("\nxaccel:%f", xaccel);
  if (p2state == WAITING)
	str="waiting";
  else
	str="chasing";
  tracef ("\nzaccel:%f\np2state:%s\n", zaccel, str);
#endif

  xangle += 0.5 * distancechange;

  // calculate ball position
  if (ballx - ballxgoal > 1.0 || ballx - ballxgoal < -1.0)
    ballx += ballmovex / 40.0;
  if (ballz - ballzgoal > 1.0 || ballz - ballzgoal < -1.0)
    ballz += ballmovez / 40.0;

#define DIV 8.0
  // Penguin 1 position calculation
  if (p1state == CHASING)
    {
#if TRACE
      diffx = p1x - ballx;
      diffz = p1z - ballz;
      tracef ("\ncheck:\n p1x - ballx: %f\n p1z - ballz: %f\n",
	      diffx, diffz);
#endif
      if (p1x - ballx <= 1.0 && p1x - ballx >= -1.0)
	{
	  if (p1z - ballz <= 1.0 && p1z - ballz >= -1.0)
	    {
	      ballx = p1x;
	      ballz = p1z;
	      p1state = WAITING;
	    }
	}
      else
        p1x = p1x + (ballx - p1x) / DIV;

      if (p1z - ballz > 1.0 || p1z - ballz < -1.0)
        p1z = p1z + (ballz - p1z) / DIV;
    }

  // Penguin 2 position calculation
  if (p2state == CHASING)
    {
#if TRACE
      diffx = p2x - ballx;
      diffz = p2z - ballz;
      tracef ("\ncheck:\n p2x - ballx: %f\n p2z - ballz: %f\n",
	      diffx, diffz);
#endif
      if (p2x - ballx < 1.0 && p2x - ballx > -1.0) {
	  if (p2z - ballz < 1.0 && p2z - ballz > -1.0)
	    {
	      ballx = p2x;
	      ballz = p2z;
	      p2state = WAITING;
	    }
	}
      else
        p2x = p2x + (ballx - p2x) / DIV;

      if (p2z - ballz > 1.0 || p2z - ballz < -1.0)
        p2z = p2z + (ballz - p2z) / DIV;
    }
}

void maybe_animate ()
{
  if (stopped)
    glutSwapBuffers ();
  else
    {
      animate ();
      display ();
    }
}

void reshape (GLsizei w, GLsizei h)
{
  glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();
  glFrustum (-1.0, 1.0, -1.0, 1.0, 2.0, 50000.0);

  glMatrixMode (GL_MODELVIEW);
  glViewport (0, 0, w, h);
}

int main (int argc, char * argv[])
{
  srand (time (NULL));	// Seed random generator.

  //glutInitDisplayString ("stencil~2 rgb double depth>=16 samples");
  glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
  glutInitWindowPosition (100, 100);
  glutInitWindowSize (350, 350);

  glutInit (&argc, argv);

  glutCreateWindow (argv[0]);

  pReadOff ("penguin.off");
  rReadOff ("rocket.off");

  // Read in images for texturing.
  readimage ();
  readimage2 ();

  glEnable (GL_DEPTH_TEST);
  // FIX could unitize rocket,penguin normals instead
  glEnable (GL_NORMALIZE);
  glEnable (GL_COLOR_MATERIAL);
  glEnable (GL_LINE_SMOOTH);
  glLineWidth (1.5);
  glEnable (GL_BLEND);
  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

#if FOG
  glEnable (GL_FOG);
  glFogf (GL_FOG_DENSITY, 0.001);
#endif

  initMaterial ();
  initLights ();

  glClearColor (0.0, 0.0, 1.0, 0.0);

  // Init textures.

  glGenTextures (2, texNames);

  glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
  //glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

  glBindTexture (GL_TEXTURE_2D, texNames[0]);

  glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  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, imagewidth, imageheight, 0, GL_RGB, GL_UNSIGNED_BYTE, image);

  glBindTexture (GL_TEXTURE_2D, texNames[1]);

  glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  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, image2width, image2height, 0, GL_RGB, GL_UNSIGNED_BYTE, image2);


  // Define call lists.

  glNewList (CL_BOX, GL_COMPILE);
  {
    initMaterial ();

    glPushMatrix ();
    glTranslatef (0.0, 0.0, 50.0);	// origin to field middle

    drawBox ();

    drawField (); 	// draw the field, goals, lines, wall, roof

    glPopMatrix ();
  }
  glEndList ();

  glNewList (CL_LIGHTS, GL_COMPILE);
  {
    lights ();
  }
  glEndList ();

  glNewList (CL_SPHERES, GL_COMPILE);
  {
    glColor3f (1.0, 1.0, 1.0);
    lightMaterial ();
    drawLightSpheres ();
  }
  glEndList ();

  glNewList (CL_PENGUIN_GREEN, GL_COMPILE);
  {
    drawGreenPenguin ();
  }
  glEndList ();

  glNewList (CL_PENGUIN_ORANGE, GL_COMPILE);
  {
    drawOrangePenguin ();
  }
  glEndList ();

#if TIMED
  if (gettimeofday (&tv, NULL) == 0)
    last_tv = tv;
  else {
    perror ("gettimeofday failed");
    return EXIT_FAILURE;
  }
#endif

  glutReshapeFunc (reshape);
  glutDisplayFunc (display);
  glutIdleFunc (maybe_animate);
  glutKeyboardFunc (Key);
  glutMouseFunc (mouseButton);
  glutMotionFunc (mouseMove);

  animate ();
  glutMainLoop ();

  return EXIT_SUCCESS;
}
