//*****************************************************************************************
// Disclaimer, Authorship and License
/*
	This code has been written to serve as a learning tool for the course MAE 410-574, 
	Virtual Reality Applications and Research, Spring 2007.
	
	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 demonstrates the use of hierarchical transformations.
// This technique can be used to alter or affect the transformation of kinematic chains etc.
// keys 1-6 are used to increase or decrease the angles of the robot's shoulder-arm-wrist.
// Try the following
// 1. Note the use of translation to the origin and back when performing certain rotation
//    operations. Why are they used so ?
// 2. Add functionality to maniupulate the two arms seperately without using a new drawLinks routine
// 3. Make the robot look fancy by the changing the links to cylinders with spheres to 
//    represent the joints.

#include <stdlib.h>
#include <iostream>
#include <math.h>
#include <glut.h>


// angle values for the arms
float shoulder = 45.;
float arm = -90.;
float wrist = 45.;

// Create values for a number of lights

// White directional light
GLfloat white_light[] = { 1., 1., 1., 1. };
GLfloat light_posn[] = { 10., 0., 10., 1. }; 


// Silvery material for the object
GLfloat mat_ambient[] = { 0.19, 0.19, 0.19, 1.0 };
GLfloat mat_diffuse[] = { 0.7, 0.2, 0.2, 1.0 };
GLfloat mat_specular[] = { 0.51, 0.51, 0.51, 1.0 };
GLfloat low_shininess[] = { 51.2 };

void drawAxis()
{
	// 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();

}

void drawLimb( )
{
	// Draw the shoulder link
	// The shoulder rotates about the X axis by 90 degress	
	glRotatef( shoulder, 1., 0., 0. );
	// certain transformations to create the shoulder should not
	// affect the other links
	glPushMatrix();
		glTranslatef( 0., 0., 2.5 );
		glScalef( 1., 1., 5. );
		glutSolidCube( 1. );
	glPopMatrix();

	// Draw the arm link, rotate it clockwise 
	glTranslatef( 0., 0., 5. );
	glRotatef( arm, 1., 0., 0. );
	glTranslatef( 0., 0., -5. );
	glPushMatrix();
		glTranslatef( 0., 0., 7.5 ); 
		glScalef( 1., 1., 5. );
		glutSolidCube( 1. );
	glPopMatrix();

	// Draw the wrist link, rotate it clockwise 
	glTranslatef( 0., 0., 10. );
	glRotatef( wrist, 1., 0., 0. );
	glTranslatef( 0., 0., -10.);
	glPushMatrix();
		glTranslatef( 0., 0., 10. ); 
		glScalef( .5, .5, 2.5 );
		glutSolidCube( 1. );
	glPopMatrix();
}


void keys( unsigned char key, int x, int y )
{

	switch( key )
	{
	case '1':
		  {
			  shoulder += 1.;
		  } break;
	case '2':
		  {
			  shoulder -= 1.;
		  }break;
	case '3':
		  {
			  arm += 1.;
		  }break;
	case '4':
		  {
			  arm -= 1.;
		  }break;
	case '5':
		  {
			  wrist += 1.;
		  }break;
	case '6':
		  {
			  wrist -= 1.;
		  }break;
	}
	
	glutPostRedisplay();
}

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 );
	
	glutPostRedisplay();
}

void draw( )
{
	// Prelims
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	glColor3f( 1., 0., 0. );
	glLoadIdentity();
 
	gluLookAt( 25., 25., 25., 0., 0., 0., 0., 1., 0. );
	
	glDisable( GL_LIGHTING );
	drawAxis();
	glEnable( GL_LIGHTING );

	glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
	glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess);
	// End of Prelims ------------------


	// Draw the body of the robot
	glPushMatrix();
		glScalef( 11. , 1. , 1. );
		glutSolidCube( 1. );
	glPopMatrix();
	
	// Draw the right arm
	glPushMatrix();
		// Translate to where the arm is at
		glTranslatef( 5., 0., 0. );
		// Add some material for the first limb
		drawLimb();
	glPopMatrix();

	// Draw the left arm
	glPushMatrix();
		glTranslatef( -5., 0., 0. );
		// Add some other material here
		drawLimb();
	glPopMatrix();

	glutSwapBuffers();
}

void init( )
{
	glClearColor( .46, .53, .6, 0. );

	// We keep our light within the display func so that we can influence its properties
	// Create the first white light
	glLightfv( GL_LIGHT0, GL_POSITION, light_posn );
	glLightfv( GL_LIGHT0, GL_AMBIENT, white_light );
	glLightfv( GL_LIGHT0, GL_DIFFUSE, white_light );
	glLightfv( GL_LIGHT0, GL_SPECULAR, white_light );	

	glEnable( GL_LIGHTING );
	glEnable( GL_LIGHT0 );

	// Enable depth testing
	glEnable( GL_DEPTH_TEST );
	
	glShadeModel( GL_SMOOTH );
}

int main(int argc, char* argv[])
{
	glutInit( &argc, argv );
	glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
	glutInitWindowPosition( 50, 50 );
	glutInitWindowSize( 640, 480 );
	glutCreateWindow( "Robot" );

	glutDisplayFunc( draw );
	glutReshapeFunc( reshape );
	glutKeyboardFunc( keys );

	init( );

	glutMainLoop();

	return 0;
}


