                                                     
#include <stdio.h>
#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#define  DIM  100                      // Number of lines on each side 
double altitude[DIM][DIM];            // Array of heights for each vertex
double spin = 0.0,                    // Amount of spin
       z = 0.0,                       // Zoom Factor
       maxAlt = 0.0,
       minAlt = 0.0,
       red = 1.0, green = 1.0, blue = 1.0;                 // RGB Components


void setAltitude() {                                 // Initializes heights
  int a, b;
  for( a = 0; a < DIM; a++) 
    for( b = 0; b < DIM; b++) {

       // Use some ugly math equation to generate a height value
       altitude[a][b]= 16*sin(a/8.0)*cos(b/8.0);

       // Figure out what the extremes in altitude might be
       maxAlt = maxAlt > altitude[a][b] ? maxAlt : altitude[a][b];
       minAlt = minAlt < altitude[a][b] ? minAlt : altitude[a][b];
    }
}

void pickColor(int a, int b ) {        // Function to determine drawing color
  double temp, range;
  range = maxAlt - minAlt;
  temp = altitude[a][b];

  // Some kind of bizarre function to pick RGB values
  green = 0.2 + fabs(temp *2/ range);
  red = 1 + (temp*2/range);
  blue = 0.2 + cos(temp*2/range);
}

void Init() {                                 // Initialization routine
  glClearColor(0.5, 0.5, 0.5, 0.0);
  glShadeModel(GL_FLAT);
}

void Reshape(int width, int height) {     //  Determines Projection type
  glViewport(0, 0, width, height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  // Draw in Perspective rather than Orthographic Projection
  // Args: (Field_of_View, Aspect_Ratio, Near_Clipping_Plane, Far_Clipping_Plane)
  gluPerspective(60.0, (float)width / (float)height, 1.00, 2*DIM);         
  glMatrixMode(GL_MODELVIEW);
}
                                                   // Defined Keys:


void keypressed(int key, int x, int y)  // Uses Special Function Keys
 { if (key == GLUT_KEY_LEFT)
     { spin -= 5; }               // Rotate left 5 degrees
   if (key ==  GLUT_KEY_RIGHT)
     { spin += 5; }               // Rotate right 5 degrees
   if (key == GLUT_KEY_UP )
     { z += 5; }                  // Move object away 
   if (key == GLUT_KEY_DOWN)
     { z -= 5; }                  // Move closer

   glutPostRedisplay();           // Update the Display
  }


drawMyStuff() {// Basic Drawing Routine
  int a, b;
  glLoadIdentity();                              
  // Specify View Position - (where you are looking.)
  // Args:  ( Eye_X, Eye_Y, Eye_Z, Cntr_X, Cntr_Y, Cntr_Z, Up_X, Up_Y, UpZ)
  gluLookAt(0.0, DIM/4.0, DIM/2.0, 0.0, 0.0, -1.0, 0.0, 1.0, 0.0);         

  glTranslatef(0.0, 0.0, (-DIM/2-z) );   // Zoom In and Out 
  glRotatef(spin, 0.0, 1.0, 0.0);            // Rotate Basic Surface around Y

  // Comple Motion starts here
  glPushMatrix();                    // Establish a new reference point 
    glTranslatef(0.0, -35.0, 0.0);   // Translate -35 in y-direction
    glColor3f(1.0, 1.0, 0.0);        // Change Drawing color to Yellow
    glutWireCube(8.0);               // Draw a wire frame Cube

    glRotatef(-spin*2, 0.0, 1.0, 0.0);  // Rotate axis in opposite direction 
    glPushMatrix();                     // Establish yet another reference point
      glColor3f(1.0, 0.0, 0.0);         // Change color to Red
      glTranslatef(20.0, 0.0, 0.0);     // Translate 20 units in x-direction
      glutWireSphere(5.0, 8, 6);        // Draw Wire Sphere with 5.0 radius
    glPopMatrix();                      // Return to previous reference

  glPopMatrix();                     // Return to first point of reference
  // End of complex motion

  for( a=0; a<DIM; a++) {
    glBegin(GL_LINE_STRIP);                  // Draw Segments in one direction
    for( b=0; b<DIM; b++) {
       pickColor(a,b);                       // Change RGB Values
       glColor3f(red, green, blue);          // Choose New Color
       
       glVertex3f((a - DIM/2 ), altitude[a][b], (b - DIM/2)); // Draw segment 
    }
    glEnd();
  }
  for( b=0; b<DIM; b++) {
    glBegin(GL_LINE_STRIP);              // Draw Segments in other direction
    for( a = 0; a < DIM; a++) {
       pickColor(a,b);                       // Change RGB Values
       glColor3f(red, green, blue);          // Choose New Color
       glVertex3f((a - DIM/2 ), altitude[a][b], (b - DIM/2)); // Draw segment

    }
    glEnd();
  }
}   

void display() {
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );  // Color and Depth
  drawMyStuff();
  glFlush();
  glutSwapBuffers();  // Double Buffering allows for smooth animation
}

main(int argc, char **argv) {
  setAltitude();                       // Initialize heights
  glutInit(&argc, argv);
  glEnable(GL_DEPTH_TEST);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH ); // Initialize modes
  glutInitWindowSize( 550, 550);
  glutInitWindowPosition( 50, 50);
  glutCreateWindow( "Cool Equations"); 
  glutSpecialFunc(keypressed);
  glutDisplayFunc(display);
  glutReshapeFunc(Reshape);
  glutMainLoop();
  return 0;

}
