/*****************************************************************************
 * file:      v11.c
 * status:    09-may-2001, pfk.
 * function:  Example of texture mapping.
 ****************************************************************************/

#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>

/* Size and position of output window. */
#define WINSIZE_X    500   /* width  in pixels         */
#define WINSIZE_Y    500   /* height in pixels         */
#define WINPOS_X     100   /* position of left         */
#define WINPOS_Y     100   /*             upper corner */

/* Array of unsigned bytes to hold texture image:
 *   minimum image size is 64x64,
 *   both sizes must be power of 2, not necessarily the same power. */
static int texImageWidth, texImageHeight;
static GLubyte *texImage;  /* ptr to 3-dimensional array:
                              texImage[texImageWidth][texImageHeight][4]
                              with four bytes per pixel */


/****************************************************************************
 * Get texture image from pgm- or ppm-file.
 * Format of input file:
 * - line containing ascii file type, e.g. strings 'P5' or 'P6'
 * - optional ascii comment lines starting with '#'
 * - ascii line containing horizontal and vertical size separated by space
 * - ascii line containing number '255'
 * - binary pixel values: single byte per pixel with grayscale if type P5 (pgm)
 * -                      triples of bytes with RGB values if type P6 (ppm)
 ***************************************************************************/
void getImageFromFile (void)
{
  #define inputLineLength 81

  int i, j;
  int byteValue, intensityLevels;
  char inputLine[inputLineLength], typeOfFile[3];
  FILE *f = fopen ("mickey.ppm", "r");

  if (f == NULL)        /* check if file exists */
  { fprintf (stderr, "\nERROR: Failed to open file 'mickey.ppm'.\n");
    exit (1);
  }

  /* Get file type. */
  do  fgets (inputLine, inputLineLength-1, f);  while (inputLine[0] == '#');
  typeOfFile[0] = inputLine[0];
  typeOfFile[1] = inputLine[1];
  typeOfFile[2] = '\0';
  printf ("  file type:  %s\n", typeOfFile);

  /* Get horizontal en vertical size. */
  do  fgets (inputLine, inputLineLength-1, f);  while (inputLine[0] == '#');
  sscanf (inputLine, "%d %d", &texImageWidth, &texImageHeight);
  printf ("  horizontal: %d\n", texImageWidth);
  printf ("  vertical:   %d\n", texImageHeight);
  /* Allocate array that will contain the texture image. */
  texImage =
    (GLubyte *) malloc (texImageWidth*texImageHeight*4*sizeof(GLubyte));

  /* Get number with maximum number of shades. */
  do  fgets (inputLine, inputLineLength-1, f);  while (inputLine[0] == '#');
  sscanf (inputLine, "%d", &intensityLevels);
  printf ("  int levels: %d\n", intensityLevels);

  /* Copy binary pixel values into texture image array. */
  for (i = texImageHeight-1; i >= 0; i--)
  { for (j = 0; j < texImageWidth; j++)
    { int position = (i*texImageWidth+j)*4;
      /* read and store RGB or grayscale values*/
      byteValue = getc (f);     /* R-value or grayscale value */
      texImage[position] = (GLubyte) byteValue;
      if (typeOfFile[1] == '6')
        byteValue = getc (f);   /* G-value if file type = R6 */
      texImage[position+1] = (GLubyte) byteValue;
      if (typeOfFile[1] == '6')
        byteValue = getc (f);   /* B-value if file type = R6 */
      texImage[position+2] = (GLubyte) byteValue;
                                /*  alpha-value */
      texImage[position+3] = (GLubyte) 255;
    }
  }
}


/****************************************************************************
 * Create texture image and do set-up for texture mapping.
 ***************************************************************************/
void progInit (void)
{   
  glClearColor (0.8, 0.8, 0.0, 0.0);
  glShadeModel (GL_FLAT);             /* flat shading */
  glEnable     (GL_DEPTH_TEST);       /* use depth buffer */

  getImageFromFile ();

  /* set pixel storage mode to byte alignment, since data
   * are stored in  GLubyte texImage[][][] */
  glPixelStorei (GL_UNPACK_ALIGNMENT, 1);

  /* 2D texture mapping */
  /* set wrap parameter for coordinates s and t to repeat */
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  /* set texture minifying and magnification to choose texture element 
   * nearest to center of pixel being textured */ 
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  /* specify 2D texture image */
  glTexImage2D    (GL_TEXTURE_2D, 0, 4, texImageWidth, texImageHeight,
                   0, GL_RGBA, GL_UNSIGNED_BYTE, texImage);
}


/****************************************************************************
 * Display textured objects:
 *   three four-sided polygons with the same 2D texture applied (cube),
 *   one   four-sided polygon  with part of the same 2D texture applied.
 ***************************************************************************/
void progDisplay (void)
{
  glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  /* enable 2D texture mapping */
  glEnable (GL_TEXTURE_2D);

  /* set texture environment variables */
  glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

  glBegin (GL_QUADS);

  /* 2D texture image and   corresponding object coordinate */
  glTexCoord2f (0.0, 0.0);  glVertex3f (-2.2, -1.0, 0.0);
  glTexCoord2f (0.0, 1.0);  glVertex3f (-2.2,  1.0, 0.0);
  glTexCoord2f (1.0, 1.0);  glVertex3f ( 1.0,  1.0, 0.0);
  glTexCoord2f (1.0, 0.0);  glVertex3f ( 1.0, -1.0, 0.0);

  glTexCoord2f (0.0, 0.0);  glVertex3f ( 1.0, -1.0,  0.0);
  glTexCoord2f (0.0, 1.0);  glVertex3f ( 1.0,  1.0,  0.0);
  glTexCoord2f (1.0, 1.0);  glVertex3f ( 2.4,  1.8, -1.4);
  glTexCoord2f (1.0, 0.0);  glVertex3f ( 2.4, -0.2, -1.4);

  glTexCoord2f (0.0, 0.0);  glVertex3f (-2.2,  1.0,  0.0);
  glTexCoord2f (0.0, 1.0);  glVertex3f (-0.8,  1.8, -1.4);
  glTexCoord2f (1.0, 1.0);  glVertex3f ( 2.4,  1.8, -1.4);
  glTexCoord2f (1.0, 0.0);  glVertex3f ( 1.0,  1.0,  0.0);

  glTexCoord2f (0.5, 0.4);  glVertex3f (-1.0,  1.8, 0.0);
  glTexCoord2f (0.5, 0.9);  glVertex3f (-1.0,  2.6, 0.0);
  glTexCoord2f (1.0, 0.9);  glVertex3f ( 0.3,  2.6, 0.0);
  glTexCoord2f (1.0, 0.4);  glVertex3f ( 0.3,  1.8, 0.0);

  glEnd ();

  /* disable 2D texture mapping */
  glDisable (GL_TEXTURE_2D);

  glFlush ();
}


/****************************************************************************
 * Reshape handling.
 ***************************************************************************/
void progReshape (int w, int h)
{
  glViewport (0, 0, (GLsizei) w, (GLsizei) h);

  glMatrixMode   (GL_PROJECTION);
  glLoadIdentity ();
  gluPerspective (60.0, (GLfloat) w/(GLfloat) h, 1.0, 10.0);

  glMatrixMode   (GL_MODELVIEW);
  glLoadIdentity ();
  glTranslatef   ( 0.3, -0.8, -3.6);
}


/****************************************************************************
 * Keyboard handling.
 ***************************************************************************/
void progKeyboard (unsigned char key, int x, int y)
{
  switch (key)
  { case 'q': exit(0);
          break;
   case 27:  exit(0);
          break;
   default:  break;
  }
}


/****************************************************************************
 * Main program.
 ***************************************************************************/
int main (int argc, char** argv)
{
  glutInit (&argc, argv);
  glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
  glutInitWindowSize (WINSIZE_X, WINSIZE_Y);
  glutInitWindowPosition (WINPOS_X, WINPOS_Y);
  glutCreateWindow (argv[0]);
  progInit ();

  glutDisplayFunc  (progDisplay);
  glutReshapeFunc  (progReshape);
  glutKeyboardFunc (progKeyboard);

  glutMainLoop ();
  return 0; 
}
