/*
Example for CS496, SDSU, Spring 2000.  Based on work of Ben Eadington, student
at CSU Stanislaus, Fall 1998.  Used by permission

This project defines an NXM array of numbers.  It then constructs a grid of
3 dimensional boxes corresponding to the entries in the array.  Each box has
a height proportional to the number stored in its corresponding array entry.
It also draws a 3 dimensional arrow pointing at the top of the highest box.
*/

#define PC

#ifdef MAC
#include "glut.h"
#endif
#ifdef OSX
#include <GLUT/glut.h>
#endif
#ifdef UNIX
#include <GL/glut.h>
#endif
#ifdef PC
#include <windows.h>
#include <wingdi.h>
#include <GL/glut.h>
#endif

#include <stdlib.h>
#include <math.h>
#include <time.h>

#define PI  3.14159
#define N 5  // Number of bars in x direction
#define M 6  // Number of bars in z direction

typedef GLfloat point3[3];

static GLfloat angle = 0.0;
GLfloat grid[N][M];    // Array to hold numbers for 3D bars.  
int maxX, maxZ;        // Will hold the location of the maximum value in grid 

void myinit(void)
{

   int i, j;
   int rand();

   srand(time(NULL));  // Seed random number generator 

// initialize array to define 3D bars with random numbers between 0 and 4
// and store location of maximum value in maxX and maxZ. 
   maxX = 0; maxZ = 0;
   for (i = 0; i < N; i++)
      for (j = 0; j < M; j++)
      {
         grid[i][j] = (rand()%40/10.0);
         if (grid[i][j] > grid[maxX][maxZ])
         { 
             maxX = i;  
             maxZ = j; 
         }
      }

   glClearColor(0.4, 0.4, 1.0, 1.0);  // blue background

   glEnable(GL_DEPTH_TEST);  // enable z buffer for hidden surface removal 
}

//	Unit cube in first octant
void cube(void)
{
    point3 cv[8] = {                 // Vertices of cube 
                    {0.0, 0.0, 0.0},
                    {0.0, 0.0, 1.0},
                    {0.0, 1.0, 0.0},
                    {0.0, 1.0, 1.0},
                    {1.0, 0.0, 0.0},
                    {1.0, 0.0, 1.0},
                    {1.0, 1.0, 0.0},
                    {1.0, 1.0, 1.0},
                   };

    glBegin(GL_QUAD_STRIP);     // Draw one quad strip
      glVertex3fv(cv[1]);
      glVertex3fv(cv[5]);
      glVertex3fv(cv[0]);
      glVertex3fv(cv[4]);
      glVertex3fv(cv[2]);
      glVertex3fv(cv[6]);
      glVertex3fv(cv[3]);
      glVertex3fv(cv[7]);
    glEnd();

    glBegin(GL_QUAD_STRIP);     // Draw second quad strip
      glVertex3fv(cv[0]);
      glVertex3fv(cv[2]);
      glVertex3fv(cv[1]);
      glVertex3fv(cv[3]);
      glVertex3fv(cv[5]);
      glVertex3fv(cv[7]);
      glVertex3fv(cv[4]);
      glVertex3fv(cv[6]);
    glEnd();
 }

void pointer(void)
{
  point3 tip[24];          // array to hold vertices for base of tip of arrow
  point3 shafttop[24];     // vertices for top of arrow shaft
  point3 shaftbottom[24];  // vertices for bottom of arrow shaft
  int i;
  GLfloat ang = PI/12;     // angle between triangle slices

  for (i = 0; i <= 23; i++)    // calculate vertices at base of arrow tip
  {
     tip[i][0] = 0.5*cos(i*ang);
     tip[i][1] = 1.0;
     tip[i][2] = 0.5*sin(i*ang);
  }

  for (i = 0; i <= 23; i++)     // calculate vertices at top and bottom of shaft
  {
     shafttop[i][0] = shaftbottom[i][0] = 0.25*cos(i*ang);
     shafttop[i][2] = shaftbottom[i][2] = 0.25*sin(i*ang);
     shafttop[i][1] = 1.0;
     shaftbottom[i][1] = 2.0;
  }

  glBegin(GL_TRIANGLE_FAN);        // Draw tip of arrow
    glVertex3f(0.0, 0.0, 0.0);
    for (i = 0; i <= 23; i++)
    {
       glVertex3fv(tip[i]);
    }
    glVertex3fv(tip[0]);
  glEnd();

  glBegin(GL_TRIANGLE_FAN);        // Draw base of tip
    glVertex3f(0.0, 1.0, 0.0);
    for (i = 0; i <= 23; i++)
    {
       glVertex3fv(tip[i]);
    }
    glVertex3fv(tip[0]);
  glEnd();

  glBegin(GL_QUAD_STRIP);          // Draw pointer shaft
    for (i = 0; i <= 23; i++)
    {
       glVertex3fv(shafttop[i]);
       glVertex3fv(shaftbottom[i]);
    }
    glVertex3fv(shafttop[0]);
    glVertex3fv(shaftbottom[0]);
  glEnd();
  
  glBegin(GL_TRIANGLE_FAN);        // Draw base of pointer shaft
    glVertex3f(0.0, 2.0, 0.0);
    for (i = 0; i <= 23; i++)
    {
       glVertex3fv(shaftbottom[i]);
    }
    glVertex3fv(shaftbottom[0]);
  glEnd();
}

void display( void )
{
   int i, j;
   int rand();

   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   // clear the window and z-buffer bit

   glPushMatrix();
   glTranslatef(N/2.0, 0.0, M/2.0); // center of rotation is center of cube
   glRotatef(angle, 0.0, 1.0, 0.0); // rotate model by angle around y-axis
   glTranslatef(-N/2.0, 0.0, -M/2.0);

   for (i = N-1; i >= 0; i--)
      for (j = 0; j < M; j++)
      {
         glColor3f((i+j)%10/10.0, (i+2*j)%10/10.0, 0.0);
         // different color for each box
         
         glPushMatrix();
         glTranslatef(i, 0.0, j);
         // move box to correct location (with grid centered at the origin)
         glScalef(0.9, grid[i][j], 0.9);
         // scale length and width for space and to scale height.
         cube();       // draw cube
         glPopMatrix();
      }

   glPushMatrix();
   glColor3f(1.0, 1.0, 1.0);         //  draw a white pointer
   glTranslatef(maxX + 0.45, grid[maxX][maxZ], maxZ + 0.45);
   // move arrow to correct location
   
   glRotatef(45, 1.0, 0.0, -1.0);
   // rotate arrow to point along the line x = y = z
   glScalef(0.5, 0.5, 0.5);  // scale arrow down to half size
   pointer();                // draw arrow
   glPopMatrix();

   glPopMatrix();  // undo last rotation you set

   glFlush();
   glutSwapBuffers();
}

void reshape(int w, int h)
{
   glViewport(0, 0, (GLsizei)w, (GLsizei)h);
   
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluPerspective(100.0, 1.0, 1.0, 3000.0);
   
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   //           eye point      center of view        up  
     gluLookAt(-4.0, 4.0, 4.0, N/2.0, 1.5, M/2.0, 0.0, 1.0, 0.0);
   // view is from outside the grid along the line x = -y = -z
}

void animate(void)
{
   angle  +=  3;        // update angle for rotation in display
   glutPostRedisplay(); // perform display again
}

void main(int argc, char** argv)
{
   // Standard GLUT initialization
   glutInit(&argc,argv);
   glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
   // Double buffering, RGB color, z-buffer set
   
   glutInitWindowSize(500,500);   // 500 x 500 pixel window
   glutInitWindowPosition(50,50); // window near top left of display
   glutCreateWindow("Project 1 - 3D Bars"); // window title
   
   glutDisplayFunc(display); // display callback when window opened
   glutReshapeFunc(reshape); // viewing the displayed object
   glutIdleFunc(animate);    // do this when other activity absent
   
   myinit();                 // set attributes
   
   glutMainLoop();           // enter event loop
}
