/* texgen.cc */

#include <iostream>
#include <cmath>

#define GLEW_STATIC 1
#include <GL/glew.h>

#include <GL/glut.h>

struct vec3
{
  GLfloat x, y, z;
  vec3 (GLfloat xx = 0, GLfloat yy = 0, GLfloat zz = 0):x(xx),y(yy),z(zz){}
  void scale (const double s) { x *= s; y *= s; z *= s; }
  double dot (const vec3 &a) const { return x * a.x + y * a.y + z * a.z; }
  double sqLen (void) const { return dot (*this); }
  double len (void) const { return (double)(sqrt (sqLen ())); }
  double invLen() const { return (double) ( 1. / sqrt (sqLen ())); }
  void normalize (void) { scale (invLen ()); }
  vec3 &operator+= (const vec3 &a) { x += a.x; y += a.y; z += a.z; return *this; }
  vec3 &operator-= (const vec3 &a) { x -= a.x; y -= a.y; z -= a.z; return *this; }
  vec3  operator-  (const vec3 &a) const { vec3 r (*this); r -= a; return r; }
  static vec3 cross (const vec3& a, const vec3& b);
  void rotate (const vec3 &axis, double angleRads)
    {
      const double cosAngle = cos (angleRads);
      const double sinAngle = sin (angleRads);
      vec3 normAxis (axis);
      normAxis.normalize ();
      vec3 cross  = vec3::cross (*this, normAxis);
      vec3 cross2 = vec3::cross (cross, normAxis);
      cross2.scale ((double)(1. - cosAngle));
      cross.scale ((double)sinAngle);
      *this += cross2 - cross;
    }
};

vec3
vec3::cross (const vec3 &a, const vec3 &b)
{
  vec3 result (a.y * b.z - a.z * b.y,
               a.z * b.x - a.x * b.z,
               a.x * b.y - a.y * b.x);
  return result;
}

GLfloat angle = 0.0f;

static void
display_callback (void)
{
  glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLoadIdentity ();
  gluLookAt (0,5,5, 0,0,0, 0,1,0);
  glRotatef (angle, 0,1,0);

  /* a base plane */
  glBegin (GL_QUADS);
    glVertex3f (-3, 0.1, -3);
    glVertex3f (-3, 0.1, 3);
    glVertex3f (3, 0.1, 3);
    glVertex3f (3, 0.1, -3);
  glEnd ();
  /* a cube on top of the plane */
  glBegin (GL_QUADS);
    glVertex3f (-1.1, 2.1, -1.1); glVertex3f (-1.1, 2.1, 1.1); glVertex3f (1.1, 2.1, 1.1); glVertex3f (1.1, 2.1, -1.1); // top

    glVertex3f (-1.1, 0.1, -1.1); glVertex3f (-1.1, 2.1, -1.1); glVertex3f (1.1, 2.1, -1.1); glVertex3f (1.1, 0.1, -1.1);
    glVertex3f (-1.1, 0.1, 1.1); glVertex3f (-1.1, 2.1, 1.1); glVertex3f (1.1, 2.1, 1.1); glVertex3f (1.1, 0.1, 1.1);
    glVertex3f (-1.1, 0.1, -1.1); glVertex3f (-1.1, 2.1, -1.1); glVertex3f (-1.1, 2.1, 1.1); glVertex3f (-1.1, 0.1, 1.1);
    glVertex3f (1.1, 0.1, -1.1); glVertex3f (1.1, 2.1, -1.1); glVertex3f (1.1, 2.1, 1.1); glVertex3f (1.1, 0.1, 1.1);
  glEnd ();


  glutSwapBuffers ();
}

static void
reshape_callback (int w, int h)
{
  glViewport (0, 0, w, h);
  glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();
  gluPerspective (45, (double)w/(double)h, .01, 20);

  // Leave us in model-view mode for our display routine
  glMatrixMode (GL_MODELVIEW);

}

static void
keyboard_callback (unsigned char key, int, int)
{
  switch (key)
    {
    case 27:
    case 'q':
    case 'Q':
      exit (0);
      break;
    case 'a': glEnable (GL_TEXTURE_GEN_S); break;
    case 'A': glDisable (GL_TEXTURE_GEN_S); break;
    case 'z': glEnable (GL_TEXTURE_GEN_T); break;
    case 'Z': glDisable (GL_TEXTURE_GEN_T); break;
    case 'e': glEnable (GL_TEXTURE_GEN_R); break;
    case 'E': glDisable (GL_TEXTURE_GEN_R); break;
    case 't':
              glTexGeni (GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
              glTexGeni (GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
              glTexGeni (GL_R, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
              glTexGeni (GL_Q, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
              break;
    case 'y':
              glTexGeni (GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
              glTexGeni (GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
              glTexGeni (GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
              glTexGeni (GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
              break;

    }
  glutPostRedisplay ();
}

static void
special_callback (int key, int, int)
{
  // Push the light around using the arrow keys.
  switch (key)
    {
    case GLUT_KEY_LEFT:
      angle += 5;
      break;
    case GLUT_KEY_RIGHT:
      angle -= 5;
      break;
    default:
      return;
      break;
    }
  glutPostRedisplay();
}

static void
init()
{
  GLuint tex;
  GLubyte pattern[64][64][64][3];
  double sp[4] = {1,0,0,0}, tp[4] = {0,1,0,0}, rp[4] = {0,0,1,0}, qp[4] = {0,0,0,1};
  glGenTextures (1, &tex);
  for (int i = 0; i < 64; i++)
    for (int j = 0; j < 64; j++)
      for (int k = 0; k < 64; k++)
        if (((i/8)+(j/8)+(k/8))%2)
          pattern[i][j][k][0]=pattern[i][j][k][1]=pattern[i][j][k][2]=255;
        else
          pattern[i][j][k][0]=pattern[i][j][k][1]=pattern[i][j][k][2]=0;
  glEnable (GL_TEXTURE_2D);
  glBindTexture (GL_TEXTURE_2D, tex);
//  gluBuild3DMipmaps (GL_TEXTURE_3D, GL_RGB, 64, 64, 64, GL_RGB, GL_UNSIGNED_BYTE, &pattern[0][0][0][0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 64, 64, 0, GL_RGB, GL_UNSIGNED_BYTE, &pattern[0][0][0][0]);
  glEnable (GL_TEXTURE_GEN_S);
  glEnable (GL_TEXTURE_GEN_T);
  glEnable (GL_TEXTURE_GEN_R);
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT);
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  glTexGendv (GL_S, GL_OBJECT_PLANE, sp);
  glTexGendv (GL_T, GL_OBJECT_PLANE, tp);
  glTexGendv (GL_R, GL_OBJECT_PLANE, rp);
  glTexGendv (GL_Q, GL_OBJECT_PLANE, qp);
  glTexGeni (GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  glTexGeni (GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  glTexGeni (GL_R, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  glTexGeni (GL_Q, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);

  glClearColor (.4, .4, .4, 1);
  glEnable (GL_DEPTH_TEST);
}

int
main (int argc, char *argv[])
{
  glutInit (&argc, argv);
  glutInitDisplayMode (GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
  glutInitWindowPosition (0, 0);
  glutInitWindowSize (512, 512);
  glutCreateWindow ("Tex Gen");

  glewInit ();
  init ();
  glutDisplayFunc (display_callback);
  glutReshapeFunc (reshape_callback);
  glutSpecialFunc (special_callback);
  glutKeyboardFunc (keyboard_callback);
  glutMainLoop();

  return 0;
}

