//FBO on a mac-- a working example.
//dylan moore
//notes.binarytree.org

// a few NOTES on setting this up in Xcode:
//Be sure to include an image in your build directory called "NeHe.tga"
//Where is the build directory? In xcode, you're either compiling in 'relase'
//or 'debug' mode. It's a dropdown at the top of your editor.
//in the project folder in the finder, you'll see a 'build' folder.
//inside of that, you'll see a 'release' and/or 'debug' folder.
//Put your image inside of the one that you're set to in xcode.
//Warning: Later, as a standalone app, that path will change.
//That is a platform specific thing, file systems.
//Include the OpenGL and GLUT frameworks, and this file in a C++ tool,
//compile and enjoy.
//-d

#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#include <OpenGL/glu.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <time.h>


#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <assert.h>
using namespace std;

//basic rotating cube w/ texture map thanks to nehe.gamedev.net/data/lessons/lesson.asp?lesson=06
//Nice starting point to show an advanced technique.

// Constants -----------------------------------------------------------------
//These don't have to be a Power of Two (128,256,512.. etc) but I've started them out here. 
//Change these to whatever window size you'd like. Your FBO will also be created at this size.
#define kWindowWidth	512
#define kWindowHeight	512

// Structures -------------------(used for loading tga)-------------------------------

typedef struct				// Create A Structure
{
	GLubyte	*imageData;		// Image Data (Up To 32 Bits)
	GLuint	bpp;			// Image Color Depth In Bits Per Pixel.
	GLuint	width;			// Image Width
	GLuint	height;			// Image Height
	GLuint	texID;			// Texture ID Used To Select A Texture
} TextureImage;				// Structure Name

// Function Prototypes -------------------------------------------------------

GLvoid InitGL(GLvoid);
GLvoid DrawGLScene(GLvoid);
GLvoid ReSizeGLScene(int Width, int Height);
GLvoid Idle(GLvoid);
bool LoadTGA(TextureImage *texture, char *filename);
GLvoid LoadGLTextures(GLvoid);

// Global Variables ----------------------------------------------------------

TextureImage 	texture[1];		// Texture Storage ( NEW )

GLfloat			xrot;			// X Rotation ( NEW )
GLfloat			yrot;			// Y Rotation ( NEW )
GLfloat			zrot;			// Z Rotation ( NEW )


//A nice macro for checking your framebuffer status. 
//I haven't filled out all of the possible status enums.

#define CHECK_FRAMEBUFFER_STATUS() \
{  \
	GLenum status; \
		status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);\
			cout<<status<<": ";\
				switch(status) \
				{ \
					case GL_FRAMEBUFFER_COMPLETE_EXT:\
						cout<<"Success making FBO."<<endl;\
							break; \
							case GL_FRAMEBUFFER_UNSUPPORTED_EXT: \
								cout<<"Unsupported FBO."<<endl;\
									/* choose different formats */ \
									break;  \
									default: \
										cout<<"Error making FBO. Put more checks in CHECK_FRAMEBUFFER STATUS"<<endl;\
											/* programming error; will fail on all hardware */  \
											assert(0); \
				} \
} 

// FBO Setup ----------------------------------------------------------------
GLuint fb, depth_rb, tex;  //globals for the framebuffer, a depth renderbuffer if you need it, and a texture handle.
void fbo_setup()
{
	//source thanks to:
	//http.download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_FrameBuffer_Object.pdf
	
	// initialize texture
	glGenTextures(1, &tex); // texture handle
	glBindTexture(GL_TEXTURE_2D, tex); //bind it.
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWindowWidth, kWindowHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); 
	// (set texture parameters here) 
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); 
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
	
	//most fbo tutorials suggest the following, but they're all LIES-- the nvidia 7600 does not like it.
	//put the params above, not commented out.
	//I'm sure experimentation and greater knowledge of the texparams will yeild nice results.
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	
	glGenerateMipmapEXT(GL_TEXTURE_2D); //generate the mipmaps for your texture.
	
	
	// create objects
	glGenFramebuffersEXT(1, &fb); // frame buffer 
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb); //bind the frame buffer
	
	// attach texture to framebuffer color buffer
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, tex, 0);
	
	//if you need depth:
	/*
	glGenRenderbuffersEXT(1, &depth_rb); // render buffer
	// initialize depth renderbuffer
	glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_rb); 
	glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, kWindowWidth, kWindowHeight); 
	
	// attach renderbuffer to framebufferdepth buffer
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_rb); 
	*/
	CHECK_FRAMEBUFFER_STATUS(); //has all gone according to plan?

}







// Main --------------------------(and cleanup)----------------------------------
void cleanup(); //prototype for your atexit function
int main(int argc, char** argv)
{
	atexit(cleanup); //C++ callback to a clean up function at exit. Boo GLUT.
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(kWindowWidth, kWindowHeight); 
    glutInitWindowPosition (100, 100);
    glutCreateWindow (argv[0]);
	
	InitGL();
	
    glutDisplayFunc(DrawGLScene); 
    glutReshapeFunc(ReSizeGLScene);
    //glutIdleFunc(Idle); //Do not want.
	
	xrot = 0;
	yrot = 0;		
	zrot = 0;
	fbo_setup();
    glutMainLoop();
	
    return 0;
}
void cleanup() //be nice to your managed memory...
{
    glDeleteFramebuffersEXT(1, &fb);
	glDeleteRenderbuffersEXT(1, &depth_rb);
	cout<<"Cleaned up.";
}

// InitGL --------------------------------------------------------------------

GLvoid InitGL(GLvoid)
{
	
	LoadGLTextures();							// Load The Texture(s) ( NEW )
	glEnable(GL_TEXTURE_2D);					// Enable Texture Mapping ( NEW )
	
	glClearColor(1.0f, 0.0f, 0.0f, 0.0f);		// This Will Clear The Background Color To Black
	glClearDepth(1.0);							// Enables Clearing Of The Depth Buffer
	glDepthFunc(GL_LESS);						// The Type Of Depth Test To Do
	glEnable(GL_DEPTH_TEST);					// Enables Depth Testing 
	glShadeModel(GL_SMOOTH);					// Enables Smooth Color Shading
												//glCullFace(GL_BACK);
	//glLightModelf(GL_LIGHT_MODEL_TWO_SIDE,1.0f);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();							// Reset The Projection Matrix
	
	gluPerspective(45.0f, (GLfloat) kWindowWidth / (GLfloat) kWindowHeight, 0.1f, 100.0f);	
	// Calculate The Aspect Ratio Of The Window
	
	glMatrixMode(GL_MODELVIEW);
	
}

void render_to_texture()
{
	// render to the FBO
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fb); 
	glPushAttrib( GL_VIEWPORT_BIT |GL_COLOR_BUFFER_BIT |GL_DEPTH_BUFFER_BIT);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glViewport( 0, 0, kWindowWidth, kWindowHeight );
	
}

void render_to_screen()
{
	glPopAttrib();
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 

}


void render_box(bool fbo) //0 is texture from file, 1 is from fbo texture.
{ 
	if(fbo==true)
	{
		glBindTexture(GL_TEXTURE_2D, tex);
	}
	else
	{
		glBindTexture(GL_TEXTURE_2D, texture[0].texID);
	}	
	
	glBegin(GL_QUADS);
	// Front Face
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// Bottom Left Of The Texture and Quad
	glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// Bottom Right Of The Texture and Quad
	glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// Top Right Of The Texture and Quad
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// Top Left Of The Texture and Quad
																// Back Face
	glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// Bottom Right Of The Texture and Quad
	glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// Top Right Of The Texture and Quad
	glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// Top Left Of The Texture and Quad
	glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// Bottom Left Of The Texture and Quad
																// Top Face
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// Top Left Of The Texture and Quad
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// Bottom Left Of The Texture and Quad
	glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// Bottom Right Of The Texture and Quad
	glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// Top Right Of The Texture and Quad
																// Bottom Face
	glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// Top Right Of The Texture and Quad
	glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// Top Left Of The Texture and Quad
	glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// Bottom Left Of The Texture and Quad
	glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// Bottom Right Of The Texture and Quad
																// Right face
	glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// Bottom Right Of The Texture and Quad
	glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// Top Right Of The Texture and Quad
	glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// Top Left Of The Texture and Quad
	glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// Bottom Left Of The Texture and Quad
																// Left Face
	glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// Bottom Left Of The Texture and Quad
	glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// Bottom Right Of The Texture and Quad
	glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// Top Right Of The Texture and Quad
	glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// Top Left Of The Texture and Quad
	glEnd();
	
	glBindTexture(GL_TEXTURE_2D, 0); //disable any bound textures.
	
}


// DrawGLScene ---------------------------------------------------------------

GLvoid DrawGLScene(GLvoid)
{    
	
	//glDisable(GL_DEPTH);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	glTranslatef(0.0f,0.0f,-5.0f);
	
	glRotatef(xrot,1.0f,0.0f,0.0f);				// Rotate On The X Axis
	glRotatef(yrot,0.0f,1.0f,0.0f);				// Rotate On The Y Axis
	glRotatef(zrot,0.0f,0.0f,1.0f);				// Rotate On The Z Axis
	
	
//Here's the meat of what you need to do to render to an fbo and use it:
	
	//set up to render to the fbo,
	render_to_texture();	
	//draw a textured box, which will only show up in the bound texture,
	render_box(false);
	//set drawing back to the screen,
	render_to_screen();	
	//render a box with the fbo as a texture:
	render_box(true);
	//all done. Enjoy.
	
    glutSwapBuffers();
	xrot += 0.03f;			// X Axis Rotation
	yrot += 0.02f;			// Y Axis Rotation
	zrot += 0.04f;			// Z Axis Rotation
	
	glutPostRedisplay();
	
    //glFlush(); //Do not want.
}

// ReSizeGLScene ------------------------------------------------------------

GLvoid ReSizeGLScene(int Width, int Height)
{
    glViewport (0, 0, (GLsizei) Width, (GLsizei) Height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
	
    gluPerspective(45.0, (GLfloat) Width / (GLfloat) Height, 0.1, 10.0);
	
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

// LoadGLTextures ----------------------------------------------------------

GLvoid LoadGLTextures(GLvoid)
{
	LoadTGA(texture, "NeHe.tga"); //You'll need to include this file in your build directory.
}

/********************> LoadTGA() <*****/
bool LoadTGA(TextureImage *texture, char *filename)			// Loads A TGA File Into Memory
{    
	GLubyte		TGAheader[12]={0,0,2,0,0,0,0,0,0,0,0,0};	// Uncompressed TGA Header
	GLubyte		TGAcompare[12];								// Used To Compare TGA Header
	GLubyte		header[6];									// First 6 Useful Bytes From The Header
	GLuint		bytesPerPixel;								// Holds Number Of Bytes Per Pixel Used In The TGA File
	GLuint		imageSize;									// Used To Store The Image Size When Setting Aside Ram
	GLuint		temp;										// Temporary Variable
	GLuint		type=GL_RGBA;								// Set The Default GL Mode To RBGA (32 BPP)
	
	FILE *file = fopen(filename, "rb");						// Open The TGA File
	
	if(	file==NULL ||										// Does File Even Exist?
		fread(TGAcompare,1,sizeof(TGAcompare),file)!=sizeof(TGAcompare) ||	// Are There 12 Bytes To Read?
		memcmp(TGAheader,TGAcompare,sizeof(TGAheader))!=0				||	// Does The Header Match What We Want?
		fread(header,1,sizeof(header),file)!=sizeof(header))				// If So Read Next 6 Header Bytes
	{
		fclose(file);										// If Anything Failed, Close The File
		return false;										// Return False
	}
	
	texture->width  = header[1] * 256 + header[0];			// Determine The TGA Width	(highbyte*256+lowbyte)
	texture->height = header[3] * 256 + header[2];			// Determine The TGA Height	(highbyte*256+lowbyte)
    
 	if(	texture->width	<=0	||								// Is The Width Less Than Or Equal To Zero
		texture->height	<=0	||								// Is The Height Less Than Or Equal To Zero
		(header[4]!=24 && header[4]!=32))					// Is The TGA 24 or 32 Bit?
	{
		fclose(file);										// If Anything Failed, Close The File
		return false;										// Return False
	}
	
	texture->bpp	= header[4];							// Grab The TGA's Bits Per Pixel (24 or 32)
	bytesPerPixel	= texture->bpp/8;						// Divide By 8 To Get The Bytes Per Pixel
	imageSize		= texture->width*texture->height*bytesPerPixel;	// Calculate The Memory Required For The TGA Data
	
	texture->imageData=(GLubyte *)malloc(imageSize);		// Reserve Memory To Hold The TGA Data
	
	if(	texture->imageData==NULL ||							// Does The Storage Memory Exist?
		fread(texture->imageData, 1, imageSize, file)!=imageSize)	// Does The Image Size Match The Memory Reserved?
	{
		if(texture->imageData!=NULL)						// Was Image Data Loaded
			free(texture->imageData);						// If So, Release The Image Data
		
		fclose(file);										// Close The File
		return false;										// Return False
	}
	
	for(GLuint i=0; i<int(imageSize); i+=bytesPerPixel)		// Loop Through The Image Data
	{														// Swaps The 1st And 3rd Bytes ('R'ed and 'B'lue)
		temp=texture->imageData[i];							// Temporarily Store The Value At Image Data 'i'
		texture->imageData[i] = texture->imageData[i + 2];	// Set The 1st Byte To The Value Of The 3rd Byte
		texture->imageData[i + 2] = temp;					// Set The 3rd Byte To The Value In 'temp' (1st Byte Value)
	}
	
	fclose (file);											// Close The File
	
	// Build A Texture From The Data
	glGenTextures(1, &texture[0].texID);					// Generate OpenGL texture IDs
	
	glBindTexture(GL_TEXTURE_2D, texture[0].texID);			// Bind Our Texture
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);	// Linear Filtered
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);	// Linear Filtered
	
	if (texture[0].bpp==24)									// Was The TGA 24 Bits
	{
		type=GL_RGB;										// If So Set The 'type' To GL_RGB
	}
	
	glTexImage2D(GL_TEXTURE_2D, 0, type, texture[0].width, texture[0].height, 0, type, GL_UNSIGNED_BYTE, texture[0].imageData);
	
	return true;											// Texture Building Went Ok, Return True
}
