#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "glaux.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <time.h>

double xspin=0,yspin=0,zspin=0,xd=0,yd=0,zd=0,speed=0,walkdip=1.5;
int count=0;

const xmax=40; const xlbound=-99*2; const xhbound=0;
const zmax=40; const zlbound=0;   const zhbound=99*2;

const scale=5;

double elev[xmax][zmax];

static GLubyte checkImage[64][64][4];
static GLuint texName;

int randint(int i)                           //Credit given to AMCCOY
  { return int(i*((double)rand()/(double)RAND_MAX)); }


void makeCheckImage()
  { int i,j,c;
    for(i=0;i<64;i++)
      { for(j=0;j<64;j++)
         { if((i==0)||(i==63)||(j==0)||(j==63))
             { checkImage[i][j][0]=255;
               checkImage[i][j][1]=255;
               checkImage[i][j][2]=0;
               checkImage[i][j][3]=255;
             }
           else
             { checkImage[i][j][0]=randint(100);
               checkImage[i][j][1]=randint(255);
               checkImage[i][j][2]=randint(100);
               checkImage[i][j][3]=255;
             }
         }
      }
  }

static void Init()
  { glClearColor(0.0,0.0,0.0,1.0);
    glShadeModel(GL_FLAT);
    makeCheckImage();
    glPixelStorei(GL_UNPACK_ALIGNMENT,1);  
    glGenTextures(1,&texName);
    glBindTexture(GL_TEXTURE_2D,texName);
    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_MAG_FILTER,GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,64,64,0,GL_RGBA,GL_UNSIGNED_BYTE,checkImage);
    glEnable(GL_DEPTH_TEST);
  }

static void Reshape(int w,int h)
  { glViewport(0,0,(GLint)w,(GLint)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0,(GLfloat)w/(GLfloat)h,1,500);
    glMatrixMode(GL_MODELVIEW);
    gluLookAt(0,5,0,100,1,0,0,1,0);
  }

int round(double n)
  { if (n-(int)n<0.5) return (int)n; else return (int)n+1; }


void Updateyd()
  { if ((xd<=xhbound)&&(xd>=xlbound)&&(zd>=zlbound)&&(zd<=zhbound))
      yd=elev[round(-xd)/scale][round(zd)/scale]+walkdip;
    else yd=walkdip;
  }

static void KPLeftArrow()
  { yspin-=5; 
  }

static void KPRightArrow()
  { yspin+=5;
  }

static void KPUpArrow()
  { if (xspin>(-85)) xspin-=5;
  }

static void KPDownArrow() 
  { if (xspin<85) xspin+=5;
  }

static void MoveForward()    
  { xd+=sin(yspin*M_PI/180)*speed; 
    zd+=cos(yspin*M_PI/180)*speed;
  }

static void KP1Button() { speed=0; }
static void KP2Button() { speed=1; }
static void KP3Button() { speed=2; }
static void KP4Button() { speed=3; }
static void KP5Button() { speed=4; }
static void KP6Button() { speed=5; }
static void KP7Button() { speed=6; }
static void KP8Button() { speed=7; }
static void KP9Button() { speed=8; }
static void KP0Button() { speed=9; }
static void KPBackSpace() { speed*=(-1); }

void DrawingRoutine()
  { int x,z;
     glEnable(GL_TEXTURE_2D);
     glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_DECAL);
     glBindTexture(GL_TEXTURE_2D,texName);
     for(z=0;z<zmax-2;z+=2)
        { for(x=0;x<xmax-2;x+=2)
            { glBegin(GL_POLYGON);
              glTexCoord2f(0.0,0.0);
              glVertex3f(x*scale,elev[x][z],z*scale);
              glTexCoord2f(0.0,1.0);
              glVertex3f(x*scale,elev[x][z+2],(z+2)*scale);
              glTexCoord2f(1.0,1.0);
              glVertex3f((x+2)*scale,elev[x+2][z+2],(z+2)*scale);
              glTexCoord2f(1.0,0.0);
              glVertex3f((x+2)*scale,elev[x+2][z],z*scale);
              glEnd();
            }
        }
     glDisable(GL_TEXTURE_2D); 
  }

void Updatewalkdip()
  { if (speed==0) walkdip=0; 
     else 
       switch (count)
         { case 0: { walkdip=0; break; }
           case 1: { walkdip=-0.1*speed; break; }
           case 2: { walkdip=-0.2*speed; break; }
           case 3: { walkdip=-0.1*speed; break; }
           case 4: { walkdip=0;    break; }
           case 5: { walkdip=0.1*speed; break; }
           case 6: { walkdip=0.2*speed; break; }
           case 7: { walkdip=0.1*speed; break; }
         }
  }

static void display()
 {  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    count=(count+1)%8;
    Updatewalkdip();
    Updateyd();
    glPushMatrix();
    glRotatef(xspin,1,0,0);
    glRotatef(yspin,0,1,0);
    glRotatef(zspin,0,0,1);
    glTranslatef(xd,-yd,-zd);
    DrawingRoutine();
    glPopMatrix();
    glFlush();
    glXSwapBuffers(auxXDisplay(), auxXWindow());
  }

static void NoKP()
  { MoveForward();
    display();
  }

int main(int argc,char **argv)
  { time_t t;srand(time(&t)); //Randomization (also AMCCOY)
    int x,z;
    for(x=0;x<xmax;x++)
      { for(z=0;z<zmax;z++)
          { 
	    if ((x>xmax-xmax/4)&&(z>zmax-zmax/4)) elev[x][z]=50; 
            else 
               elev[x][z]=(-3*sqrt(x*z));
          }
      }
    

    auxInitDisplayMode(AUX_DOUBLE | AUX_RGBA);
    auxInitPosition(0,0,800,400);
    if (auxInitWindow("SC3.cc")==GL_FALSE) auxQuit();
    Init();
    auxIdleFunc(NoKP);
    auxExposeFunc(Reshape);
    auxReshapeFunc(Reshape);
    auxKeyFunc(AUX_UP,KPUpArrow);
    auxKeyFunc(AUX_DOWN,KPDownArrow);
    auxKeyFunc(AUX_LEFT,KPLeftArrow);
    auxKeyFunc(AUX_RIGHT,KPRightArrow);
    auxKeyFunc('1',KP1Button);
    auxKeyFunc('2',KP2Button);
    auxKeyFunc('3',KP3Button);
    auxKeyFunc('4',KP4Button);
    auxKeyFunc('5',KP5Button);
    auxKeyFunc('6',KP6Button);
    auxKeyFunc('7',KP7Button);
    auxKeyFunc('8',KP8Button);
    auxKeyFunc('9',KP9Button);
    auxKeyFunc('0',KP0Button);
    auxKeyFunc('b',KPBackSpace);
    auxMainLoop(display);
    return 0;
 }


