/*********************************************************************
*                                                                    *          
*                              FLY                                   *
*                           --E#Y#E--	    			     *
*                         ===MUSCLE===                               *
*                                                                    *
*********************************************************************/
/* Header Files */
#include "opengl.h"

/*-------------------------------------------------------------------------*/ 

PFNGLGENBUFFERSARBPROC glGenBuffers = NULL;
PFNGLBINDBUFFERPROC glBindBuffer = NULL;
PFNGLBUFFERDATAPROC glBufferData = NULL;

/* Buffer */
GLuint vbos3[MAX_BUFFERS];

/* Structure Float */
struct f_float
{
        struct f_color col;
	struct f_texture tex;
	struct f_triangles tri;
}gl;

/* Shader Program */
struct f_Shader
{
        unsigned int shaderProgram;
        unsigned int basicProgram;
        unsigned int cubeProgram;
}g;

/*-------------------------------------------------------------------------*/ 

/* Motor */
char code03;
float code1;
float code2;
float xRotation;
float zRotation;

/* Scale */
float Fplus = 1.0;
float m_scale;

/* Cube Map */
GLuint cubeMapTexID;

/*-------------------------------------------------------------------------*/ 

/* Bool */
char Fday = TRUE;
char Fnight = FALSE;
char Fcloud = FALSE;
char Fgrid = FALSE;
char Fiso = FALSE;
char Fwind = FALSE;
char Ftemp = FALSE;
char Fpurple = FALSE;

/*-------------------------------------------------------------------------*/ 

/* Vector */
GLfloat vcolor3[AV];
GLfloat tcircle3[AV];
GLfloat vcircle3[AV];

/* Buffer */
GLfloat m_colors[AV];
GLfloat m_texCoords[AV];
GLfloat m_vertices[AV];

/* Index */
GLuint m_indices[AV];

/* Texture */
GLubyte *bla;
GLubyte *des;
GLubyte *sea;
GLubyte *sky;

/*-------------------------------------------------------------------------*/ 

/* Load Texture */
void loadTexture()
{
	#if PNG_TEXTURE
	/* Textures PNG */
	const char forestPNG[] = "PIXEL/forest.png";
	const char desertPNG[] = "PIXEL/desert.png";
	const char seaocePNG[] = "PIXEL/sea.png";
	const char cloudPNG[] = "PIXEL/cloud.png";
	
	/* Read PNG */
	read_png(forestPNG, &bla);		
	read_png(desertPNG, &des);
	read_png(seaocePNG, &sea);
	read_png(cloudPNG, &sky);	

	/* Load Textures */
	printf(" PNG TEXTURES WORLD: \n");
	#endif
}

/*-------------------------------------------------------------------------*/ 

/* GLEW Init */
void glewStatus()
{
        /* GLEW Init */
        GLenum err = glewInit();
        if(GLEW_OK != err)
        {
                fprintf(stderr," GLEW ERROR: %s \n", glewGetErrorString(err));
        }
        
        /* GLEW Version */
        fprintf(stdout," GLEW VERSION: %s \n", glewGetString(GLEW_VERSION));
        
        /* Version 130 */
        if(GLEW_VERSION_1_3)
        {
                printf(" OPENGL SUPPORTED: \n");
        }
        
        /* Shader Version */
	char *shaderversion = (char *)glGetString(GL_SHADING_LANGUAGE_VERSION); 
	if(GL_SHADING_LANGUAGE_VERSION)
	{
	        printf(" SHADER VERSION: %s \n", shaderversion);
	}
	
	/* OpenGL Version */
	char *version = (char *)glGetString(GL_VERSION); 
	if(GL_VERSION)
	{
	        printf(" OPENGL VERSION: %s \n", version);
	}
}

/*-------------------------------------------------------------------------*/ 

/* Shader Language */
char textureShader3()
{	
        /* Fly Version */
        static const GLchar fly_vert[] = "GLSL/fly.vert";
        static const GLchar fly_frag[] = "GLSL/fly.frag";

	/* Create GLSL Shaders / Compile Shaders */
        createShader(fly_vert, fly_frag); 
        createProgram(g.shaderProgram);
	/* Init */
	Location(g.shaderProgram, 0, "a_Vertex");
	Location(g.shaderProgram, 1, "a_TexCoord");
	Location(g.shaderProgram, 2, "a_Color");
	/* Re link the program */
	/*linkProgram(g.shaderProgram);*/
        /*bindShader(g.shaderProgram);*/

        /*-----------------------------------------------------------------*/

        /* Cube Version */
        static const GLchar cube_vert[] = "GLSL/cube.vert";
        static const GLchar cube_frag[] = "GLSL/cube.frag";

        /* Create GLSL Shaders / Compile Shaders */
        createShader(cube_vert, cube_frag);
        createProgram(g.cubeProgram);
	/* Init */
	Location(g.cubeProgram, 0, "c_Vertex");
	Location(g.cubeProgram, 1, "c_TexCoord");
	Location(g.cubeProgram, 2, "c_Normal");
	/* Re link the program */
	linkProgram(g.cubeProgram);
        /*bindShader(g.cubeProgram);*/
	
	/*-----------------------------------------------------------------*/
 
         /* Basic Version */
        static const GLchar basic_vert[] = "GLSL/basic.vert";
        static const GLchar basic_frag[] = "GLSL/basic.frag";
 
        /* Create GLSL Shaders / Compile Shaders */
        createShader(basic_vert, basic_frag);
        createProgram(g.basicProgram);
        /* Init */
	Location(g.basicProgram, 0, "b_Vertex");
	Location(g.basicProgram, 1, "b_TexCoord");
	/* Re link the program */
	linkProgram(g.basicProgram);
        bindShader(g.basicProgram);
        
        return TRUE;
}

/*-------------------------------------------------------------------------*/ 

/* Init Grid */
char initGrid()
{	
        glGenBuffers = (PFNGLGENBUFFERSARBPROC)glXGetProcAddress
        ((const GLubyte*)"glGenBuffers");
        glBindBuffer = (PFNGLBINDBUFFERPROC)glXGetProcAddress
        ((const GLubyte*)"glBindBuffer");
        glBufferData = (PFNGLBUFFERDATAPROC)glXGetProcAddress
        ((const GLubyte*)"glBufferData");

	/* Buffer */
        if(!glGenBuffers || !glBindBuffer || !glBufferData)
        {
                printf("VBO: NOT SUPPORTED \n");
                return FALSE;
        }
        
        /*-----------------------------------------------------------------*/
 
	#if LAND_VECTOR
	/* Texture */
        const char forestFile[] = "DATA/forest.fs";
        const char desertFile[] = "DATA/desert.fs";
        const char seaoceFile[] = "DATA/seaoce.fs";
        const char cloudFile[] = "DATA/cloud.fs";
        
	/* Texture */
	GridRender(forestFile);
	GridRender(desertFile);
	GridRender(seaoceFile);
	GridRender(cloudFile); 
	
	/*-----------------------------------------------------------------*/
	
	/* Grid */
        const char squareFile[] = "DATA/square.fs";
        const char gridFile[] = "DATA/grid.fs";
        const char isobarFile[] = "DATA/isobar.fs";
        const char terrainFile[] = "DATA/terrain.fs";
        const char windFile[] = "DATA/wind.fs";
        const char temperaturFile[] = "DATA/temperatur.fs";
	
	/* Grid */
	GridRender(squareFile);
	GridRender(isobarFile);
	GridRender(terrainFile);
	GridRender(gridFile);
	GridRender(windFile);
	GridRender(temperaturFile);
	#endif
	
	/*-----------------------------------------------------------------*/

	/* Create Color Buffer */
	glGenBuffers(MAX_BUFFERS, &vbos3[0]);
        glBindBuffer(GL_ARRAY_BUFFER, vbos3[COLOR_BUFFER]); 
        glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 4 * AV, &m_colors[0], GL_STATIC_DRAW);

	/* Create Texture Buffer */ 
        glBindBuffer(GL_ARRAY_BUFFER, vbos3[NORMAL_BUFFER]); 
        glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 2 * AV, &m_texCoords[0], GL_STATIC_DRAW);

        /* Create Vertex Buffer */        
        glBindBuffer(GL_ARRAY_BUFFER, vbos3[VERTEX_BUFFER]); 
        glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * AV, &m_vertices[0], GL_STATIC_DRAW);
               
        /*-----------------------------------------------------------------*/
        
        /* Create Element Buffer */
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbos3[INDEX_BUFFER]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * 3 * AV, &m_indices[0], GL_STATIC_DRAW);
                
        return TRUE;
}

/*-------------------------------------------------------------------------*/ 

/* Main Init */
char initMain()
{	
	/* Background Color */
	glEnable(GL_BLEND);
	glEnable(GL_AMBIENT);
	glEnable(GL_LIGHT7);
	glEnable(GL_LIGHTING);	
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_SCISSOR_TEST);
	glEnable(GL_COLOR_MATERIAL);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	
	/*-----------------------------------------------------------------*/
	
	/* Night */
	if(Fnight)
	{	
		glClearColor( 0.10f , 0.20f , 0.30f , 0.5f );
		glDisable(GL_LIGHT7);
	}
	/* Day */
	else if(Fday)
	{	
		glClearColor( 0.30f , 0.50f , 0.70f , 0.5f );
		glDisable(GL_LIGHTING);
        }
	/* Grid */
	else
	{	
		glClearColor( 0.01f , 0.01f , 0.01f, 0.5f );
		glDisable(GL_LIGHTING);
        }
        
        /*------------------------------------------------------------------*/
 
        /* Clear everything */
        glClearDepth(1.0);
        
        /* Enable */
        glEnable(GL_CULL_FACE);
	glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LEQUAL);
        
        /* Viewport */
        GLint viewport[4];
        glGetIntegerv(GL_VIEWPORT, viewport);
        /* Viewport[2] stores the width of the viewport, vieport[3] stores the height */
        /* We pass these into our font so the ortho mode can set the resolution for the window */
        TMFont("FONT/mono.png", viewport[2], viewport[3], 25.0f);

        /*------------------------------------------------------------------*/
        
        /* Initialize the Font */
        if(!initFont()) 
        {
                printf(" COULD NOT INITIALIZE THE FONT \n"); 
                return FALSE;
        }
        /* Initialize the Flybox */
        if(!initBox()) 
        {
                printf(" COULD NOT INITIALIZE THE FLYBOX \n"); 
                return FALSE;
        } 
        /* Initialize the Cockpit */
        if(!initCockpit()) 
        {
                printf(" COULD NOT INITIALIZE THE COCKPIT \n"); 
                return FALSE;
        } 
        /* Initialize the Grid */
        if(!initGrid()) 
        {
                printf(" COULD NOT INITIALIZE THE GRID \n"); 
                return FALSE;
        } 
        /* Initialize the Shader */
        if(!textureShader3()) 
        {
                printf(" COULD NOT INITIALIZE THE SHADER \n"); 
                return FALSE;
        } 
        
        return TRUE;
}

/*-------------------------------------------------------------------------*/ 

/* X / Y / Z / W */
float F_Vertex(float fx, float fy, float fz)
{
        float vcircle3[3];
        
        vcircle3[0] = 0;
        vcircle3[1] = 0; 
        vcircle3[2] = 0;
        
        vcircle3[0] = fx;
        vcircle3[1] = fy; 
        vcircle3[2] = fz;
        
        return *vcircle3;     
}

/*-------------------------------------------------------------------------*/ 

/* R / G / B / A */
float F_Color(float fr, float fg, float fb, float fa) 
{
        float vcolor3[4];
        
        vcolor3[0] = 0;
        vcolor3[1] = 0; 
        vcolor3[2] = 0;
        vcolor3[3] = 0;
        
        vcolor3[0] = fr;
        vcolor3[1] = fg; 
        vcolor3[2] = fb;
        vcolor3[3] = fa;
        
        return *vcolor3;       
}

/*-------------------------------------------------------------------------*/ 

/* S / T / P / Q */
float F_Textur(float fs, float ft)
{
        float tcircle3[2];
        
        tcircle3[0] = 0;
        tcircle3[1] = 0;

        tcircle3[0] = fs;
        tcircle3[1] = ft;
        
        return *tcircle3;       
}

/*-------------------------------------------------------------------------*/

/* Render Grid */
void GridRender(const char *nFile)
{
        char code[ID];
	/*int world = 0;*/
	int counter = 0;
	const float pt = 0.05;
	
	/* Load File */
	FILE *grid;
	grid=fopen(nFile,"r");

        /* Grid File */
	while((fscanf(grid,"%s  %f %f %f %f  %f %f  %f %f %f", &code[0], 
	        &gl.col.r, &gl.col.g, &gl.col.b, &gl.col.a,
	        &gl.tex.s, &gl.tex.t, &gl.tri.x, &gl.tri.y, &gl.tri.z)) != EOF)
	{ 	
		/* Counter */
		counter++;
	
		#if LAND_VECTOR
	        /* MASSTAB */
	        m_scale = (gl.tri.z * Fplus) * pt;
	        m_scale = (gl.tri.y * Fplus) * pt; 
		m_scale = (gl.tri.x * Fplus) * pt; 
		
		/* VERTEX */
		F_Color(gl.col.r, gl.col.g, gl.col.b, gl.col.a);
		F_Textur(gl.tex.s, gl.tex.t); 
		F_Vertex(gl.tri.x, gl.tri.y, gl.tri.z);
		
		/* ELEMENTS */
		/*m_indices[world++];*/
		/*m_indices[world++];*/
		/*m_indices[world++];*/
		#else
	
		/* MASSTAB */
		gl.tri.x = (gl.tri.x * Fplus); 
	        gl.tri.y = (gl.tri.y * Fplus); 
	        gl.tri.z = (gl.tri.z * Fplus);
	
		/* ARRAY */
		glColor4f(gl.col.r, gl.col.g, gl.col.b, gl.col.a);
		glTexCoord2f(gl.tex.s, gl.tex.t);
		glScalef(gl.tri.x, gl.tri.y, gl.tri.z);
		glVertex3f(gl.tri.x, gl.tri.y, gl.tri.z);
		#endif
	}
	
	/* Buffer Counter */
	/*printf(" BUFFER WORLD: %d \n", counter);*/
      
        /* Close File */
	fclose(grid);
}

/*-------------------------------------------------------------------------*/ 

/* Polygon Mode */
void PolygonCircle(const GLenum mode, const GLenum front, const char *nFile)
{
	glPolygonMode(GL_FRONT_AND_BACK, front); 
	glBegin(mode);	
	GridRender(nFile);
	glEnd();
}

/*-------------------------------------------------------------------------*/ 

/* Draw Arrays */
void DrawCircle(const GLenum mode, const GLenum front, const GLint count, const GLint offset)
{
        /*glTranslatef(0.0, 0.0, -code1);*/
        glPolygonMode(GL_FRONT_AND_BACK, front);
        /*glDrawArrays(GL_LINES, 888, 888);*/
        glPushMatrix();   
        
        /* Masstab */
        glScalef(m_scale, m_scale, m_scale);
        
        /* Move Line */
        /*glRotatef(xRotation, 1.0f, 0.0f, 0.0f);*/
	/*glRotatef(zRotation, 0.0f, 0.0f, 1.0f);*/ 
        
	#if DRAW_ELEMENTS
	glDrawElements(mode, count, GL_UNSIGNED_INT, 0);
	#else
	glDrawArrays(mode, offset, count);
	/*glDrawArrays(GL_LINES, 3480, 3480);*/
	#endif
	glPopMatrix();
}

/*-------------------------------------------------------------------------*/ 

/* Render Landscape */
void SubImage(const GLint count, const char *nFile, GLubyte *pixel)
{
        /* Enable Texture */
	glEnable(GL_TEXTURE_2D);
	
	/* Filtering Mode */
	/*glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);*/
	/*glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);*/
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
	/*glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);*/
	/*glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);*/
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        
        /* Image Data */
        glTexImage2D(GL_TEXTURE_2D,
                     0,                      /* level */
                     GL_RGBA8,               /* internal format */
                     128,                    /* width */
                     128,                    /* height */ 
                     0,                      /* border */
                     GL_BGRA,                /* format */ 
                     GL_UNSIGNED_BYTE,       /* type */
                     pixel                   /* data */
        );                 
                                                 
	glTexSubImage2D(GL_TEXTURE_2D,0,0,0,128,128,GL_BGRA,GL_UNSIGNED_BYTE,pixel);	
	glTexSubImage2D(GL_TEXTURE_2D,0,2,0,128,128,GL_BGRA,GL_UNSIGNED_BYTE,pixel);
	glTexSubImage2D(GL_TEXTURE_2D,0,0,2,128,128,GL_BGRA,GL_UNSIGNED_BYTE,pixel);
	glTexSubImage2D(GL_TEXTURE_2D,0,2,2,128,128,GL_BGRA,GL_UNSIGNED_BYTE,pixel);

	#if LAND_VECTOR
	DrawCircle(GL_TRIANGLES, GL_FILL, count, 0);
	#else
	PolygonCircle(GL_TRIANGLES, GL_FILL, nFile);
	#endif
	
	/* Disable Texture */
	glDisable(GL_TEXTURE_2D);
}

/*-------------------------------------------------------------------------*/ 

/* Point Speed */
void pointSpeed(float dt)
{
        const float xSpeed = 20.0f;
        const float ySpeed = 60.0f;
        
        xRotation += xSpeed * dt;
        zRotation += ySpeed * dt;
        
        if(xRotation > 360.0f) { 
           xRotation -= 360.0f;
        }
        if(zRotation > 360.0f) { 
           zRotation -= 360.0f;
        }
}

/*-------------------------------------------------------------------------*/ 

/* Point Grid */
void pointGrid(float dt)
{
        float speed = 0.2f;
        float maxSpeed = +90.0f;
        float minSpeed = -90.0f; 
 
        /* Speed */
        pointSpeed(dt);
 
        /* Line */ 
        if(code03)
        {
                  code1 += speed;
                  code2 -= speed;
        }
        else
        {
                  code1 -= speed;
                  code2 += speed;
        }
        
        /* Limit */
        if(code1 >= maxSpeed)
        {
                  code03 = FALSE;
        }
        else if(code1 <= minSpeed)
        {
                  code03 = TRUE;
        }
}

/*-------------------------------------------------------------------------*/ 

/* Moving Objects */
void vectorObjects() 
{	        
	/* Primitives */
        const GLenum modeP = GL_POINTS;
        const GLenum modeL = GL_LINES;
        const GLenum modeT = GL_TRIANGLES;
        
        /* Mode */
        const GLenum frontP = GL_POINT;
        const GLenum frontL = GL_LINE;
        const GLenum frontF = GL_FILL;
        
        /*-----------------------------------------------------------------*/

	/* Offset */
        const GLint squareOffset =  888;     /*  900 */ 
        const GLint isobarOffset = 1752;     /* 1800 */
        const GLint terrainOffset = 2616;    /* 2700 */
        const GLint gridOffset = 3480;       /* 3600 */
        const GLint windOffset = 4344;       /* 4500 */
        const GLint tempOffset = 5208;       /* 5300 */
        
        /* Count */
        const GLint countP = 864;       /* 864 */
        const GLint countL = 864;       /* 864 */
        const GLint countT = 864;       /* 864 */
        
        /*-----------------------------------------------------------------*/

        /* Size */
        float lineWidth = 3.0f;
        float pointSize = 7.0f;
      
	/* Enable Anti-aliasing */
        glEnable(GL_POINT_SMOOTH);
        glLineWidth(lineWidth);
        glPointSize(pointSize);
        
        /*-----------------------------------------------------------------*/

	/* Terrain */
	if(Fgrid || Fiso) 
	{
	        DrawCircle(modeP, frontP, countP, squareOffset);
	        DrawCircle(modeL, frontL, countL, gridOffset);
	        DrawCircle(modeT, frontF, countT, terrainOffset); 
	        
	        /* Isobar */
	        if(Fiso) 
	        {
	                DrawCircle(modeP, frontP, countP, isobarOffset);
	                DrawCircle(modeT, frontL, countL, isobarOffset);
	                DrawCircle(modeT, frontF, countT, isobarOffset);
	        }
	}
	/* Wind */
	else if(Fwind) 
	{
	        DrawCircle(modeP, frontP, countP, squareOffset);
	        glEnable(GL_CULL_FACE);
	        DrawCircle(modeT, frontF, countL, gridOffset);
	        glDisable(GL_CULL_FACE);
	        DrawCircle(modeT, frontF, countT, windOffset); 
	}
	/* Celsius */
	else if(Ftemp) 
	{
	        DrawCircle(modeP, frontP, countP, squareOffset);
	        DrawCircle(modeT, frontL, countL, gridOffset); 
	        DrawCircle(modeT, frontF, countT, tempOffset); 
	}
	
	/*-----------------------------------------------------------------*/
	
	/* Disable Anti-aliasing */
        glDisable(GL_POINT_SMOOTH);
}	
	
/*-------------------------------------------------------------------------*/ 

/* Moving Objects */
void polygonObjects() 
{        
	/* Primitives */
        const GLenum modeP = GL_POINTS;
        const GLenum modeL = GL_LINES;
        const GLenum modeT = GL_TRIANGLES;
        
        /* Mode */
        const GLenum frontP = GL_POINT;
        const GLenum frontL = GL_LINE;
        const GLenum frontF = GL_FILL;
        
        /*-----------------------------------------------------------------*/

        /* Grid */
        const char squareFile[] = "DATA/square.fs";
        const char gridFile[] = "DATA/grid.fs";
        const char isobarFile[] = "DATA/isobar.fs";
        const char terrainFile[] = "DATA/terrain.fs";
        const char windFile[] = "DATA/wind.fs";
        const char temperaturFile[] = "DATA/temperatur.fs";
        
        /*-----------------------------------------------------------------*/

        /* Size */
        float lineWidth = 3.0f;
        float pointSize = 7.0f;
      
	/* Enable Anti-aliasing */
        glEnable(GL_POINT_SMOOTH);
        glLineWidth(lineWidth);
        glPointSize(pointSize);
        
        /*-----------------------------------------------------------------*/

        /* Terrain */
	if(Fgrid || Fiso) 
	{
	        PolygonCircle(modeP, frontP, squareFile);
	        PolygonCircle(modeL, frontL, gridFile);       
	        PolygonCircle(modeT, frontF, terrainFile); 
	        
	        /* Isobar */
	        if(Fiso) 
	        {
	                PolygonCircle(modeP, frontP, isobarFile);
	                PolygonCircle(modeT, frontL, isobarFile);
	                PolygonCircle(modeT, frontF, isobarFile);
	        }
	}
	/* Wind */
	else if(Fwind) 
	{ 
	        PolygonCircle(modeP, frontP, squareFile);
	        glEnable(GL_CULL_FACE);
	        PolygonCircle(modeT, frontF, gridFile);
	        glDisable(GL_CULL_FACE);
	        PolygonCircle(modeT, frontF, windFile); 
	}
	/* Fahrenheit */
	else if(Ftemp) 
	{ 
	        PolygonCircle(modeP, frontP, squareFile);
	        PolygonCircle(modeT, frontL, gridFile);
	        PolygonCircle(modeT, frontF, temperaturFile); 
	}
	
	/*-----------------------------------------------------------------*/
	        
	/* Disable Anti-aliasing */
        glDisable(GL_POINT_SMOOTH);
}

/*-------------------------------------------------------------------------*/ 

/* Moving Objects */
void textureObjects() 
{
        int PIX = 4;
        GLuint texID[EV];

        /* Basic Program */
        linkProgram(g.basicProgram);
        bindShader(g.basicProgram);
        Uniform(g.basicProgram, "texture1", 0);

	/* Generate Textures */
	glGenTextures(PIX, texID);
	glBindTexture(GL_TEXTURE_2D_ARRAY, texID[PIX]);
	
	/*-----------------------------------------------------------------*/
	
	/* Texture */
        const char forestFile[] = "DATA/forest.fs";
        const char desertFile[] = "DATA/desert.fs";
        const char seaoceFile[] = "DATA/seaoce.fs";
        const char cloudFile[] = "DATA/cloud.fs";
	
	/* Count */
	const GLint countF = 288;       /* 288 */
        const GLint countD = 672;       /* 384 */
        const GLint countS = 864;       /* 192 */
	const GLint countC = 888;       /*  24 */
	
	/*-----------------------------------------------------------------*/
	
	/* Landscape */
	if(Fnight || Fday || Fcloud)
	{
	        /* SubImage */
	        SubImage(countF, forestFile, bla);
	        SubImage(countD, desertFile, des);
	        SubImage(countS, seaoceFile, sea); 
	        
	        /* Cloud */
	        if(Fcloud)
	        {  
	                /* Translate / Rotate */
	                /*glTranslatef(0.0, 0.0, -code1);*/
	                /*glRotatef(-code1, 0.0f, 1.0f, 0.0f);*/
	                /*glRotatef(-code2, 0.0f, 0.0f, 0.0f);*/
	                                
	                /* Cloud File */
	                SubImage(countC, cloudFile, sky); 
	        }
	}

	/* Delete Textures */
	glDeleteTextures(PIX, texID);
}

/*-------------------------------------------------------------------------*/ 

/* Render Objects */
void vertexObjects() 
{
        /* Active Texture */
	/*glActiveTextureARB(GL_TEXTURE5 + PIX);*/
	/*glClientActiveTextureARB(GL_TEXTURE5);*/

        /* Attrib Array */
	glEnableVertexAttribArray(0);
    	glEnableVertexAttribArray(1);
	glEnableVertexAttribArray(2);

        /*-----------------------------------------------------------------*/
        
        /* Bind Color Buffer */
        glBindBuffer(GL_ARRAY_BUFFER, vbos3[COLOR_BUFFER]);

        /* Color */
        const GLint csize = 3;                      /* ( R, G, B, A) */
        const GLenum ctype = GL_FLOAT;              /* the data is 8bit unsigned values */
        const GLboolean cnormalize = FALSE;         /* normalize the data (convert from 0-255 to 0-1) */
        const GLsizei cstride = 0;                  /* 0 = move forward size * sizeof(type) */
        const GLbyte coffset = 0;                   /* start at the beginning of the buffer */

        glVertexAttribPointer((GLuint)2, csize, ctype, cnormalize, cstride, &vcolor3[coffset]);

        /*-----------------------------------------------------------------*/

        /* Bind Vertex Buffer */
        glBindBuffer(GL_ARRAY_BUFFER, vbos3[VERTEX_BUFFER]);

        /* Vertex */
        const GLint vsize = 3;			    /* ( X, Y, Z, W ) */
        const GLenum vtype = GL_FLOAT;
        const GLboolean vnormalize = FALSE;
        const GLsizei vstride = 0;
        const GLbyte voffset = 0;

        glVertexAttribPointer((GLuint)0, vsize, vtype, vnormalize, vstride, &vcircle3[voffset]);
        
        /*-----------------------------------------------------------------*/

        /* Bind Normal Buffer */
        glBindBuffer(GL_ARRAY_BUFFER, vbos3[NORMAL_BUFFER]);

        /* Texture */
        const GLint tsize = 2;			    /* ( S, T, P, Q ) */
        const GLenum ttype = GL_FLOAT;
        const GLboolean tnormalize = FALSE;
        const GLsizei tstride = 0;
        const GLbyte toffset = 0;

        glVertexAttribPointer((GLuint)1, tsize, ttype, tnormalize, tstride, &tcircle3[toffset]);

        /*-----------------------------------------------------------------*/

        /* Objects */
        #if LAND_VECTOR
        vectorObjects();
        #else
        polygonObjects();
        #endif
        textureObjects();

        /*-----------------------------------------------------------------*/

        /* Disable Array */
        glDisableVertexAttribArray(2);
        glDisableVertexAttribArray(1);
        glDisableVertexAttribArray(0);

        /* Active Texture */
	/*glActiveTextureARB(GL_TEXTURE0);*/
	/*glClientActiveTextureARB(GL_TEXTURE0);*/
}

/*-------------------------------------------------------------------------*/ 

/* Render Objects */
void renderObjects() 
{
	/* Vertex Array */
	glEnableClientState(GL_COLOR_ARRAY);
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);

        /*-----------------------------------------------------------------*/
        
        /* Bind Color Buffer */
        glBindBuffer(GL_ARRAY_BUFFER, vbos3[COLOR_BUFFER]);

        /* Color */
        const GLint csize = 4;                      /* ( R, G, B, A) */
        const GLenum ctype = GL_FLOAT;              /* the data is 8bit unsigned values */
        const GLsizei cstride = 0;                  /* 0 = move forward size * sizeof(type) */
        const GLbyte coffset = 0;                   /* start at the beginning of the buffer */

        glColorPointer(csize, ctype, cstride, &vcolor3[coffset]);

        /*-----------------------------------------------------------------*/

        /* Bind Vertex Buffer */
        glBindBuffer(GL_ARRAY_BUFFER, vbos3[VERTEX_BUFFER]);

        /* Vertex */
        const GLint vsize = 3;			    /* ( X, Y, Z, W ) */
        const GLenum vtype = GL_FLOAT;
        const GLsizei vstride = 0;
        const GLbyte voffset = 0;

        glVertexPointer(vsize, vtype, vstride, &vcircle3[voffset]);
        
        /*-----------------------------------------------------------------*/

        /* Bind Normal Buffer */
        glBindBuffer(GL_ARRAY_BUFFER, vbos3[NORMAL_BUFFER]);

        /* Texture */
        const GLint tsize = 2;			    /* ( S, T, P, Q ) */
        const GLenum ttype = GL_FLOAT;
        const GLsizei tstride = 0;
        const GLbyte toffset = 0;

        glTexCoordPointer(tsize, ttype, tstride, &tcircle3[toffset]);

        /*-----------------------------------------------------------------*/

        /* Objects */
        #if LAND_VECTOR
        vectorObjects();
        #else
        polygonObjects();
        #endif
        textureObjects();

        /*-----------------------------------------------------------------*/
        
	/* Disable Client State */
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_COLOR_ARRAY);
}

/*-------------------------------------------------------------------------*/ 

/* Render Landscape */
void Landscape(int width, int height)
{
	/* Matrix */
	float modelview3[ID];
        float projection3[ID];

        /* Texture */
        glDisable(GL_SCISSOR_TEST);
        
	/* Render Triangles */
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
	
	/* PlaneView */
	CameraLook();
	PlaneView();
	
	/*------------------------------------------------------------------*/
	
	/* Font */
	printString("VULKANO:", 110, 25);
	
	/*------------------------------------------------------------------*/
	
	/* Passing Data */
	glGetFloatv(GL_PROJECTION_MATRIX, projection3);
	glGetFloatv(GL_MODELVIEW_MATRIX, modelview3);
	
	/* Matrix */
	Uniform4x4(g.shaderProgram, "projection_matrix", 0, projection3);
        Uniform4x4(g.shaderProgram, "modelview_matrix", 1, modelview3);  
		
	/**************************************************************/
        /* Draw the plane with stencil on                             */ 
        /**************************************************************/

        /* Enable Depth */
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_STENCIL_TEST);      /* Enable stenciling */
        glDepthMask(GL_FALSE);          /* Disable depth writes */
        glDepthFunc(GL_LESS);

        /* Always replace the stencil value whether it passes or fails */
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);

        /* Make the test always pass */
        glStencilFunc(GL_ALWAYS, 1, 0xFFFFFFFF);

        /* Flybox */
	initBox();
	render(0, 0, 0, g.basicProgram);

        /* Re-enable depth writes */
        glDepthMask(GL_TRUE);

        /*********************************************************************/
        /* Draw the sphere's reflection, with blending and stencil testing   */
        /*********************************************************************/
        
	/* Cube Program */
        bindShader(g.cubeProgram);
        sendUniform3f(g.cubeProgram, "camera_position", 0.0f, 0.0f, 0.0f);
        Uniform(g.cubeProgram, "texture2", 0);
        
        /* Cube Map */
        glBindTexture(GL_TEXTURE_CUBE_MAP, cubeMapTexID);

        /* Only render where the stencil value is 1 */
        glStencilFunc(GL_EQUAL, 1, 0xFFFFFFFF);
        /* Don’t change the stencil values any more */
        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);

        /* Purple Look */
        if(Fpurple)
        {
                glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
        }
        
	/* Render Objects */
        #if VERTEX_LAND
        vertexObjects(); 
	#else
	renderObjects(); 
	#endif
	
	/*------------------------------------------------------------------*/

	/* Shader Program */
	linkProgram(g.shaderProgram);                /* Re link the program */
        bindShader(g.shaderProgram);                 /* Enable our shader */
	Uniform(g.shaderProgram, "texture0", 0);

        /* Disable the stencil test */
        glDisable(GL_STENCIL_TEST);

        /**************************************************************/
        /* Draw the sphere normal                                     */ 
        /**************************************************************/
	
	/* Render Objects */
        #if VERTEX_LAND
        vertexObjects(); 
	#else
	renderObjects(); 
	#endif 
	
	/* Cube Map */
	#if CUBE_MAP
	CubeMap();
	#endif
  	
	/*------------------------------------------------------------------*/

        /* Delete Buffer */
        glDeleteBuffers(MAX_BUFFERS, &vbos3[0]);

        /* Delete Shader */
        DeleteShader(g.shaderProgram);
        DeleteShader(g.cubeProgram);
        DeleteShader(g.basicProgram);
	
	/* Disable */
	glDisable(GL_COLOR_MATERIAL);
	glDisable(GL_LIGHTING);
	glDisable(GL_AMBIENT);
	glFlush();
}

/*-------------------------------------------------------------------------*/

/* Render Landscape */
void CubeMap()
{
        int TEX = 0;
        
        /* Clear */
        glClear(GL_DEPTH_BUFFER_BIT);
        glLoadIdentity();
        
        /* Begin Box */
        beginCubeMapRendering(256);  
        glEnable(GL_SCISSOR_TEST);
      
        /* Number of Texture */
        for(TEX = 0; TEX < 6; ++TEX) 
        {                   
                /* Cube Render */
                beginCubeMapFace(TEX);
                        
                /* Flybox */
                initBox();
                render( 0, 0, 0, g.basicProgram);
                       
                /* Translate Y-Achse */
	        glTranslatef(0.0f, 2.0f, 0.0f);
                        
                /* Render Objects */
                #if VERTEX_LAND
                vertexObjects(); 
	        #else
	        renderObjects(); 
	        #endif
                finishCubeMapFace(TEX, 256);
        } 
        
        /* End Box */
        endCubeMapRendering(); 
        glDisable(GL_SCISSOR_TEST);
}

/*-------------------------------------------------------------------------*/

/* Process Event */
void process_event1(XEvent report)
{    
	KeySym key;
	key = XLookupKeysym(&report.xkey, 0);
	
	/* Keyboard Control Keys */	
	switch(key)
	{
	        /* GRID */
		case XK_F1: 
		Fday = FALSE; Fnight = FALSE; Fcloud = FALSE;
                Fgrid = TRUE; Fiso = FALSE; Fwind = FALSE; Ftemp = FALSE; 
		break;
		case XK_F2: 
		Fday = FALSE; Fnight = FALSE; Fcloud = FALSE;
                Fgrid = TRUE; Fiso = TRUE; Fwind = FALSE; Ftemp = FALSE; 
		break;
		case XK_F3: 
		Fday = FALSE; Fnight = FALSE; Fcloud = FALSE;
                Fgrid = FALSE; Fiso = FALSE; Fwind = TRUE; Ftemp = FALSE; 
		break;
		case XK_F4: 
		Fday = FALSE; Fnight = FALSE; Fcloud = FALSE;
                Fgrid = FALSE; Fiso = FALSE; Fwind = FALSE; Ftemp = TRUE; 
		break;
	
	        /* TEXTURE */
		case XK_F5: 
	        Fday = TRUE; Fnight = FALSE; Fcloud = FALSE;
                Fgrid = FALSE; Fiso = FALSE; Fwind = FALSE; Ftemp = FALSE; 
		break;
		case XK_F6:
		Fday = TRUE; Fnight = FALSE; Fcloud = TRUE;
                Fgrid = FALSE; Fiso = FALSE; Fwind = FALSE; Ftemp = FALSE; 
		break;
		case XK_F7:
		Fday = FALSE; Fnight = TRUE; Fcloud = FALSE;
                Fgrid = FALSE; Fiso = FALSE; Fwind = FALSE; Ftemp = FALSE; 
		break;
		
		/* CLEAR */
		case XK_F8: 
		Fday = FALSE; Fnight = FALSE; Fcloud = FALSE;
                Fgrid = FALSE; Fiso = FALSE; Fwind = FALSE; Ftemp = FALSE; 
                break;
		
		/* PURPLE */
		case XK_F9: Fpurple = TRUE; break;
		case XK_F10: Fpurple = FALSE; break;
		
		/* MASSTAB */
		case XK_i: Fplus += plus; break;
		case XK_o: Fplus -= minus ; break;

		case XK_Escape: 
		closeWin(); 
		break;
	}
}

/*-------------------------------------------------------------------------*/ 

