/*****************************************************************************
** This program performs a subdivision into a Sierpinski triangle using a   **
**     geometry shader to divide a triangle into three, output that into a  **
**     GPU buffer using OpenGL's transform feedback, and then pipes that    **
**     output back into the shader to perform another subdivision.          **
**                                                                          **
** This is equivalent to the demo code that has been on my webpage for a    **
**     number of years, only this version uses IGLU.                        **
**                                                                          **
** Chris Wyman (1/13/2012)                                                  **
*****************************************************************************/

#include "iglu.h"
using namespace iglu;

// The maximum number of interative subdivision steps.  This could be 
//   arbitrarily high, except at some point the triangles are sub-pixel, and
//   each subdivision requires 3 times as many vertices, so the buffers need
//   to be quite large.  Currently the buffers can handle up to 8 interations
#define MAX_LEVELS_TO_TRAVERSE   8

// We'll have 2 GLSL shaders:  one outputs results, one subdivides triangles
IGLUShaderProgram::Ptr traverse, output;

// We'll have a vertex array for our initial triangle, plus to to ping-pong
//   between as we subdivide out triangle into additional triangles.
IGLUVertexArray::Ptr inputTriangle, pingPongBufs[2];

// Traversal parameters
//int traversalLevels = 2;   // # interations the user has chosen to display
int currentBuffer=0;       // Stores which ping-pong buffer is current

// The transform feedback OpenGL ID.  
IGLUTransformFeedback::Ptr feedback;

// Initial triangle data passed to the traversal routine
GLfloat initialTriangleData[12] = {-1,-0.5,0,1,  1,-0.5,0,1,  0,1,0,1};

IGLUInt traversalLevels( 2, 0, MAX_LEVELS_TO_TRAVERSE,   "Number of recursive traversals:" );

// Prototype for the simple hierarchical traversl function
void SubdivideTriangle( void );


// Handle drawing.
void display ( void )
{
	// Subdivide our original triangle into the right number of outputs
	SubdivideTriangle( ); 

	// Draw the subdivided set of triangles on screen
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	output->Enable();
	if (traversalLevels<=0)  // Did the user ask not to subdivide?  Use original geometry
		inputTriangle->DrawArrays( GL_TRIANGLES, 0, 3 );  
	else                     // Use last buffer of subdivided triangles
		pingPongBufs[currentBuffer]->DrawTransformFeedback( GL_TRIANGLES, feedback );
	output->Disable();

	// Print instructions on the screen
	IGLUDraw::DrawText( IGLU_FONT_VARIABLE, 10, 30, "+/- changes # triangles", 1.25 );
}

// Handle keystrokes.
void keys(unsigned char key, int x, int y)
{
	// Update, if the appropriate key, else quit.
	if (!traversalLevels.UpdateFromInput( key )) 
		exit(0);
}

int main(int argc, char** argv)
{
	IGLUWindow *myWin = new IGLUWindow( 768, 768, "Intermediate IGLU Example:  Sierpinski Triangle via Transform Feedback" );
	myWin->SetWindowProperties( IGLU_WINDOW_DOUBLE |
								IGLU_WINDOW_REDRAW_ON_IDLE |
								IGLU_WINDOW_W_FRAMERATE );
	myWin->SetDisplayCallback( display );
	myWin->SetIdleCallback( IGLUWindow::NullIdle );
	myWin->SetKeyboardCallback( keys );
	myWin->CreateWindow( argc, argv );

	// Load the shader that outputs the red triangle(s) onto the screen
	output = new IGLUShaderProgram( "simpleOutput.vert.glsl", "simpleOutput.frag.glsl" );
	output["mvp"] = IGLUMatrix4x4::Perspective( 90.0, 1.0, 0.1, 10 ) *
	                IGLUMatrix4x4::LookAt( vec3(0, 0.2, 1), vec3(0, 0.2, 0), vec3(0, 1, 0) );

	// Load a shader that takes a buffer of triangles and subdivides each into 3 smaller triangles
	traverse = new IGLUShaderProgram( "simpleTraversal.vert.glsl", 
	                                  "simpleTraversal.geom.glsl", 
	                                  "simpleTraversal.frag.glsl" );
	traverse->SetTransformFeedbackVaryings( "gl_Position" );    // What gets output to our transform feedback buffer?
	traverse->SetProgramEnables( IGLU_GLSL_RASTERIZE_DISCARD ); // When we use this program, don't actually rasterize. 

	// The buffer storing our initial triangle data
	inputTriangle = new IGLUVertexArray();
	inputTriangle->SetVertexArray( sizeof(initialTriangleData), initialTriangleData, IGLU_STATIC|IGLU_DRAW );
	inputTriangle->EnableAttribute( traverse["vertex"], 4, GL_FLOAT, 0, BUFFER_OFFSET( 0 ) );

	// 2 temporary buffers, each of which can store, at max, 2^15 4-component outputs
	pingPongBufs[0] = new IGLUVertexArray();
	pingPongBufs[0]->SetVertexArray( ( 1 << 15 ) * sizeof(float) * 4, NULL, IGLU_STREAM|IGLU_COPY );
	pingPongBufs[0]->EnableAttribute( traverse["vertex"], 4, GL_FLOAT, 0, BUFFER_OFFSET( 0 ) );
	pingPongBufs[1] = new IGLUVertexArray();
	pingPongBufs[1]->SetVertexArray( ( 1 << 15 ) * sizeof(float) * 4, NULL, IGLU_STREAM|IGLU_COPY );
	pingPongBufs[1]->EnableAttribute( traverse["vertex"], 4, GL_FLOAT, 0, BUFFER_OFFSET( 0 ) );

	// Create an OpenGL transform feedback object  
	feedback = new IGLUTransformFeedback();

	traversalLevels.AddKeyResponse( '+',  +1 );
	traversalLevels.AddKeyResponse( '=',  +1 );
	traversalLevels.AddKeyResponse( '-', -1 );
	traversalLevels.AddKeyResponse( '_', -1 );

	IGLUWidgetWindow *uiWin = new IGLUWidgetWindow( 300, 80, "UI Widget Window" );
	uiWin->AddWidget( &traversalLevels );
	uiWin->CreateWindow();

	/* start it! */
	IGLUWindow::Run();
	return 0;
}




// A simple hierarchical traversal using the transform feekback mechanism, which
//    subdivides one triangle into three, and interatively applies this subdivision
//    to the output from the prior subdivision a number of times.
void SubdivideTriangle( void )
{
	// We have two output buffers. (Why?  Because we can't read & write to 1 
	//    buffer at the same time.)  This means we need to keep track of which
	//    buffer we're reading to and which we're writing to.  We'll be outputting
	//    to 'currentBuffer' and reading from the other one.
	currentBuffer=0;

	// Enable our subdivision shader and start the transform feedback process
	traverse->Enable();

	// Traverse down the triangle 'hierarchy,' starting with the root node (i.e., one triangle)
	for (int stepCount = 0; stepCount < traversalLevels; stepCount++,currentBuffer=1-currentBuffer)
	{
		// Setup where we'll output the data this iteration, then start feedback
		feedback->AttachBuffer( pingPongBufs[currentBuffer]->GetVertexBuffer() );
		feedback->Begin( GL_TRIANGLES );
		
		// Go ahead and actually perform a single traversal step by "drawing"
		if (stepCount == 0)
			// This is the first subdivision.  Use initial input geometry!
			inputTriangle->DrawArrays( GL_TRIANGLES, 0, 3 );  
		else
			// This is a subsequent subdivision.  Use the output from the previous division!
			pingPongBufs[1-currentBuffer]->DrawTransformFeedback( GL_TRIANGLES, feedback );

		// Stop piping data to the buffer specified above
		feedback->End();
	}

	// We're done with with our subdivision. Unbind shader
	traverse->Disable();

	// The loop above leaves currentBuffer toggled to the wrong one
	currentBuffer = 1-currentBuffer;
}

