//This tutorial is going to extend on the last heightmap tutorial
//The difference here is that I am now adding lighting to the
//terrain using a thing known as a normal. I have also added a little scale
//feature to the terrain, so you can set the spacing between the vertices.

//So lets get started shall we

//--------------------------------------------------
//First Section: Variables

//Now there are a few variables that I have added, some of them
//are explained in the lighting tutorials.

//This is the scale for the terrain, we can set
//the distance between the vertices with this. This can
//be used to create larger terrains.
//float scale = 1; 

//here i am creating a struct for our normals
//typedef struct
//{
//float x; //the x position of the current normal
//float y; //the y position of the current normal
//float z; //the z position of the current normal
//}NORMALS; //I have chosen to call them NORMALS - can be whatever you want

//NORMALS ab; //normal for length AB - can be whatever you want
//NORMALS ac; //normal for length AC - can be whatever you want

//NORMALS n; //our final/main normal

//here we set the materials for the terrain
//GLfloat whiteDiffuseMaterial[] = {1.0, 1.0, 1.0}; //set the diffuse material to white
//GLfloat greyAmbientMaterial[] = {0.25, 0.25, 0.25}; //set the shadows material to grey, the lower the number, the more intense the shadows
//GLfloat whiteSpecularMaterial[] = {1.0, 1.0, 1.0}; //set the specular material to white

//diffuse light color variables - 1,1,1 makes white
//GLfloat dlr = 1.0;
//GLfloat dlg = 1.0;
//GLfloat dlb = 1.0;

//ambient light color variables - 1,1,1 makes white
//GLfloat alr = 1.0;
//GLfloat alg = 1.0;
//GLfloat alb = 1.0;

//ligth position variables
//GLfloat lx = -40.0; the x position
//GLfloat ly = 100.0; the y position
//GLfloat lz = -30.0; the z position
//GLfloat lw = 0.0; //setting this to 0 sets the light as a diffuse light

//--------------------------------------------------
//Section 2: Calculating the normals

//here is our function to calculate the normals
//with this you supply:
//x1,y1,z1 - the points of the current vertex
//x2,y2,z2 - the points of the vertex to the right
//x3,y3,z3 - the points of the vertex to the left
//each within the current shape.
//void calculateNormals (float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3) {

//double d; //d will hold the distance of each vector normal

//get the length of the vector to the right
//ab.x = x2 - x1; 
//ab.y = y2 - y1;
//ab.z = z2 - z1;
			
//get the length of the vector to the left
//ac.x = x3 - x1;
//ac.y = y3 - y1;
//ac.z = z3 - z1;

//here we do our dot product
//this is something you learn about when going
//over vectors. a dot product is equal to 0, and
//is therefore perpendicular to the current vector.
//n.x = (ab.y * ac.z) - (ab.z * ac.y);
//n.y = (ab.z * ac.x) - (ab.x * ac.z);
//n.z = (ab.x * ac.y) - (ab.y * ac.x);
	
//here we get the distance of our normal vector
//d = sqrt((n.x*n.x) + (n.y*n.y) + (n.z*n.z));

//then we normalize it by dividing the current
//vector by its distance.
//n.x = n.x / d;
//n.y = n.y / d;
//n.z = n.z / d;
//}

//--------------------------------------------------
//Section 3: Displaying the Heightmap with the Normals

//To do this the first thing we have to do is add materials
//to the object as we are using lighting now and our
//color function doesn't manage lights.

//Now in here you will notice tha I call calculate
//normals for each vertex and then add a normal.

//The hardest part here is working our which
//number goes with which x,y and z. But once
//you work it out, I believe the results
//are pretty good. Not the best but
//alot better then without shading.

//void DisplayHeightMap (void) {
//float Height;
//set the materials for the heightmap
//glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, whiteDiffuseMaterial); 
//glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT,greyAmbientMaterial); 
//glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, whiteSpecularMaterial); 

//for (mapX = 1; mapX < MapWidth; mapX +=4){ 
//for (mapZ = 1; mapZ < MapHeight; mapZ+=4){ 
//float Height1 = HeightMap[mapX][mapZ];
//float Height2 = HeightMap[mapX][mapZ+4];
//float Height3 = HeightMap[mapX+4][mapZ];
//float Height4 = HeightMap[mapX+4][mapZ+4];

//	glBegin(GL_TRIANGLE_STRIP);
//	Height = HeightMap[mapX][mapZ];
//	glTexCoord2f(0,0); 
	//now we calculate our normals, we do this for each vertex
//	calculateNormals(mapX,Height1,mapZ,mapX+4,Height3,mapZ,mapX,Height2,mapZ+4);
	//then we add the normal
//	glNormal3f(n.x,n.y,n.z);
//	glVertex3f(mapX*scale,Height,mapZ*scale);
	
//	Height = HeightMap[mapX][mapZ+4];
//	glTexCoord2f(0,1); 
	//now we calculate our normals, we do this for each vertex
//	calculateNormals(mapX,Height2,mapZ+4,mapX,Height1,mapZ,mapX+4,Height4,mapZ+4);
	//then we add the normal
//	glNormal3f(n.x,n.y,n.z);
//	glVertex3f(mapX*scale,Height,(mapZ+4)*scale);

//	Height = HeightMap[mapX+4][mapZ];
//	glTexCoord2f(1,0); 
	//now we calculate our normals, we do this for each vertex
//	calculateNormals(mapX+4,Height3,mapZ,mapX+4,Height4,mapZ+4,mapX,Height1,mapZ);
	//then we add the normal
//	glNormal3f(n.x,n.y,n.z);
//	glVertex3f((mapX+4)*scale,Height,mapZ*scale);

//	Height = HeightMap[mapX+4][mapZ+4];
//	glTexCoord2f(1,1); 
	//now we calculate our normals, we do this for each vertex
//	calculateNormals(mapX+4,Height4,mapZ+4,mapX,Height2,mapZ+4,mapX+4,Height3,mapZ);
	//then we add the normal
//	glNormal3f(n.x,n.y,n.z);
//	glVertex3f((mapX+4)*scale,Height,(mapZ+4)*scale);
//	glEnd();
//}
//}
//}

//And if you can work that all out, you should have a little idea on how
//to use normals. if you are not sure of the math behind it. I will
//be writing a tutorial on vector math in the forum, or you
//can just google search it as normals are not just for opengl
//they are for all 3D programs.

#include <GL/gl.h>
#include <GL/glut.h>
#include <windows.h>
#include <stdio.h>
#include <math.h>
#include <iostream.h>

int mapX;
int mapY;
int mapZ;

const MapWidth = 384;
const MapHeight = 384;

float scale = 1; //the scale for the terrain *also new*

//here i am creating a struct for our normals
typedef struct
{
float x; //the x position of the current normal
float y; //the y position of the current normal
float z; //the z position of the current normal
}NORMALS; //I have chosen to call them NORMALS - can be whatever you want

NORMALS ab; //normal for length AB - can be whatever you want
NORMALS ac; //normal for length AC - can be whatever you want

NORMALS n; //our final/main normal

//here we set the materials for the terrain
GLfloat whiteDiffuseMaterial[] = {1.0, 1.0, 1.0}; //set the diffuse to white
GLfloat greyAmbientMaterial[] = {0.25, 0.25, 0.25}; //set the shadows to grey, the lower the number, the more intense the shadows
GLfloat whiteSpecularMaterial[] = {1.0, 1.0, 1.0}; //set the specular to white

//diffuse light color variables
GLfloat dlr = 1.0;
GLfloat dlg = 1.0;
GLfloat dlb = 1.0;

//ambient light color variables
GLfloat alr = 1.0;
GLfloat alg = 1.0;
GLfloat alb = 1.0;

//ligth position variables
GLfloat lx = -50.0;
GLfloat ly = 100.0;
GLfloat lz = -50.0;
GLfloat lw = 0.0; //setting this to 0 sets the light as a diffuse light

GLuint texture[10];

BYTE HeightMap[MapWidth][MapHeight];

GLuint LoadTextureRAW( const char * filename, int width, int height );
void FreeTexture( GLuint texture );

void LoadHeightMap (char* Filename, int Width, int Height) {

int smoothen;

FILE * File;

File = fopen( Filename, "rb" );

fread( HeightMap, 1, Width * Height * 3, File );

fclose( File );

for (smoothen = 0; smoothen < 3; smoothen++){
for (mapX = 1; mapX < Width; mapX ++){ 
for (mapZ = 1; mapZ < Height * 3; mapZ++){ 
mapY = HeightMap[mapX][mapZ];
mapY += HeightMap[mapX-1][mapZ];
mapY += HeightMap[mapX+1][mapZ];
mapY += HeightMap[mapX][mapZ-1];
mapY += HeightMap[mapX][mapZ+1];
mapY += HeightMap[mapX-1][mapZ-1];
mapY += HeightMap[mapX-1][mapZ+1];
mapY += HeightMap[mapX+1][mapZ-1];
mapY += HeightMap[mapX+1][mapZ+1];
mapY = mapY/9;
HeightMap[mapX][mapZ] = mapY ;
}
}
}
}

//here is our function to calculate the normals
//with this you supply:
//x1,y1,z1 - the points of the current vertex
//x2,y2,z2 - the points of the vertex to the right
//x3,y3,z3 - the points of the vertex to the left
//each within the current shape.
void calculateNormals (float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3) {

double d; //d will hold the distance of each vector normal

//get the length of the vector to the right
ab.x = x2 - x1; 
ab.y = y2 - y1;
ab.z = z2 - z1;
			
//get the length of the vector to the left
ac.x = x3 - x1;
ac.y = y3 - y1;
ac.z = z3 - z1;

//here we do our dot product
//this is something you learn about when going
//over vectors. a dot product is equal to 0, and
//is therefore perpendicular to the current vector.
n.x = (ab.y * ac.z) - (ab.z * ac.y);
n.y = (ab.z * ac.x) - (ab.x * ac.z);
n.z = (ab.x * ac.y) - (ab.y * ac.x);
	
//here we get the distance of our normal vector
d = sqrt((n.x*n.x) + (n.y*n.y) + (n.z*n.z));

//then we normalize it by dividing the current
//vector by its distance.
n.x = n.x / d;
n.y = n.y / d;
n.z = n.z / d;
}

void DisplayHeightMap (void) {
float Height;
//set the materials for the heightmap
glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, whiteDiffuseMaterial); 
glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT,greyAmbientMaterial); 
glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, whiteSpecularMaterial); 

for (mapX = 1; mapX < MapWidth; mapX +=4){ 
for (mapZ = 1; mapZ < MapHeight; mapZ+=4){ 
float Height1 = HeightMap[mapX][mapZ];
float Height2 = HeightMap[mapX][mapZ+4];
float Height3 = HeightMap[mapX+4][mapZ];
float Height4 = HeightMap[mapX+4][mapZ+4];

	glBegin(GL_TRIANGLE_STRIP);
	Height = HeightMap[mapX][mapZ];
	glTexCoord2f(0,0); 
	//now we calculate our normals, we do this for each vertex
	calculateNormals(mapX,Height1,mapZ,mapX+4,Height3,mapZ,mapX,Height2,mapZ+4);
	//then we add the normal
	glNormal3f(n.x,n.y,n.z);
	glVertex3f(mapX*scale,Height,mapZ*scale);
	
	Height = HeightMap[mapX][mapZ+4];
	glTexCoord2f(0,1); 
	//now we calculate our normals, we do this for each vertex
	calculateNormals(mapX,Height2,mapZ+4,mapX,Height1,mapZ,mapX+4,Height4,mapZ+4);
	//then we add the normal
	glNormal3f(n.x,n.y,n.z);
	glVertex3f(mapX*scale,Height,(mapZ+4)*scale);

	Height = HeightMap[mapX+4][mapZ];
	glTexCoord2f(1,0); 
	//now we calculate our normals, we do this for each vertex
	calculateNormals(mapX+4,Height3,mapZ,mapX+4,Height4,mapZ+4,mapX,Height1,mapZ);
	//then we add the normal
	glNormal3f(n.x,n.y,n.z);
	glVertex3f((mapX+4)*scale,Height,mapZ*scale);

	Height = HeightMap[mapX+4][mapZ+4];
	glTexCoord2f(1,1); 
	//now we calculate our normals, we do this for each vertex
	calculateNormals(mapX+4,Height4,mapZ+4,mapX,Height2,mapZ+4,mapX+4,Height3,mapZ);
	//then we add the normal
	glNormal3f(n.x,n.y,n.z);
	glVertex3f((mapX+4)*scale,Height,(mapZ+4)*scale);
	glEnd();
}
}
}

void enable (void) {
	glEnable(GL_DEPTH_TEST); //enable the depth testing
	glDepthFunc(GL_LEQUAL); //set the depth function
	glFrontFace(GL_CCW); //set which face is facing forward
	glCullFace(GL_BACK); //set the face to be culled
	glEnable(GL_CULL_FACE);	//enable culling to speed up the processing time
	glShadeModel (GL_SMOOTH); //set the shade model to smooth
	glEnable(GL_TEXTURE_2D); //enable texturing
	glEnable(GL_LIGHTING); //enable the lighting
	glEnable(GL_LIGHT0); //enable our diffuse light
	glEnable (GL_LIGHT1); //enable our ambient light
	glEnable(GL_NORMALIZE); //enable normalizing of normals, wont make a difference if disabled
}

void lights (void) {
	GLfloat DiffuseLight[] = {dlr, dlg, dlb}; //set DiffuseLight[] to the specified values
	GLfloat AmbientLight[] = {alr, alg, alb}; //set AmbientLight[] to the specified values
	glLightfv (GL_LIGHT0, GL_DIFFUSE, DiffuseLight); //change the light accordingly
	glLightfv (GL_LIGHT1, GL_AMBIENT, AmbientLight); //change the light accordingly
	GLfloat LightPosition[] = {lx, ly, lz, lw}; //set the LightPosition to the specified values
	glLightfv (GL_LIGHT0, GL_POSITION, LightPosition); //change the light accordingly
}

void display (void) {
	glClearColor (0.0,0.0,0.0,1.0);
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity(); 
	enable();
	lights(); //setup the lights
	glTranslatef(-50, -100, -50); //move to a position so we can see our terrain when rotated
	glRotatef(30, 1,0,0); //rotate to look down on the terrain
	glRotatef(150, 0,1,0); //rotate to put the terrain into view
	glBindTexture(GL_TEXTURE_2D, texture[0]); //bind our texture
	DisplayHeightMap(); //display our heightmap
	glutSwapBuffers();
}

void init (void) {
	texture[0] = LoadTextureRAW("texture.raw", 256, 256);
	LoadHeightMap ("height.raw", 128, 128);
}

void reshape (int w, int h) {
	glViewport (0, 0, (GLsizei)w, (GLsizei)h);
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity ();
	gluPerspective (60, (GLfloat)w / (GLfloat)h, 0.01, 500.0);
	glMatrixMode (GL_MODELVIEW);
}

int main (int argc, char **argv) {
    glutInit (&argc, argv);
	glutInitDisplayMode (GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA);
	glutInitWindowSize (500, 500); 
	glutInitWindowPosition (100, 100);
	glutCreateWindow ("A basic OpenGL Window");

	init();

    glutDisplayFunc (display);
	glutIdleFunc (display);
	glutReshapeFunc (reshape);

	glutMainLoop ();

	return 0;
}

GLuint LoadTextureRAW( const char * filename, int width, int height )
{
  GLuint texture[1];
  unsigned char * data;
  FILE * file;

  file = fopen( filename, "rb" );
  if ( file == NULL ) return 0;

  data = (unsigned char *)malloc( width * height * 3 );

  fread( data, width * height * 3, 1, file );
  fclose( file );

  glGenTextures(1, &texture[0] );			

  glBindTexture(GL_TEXTURE_2D, texture[0]);

  glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );

  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

  gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, GL_RGB, GL_UNSIGNED_BYTE, data);

  free( data );

  return texture[0];

}

void FreeTexture( GLuint texture )
{

  glDeleteTextures( 1, &texture );

}