/***********************************************************************/
/*                                                                     */
/*  NAME     : Terry Fleury            PROGRAM : mp5                   */
/*  NET ID   : cs318ta1                COURSE  : CS318 - Spring 1999   */
/*  DUE DATE : November 19, 1999                                       */
/*  PURPOSE  : In this MP, we use the midpoint displacement method     */
/*             to generate a fractal mountain.  We first generate a    */
/*             square fractal terrain, and then conform this landscape */
/*             to a cone-shaped mountain.                              */
/*  INPUT    : Left Mouse Button - Controls the camera's movement      */
/*                around the mountain.  Click and hold to activate.    */
/*                If the mouse is moved Left/Right, we change the      */
/*                azimuth (rotation around the z-axis), and if moved   */
/*                Up/Down, we change the elevation from the z-axis.    */
/*             Right Mouse Button - Controls the camera's movement     */
/*                also.  Click and hold to activate.  If the mouse is  */
/*                moved Left/Right, we rotate about the vector from    */
/*                the camera to the origin, and if moved Up/Down, we   */
/*                move the camera closer to/farther from the mountain. */
/*             "q" - quit.  Exits the program.                         */
/*             "1" - "6" - change the level of detail.  The numbers    */
/*                1-6 indicate the number of subdivisions to perform   */
/*                when making the fractal grid, where 1 corresponds    */
/*                to 1 subdivision of the original square, resulting   */
/*                in a grid of 4 squares.                              */
/*             "c" - color.  (1-unit grad)  Toggles the color of the   */
/*                mountain from all green to brown/green/white.        */
/*             "m" - mountain.  (1-unit grad)  Toggles the display of  */
/*                 either the "cone-shaped" mountain or the "flat"     */
/*                 fractal landscape.  If in "landscape" mode, we      */
/*                 also draw a black outline around the grid squares.  */
/*             UpArrow/DownArrow - increase/decrease the H value.      */
/*                (1-unit grad)  By increasing/decreasing the H value  */
/*                (where H = 3-D and D is the fractal dimension), we   */
/*                can make the landscape/mountain smoother/bumpier.    */
/*                                                                     */
/***********************************************************************/

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

#define CONE    2.0         /* Height of the cone (at the tip) */
#define BASE    2.0         /* Length of one side of mountain grid base */
#define GRID    65          /* Max of 2^6+1 points per grid side */
#define MAX(A,B) ((A) > (B) ? (A) : (B))

/*********************/
/*  DATA STRUCTURES  */
/*********************/
/* Point holds the (X,Y,Z) position of a vertex */
typedef struct PointStruct { GLfloat X, Y, Z; } PointType;

/*************************/
/*  FUNCTION PROTOTYPES  */
/*************************/
void Initialize(void);
void SetCameraPosition(void);
void UpdateDisplay(void);
void CalculateMountain(void);
void DrawMountain(void);
void MouseButtonPressed(int,int,int,int);
void MouseMovement(int,int);
void ReshapeWindow(int,int);
void QuitProgram(void);

/**********************/
/*  GLOBAL VARIABLES  */
/**********************/
GLint Azimuth = 30;             /* Azimuth, Elevation, and Twist are used */
GLint Elevation = 60;           /*    to compute the camera position when */
GLint Twist = 0;                /*    viewing the objects in the scene.   */
float Focal = -11.0;            /* Distance from origin to camera */
int ColoredMountain = 0;        /* Multicolored mountain or 1 color only */
int MountainMode = 1;           /* Draw a mountain or a landscape? */
int LevelOfDetail = 1;          /* Detail for our fractal mountain */
float HValue = 0.6;             /* H = 3 - D, where D is fractal dimension */
PointType Grid[GRID][GRID];     /* Array to hold grid points for landscape */
int LeftButtonDown = 0;         /* Boolean if the Left Button is down */
int RightButtonDown = 0;        /* Boolean if the Right Button is down */
int MouseButtonDown = -1;       /* Which button is down? (Left or Right) */

/***********************************************************************/
/*  Initialize is called at program invocation and sets up the main    */
/*  window.  It also intitializes the lighting for the scene.          */
/***********************************************************************/

void Initialize(void)
{
  /* Variables for the lighting model */
  GLfloat LAmbient[] = {0.2,0.2,0.2,1.0};
  GLfloat LDiffuse[] = {1.0,1.0,1.0,1.0};
  GLfloat LPosition[] = {5.0,-9.0,20.0,1.0};
  GLfloat LModelAmbient[] = {0.2,0.2,0.2,1.0};

  /* Initialize the window and color mode */
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
  glutInitWindowSize(600,600);
  glutCreateWindow("CS318 MP5 - Solution");

  /* Initialize lights and lighting properties */
  glLightfv(GL_LIGHT0,GL_AMBIENT,LAmbient);
  glLightfv(GL_LIGHT0,GL_DIFFUSE,LDiffuse);
  glLightfv(GL_LIGHT0,GL_POSITION,LPosition);
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT,LModelAmbient);
  glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,1);

  glEnable(GL_LIGHT0);
  glEnable(GL_LIGHTING);           /* Turn on the light */
  glEnable(GL_DEPTH_TEST);         /* Use the depth buffer */
  glEnable(GL_COLOR_MATERIAL);     /* Allows us to use glColor for surfaces */
  glShadeModel(GL_FLAT);           /* Use flat shading - faster */

  glClearColor(0.0,0.0,0.0,0.0);   /* Background is black */
 
  SetCameraPosition();
  
  srand(time(NULL));               /* Set the random seed */
  CalculateMountain();             /* Calculate the first Mountain */
}

/***********************************************************************/
/*  SetCameraPosition is called by several procedures to move the      */
/*  camera to the correct viewing position, and then set GL_MODELVIEW  */
/*  to allow drawing of objects on the screen.                         */
/***********************************************************************/

void SetCameraPosition(void)
{
  /* Set the viewing transformation */
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(15.0,1.0,1.0,100.0);

  glTranslated(0.0,0.0,Focal);    /* Move camera down the negative z-axis */
  glRotated(-Twist,0.0,0.0,1.0);  /* Do a polar projection for camera */
  glRotated(-Elevation,1.0,0.0,0.0);
  glRotated(Azimuth,0.0,0.0,1.0);
  glTranslated(0.0,0.0,-0.8);     /* Center camera about mountain */

  glMatrixMode(GL_MODELVIEW);
}

/***********************************************************************/
/*  UpdateDisplay is called by the main event loop to refresh the      */
/*  contents of the display and to show the mountain.                  */
/***********************************************************************/

void UpdateDisplay(void)
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  SetCameraPosition();

  DrawMountain();

  glutSwapBuffers();
}

/***********************************************************************/
/*  RandomGaussian returns a random number from a Gaussian             */
/*  distribution with the given "mean" and "stddev".  This algorithm   */
/*  was adapted from "Numerical Recipies in C" in the section on       */
/*  "Normal Deviates".                                                 */
/***********************************************************************/

float RandomGaussian(float mean, float stddev)
{
  float factor;
  float radiussq;                    
  float value1, value2;

  do
    {
      value1 = 2.0 * (float)rand() / RAND_MAX - 1.0;
      value2 = 2.0 * (float)rand() / RAND_MAX - 1.0;
      radiussq = value1 * value1 + value2 * value2;
    } while ((radiussq >= 1.0) || ((radiussq - 0.000001) < 0.0));

  factor = sqrt(-2.0 * log(radiussq) / radiussq);

  return value1 * factor * stddev + mean;
}

/***********************************************************************/
/*  ActualHeight takes in a point (X,Y) plane and returns the height   */
/*  of the control surface of the cone at that point.                  */
/***********************************************************************/

float ActualHeight(float X, float Y)
{
  float Radius;

  Radius = sqrt(X*X + Y*Y);

  if (Radius > 1.0)
    return 0.0;
  else
    return CONE * (1.0 - Radius);
}

/***********************************************************************/
/*  ConstrainToCone takes the fractal landscape and makes it conform   */
/*  to a cone-shaped mountain.  It does this by getting the difference */
/*  of the actual height (of the cone) with the height of the          */
/*  landscape at each grid point.  This value is used to generate a    */
/*  random number that is added to the current Z value.                */
/***********************************************************************/

void ConstrainToCone(void)
{
  int i, j;
  int Step;             /* Which grid points do we need to check */
  float Mean, StdDev;

  Step = (GRID-1) / (int)pow(2,LevelOfDetail);

  for (i = 0; i < GRID; i += Step)
    for (j = 0; j < GRID; j += Step)
      {
        Mean = ActualHeight(Grid[i][j].X,Grid[i][j].Y) - Grid[i][j].Z;
        StdDev = 0.3 * fabs(Mean) / (HValue + 1);
        Grid[i][j].Z += RandomGaussian(Mean,StdDev);
      }
}

/***********************************************************************/
/*  Mid takes in a Value (which probably corresponds to the average    */
/*  of two Z values) and a Standard Deviation, and returns a random    */
/*  number based on the Value and StdDev.                              */
/***********************************************************************/

float Mid(float Value, float StdDev)
{
  return Value + RandomGaussian(0.0,StdDev);
}

/***********************************************************************/
/*  CalculateMountain is called when the program first starts or when  */
/*  the user hits a number to designate a new level of detail.  It     */
/*  first initializes the global array Grid, sets the four corners to  */
/*  be the base of the mountain, and then subdivides the square,       */
/*  generating random z-values for the sides and middle of each new    */
/*  square.  This generates our fractal landscape.  As a final step,   */
/*  ConstrainToCone is called to make it a mountain.                   */
/***********************************************************************/

void CalculateMountain(void)
{
  int i, j;
  int Level;       /* Current Level of Subdivision */
  int Step;        /* Current step size for nested "for" loops */
  int Half;        /* Half of current step size */
  float StdDev;    /* Standard Deviation for current iteration */

  /* Initialize the Grid to all zeros */
  for (i = 0; i < GRID; i++)
    for (j = 0; j < GRID; j++)
      {
        Grid[i][j].X = 0.0;
        Grid[i][j].Y = 0.0;
        Grid[i][j].Z = 0.0;
      }

  /* Set the initial four corners of the grid landscape */
  /* Upper Left Corner (-1,1) */
  Grid[0][0].X = -BASE / 2;
  Grid[0][0].Y = BASE / 2;

  /* Upper Right Corner (1,1) */
  Grid[0][GRID-1].X = BASE / 2;
  Grid[0][GRID-1].Y = BASE / 2;

  /* Lower Left Corner (-1,-1) */
  Grid[GRID-1][0].X = -BASE / 2;
  Grid[GRID-1][0].Y = -BASE / 2;

  /* Lower Right Corner (1,-1) */
  Grid[GRID-1][GRID-1].X = BASE / 2;
  Grid[GRID-1][GRID-1].Y = -BASE / 2;

  Step = GRID - 1;
  Half = Step / 2;
  StdDev = 0.3;

  for (Level = 1; Level <= LevelOfDetail; Level++)
    {
      /* Set the Standard Deviation for this level of detail.  This */
      /* value gets progressively smaller as the detail gets finer. */
      StdDev *= pow(0.5,0.5*HValue);

      for (i = Half; i < GRID-1; i += Step)
        for (j = Half; j < GRID-1; j += Step)
          {
            /* Set the Top Row only for the first row */
            if (i == Half)
              {
                Grid[0][j].X = (Grid[0][j-Half].X + Grid[0][j+Half].X) / 2.0;
                Grid[0][j].Y = (Grid[0][j-Half].Y + Grid[0][j+Half].Y) / 2.0;
                Grid[0][j].Z = Mid((Grid[0][j-Half].Z + Grid[0][j+Half].Z)/2.0,
                                 StdDev);
              }

            /* Set the Left Row only for the first column */
            if (j == Half)
              {
                Grid[i][0].X = (Grid[i-Half][0].X + Grid[i+Half][0].X) / 2.0;
                Grid[i][0].Y = (Grid[i-Half][0].Y + Grid[i+Half][0].Y) / 2.0;
                Grid[i][0].Z = Mid((Grid[i-Half][0].Z + Grid[i+Half][0].Z)/2.0,
                                 StdDev);
              }

            /* Set the Bottom Row */
            Grid[i+Half][j].X = (Grid[i+Half][j-Half].X + 
                                 Grid[i+Half][j+Half].X) / 2.0;
            Grid[i+Half][j].Y = (Grid[i+Half][j-Half].Y + 
                                 Grid[i+Half][j+Half].Y) / 2.0;
            Grid[i+Half][j].Z = Mid((Grid[i+Half][j-Half].Z + 
                                     Grid[i+Half][j+Half].Z) / 2.0, StdDev);


            /* Set the Right Row */
            Grid[i][j+Half].X = (Grid[i-Half][j+Half].X +
                                 Grid[i+Half][j+Half].X) / 2.0;
            Grid[i][j+Half].Y = (Grid[i-Half][j+Half].Y +
                                 Grid[i+Half][j+Half].Y) / 2.0;
            Grid[i][j+Half].Z = Mid((Grid[i-Half][j+Half].Z +
                                     Grid[i+Half][j+Half].Z) / 2.0, StdDev);
              
            /* Finally, set the middle of the square */
            Grid[i][j].X = (Grid[i-Half][j].X + Grid[i+Half][j].X) / 2.0;
            Grid[i][j].Y = (Grid[i-Half][j].Y + Grid[i+Half][j].Y) / 2.0;
            Grid[i][j].Z = Mid((Grid[i-Half][j-Half].Z +
                                Grid[i-Half][j+Half].Z +
                                Grid[i+Half][j-Half].Z +
                                Grid[i+Half][j+Half].Z) / 4.0, StdDev);
          }
      Step /= 2;  /* Update Step and Half for the next subdivision */
      Half /= 2;
    }

  /* Finally, turn the landscape into a mountain. */
  if (MountainMode)
    ConstrainToCone();
}

/***********************************************************************/
/*  Max4 returns the maximum of 4 float numbers.                       */
/***********************************************************************/

float Max4(float A, float B, float C, float D)
{
  float Max1, Max2, Max3;

  Max1 = MAX(A,B);
  Max2 = MAX(C,D);
  Max3 = MAX(Max1,Max2);
  return Max3;
}

/***********************************************************************/
/*  NormalVector takes in three vertices that define two consecutive   */
/*  edges of a polygon and returns a normalized normal vector.         */
/***********************************************************************/

PointType NormVector(PointType P1, PointType P2, PointType P3)
{
  PointType Norm, V1, V2;
  float vlen;

  /* Calculate the two edge vectors */
  V1.X = P2.X - P1.X;
  V1.Y = P2.Y - P1.Y;
  V1.Z = P2.Z - P1.Z;
  V2.X = P3.X - P2.X;
  V2.Y = P3.Y - P2.Y;
  V2.Z = P3.Z - P2.Z;
  
  /* Calculate the cross product */
  Norm.X = V1.Y*V2.Z - V1.Z*V2.Y;
  Norm.Y = V1.Z*V2.X - V1.X*V2.Z;
  Norm.Z = V1.X*V2.Y - V1.Y*V2.X;

  /* Normalize the resulting normal vector */
  vlen = sqrt(Norm.X*Norm.X + Norm.Y*Norm.Y + Norm.Z*Norm.Z);
  Norm.X /= vlen;
  Norm.Y /= vlen;
  Norm.Z /= vlen;

  return Norm;
}

/***********************************************************************/
/*  DrawSquare is called by DrawMountain to draw each indiviual square */
/*  of the fractal mountain.  It takes in four points (specified in    */
/*  counterclockwise order), subdivides it into two triangles, sets    */
/*  the normal vector for each triangle, and draws the triangles.      */
/***********************************************************************/

void DrawSquare(PointType P1, PointType P2,
                PointType P3, PointType P4)
{
  PointType Norm;

  /* Draw one triangle of a square */
  Norm = NormVector(P1,P2,P3);
  glBegin(GL_POLYGON);
    glNormal3f(Norm.X,Norm.Y,Norm.Z);
    glVertex3f(P1.X,P1.Y,P1.Z);
    glVertex3f(P2.X,P2.Y,P2.Z);
    glVertex3f(P3.X,P3.Y,P3.Z);
  glEnd();

  /* Draw the other triangle of a square */
  Norm = NormVector(P3,P4,P1);
  glBegin(GL_POLYGON);
    glNormal3f(Norm.X,Norm.Y,Norm.Z);
    glVertex3f(P3.X,P3.Y,P3.Z);
    glVertex3f(P4.X,P4.Y,P4.Z);
    glVertex3f(P1.X,P1.Y,P1.Z);
  glEnd();
}

/***********************************************************************/
/*  DrawMountain is called by UpdateDisplay to draw the various        */
/*  Squares that make up the mountain.  Since the four corners of a    */
/*  square may not be coplanar, I divide the squares up into triangles */
/*  and plot these triangles instead.  Also, I draw the squares with   */
/*  a black outline if in "landscape" mode to better see the squares.  */
/***********************************************************************/

void DrawMountain(void)
{
  int i, j;
  int Step;             /* Which grid points do we need to check */
  float MaxVal;         /* Maximum z-value of four corners */

  Step = (GRID-1) / (int)pow(2,LevelOfDetail);

  for (i = 0; i < GRID-1; i += Step)
    for (j = 0; j < GRID-1; j += Step)
      {
        if (ColoredMountain)
          {
            MaxVal = Max4(Grid[i][j].Z,Grid[i][j+Step].Z,
                          Grid[i+Step][j].Z,Grid[i+Step][j+Step].Z);
            if (MaxVal < CONE * 0.25)
              glColor3f(0.6,0.5,0.3);   /* Brown */
            else if (MaxVal > CONE * 0.85)
              glColor3f(1.0,1.0,1.0);   /* White */
            else
              glColor3f(0.1,0.8,0.1);   /* Green */
          }
        else
          glColor3f(0.1,0.8,0.1);   /* Green */
        
        DrawSquare(Grid[i][j],Grid[i+Step][j],
                   Grid[i+Step][j+Step],Grid[i][j+Step]);

        if (!MountainMode)
          {
            /* Draw each grid square with a black outline */
            glColor3f(0.1,0.1,0.1);   /* Black */

            glBegin(GL_LINE_LOOP);
              glVertex3f(Grid[i][j].X,Grid[i][j].Y,Grid[i][j].Z);
              glVertex3f(Grid[i+Step][j].X,Grid[i+Step][j].Y,Grid[i+Step][j].Z);
              glVertex3f(Grid[i+Step][j+Step].X,Grid[i+Step][j+Step].Y,
                         Grid[i+Step][j+Step].Z);
              glVertex3f(Grid[i][j+Step].X,Grid[i][j+Step].Y,Grid[i][j+Step].Z);
            glEnd();
          }
      }
}

/***********************************************************************/
/*  MouseButtonPressed is called when a Mouse Button is pressed or     */
/*  released.  It updates the value of MouseButtonDown, which is used  */
/*  by the procedure MouseMovement to set the camera position.  This   */
/*  procedure is necessary so that at most 1 mouse button (Left or     */
/*  Right) is active at any given time.  Otherwise, the camera would   */
/*  go wacky!                                                          */
/***********************************************************************/

void MouseButtonPressed(int Button, int State, int X, int Y)
{
  if (State == GLUT_DOWN)
    {
      if (Button == GLUT_LEFT_BUTTON)
        {
          LeftButtonDown = 1;
          if (!RightButtonDown)
            MouseButtonDown = GLUT_LEFT_BUTTON;
        }
      else if (Button == GLUT_RIGHT_BUTTON)
        {
          RightButtonDown = 1;
          if (!LeftButtonDown)
            MouseButtonDown = GLUT_RIGHT_BUTTON;
        }
    }
  else if (State == GLUT_UP)
    {
      MouseButtonDown = -1;
      if (Button == GLUT_LEFT_BUTTON)
        {
          LeftButtonDown = 0;
          if (RightButtonDown)
            MouseButtonDown = GLUT_RIGHT_BUTTON;
        }
      else if (Button == GLUT_RIGHT_BUTTON)
        {
          RightButtonDown = 0;
          if (LeftButtonDown)
            MouseButtonDown = GLUT_LEFT_BUTTON;
        }
    }
}

/***********************************************************************/
/*  MouseMovement is called when the user moves the mouse in the       */
/*  window and a mouse button is down.  It updates the position of     */
/*  the camera.  It checks the values LastX and LastY to see if the    */
/*  mouse has moved since the last call.  If so, it modifies the       */
/*  values Azimuth and Elevation (if the Left Mouse Button was down),  */
/*  or the Twist and Focal Point (if the Right Mouse Button was down). */
/***********************************************************************/

void MouseMovement(int X, int Y)
{
  static int LastX = 0;  /* Holds the old X coord of mouse */
  static int LastY = 0;  /* Holds the old Y coord of mouse */

  if (MouseButtonDown == GLUT_LEFT_BUTTON)
    {
      if (X > LastX)
        Azimuth = (Azimuth - 3) % 360;
      else if (X < LastX)
        Azimuth = (Azimuth + 3) % 360;

      if (Y > LastY)
        Elevation = (Elevation + 3) % 360;
      else if (Y < LastY)
        Elevation = (Elevation - 3) % 360;
    }
  else if (MouseButtonDown == GLUT_RIGHT_BUTTON)
    {
      if (X > LastX)
        Twist = (Twist + 3) % 360;
      else if (X < LastX)
        Twist = (Twist - 3) % 360;

      if (Y > LastY)
        {
          if (Focal > -16.0)
            Focal = Focal - 0.5;
        }
      else if (Y < LastY)
        {
          if (Focal < -6.0)
            Focal = Focal + 0.5;
        }
    }

  LastX = X;
  LastY = Y;
  glutPostRedisplay();
}

/***********************************************************************/
/*  ReshapeWindow is called if the main window needs to be resized.    */
/***********************************************************************/

void ReshapeWindow(int Width, int Height)
{
  SetCameraPosition();
  glViewport(0,0,Width,Height);
}

/***********************************************************************/
/*  ToggleColoredMountain is called when the user presses "c" to       */
/*  change the color(s) of the mountain.                               */
/***********************************************************************/

void ToggleColoredMountain(void)
{
  ColoredMountain = !ColoredMountain;
}

/***********************************************************************/
/*  ToggleMountainMode is called when the user presses "m" to draw     */
/*  either the mountain, or just the fractal landscape.  It needs to   */
/*  call CalculateMountain to recompute z-values.                      */
/***********************************************************************/

void ToggleMountainMode(void)
{
  MountainMode = !MountainMode;
  CalculateMountain();
}

/***********************************************************************/
/*  ChangeDetail is called when the user presses "1" - "6" to change   */
/*  the level of detail (recursion) for the number of squares in the   */
/*  grid.                                                              */
/***********************************************************************/

void ChangeDetail(int Level)
{
  if (LevelOfDetail != Level)
    {
      LevelOfDetail = Level;
      CalculateMountain();
    }
}

/***********************************************************************/
/*  ChangeHValue is called when the user presses the UpArrow/DownArrow */
/*  to increase/decrease the H value (H = 3-D), which makes the        */
/*  fractal landscape smoother/rougher.                                */
/***********************************************************************/

void ChangeHValue(float Increment)
{
  float NewHValue;

  NewHValue = HValue + Increment;
  if ((NewHValue > 0.01) && (NewHValue < 1.99))
    {
      HValue = NewHValue;
      CalculateMountain();
    }
}

/***********************************************************************/
/*  SpecialKeyPressed is called when the user presses a "special" key  */
/*  on the keyboard, such as an arrow key.                             */
/***********************************************************************/

void SpecialKeyPressed(int Key, int X, int Y)
{
  switch (Key)
    {
      case GLUT_KEY_UP   : ChangeHValue(0.2);  break;
      case GLUT_KEY_DOWN : ChangeHValue(-0.2); break;
    }
  glutPostRedisplay();
}

/***********************************************************************/
/*  KeyPressed is called when the user presses a key on the keyboard.  */
/*  It calls the appropriate procedure based on the input.             */
/***********************************************************************/

void KeyPressed(unsigned char Key, int X, int Y)
{
  switch (toupper(Key))
    {
      case 'Q' : QuitProgram(); break;
      case 'C' : ToggleColoredMountain(); break;
      case 'M' : ToggleMountainMode(); break;
      case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : 
        ChangeDetail(Key - '0'); break;
    }
  glutPostRedisplay();
}

/***********************************************************************/
/*  QuitProgram is called to exit gracefully.                          */
/***********************************************************************/

void QuitProgram(void)
{
  exit(0);
}

/***********************************************************************/
/*  PrintUsage is called at program startup to print out a short       */
/*  help message on how to use the program.                            */
/***********************************************************************/

void PrintUsage(void)
{
  printf("CS318 - MP5 - Fractal Mountain\n");
  printf("INPUTS:\n");
  printf("Left Mouse Button  - Click/Hold to Rotate Camera Azimuth/Elevation\n")
;
  printf("Right Mouse Button - Click/Hold to Zoom (Up/Down) or Twist (Left/Right)\n");
  printf("'1' to '6'         - Set Level of Detail / Subdivision\n");
  printf("Up and Down Arrows - Increase/Decrease the H Value (Roughness)\n");
  printf("'c' or 'C'         - Toggle Colored Mountain (1-unit only)\n");
  printf("'m' or 'M'         - Toggle Mountain / Landscape (1-unit only)\n");
  printf("'q' or 'Q'         - Quit Program\n");
}


/**************************/
/*   BEGIN MAIN PROGRAM   */
/**************************/

int main(int argc, char** argv)
{
  glutInit(&argc,argv);

  PrintUsage();

  Initialize();    /* Set up main window and variables */
  
  /* Set the Mouse / Keyboard / Reshape / Display callbacks */
  glutKeyboardFunc(KeyPressed);
  glutSpecialFunc(SpecialKeyPressed);
  glutMouseFunc(MouseButtonPressed);
  glutMotionFunc(MouseMovement);
  glutReshapeFunc(ReshapeWindow);
  glutDisplayFunc(UpdateDisplay);
  glutMainLoop();
}

/**************************/
/*    END MAIN PROGRAM    */
/**************************/


