

#include "Mode_SimpleReflectionsDisplay.h"
using namespace iglu;

// Our main window (created in main.cpp)
extern IGLUMultiDisplayWindow::Ptr    myWin;

// Other stuff common between display modes that was initialized in main.cpp
extern IGLUOBJReader::Ptr      objCow;   // Our cow model
extern IGLUTrackball::Ptr	   ball;     // Our trackball 

// Geometry for our reflective plane;
IGLUVertexArray::Ptr           reflectivePlane = 0;

// The data needed inside this display mode
IGLUShaderProgram::Ptr         cowReflectionShader = 0;
IGLUShaderProgram::Ptr         planeReflectionShader = 0;

// Matrices used for our reflective cow display
IGLUMatrix4x4           reflectView     = IGLUMatrix4x4::LookAt( vec3(0, 6, 6), vec3(0, 2, 0), vec3(0, 1, 0) );
IGLUMatrix4x4           cowScaleMat     = IGLUMatrix4x4::Scale( 1.5 );
IGLUMatrix4x4           planeScale      = IGLUMatrix4x4::Scale( 4 );
IGLUMatrix4x4           cowTranslateMat = IGLUMatrix4x4::Translate( 0, 2, 0 );

// The image that will store the reflection (before pasting it on our reflective plane).
IGLUFramebuffer::Ptr	       reflectBuffer = 0;

// A variable controlling how far the cow is from the plane
IGLUBool    onlyDisplayReflection   ( false, "Only display reflection buffer");
IGLUFloat   cowReflectionTranslate  ( 2, IGLURange<float>(0.5,4.5), 0.1,   "Cow position" );
IGLUInt     reflectBufferResolution ( 768, IGLURange<int>(128,2048), 1,   "Reflection buffer resolution" );



void SimpleReflectionsDisplay::Display( void )
{
	glClearColor( 0, 1, 0, 1 );
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	// Update the cow translation matrix with the current distance
	cowTranslateMat   = IGLUMatrix4x4::Translate( 0, cowReflectionTranslate, 0 );

	// -----------------------------------------------------------
	// First, draw the reflection to an off-screen buffer/texture
	// -----------------------------------------------------------

	// Create a reflection of the cow in the 'reflection' buffer
	reflectBuffer->Bind();
	reflectBuffer->Clear();
		cowReflectionShader->Enable(); 
		    cowReflectionShader["model"]       = IGLUMatrix4x4::Scale( 1, -1, 1 ) *  // Matrix to flip around the mirror
		                                         cowTranslateMat * cowScaleMat;
			cowReflectionShader["view"]        = reflectView * ball->GetMatrix();
			cowReflectionShader["mirrorPlane"] = vec4( 0, -1, 0, 0 );  // The plane in the mirror...
			objCow->Draw( cowReflectionShader ); 
		cowReflectionShader->Disable(); 
	reflectBuffer->Unbind();

	if (!onlyDisplayReflection)
	{
		// -----------------------------------------------------------
		// Now, the unreflected part of the scene.
		// -----------------------------------------------------------

		// Draw the unreflected cow above the mirror.
		cowReflectionShader->Enable(); 
			cowReflectionShader["model"]       = cowTranslateMat * cowScaleMat;
			cowReflectionShader["view"]        = reflectView * ball->GetMatrix();
			cowReflectionShader["mirrorPlane"] = vec4( 0, 1, 0, 0 );  // The plane *not* in the mirror...
			objCow->Draw( cowReflectionShader ); 
		cowReflectionShader->Disable(); 

		// Draw a 'mirror' plane textured with the reflected cow image.
		planeReflectionShader->Enable(); 
			planeReflectionShader["modelview"] = reflectView * ball->GetMatrix() * planeScale;
			reflectivePlane->DrawArrays( GL_TRIANGLE_STRIP, 0, 4 ); 
		planeReflectionShader->Disable(); 
	}
	else
		IGLUDraw::Fullscreen( reflectBuffer[0] );

	// Return the clear color back to normal
	glClearColor( 0, 0, 0, 1 );
}


// Initialize variables and stuff needed for our simple model display
void SimpleReflectionsDisplay::Initialize( int trackNum )
{
	// Create a buffer to store our reflection!
	reflectBuffer = IGLUFramebuffer::Create( GL_RGBA8, 768, 768, true );

	// Add user interaction variables to the widget window
	myWin->AddWidget( trackNum, &cowReflectionTranslate );
	myWin->AddWidgetSpacer( trackNum );
	myWin->AddWidget( trackNum, &reflectBufferResolution, reflectBuffer->GetResizeCallback() ); 
	myWin->AddWidget( trackNum, &onlyDisplayReflection );

	// Now create our shader from these strings
	cowReflectionShader = new IGLUShaderProgram( "shaders/cowReflectionShader.vert.glsl", 
		                                         "shaders/cowReflectionShader.frag.glsl" );
	cowReflectionShader["project"]     = IGLUMatrix4x4::Perspective( 60, 1, 3, 20 );  
	cowReflectionShader["model"]       = IGLUMatrix4x4::Scale( 1, -1, 1 ) *    // Matrix to flip around the mirror
		                                 IGLUMatrix4x4::Translate( 0, 2, 0 ) * // Cow's translate matrix
										 IGLUMatrix4x4::Scale( 0.4 );          // Cow's scale matrix

	planeReflectionShader = new IGLUShaderProgram( "shaders/planeReflectionShader.vert.glsl", 
		                                           "shaders/planeReflectionShader.frag.glsl" );
	planeReflectionShader["project"]   = IGLUMatrix4x4::Perspective( 60, 1, 3, 20 );
	planeReflectionShader["mirrorTex"] = reflectBuffer[0];

	// Create our plane geometry
	float planeData[] = { 0,1,0, -1,0,-1,  0,1,0, -1,0,1,  0,1,0, 1,0,-1,  0,1,0, 1,0,1 };
	reflectivePlane = new IGLUVertexArray();
	reflectivePlane->SetVertexArray( sizeof( planeData ), planeData );
	reflectivePlane->EnableAttribute( IGLU_ATTRIB_VERTEX,   3, GL_FLOAT, 6*sizeof(float), BUFFER_OFFSET(3*sizeof(float)) );
	reflectivePlane->EnableAttribute( IGLU_ATTRIB_NORMAL,   3, GL_FLOAT, 6*sizeof(float), BUFFER_OFFSET(0) );
}