/*
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.
*/

#include "glut.h"
#include <stdlib.h>
#include <math.h>
#include <time.h>

#define PI  3.14159
#define N 5
#define M 6

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();
   GLfloat mat_specular[] = {0.5, 0.5, 0.5, 1.0};
   GLfloat mat_diffuse[] = {0.5, 0.5, 0.5, 1.0};
   GLfloat mat_ambient[] = {0.5, 0.5, 0.5, 1.0};
   GLfloat mat_shininess = {50.0};
   GLfloat light_ambient[] = {0.5, 0.5, 0.5, 1.0};
   GLfloat light_diffuse[] = {0.5, 0.5, 0.5, 1.0};
   GLfloat light_specular[] = {0.5, 0.5, 0.5, 1.0};
   GLfloat position[] = {7.0, 7.0, 7.0, 0.0};


   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; 
         }
      }

     /* attributes */

   glClearColor(0.4, 0.4, 1.0, 1.0);     /* blue background */

   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);

   glEnable(GL_SMOOTH);   /* enable smooth shading */
   glEnable(GL_DEPTH_TEST);  /* enable z buffer for hidden surface removal */
}

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);
/*     gluLookAt(-4.0, 4.0, 4.0, 0, 0, 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 */
}

