
#ifdef _WIN32
#include <windows.h>
#define OURPATH "media/"
#else
#include <GL/glx.h>
#define OURPATH "../../../../Common/media/"
#endif
#define GLH_NVEB_USING_NVPARSE
#define GLH_EXT_SINGLE_FILE
#include "glh_nveb.h"
#include "glh_extensions.h"
#include "glh_genext.h"
#include "glh_glut.h"
#include "glh_obs.h"
#include "array_texture.h"
#include "data_path.h"
#include "nv_png.h"
#include "nvparse.h"
#include "read_text_file.h"
#include "objects.h"
#include "linalg.cpp"
// Declare the effect browser information



NVEB_EFFECT_NAME("Volumetric Fog");
NVEB_EFFECT_VERSION("1.0");
NVEB_EFFECT_LOCATION("3rd Party\\Atmospheric");
NVEB_EFFECT_ABOUT(0, NULL, "Volumetric Fog", "NVEffectsExplained.htm#Volumetric_Fog");
NVEB_EFFECT_ABOUT(1, "Date", "May 2001", NULL);
NVEB_EFFECT_ABOUT(2, NULL, "Peter Popov", "mailto:sppopov@ccas.ru");
NVEB_EFFECT_EXTENSIONS("GL_NV_vertex_program");

float a,b;

using namespace glh;



// Some structures for storing data.

float hinvrads[4];
float  invrads[4];
float   sdists[4];
float sinvrads[4]; 

struct eyespaceobject
{
	vec3 center;
	float offset;
	float radius;

	void dump(int saddress,int auxaddress,int num)
	{
		 float model[3][4];
		 float d,d_2,d_3,d_4;
		 float id,id_2,id_3,id_4;
		 float r,r_2,r_3,r_4;
		 float ir,ir_2,ir_3,ir_4;

		 vec3 currpos;
		 vec3 normpos;
		
		glGetProgramParameterfvNV(GL_VERTEX_PROGRAM_NV,0,GL_PROGRAM_PARAMETER_NV,model[0]);
		glGetProgramParameterfvNV(GL_VERTEX_PROGRAM_NV,1,GL_PROGRAM_PARAMETER_NV,model[1]);
		glGetProgramParameterfvNV(GL_VERTEX_PROGRAM_NV,2,GL_PROGRAM_PARAMETER_NV,model[2]);

		
			currpos.x= model[0][0]*center.x+
		              model[0][1]*center.y+
					  model[0][2]*center.z+
					  model[0][3];

			currpos.y= model[1][0]*center.x+
		              model[1][1]*center.y+
					  model[1][2]*center.z+
					  model[1][3];

			currpos.z= model[2][0]*center.x+
		              model[2][1]*center.y+
					  model[2][2]*center.z+
					  model[2][3];

			normpos=normalize(currpos);
			currpos=currpos-offset*normpos;
			
			d=currpos.norm();
			id=1.0f/d;
			r=radius;
			ir=1.0f/r;
			d_4=d*(d_3=d*(d_2=d*d));
			id_4=id*(id_3=id*(id_2=id*id));
			r_4=r*(r_3=r*(r_2=r*r));
			ir_4=ir*(ir_3=ir*(ir_2=ir*ir));
			normpos=currpos*id;

			hinvrads[num]=.5f*ir;
			invrads[num]=ir*ir;
            sdists[num]=d_2+0.1;

			glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV,auxaddress,
				hinvrads[0],hinvrads[1],hinvrads[2],hinvrads[3]);
			glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV,auxaddress+1,
				 invrads[0], invrads[1], invrads[2], invrads[3]);
			glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV,auxaddress+2,
  				sdists[0],sdists[1],sdists[2],sdists[3]);
			

			glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV,saddress,currpos.x,currpos.y,currpos.z,1.0f);
			glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV,saddress+1,normpos.x,normpos.y,normpos.z,1.0f);
	

	};
};

// Vertex Program ID (Similar to a texture object ID)

GLuint vpid;



// Texture object for the texture.
tex_object_2D decaltex;


void CheckParseErrors()
{
	for (char * const * errors = nvparse_get_errors(); *errors; errors++)
		fprintf(stderr, *errors);
}





// Display function.
void display()
{
	
	
	glClear( GL_COLOR_BUFFER_BIT |GL_DEPTH_BUFFER_BIT );

  
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
	glTranslatef(0,0,-175);
	b+=.6;
			
	glRotatef(-60,1,0,0);
    glRotatef(b,0,0,1);
    
    glBindProgramNV( GL_VERTEX_PROGRAM_NV, vpid );
    glEnable( GL_VERTEX_PROGRAM_NV );

    eyespaceobject light;
	light.radius=40;
	light.offset=20;

	static float ttime=-123987;
	ttime+=0.021f;

	{
	light.center=20.0f*vec3(0.0f,0.0f,1.0f);//sin(ttime),cos(ttime),2.5*sin(ttime*.789));
	light.dump(12,20,0);
	}

	{
	light.center=20.0f*vec3(sin(ttime+2.0f*PI/3.0f),cos(ttime+2.0f*PI/3.0f),
		         2.5f*sin(ttime*0.589f));
	light.dump(14,20,1);
	}

	{
	light.center=20.0*vec3(sin(ttime+4.0f*PI/3.0f),cos(ttime+4.0f*PI/3.0f),
		        2.5f*sin(ttime*-0.889f));
	light.dump(16,20,2);
	}

	{
	light.radius=100.0f;
	light.center=vec3(0,0,-80);
	light.dump(18,20,3);
	}
	


    decaltex.bind();
    decaltex.enable();
	paint_objects();
	decaltex.disable();
	glutSwapBuffers();
}




void init_opengl()
{
	if( !glh_init_extensions( "GL_NV_vertex_program" ) )
	    {
		cerr << "Necessary extensions were not supported:" << endl
			 << glh_get_unsupported_extensions() << endl << endl
			 << "Press <enter> to quit." << endl;
		char buff[10];
		cin.getline(buff, 10);
		exit( -1 );
	    }

    // Create the vertex program.

glGenProgramsNV( 1, &vpid );

 glBindProgramNV( GL_VERTEX_PROGRAM_NV,vpid);
nvparse(
"!!VP1.0\n"
"#modelview transformation\n"
"#result:  R0= coordinates\n"
"DP4 R0.x,v[OPOS],c[0];\n"
"DP4 R0.y,v[OPOS],c[1];\n"
"DP4 R0.z,v[OPOS],c[2];\n"
"DP4 R0.w,v[OPOS],c[3];\n"
"#projection transformation\n"
"#result R1=window coordinates\n"
"DP4 R1.x,R0,c[4];\n"
"DP4 R1.y,R0,c[5];\n"
"DP4 R1.z,R0,c[6];\n"
"DP4 R1.w,R0,c[7];\n"
"#normal transformation\n"
"#result R2=normal \n"
"DP3 R2.x, c[0], v[NRML];\n"
"DP3 R2.y, c[1], v[NRML];\n"
"DP3 R2.z, c[2], v[NRML];\n"
"#lighting calculation\n"
"#result R2.w = intensivity\n" 
"DP3 R2.w, c[33], R2;\n"
"#Normalization. \n"
"#result R5=normalize (R0),r4.z=length(R0);\n"
"DP3 R4.x,R0,R0;\n"
"RSQ R4.y,R4.x;\n"
"MUL R4.z,R4.x,R4.y;\n"
"MUL R5,R0,R4.yyyy;\n"
"#dot products \n"
"#result R6=dot(R5,to_fog_cloud_1 .... to_fog_cloud_4)\n"
"DP3 R6.x,R5,c[12];\n"
"DP3 R6.y,R5,c[14];\n"
"DP3 R6.z,R5,c[16];\n"
"DP3 R6.w,R5,c[18];\n"
"#result R7:[0,1]\n"
"ADD R7,-R6,R4.zzzz;\n"
"MUL R7,R7,c[20];\n"
"ADD R7,c[25],R7; #R7+=.5;\n"
"MAX R7,c[24],R7; #R7 max=0\n"
"MIN R7,c[26],R7; #R7 min=1\n"
"#result R8= squared dist (view ray,fog_cloud_1_center .... fog_cloud_4_center)\n"
"MUL R8,R6,R6;\n"
"ADD R8,c[22],-R8;\n"
"MUL R8,R8,c[21];\n"
"MIN R8,c[26],R8;\n"
"ADD R8,-R8,c[26];\n"
"#\n"
"MUL R10,R7,R7;\n"
"MUL R11,R10,R7;\n"
"MUL R10,R10,c[28];\n"
"MUL R11,R11,c[27];\n"
"ADD R7,R10,-R11;\n"
"#\n"
"MUL R9,R8,R7;\n"
"MUL R9,R8,R9;\n"
"DP4 R9,R9,c[29];\n"
"MUL R3.xyzw,v[COL0].xyzw,R2.wwww;\n"
"MOV o[COL1],R3;\n"
"MOV o[COL0],R9.x;\n"
"MOV o[HPOS],R1;\n"
"MOV o[TEX0],v[TEX0];\n"
"END\n");

	
    CheckParseErrors();

	//LOAD_VERTEX_PROGRAM( vpid, "vtxprg_psfog.vp" );

	

	
	glEnable(GL_VERTEX_PROGRAM_NV);
	glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 4, GL_PROJECTION, GL_IDENTITY_NV);
    glTrackMatrixNV(GL_VERTEX_PROGRAM_NV, 0, GL_MODELVIEW, GL_IDENTITY_NV);


	glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV,24,  0,0,0,0);
	glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV,25,  .5,.5,.5,.5);
	glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV,26,  1.0,1.0,1.0,1.0);
    glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV,27,  2.0,2.0,2.0,2.0);
    glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV,28,  3.0,3.0,3.0,3.0);
	glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV,29,  0.9,0.9,0.9,3.0);//fog density
    glProgramParameter4fNV(GL_VERTEX_PROGRAM_NV,33,  0,0,1,1);
    

    

	glEnable(GL_REGISTER_COMBINERS_NV);


	
    decaltex.bind();
    array2<vec3ub> decal_img;
    read_png_rgb(OURPATH"texture.png", decal_img);
    make_rgb_texture( decal_img, true );
    decaltex.parameter( GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
    decaltex.parameter( GL_TEXTURE_MAG_FILTER, GL_LINEAR );

  
    // Setup the blending state.

     nvparse("!!RC1.0\n"
		     "const0 = ( 0.97, 0.63, 0.2, 1 );\n"
			 "{\n"
			 "   rgb {\n"
			 "           spare0 = col1 * tex0;\n"
             "           spare1 = col0 * const0;\n"
			 "        }\n"
			 "}\n"
			 "out.rgb = spare0.rgb*unsigned_invert(col0)+spare1;\n");

   CheckParseErrors();
	

	glEnable(GL_DEPTH_TEST);
}



void keyboard( unsigned char k, int x, int y )
{
    if ( k == 27 || k == 'q' )
        {
        glDeleteProgramsNV( 1, &vpid );
        exit( 0 );
        }

	static GLenum fillmode = GL_FILL;

	if( k == 'w' || k == 'W' )
	{
		if( fillmode == GL_FILL )
			fillmode = GL_LINE;
		else if( fillmode == GL_LINE )
			fillmode = GL_FILL;

		glPolygonMode( GL_FRONT_AND_BACK, fillmode );
	}
}

bool pressed=false;

void
mouse(int button, int state, int x, int y)
{
	if(state==GLUT_DOWN)
	{
		pressed=true;
		//::MessageBox(NULL,"asd","asd",MB_OK);
	}
	else
		pressed=false;
}



void reshape(int width, int height)
{
	glMatrixMode (GL_PROJECTION);   
	glLoadIdentity (); 
	gluPerspective(40,(width+.1)/(height+.1),20.,450.0);
	glMatrixMode (GL_MODELVIEW); 
	glViewport (0, 0, width, height); 
}

void motion(int x, int y)
{
	if(pressed)
	{
		b=x*0.2f;
		a=y*0.2f;
	}
}

int main( int argc, char **argv )
{
    glutInit( &argc, argv );
    glutInitDisplayMode( GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB );
    glutInitWindowSize( 800, 600 );
    glutInitWindowPosition( 256, 256 );
    glutCreateWindow( "vtrxprg_psfog" );

	glutIdleFunc(display);
	glutKeyboardFunc(keyboard);
    glutDisplayFunc(display);
    glutMotionFunc(motion);
    glutReshapeFunc(reshape);
    glutMouseFunc(mouse);

    // Do some OpenGL initialization.
	init_opengl();
	
	build_objects();
	glutMainLoop();

	return 0;
}

