/*************************************************************************
** Mode_VisualizeVoxelizedShadowVolumes.cpp                             **
** --------------------------                                           **
**                                                                      **
** This is a much more complex display mode that shows how they might   **
**    be used in your application.  Please see Mode_BlankDisplay.h for  **
**    more details about the data structure.                            **
**                                                                      **
** Chris Wyman (04/06/2012)                                             **
*************************************************************************/

#include "../projectIncludes.h"
#include "Mode_VisualizeVoxelizedShadowVolumes.h"

// A global data structure containing all shared data for this program
extern RenderingData *data;


// Draws our scene using shadow maps (creating using a parabolic projection)
void VisualizeVoxelizedShadowVolumes::Display( void )
{
	// Compute the eye-space light position (used multiple times per frame)
	vec4 esLightPos = data->mat->eyeView * vec4( data->param->lightPos, 1.0f );
	
	//Render into the shadow map. 
	CreateParaboloidShadowMap( data->fbo->paraboloidShadowMap, esLightPos );

	// Find the angle to bound the epipolar light extents for the current view.
	float minDist, maxDist;
	ReturnMinAndMaxLightAngles( esLightPos, &minDist, &maxDist );

	// Rectify our shadow map into the epipolar voxel space
	RectifySMapToEpipolarVoxelBuffer( data->fbo->epipolarVoxels, 
		                              data->glsl->rectifySMap,  
									  esLightPos.xyz(), 
									  minDist, maxDist );

	// Create our voxelized shadow map!
	CreateVSVUsingRectifiedSMap();
	
	// OK.  Now draw the scene for real!
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	
	// Setup some shader variable values constant over all the geometry in the frame
	data->glsl->combineDrawVoxelViz["esLightPos"]     = esLightPos;
	data->glsl->combineDrawVoxelViz["view"]           = data->mat->eyeView;
	data->glsl->combineDrawVoxelViz["sMapBias"]       = data->ui->zBias;
	data->glsl->combineDrawVoxelViz["intensity"]      = data->ui->heatMapIntensity;
	data->glsl->combineDrawVoxelViz["maxAngle_L_V"]   = 0.0;
	data->glsl->combineDrawVoxelViz["minAngle_L_V"]   = maxDist;
	data->glsl->combineDrawVoxelViz["clampToVisible"] = data->ui->visVoxelsOnly.GetValue() ? 1.0 : 0.0;
	data->glsl->combineDrawVoxelViz["useVSVForSurface"] = data->ui->useVSVForSurface.GetValue() ? 1.0 : 0.0;
	data->glsl->combineDrawVoxelViz["vsvBias"]        = data->ui->vsvSurfBias;
	data->glsl->combineDrawVoxelViz["padScaleCoef"]   = data->ui->vsvPadCoef;

	// Draw the geometry, modifying per-geometry shader variables as needed
	data->glsl->combineDrawVoxelViz->Enable();
		data->glsl->combineDrawVoxelViz["model"] = data->mat->cowPosition * data->ui->ball->GetMatrix();
		data->geom->cow->Draw( data->glsl->combineDrawVoxelViz );         // Draw cow geometry
		data->glsl->combineDrawVoxelViz["model"] = IGLUMatrix4x4::Identity();
		data->geom->cornellBox->Draw( data->glsl->combineDrawVoxelViz );  // Draw Cornell Box geometry
	data->glsl->combineDrawVoxelViz->Disable();
}


// Initialization required for our simple shadow map rendering approach
void VisualizeVoxelizedShadowVolumes::Initialize( int trackNum )
{
	// Initialize UI variables for the widget window
	data->ui->visVoxelsOnly.Initialize( true, "Count only non-occluded voxels" );
	data->ui->useVSVForSurface.Initialize( false, "Use VSV for surface shadows" );
	data->ui->vsvSurfBias.Initialize( 0.0, IGLURange<float>(0,2), 0.01, "VSV phi-bias (surface shadowing)" );

	// Initialize shaders
	data->glsl->combineDrawVoxelViz = new IGLUShaderProgram( "shaders/lambertianWithVoxelShadowViz.vert.glsl",
									  		                 "shaders/lambertianWithVoxelShadowViz.frag.glsl" );
	data->glsl->combineDrawVoxelViz["voxels0"]     = data->fbo->epipolarVoxels[ IGLU_COLOR0 ];
	data->glsl->combineDrawVoxelViz["voxels1"]     = data->fbo->epipolarVoxels[ IGLU_COLOR1 ];
	data->glsl->combineDrawVoxelViz["voxels2"]     = data->fbo->epipolarVoxels[ IGLU_COLOR2 ];
	data->glsl->combineDrawVoxelViz["voxels3"]     = data->fbo->epipolarVoxels[ IGLU_COLOR3 ];
	data->glsl->combineDrawVoxelViz["shadowTex"]   = data->fbo->paraboloidShadowMap[ IGLU_DEPTH ];
	data->glsl->combineDrawVoxelViz["nearDist"]    = data->ui->lightNear;
	data->glsl->combineDrawVoxelViz["farDist"]     = data->ui->lightFar;
	data->glsl->combineDrawVoxelViz["proj"]        = data->mat->eyeProj;
	data->glsl->combineDrawVoxelViz["matlInfoTex"] = IGLUOBJMaterialReader::s_matlCoefBuf;


	// Add widgets to the built-in UI window.  Also setup callbacks (if needed)
	data->win->AddWidget( trackNum, &data->ui->lightMovement, new IGLUVariableCallback( UpdateLightPosition ) );
	data->win->AddWidget( trackNum, &data->ui->heatMapIntensity );
	data->win->AddWidget( trackNum, &data->ui->visVoxelsOnly );
	data->win->AddWidget( trackNum, &data->ui->useVSVForSurface );
	data->win->AddWidget( trackNum, &data->ui->vsvSurfBias );
	data->win->AddWidgetSpacer( trackNum );
	data->win->AddWidget( trackNum, &data->ui->vsvWidth, new IGLUSimpleCallback( UpdateVSVResolution ) );
	data->win->AddWidget( trackNum, &data->ui->vsvHeight, new IGLUSimpleCallback( UpdateVSVResolution ) );
}

