

#include "Mode_SierpinskiDisplay.h"
using namespace iglu;

#define MAX_LEVELS_TO_TRAVERSE   8

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

// The transform feedback OpenGL ID / encapsulation  
IGLUTransformFeedback::Ptr triangleFeedback;

// We have 2 GLSL shaders:  one outputs results, one subdivides triangles
IGLUShaderProgram::Ptr     triangleSubdivide, triangleOutput;

// Vertex arrays we'll use to render from (and into)
IGLUVertexArray::Ptr inputTriangleArray;   // The base case (i.e., one triangle)
IGLUVertexArray::Ptr pingPongTriangles[2]; // Intermediate buffers we both read & write to

// Which buffer (of the ping-pong intermediary buffers) is current to draw from?
int currentPingPongBuffer=0;

// A UI variable controling how many times we'll iterate on the GPU.
IGLUInt     traversalLevels( 2, IGLURange<int>(0, MAX_LEVELS_TO_TRAVERSE), 1, "Number of triangle subdivisions:" );

// Prototype for the function that does the complex traversal (see bottom of file)
void SubdivideTriangle( void );


void SierpinskiDisplay::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 );
	triangleOutput->Enable();
	if ( traversalLevels <= 0 )  // Did the user ask not to subdivide?  Use original geometry
		inputTriangleArray->DrawArrays( GL_TRIANGLES, 0, 3 );  
	else                         // Use last buffer of subdivided triangles
		pingPongTriangles[currentPingPongBuffer]->DrawTransformFeedback( GL_TRIANGLES, triangleFeedback );
	triangleOutput->Disable();
}


// Initialize variables and stuff needed for our simple model display
void SierpinskiDisplay::Initialize( int trackNum )
{
	// Add user interaction variables to the widget window
	myWin->AddWidget( trackNum, &traversalLevels );

	// Load the shader that outputs the red triangle(s) onto the screen
	triangleOutput = new IGLUShaderProgram( "shaders/subdividedTriangleOutput.vert.glsl", "shaders/subdividedTriangleOutput.frag.glsl" );
	triangleOutput["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
	triangleSubdivide = new IGLUShaderProgram( "shaders/subdivideTriangles.vert.glsl", 
	                                           "shaders/subdivideTriangles.geom.glsl", 
	                                           "shaders/subdivideTriangles.frag.glsl" );
	triangleSubdivide->SetTransformFeedbackVaryings( "gl_Position" );    // What gets output to our transform feedback buffer?
	
	// The buffer storing our initial triangle data
	GLfloat initialTriangleData[12] = {-1,-0.5,0,1,  1,-0.5,0,1,  0,1,0,1};
	inputTriangleArray = new IGLUVertexArray();
	inputTriangleArray->SetVertexArray( sizeof(initialTriangleData), initialTriangleData, IGLU_STATIC|IGLU_DRAW );
	inputTriangleArray->EnableAttribute( IGLU_ATTRIB_VERTEX, 4, GL_FLOAT, 0, BUFFER_OFFSET( 0 ) );

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

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


// 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.
	currentPingPongBuffer=0;

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

	// Traverse down the triangle 'hierarchy,' starting with the root node (i.e., one triangle)
	for (int stepCount = 0; stepCount < traversalLevels; stepCount++,currentPingPongBuffer=1-currentPingPongBuffer)
	{
		// Setup where we'll output the data this iteration, then start feedback
		triangleFeedback->AttachBuffer( pingPongTriangles[currentPingPongBuffer]->GetVertexBuffer() );
		triangleFeedback->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!
			inputTriangleArray->DrawArrays( GL_TRIANGLES, 0, 3 );  
		else
			// This is a subsequent subdivision.  Use the output from the previous division!
			pingPongTriangles[1-currentPingPongBuffer]->DrawTransformFeedback( GL_TRIANGLES, triangleFeedback );

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

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

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