/***************************************************************
 
 Beispielprogramm "Quadrat"
 mit OpenGL
 Verwendung der GLUT-Bibliothek
 Prof. G. Schaefer, W. Oertel
 
 ****************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <GLUT/glut.h>
#include <math.h>

/*================================================*/

#define DELTA 0.1
GLfloat viewport[3];
GLfloat alpha = 0.2, beta = 0.8, distance = 5;
int linemode = 0;
int	rotate = 0;
int show_depth = 0;

unsigned int depth_fbo;
unsigned int depth_texture;
unsigned int color_fbo;
unsigned int color_texture;

void InitFBODepth() {
	glGenTextures(1, &color_texture); 
    glBindTexture(GL_TEXTURE_2D, color_texture); 
	
    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 1400, 1400, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL); 
	
    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_LINEAR);  
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);  
	
    glBindTexture(GL_TEXTURE_2D, 0);
	
    glGenRenderbuffersEXT(1, &depth_fbo);  
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depth_fbo);  
	
    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, 1400, 1400);
	glFramebufferTexture2DEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, GL_TEXTURE_2D, depth_texture, 0);
	
    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_fbo);	
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
}

void InitFBOColor() {
    glGenTextures(1, &color_texture); 
    glBindTexture(GL_TEXTURE_2D, color_texture); 
	
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1400, 1400, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); 
	
    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_LINEAR);  
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);  
	  
    glBindTexture(GL_TEXTURE_2D, 0);
}

void InitFBO() {
	InitFBODepth();
	InitFBOColor();
	
	glGenFramebuffersEXT(1, &color_fbo); 
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, color_fbo); 
	
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, color_texture, 0); 
    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_fbo); 
	
    GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); 
	
    if (status != GL_FRAMEBUFFER_COMPLETE_EXT) 
    {  
		exit(0);
    }  
	
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 
	
}

void ToogleDepthDisplay() {
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, color_fbo);
	
	if(show_depth) {
		glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0);
		glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, depth_fbo);
	} else {
		glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, 0);
		glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, color_texture, 0);
	}
	
	glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depth_fbo);

	GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); 
	
	if (status != GL_FRAMEBUFFER_COMPLETE_EXT) 
    {  
		exit(0); 
    }  
	
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);  
	
}

static void RenderScene(void);

void myIdle(void) {
	if(show_depth)
		alpha -= DELTA/10;
	else
		alpha -= DELTA/100;
	RenderScene();
}

void myKeyboard(unsigned char theKey, int mouseX, int mouseY)
{
	switch (theKey)
	{
		case 'v':
			distance -= DELTA;
			RenderScene();
			break;
		case 'z':
			distance += DELTA;
			RenderScene();
			break;
		case 'p':
			linemode++;
			linemode = linemode % 3;
			RenderScene();
			break;
		case 'r':
			if(rotate) {
				glutIdleFunc(0);
				rotate = 0;
			} else {
				glutIdleFunc(myIdle);
				rotate = 1;
			}
			break;
		case 'd':
			if(show_depth) show_depth = 0;
			else show_depth = 1;
			RenderScene();
			break;
		case 'e':
			exit(-1);
	}
}

void mySpecial(int specKey, int mouseX, int mouseY)
{
	switch (specKey)
	{
		case GLUT_KEY_LEFT:  
			alpha -= DELTA;
			RenderScene();
			break;
		case GLUT_KEY_RIGHT:  
			alpha += DELTA;
			RenderScene();
			break;
		case GLUT_KEY_UP:  
			beta -= DELTA;
			RenderScene();
			break;
		case GLUT_KEY_DOWN:
			RenderScene();
			beta += DELTA;
			break;
	}
}

GLfloat f1[4][3] = { {0,0,0},{0,0,1},{0,1,1},{0,1,0} };
GLfloat f2[4][3] = { {0,0,0},{1,0,0},{1,0,1},{0,0,1} };	
GLfloat f3[4][3] = { {1,0,0},{1,1,0},{1,1,1},{1,0,1} };
GLfloat f4[4][3] = { {0,1,0},{0,1,1},{1,1,1},{1,1,0} };	
GLfloat f5[4][3] = { {0,0,1},{1,0,1},{1,1,1},{0,1,1} };	
GLfloat f6[4][3] = { {0,0,0},{0,1,0},{1,1,0},{1,0,0} };	

static void myinit(void)
{   	
   	glClearColor (1.0, 1.0, 1.0, 1.0); /* white */
	
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_DEPTH_TEST);
	glShadeModel (GL_SMOOTH);
	glEnable(GL_BLEND);
	glAlphaFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_POLYGON_SMOOTH);
	glEnable(GL_LINE_SMOOTH);
	
	glDepthFunc(GL_LEQUAL);
	glClearDepth(1);
	
	InitFBO();
	
	/* cube list */
	glNewList(42, GL_COMPILE);
	glPushMatrix();
	
	glBegin(GL_POLYGON);/* left */
	glColor3f(0,0,0);
	glVertex3fv(f1[0]);
	glColor3f(0,0,1);
	glVertex3fv(f1[1]);
	glColor3f(0,1,1);
	glVertex3fv(f1[2]);
	glColor3f(0,1,0);
	glVertex3fv(f1[3]);
	glEnd(); 
	glBegin(GL_POLYGON);/* bot */
	glColor3f(0,0,0);
	glVertex3fv(f2[0]);
	glColor3f(1,0,0);
	glVertex3fv(f2[1]);
	glColor3f(1,0,1);
	glVertex3fv(f2[2]);
	glColor3f(0,0,1);
	glVertex3fv(f2[3]);
	glEnd(); 
	glBegin(GL_POLYGON);/* right */
	glColor3f(1,0,0);
	glVertex3fv(f3[0]);
	glColor3f(1,1,0);
	glVertex3fv(f3[1]);
	glColor3f(1,1,1);
	glVertex3fv(f3[2]);
	glColor3f(1,0,1);
	glVertex3fv(f3[3]);
	glEnd(); 
	glBegin(GL_POLYGON);/* top */
	glColor3f(0,1,0);
	glVertex3fv(f4[0]);
	glColor3f(0,1,1);
	glVertex3fv(f4[1]);
	glColor3f(1,1,1);
	glVertex3fv(f4[2]);
	glColor3f(1,1,0);
	glVertex3fv(f4[3]);
	glEnd();
	glBegin(GL_POLYGON); /* front */
	glColor3f(0,0,1);
	glVertex3fv(f5[0]);
	glColor3f(1,0,1);
	glVertex3fv(f5[1]);
	glColor3f(1,1,1);
	glVertex3fv(f5[2]);
	glColor3f(0,1,1);
	glVertex3fv(f5[3]);
	glEnd();
	glBegin(GL_POLYGON); /* back */
	glColor3f(0,0,0);
	glVertex3fv(f6[0]);
	glColor3f(0,1,0);
	glVertex3fv(f6[1]);
	glColor3f(1,1,0);
	glVertex3fv(f6[2]);
	glColor3f(1,0,0);
	glVertex3fv(f6[3]);
	glEnd();
	
	
	glPopMatrix();
	glEndList(); 
	
	/* cube list */
	glNewList(43, GL_COMPILE);
	glPushMatrix();
	
	glBegin(GL_LINE_LOOP);/* left */
	glColor3f(0,0,0);
	glVertex3fv(f1[0]);
	glVertex3fv(f1[1]);
	glVertex3fv(f1[2]);
	glVertex3fv(f1[3]);
	glEnd(); 
	glBegin(GL_LINE_LOOP);/* bot */
	glColor3f(0,0,0);
	glVertex3fv(f2[0]);
	glVertex3fv(f2[1]);
	glVertex3fv(f2[2]);
	glVertex3fv(f2[3]);
	glEnd(); 
	glBegin(GL_LINE_LOOP);/* right */
	glColor3f(0,0,0);
	glVertex3fv(f3[0]);
	glVertex3fv(f3[1]);
	glVertex3fv(f3[2]);
	glVertex3fv(f3[3]);
	glEnd(); 
	glBegin(GL_LINE_LOOP);/* top */
	glColor3f(0,0,0);
	glVertex3fv(f4[0]);
	glVertex3fv(f4[1]);
	glVertex3fv(f4[2]);
	glVertex3fv(f4[3]);
	glEnd();
	glBegin(GL_LINE_LOOP); /* front */
	glColor3f(0,0,0);
	glVertex3fv(f5[0]);
	glVertex3fv(f5[1]);
	glVertex3fv(f5[2]);
	glVertex3fv(f5[3]);
	glEnd();
	glBegin(GL_LINE_LOOP); /* back */
	glColor3f(0,0,0);
	glVertex3fv(f6[0]);
	glVertex3fv(f6[1]);
	glVertex3fv(f6[2]);
	glVertex3fv(f6[3]);
	glEnd();
	
	
	glPopMatrix();
	glEndList(); 
	
}

float data[1400*1400];

void darkenDepth() {
	int a;
	
	for(a = 0; a < 1400*1400; a++) {
		data[a] = pow(data[a], 8);
	}
}

void RenderFBO() {
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, color_fbo);
	glPushAttrib(GL_VIEWPORT_BIT | GL_ENABLE_BIT);
	
	glViewport(0, 0, 1400, 1400);
	
	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
	
	viewport[0] = distance*sin(beta)*sin(alpha)+0.5;
	viewport[1] = distance*cos(beta)+0.5;
	viewport[2] = distance*sin(beta)*cos(alpha)+0.5;
	
	gluLookAt(viewport[0], viewport[1], viewport[2],0.5, 0.5, 0.5, 0.0, cos(beta-3.141593*0.5), 0.0);

	if(linemode < 2) {
		glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		glEnable(GL_POLYGON_OFFSET_FILL);
		glPolygonOffset(1, 1);
		glCallList(42);
		glDisable(GL_POLYGON_OFFSET_FILL);
	}

	if(linemode) {
		glCallList(43);
	}

	if(show_depth) {
		glReadPixels(0, 0, 1400, 1400, GL_DEPTH_COMPONENT, GL_FLOAT, data);
		glDrawPixels(1400, 1400, GL_LUMINANCE, GL_FLOAT, data);
	}
	 
	glPopAttrib();
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
	
	
}

static void RenderScene(void)
{
	RenderFBO();
	
	glColor4f(1, 1, 1, 1);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();
	
    glTranslatef(0.0f, 0.0f, -2.0f); 
	
    glBindTexture(GL_TEXTURE_2D, color_texture); 
	
    glBegin(GL_QUADS); 
	
    glTexCoord2f(0.0f, 0.0f); 
    glVertex3f(-1.0f, -1.0f, 0.0f); 
	
    glTexCoord2f(0.0f, 1.0f); 
    glVertex3f(-1.0f, 1.0f, 0.0f); 
	
    glTexCoord2f(1.0f, 1.0f); 
    glVertex3f(1.0f, 1.0f, 0.0f); 

    glTexCoord2f(1.0f, 0.0f); 
    glVertex3f(1.0f, -1.0f, 0.0f); 
	
    glEnd(); 
	
    glBindTexture(GL_TEXTURE_2D, 0); 
	
    glutSwapBuffers(); 
}

static void myReshape(int w, int h)
{
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();  
    gluPerspective(30, w/h, 1.0, 100.0);
    glMatrixMode(GL_MODELVIEW);	
}

/*  Main Loop */

int main(int argc, char** argv)
{
    /* GLUT functions to set up the window */
    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_ALPHA | GLUT_DEPTH | GLUT_MULTISAMPLE | GLUT_ACCUM);
    glutInitWindowPosition (0, 0);
	glutInitWindowSize(700, 700);
	glutInit(&argc, argv);
    glutCreateWindow (argv[0]);
    
    myinit();			/* Initialization */
	puts(glGetString(GL_VERSION));
		
    glutKeyboardFunc(myKeyboard);
    glutSpecialFunc(mySpecial);
    glutReshapeFunc (myReshape); /* Function to call when window changes size */
    glutDisplayFunc(RenderScene); /* Creating the Szene */
    glutMainLoop();	
	
	return EXIT_SUCCESS;
}





