#include "ofApp.h"

/**
 * tig: This example shows how to draw instanced geometry using 
 * ofVboMesh.drawInstanced() and matching GLSL shader code.
 * The most interesting things will happen in the shader files, 
 * and in the draw() method, where everything is set up properly 
 * to allow instanced rendering.
 *
 * Note that you can feed any ofVboMesh to the instanced draw method.
 *
 */

//--------------------------------------------------------------
void ofApp::setup(){
	
	// initialize variables:
	
	isShaderDirty = true;  // this flag will tell us whether to reload our shader from disk.
						   // this allows you to change your shaders without having to restart
						   // your app. we'll set it up so that pressing the SPACE key on your
						   // keyboard will reload the shader.
	
	// initialize screen, lock framerate to vsync:

	ofSetFrameRate(0);
	ofSetVerticalSync(true);

	
	// generate a box vboMesh from a primitive.
	
	ofBoxPrimitive tmpBox;
	// set the size to be 2 units.
	tmpBox.set(2);
	
	mVboBox = tmpBox.getMesh();
	
	// load depth image
	ofDisableArbTex();
	// note that we disable arb tex, meaning we will use normalized texture coordinates,
	// where a texture's x and y coordinate are each expressed as a normalized float.
	// this makes things slightly easier in the shader.
	
	// load the depth image into our texture
	ofLoadImage(mTexDepth, "depth_image.png");
	ofEnableArbTex();

	mCamMain.setupPerspective(false,60,0,10000);
	
}

//--------------------------------------------------------------
void ofApp::update(){
	
	if (isShaderDirty){
		
		// only reload the shader if it is 'dirty', i.e. the user has either requested reloading
		// or the 'isShaderDirty' flag has been initialized to true in setup()
		
		// Since we are using an ofPtr around the shader, the old shader will get destroyed
		// automatically as soon as we assign a new shader object to our mShdInstanced.
		
		ofLogNotice() << "Reloading Shader.";
		mShdInstanced = ofPtr<ofShader>(new ofShader());
		// most of the instanced drawing magic happens in the shaders:
#ifdef USE_PROGRAMMABLE_GL
		// if we are using programmable GL, we load the GLSL version 150 shader pair.
		mShdInstanced->load("shaders/instanced.vert", "shaders/instanced.frag");
#else
		// if we are using fixed function GL, we load the GLSL version 120 shader pair.
		mShdInstanced->load("shaders/instanced_120.vert", "shaders/instanced_120.frag");
#endif
		GLint err = glGetError();
		if (err != GL_NO_ERROR){
			ofLogNotice() << "Load Shader came back with GL error:	" << err;
		}
		
		isShaderDirty = false;
	}
	
	ofSetWindowTitle(ofToString(ofGetFrameRate(), 2));
	
}

//--------------------------------------------------------------
void ofApp::draw(){
	
	ofEnableDepthTest();
	// we don't care about alpha blending in this example, and by default alpha blending is on in openFrameworks > 0.8.0
	// so we de-activate it for now.
	ofDisableAlphaBlending();
	
	ofBackgroundGradient(ofColor(18,33,54), ofColor(18,22,28));
	
	ofSetColor(ofColor::white);
	mCamMain.begin();
	
	// bind the shader
	mShdInstanced->begin();
	// give the shader access to our texture
	mShdInstanced->setUniformTexture("tex0", mTexDepth, 0);
	// feed the shader a normalized float value that changes over time, to animate things a little
	mShdInstanced->setUniform1f("timeValue", (ofGetElapsedTimeMillis() % 30000) / 30000.0f);
	// we only want to see triangles facing the camera.
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);

	// let's draw 128 * 128 == 16384 boxes !
	mVboBox.drawInstanced(OF_MESH_FILL, 128*128);

	glDisable(GL_CULL_FACE);
	mShdInstanced->end();
	
	mCamMain.end();

	ofDisableDepthTest();

	ofSetColor(ofColor::white);
	ofDrawBitmapString("Use mouse to move camera.\nPress 'f' to toggle fullscreen;\nSPACEBAR to reload shader.", 10, 20);
	
	ofEnableAlphaBlending();
	
	
}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){
	
}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){
	
	switch (key) {
		case ' ':
			isShaderDirty = true;
			// mark the shader as dirty - this will reload the shader.
			break;
		case 'f':
			ofToggleFullscreen();
			break;
		default:
			break;
	}
}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){
	
}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){
	
}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){
	
}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){
	
}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){
	
}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){
	
}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){
	
}
