//*****************************************************************************************
// Disclaimer, Authorship and License
/*
	This code has been written to serve as a learning tool for the course MAE 574, 
	Virtual Reality Applications and Research, Spring 2009.
	
	Large parts of the code has been adapted from the examples given in the OpenGL 
	Programming Guide by Woo et. al and also from Computer Graphics Using OpenGL by FS Hill
	Some parts of the code has also been adapted from various resources available through 
	out the internet.I thank all those whose code and resources I have used to write these 
	examples. You may use this code for any non-commerical purpose. Feel free to include 
	and use parts of this code for your own application, but remember that this code is not
	entirely bug free, use it at your own risk..If you plan on using the code for your any 
	academic purpose please drop me an email. I would like to attach a link to your course 
	from my home page.

	The Author of this code is Govindarajan Srimathveeravalli, Dept. of Mech and Aero Eng. ,
	University at Buffalo.
*/
//******************************************************************************************

// This code here demonstrates the impact of adding light(s) to a scene, effect shading has on
// the appearence of objects and the interaction between various parts of light of and the
// material added onto the object. This also demonstrates the effect of specular material property
// on final appearence, the use of glPush/PopAttrib and animation of light sources. 
//
// Try the following.
// 1. Disable the depth buffer and see what happens to the spheres. Why does this happen?
//    Look at the code for clues with order of rendering.
// 2. Remove the specular material property of the red sphere, but leave it on the light.How
//    does its appearence change now?
// 3. What would happen if the lights are not disabled before drawing the axis?
// 4. Play with the color of the sides. What would happen if a new sphere is draw just outside
//    the sides push-popattrib? What would its color be?
// 5. What will happen if you turn the push popattrib off for the 3rd sphere?


#include <stdlib.h>
#include <iostream>
#include <math.h>
#include <glut.h>

using namespace std;

float value = 0.;

// White directional light (note: Ideally for a "correct" lighting situation diffuse : ambient and :
// specular are approximately in the progression 3:2:1
// Also note the light position's w value (0), puts the light at infinity, with direction along positive
// Z axis
GLfloat white_light_diff[] = { 1., 1., 1., 1. };
GLfloat white_light_amb[] = { .2, .2, .2, 1. };
GLfloat light_posn[] = { 0., 0., 1., 0. }; 

// White spotlight :- the spot light points downwards from the emissive white sphere source.
GLfloat spot_posn[] = { 2., 8., 5., 1. }; 
GLfloat spot_direcn[] = { .2, -1., -2.15 };
GLfloat spot_spec[] = { 0.6, 0.6, 0.6, 1. };
float spot_cutoff = 15.;
float spot_expo = 0.0;

// Blue ball
GLfloat mat_diffuse0[] = { 0., 0., 0.8, 1.0 };
GLfloat mat_ambient0[] = { 0., 0., .3, 1.0 };

// Gray for the bottom
GLfloat mat_diffuse1[] = { 0.3, 0.3, 0.3, 1.0 };
GLfloat mat_ambient1[] = { 0.1, 0.1, .1, 1.0 };

// darker sides
GLfloat mat_diffuse2[] = { 0.4, 0., 0.4, 1.0 };
GLfloat mat_ambient2[] = { 0.1, 0., 0.1, 1.0 };

// red ball, the red ball has specular lighting that the blue ball doesn't. For all practical purposes
// both balls have similar material properties. The red ball is lit by the spot light, the blue ball 
// falls out of it's cone. 
GLfloat mat_diffuse3[] = { 0.8, 0.0, 0.0, 1.0 };
GLfloat mat_specular3[] = { 0.2, 0., 0., 1.0 };
GLfloat mat_ambient3[] = { 0.1, 0., 0.0, 1.0 };


bool animate = false;

void keys( unsigned char key, int x, int y )
{
	switch( key )
	{
		// Disable main light source
	case 'a':
		{
			glDisable( GL_LIGHT0 );
		} break;
	case 'A' :
		{
			glEnable( GL_LIGHT0 );
		} break;
		
		// disable the spotlight
	case 's' :
		{
			glDisable( GL_LIGHT1 );
		} break;
	case 'S' :
		{
			glEnable( GL_LIGHT1 );
		} break;

		// disable the spotlight
	case 'd' :
		{
			animate = true;
		} break;
	case 'D' :
		{
			animate = false;
		} break;
	}
	glutPostRedisplay();
}

void idle( int dummy )
{
	if( animate )
		value += 0.01;
		if( value > 360 )
			value = 0;

	glutPostRedisplay();
	glutTimerFunc( 30, idle, 0);
}


void reshape( int w, int h )
{
	glViewport( 0, 0, (GLsizei)w, (GLsizei)h );
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();
	glFrustum( -.4, .4, -.3, .3, 1., 200. );
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();

	glutPostRedisplay();
}

void draw( )
{
	// Here alongwith the color buffer, we clear the depth buffer for proper rendering of "objects 
	// in 3D. 
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	glColor3f( 1., 0., 0. );
	glLoadIdentity();

	//The first viewing command
	gluLookAt( 15., 15., 15., 0., 0., 0., 0., 1., 0. );

	// Note that the axis is rendered using glColor
	glDisable( GL_LIGHTING );
	// draw the axis
	glBegin( GL_LINES );
		glColor3f( 1., 0., 0. );
		glVertex3f( -200., 0., 0. );
		glVertex3f( 200., 0., 0. );
	glEnd();

	glBegin( GL_LINES );
		glColor3f( 0., 1., 0. );
		glVertex3f( 0., -200., 0. );
		glVertex3f( 0., 200., 0. );
	glEnd();

	glBegin( GL_LINES );
		glColor3f( 0., 0., 1. );
		glVertex3f( 0., 0., -200. );
		glVertex3f( 0., 0., 200. );
	glEnd();
	glEnable( GL_LIGHTING );

	// glPushAttrib stores all current settings for lighting state, and
	// ensures that it does not affect the objects outside this loop
	glPushAttrib( GL_LIGHTING );
	glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse1);
	glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient1);
	//XZ
	glBegin( GL_POLYGON );
		glNormal3f( 0., 1., 0. );
		glVertex3f( 0., 0., 0. );
		glVertex3f( 0., 0., 10. );
		glVertex3f( 10., 0., 10. );
		glVertex3f( 10., 0., 0. );
	glEnd();
	glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse2);
	glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient2);
	//XY
	glBegin( GL_POLYGON );
		glNormal3f( 0., 0., 1. );
		glVertex3f( 0., 0., 0. );
		glVertex3f( 0., 10., 0. );
		glVertex3f( 10., 10., 0. );
		glVertex3f( 10., 0., 0. );
	glEnd();
	//YZ
	glBegin( GL_POLYGON );
		glNormal3f( 1., 0., 0. );
		glVertex3f( 0., 0., 0. );
		glVertex3f( 0., 0., 10. );
		glVertex3f( 0., 10., 10. );
		glVertex3f( 0., 10., 0. );
	glEnd();
	glPopAttrib();
	
	glPushAttrib( GL_LIGHTING );
	glPushMatrix();
		// set material
		glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse0);
		glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient0);
			glTranslatef( 5., 2., 2. );
			glutSolidSphere( 1., 20, 20 );
	glPopMatrix();
	glPopAttrib();

	glPushAttrib( GL_LIGHTING );
	glPushMatrix();
		// set material
		glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient3);
		glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse3);
		glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular3);
			glTranslatef( 2., 2., 5. );
			glutSolidSphere( 1., 20, 20 );
	glPopMatrix();
	glPopAttrib();

	
	glPushMatrix();
		if(animate)
		{
			glTranslatef( 2+cos(value), 8, 5+sin(value) );
			GLfloat posn[] = { 2+cos(value), 8., 5+sin(value), 1. };
			glLightfv( GL_LIGHT1, GL_POSITION, spot_posn );
		}
		else
		{
			glTranslatef( 2., 8., 5.);
			GLfloat posn[] = { 2., 8., 5., 1. };
			glLightfv( GL_LIGHT1, GL_POSITION, spot_posn );
		}
		glPushAttrib( GL_LIGHTING );
			//make an "emmisive material"
			GLfloat emiss[] = { 1., 1., 1., 1. };
			glMaterialfv( GL_FRONT, GL_EMISSION, emiss);
			glutSolidSphere( 0.1, 20, 20 );
		glPopAttrib();	
	glPopMatrix();
	
	glutSwapBuffers();
}

void init( )
{
	glClearColor( .46, .53, .6, 0. );

	// Enable depth testing
	glEnable( GL_DEPTH_TEST );

	// We keep our light within the display func so that we can influence its properties
	// Create the first white light (made largely ambient and diffuse (source at infinity)
	glLightfv( GL_LIGHT0, GL_POSITION, light_posn );
	glLightfv( GL_LIGHT0, GL_DIFFUSE, white_light_diff );
	glLightfv( GL_LIGHT0, GL_AMBIENT, white_light_amb );

	//// Second light is a white spotlight
	glLightfv( GL_LIGHT1, GL_DIFFUSE, white_light_diff );
	glLightfv( GL_LIGHT1, GL_SPECULAR, spot_spec);
	glLightfv( GL_LIGHT1, GL_POSITION, spot_posn );
	glLightfv( GL_LIGHT1, GL_SPOT_DIRECTION, spot_direcn );
	glLightfv( GL_LIGHT1, GL_SPOT_CUTOFF, &spot_cutoff );
	glLightfv( GL_LIGHT1, GL_SPOT_EXPONENT, &spot_expo );

	glEnable( GL_LIGHT0 );
	glEnable( GL_LIGHT1 );
}

int main(int argc, char* argv[])
{
	glutInit( &argc, argv );
	glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
	glutInitWindowPosition( 50, 50 );
	glutInitWindowSize( 640, 480 );
	glutCreateWindow( "Lights" );

	glutReshapeFunc( reshape );
	init();
	glutDisplayFunc( draw );
	glutKeyboardFunc( keys );
	glutTimerFunc( 30, idle, 0);

	glutMainLoop();

	return 0;
}


