//*****************************************************************************************
// 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 demonstrates the effect of blending operations in opengl. Blending is primarily
// a "pixel" level operation performed at the level of the framebuffer. This can be used to
// bring about many interesting effects. Other interesting effects that work at the level of
// the pixel level include fog effects. The use of depth mask is depicted here to ensure "correct"
// rendering of transparent objects. A short demo of polygon offset is shown here too.
//
// Try the following.
// 1. Change the fog effect such that everything is set to the background clear color. How do
//    the objects look like with respect to the background now?
// 2. Change the ordering of the polygons offset such that the smaller polygons are offset and 
//    not the larger ones.
// 3. Play around with different combination of choices for the glBlendFunc and notice what happens



#include <stdlib.h>
#include <iostream>
#include <math.h>
#include <glut.h>

using namespace std;

bool depth = false;

GLfloat white_light_diff[] = { 1., 1., 1., 1. };
GLfloat white_light_amb[] = { .2, .2, .2, 1. };
GLfloat light_posn[] = { 0., 0., 1., 0. }; 

// Blue ball (solid)
GLfloat mat_diffuse0[] = { 0., 0.4, 0.8, 1.0 };
GLfloat mat_ambient0[] = { 0., 0.1, .3, 1.0 };

// transparent
GLfloat mat_diffuse1[] = { 0.8, 0.0, 0.0, 0.5 };
GLfloat mat_ambient1[] = { 0.1, 0., 0.0, 1.0 };


void keys( unsigned char key, int x, int y )
{
	switch( key )
	{

	case 'f':
		{
			glDisable( GL_FOG );
		} break;
	case 'F' :
		{
			glEnable( GL_FOG );
		} break;

	case 'd':
		{
			depth = true;
		} break;
	case 'D' :
		{
			depth = false;
		} 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 );
	glLoadIdentity();

	glutPostRedisplay();
}

void draw( )
{
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	glLoadIdentity();

	gluLookAt( 0., 0., 30., 0., 0., 0., 0., 1., 0. );

	// draw the axis lines
	glPushAttrib( GL_LIGHTING );
	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();

	// enable the offset of polygons such that all "three"
	// polygons will get rendered correctly.
	glEnable( GL_POLYGON_OFFSET_FILL );
	glPolygonOffset( 0, 1.0 );
	// render an opaque square behind the triangle
	glBegin( GL_POLYGON );
		glColor4f( 0., 1., 0., 1.0 );
		glVertex3f( -5., -5., -1.0 );
		glVertex3f( 5., -5., -1.0 );
		glVertex3f( 5., 5., -1.0 );
		glVertex3f( -5., 5., -1.0 );
	glEnd();
	glDisable( GL_POLYGON_OFFSET_FILL );

	//first polygon on square
	glBegin( GL_POLYGON );
		glColor4f( 0., 0., 1., 1.0 );
		glVertex3f( -5., -5., -1.0 );
		glVertex3f( 0., -5., -1.0 );
		glVertex3f( 0., 0., -1.0 );
		glVertex3f( -5., 0., -1.0 );
	glEnd();

	//second polygon to be drawn on square, same z values
	glBegin( GL_POLYGON );
		glColor4f( 1., 1., 1., 1.0 );
		glVertex3f( 0., -5., -1.0 );
		glVertex3f( 5., -5., -1.0 );
		glVertex3f( 5., 0., -1.0 );
		glVertex3f( 0., 0., -1.0 );
	glEnd();

	glEnable( GL_BLEND );
	// disable depthmask for "correct" rendering of translucent objects
	glDepthMask( GL_FALSE );
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	// draw a transparent red triangle
	glBegin( GL_TRIANGLES );
		glColor4f( 1., 0., 0., 0.5 );
		glVertex3f( -5., 0., 0. );
		glVertex3f( 5., 0., 0. );
		glVertex3f( 0., 8., 0. );
	glEnd();
	glDisable( GL_BLEND );
	// disable depthmask for "correct" rendering
	glDepthMask( GL_TRUE );
	glPopAttrib();

	// solid blue balls
	glPushAttrib( GL_LIGHTING );
		glEnable( GL_LIGHTING );
		glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient0);
		glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse0);
		for( unsigned int i=0; i<10; i++)
		{
			glPushMatrix();
				glTranslatef( 5, 0., float(i*3) );
				glutSolidSphere( 0.5, 20, 20 );
			glPopMatrix();
		}
		glDisable( GL_LIGHTING );
	glPopAttrib();

	//transparent red cubes (notice how the toggling of the depth mask affects
	// final rendering)
	glPushAttrib( GL_LIGHTING );
		glEnable( GL_LIGHTING );
		glEnable( GL_BLEND );
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		if( depth )
			glDepthMask( GL_FALSE );
		glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient1);
		glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse1);
		for( unsigned int i=0; i<10; i++)
		{
			glPushMatrix();
				glTranslatef( -5.0, 0., float(i*3) );
				glutSolidCube( 2.0 );
			glPopMatrix();
		}
		glDisable( GL_LIGHTING );
		glDisable( GL_BLEND );
		if( depth )
			glDepthMask( GL_TRUE );
	glPopAttrib();

	glutSwapBuffers();
}

void init( )
{
	glClearColor( .46, .53, .6, 1. );

	// Enable depth testing
	glEnable( GL_DEPTH_TEST );

	// 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 );
	glEnable( GL_LIGHT0 );

	// Create fog effect here
	glFogi( GL_FOG_MODE, GL_EXP );
	GLfloat fcolor[] = { .2, .0, .1, 1. };
	glFogfv( GL_FOG_COLOR, fcolor );
	glFogf( GL_FOG_DENSITY, 0.1 );
	glHint( GL_FOG_HINT, GL_DONT_CARE );
	glFogf( GL_FOG_START, -1.0 );
	glFogf( GL_FOG_END, 50.0 );
}

int main(int argc, char* argv[])
{
	glutInit( &argc, argv );
	glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH );
	glutInitWindowPosition( 50, 50 );
	glutInitWindowSize( 640, 480 );
	glutCreateWindow( "Blending" );

	glutReshapeFunc( reshape );
	init();
	glutDisplayFunc( draw );
	glutKeyboardFunc( keys );

	glutMainLoop();

	return 0;
}


