/* -*- mia-c++ -*-
 *
 * This file is part of viewitgui - a library and program for the
 * visualization of 3D data sets. 
 *
 * Copyright (c) Leipzig, Madrid 1999-2013 Mirco Hellmann, Gert Wollny
 *
 * viewitgui is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * viewitgui is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with viewitgui; if not, see <http://www.gnu.org/licenses/>.
 */

#define GL_GLEXT_PROTOTYPES 1
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>

#include <viewit/volumerenderer.hh>
#include <cmath>

/**************************************************************************************************************
                     the shader class represents a OpenGL Shading Langunge Shader
*/

class TGLShader{

public:
	GLhandleARB ShadeProgram;					//handle for the shaderprogram

	TGLShader();	
	~TGLShader();
	
	void useShader();		//use the created shader
	void stopShader();		//unload the shader

	static const char frag_shader[];
	static const char vert_shader[];

private:

	void install_shader(const GLcharARB *VertexShaderSource, const GLcharARB *FragmentShaderSource);	//complete shader install
	int shaderSize(char *fileName);										//getting the size
	int readShader(char *fileName, char *shaderText, int size);						//read shader file
	int readShaderSource(char *fileName, GLcharARB **Shader);						//read the shader
	GLhandleARB 	volumeVS, volumeFS;									//handles for ver&frag
	
};


TGLShader::TGLShader()
{
	install_shader(vert_shader, frag_shader);
}

TGLShader::~TGLShader()
{
	glDeleteObjectARB(ShadeProgram);		//delete the shading program
}

int TGLShader::shaderSize(char *fileName)
{
    //
    // Returns the size in bytes of the shader fileName.
    // If an error occurred, it returns -1.
    //
    // File name convention:
    //
    // <fileName>.vert
    // <fileName>.frag
    //
    FILE* shader;
    char name[100];
    int count;

    strcpy(name, fileName);


    //
    // Open the file
    //
    shader = fopen(name, "r");
    if (!shader)
        return -1;

    //
    // Seek to the end and find its position
    //
    count = fseek(shader, 0, SEEK_END);
    count = ftell(shader);

    fclose(shader);
    return count;
}


int TGLShader::readShader(char *fileName, char *shaderText, int size)
{
    //
    // Reads a shader from the supplied file and returns the shader in the
    // arrays passed in. Returns 1 if successful, 0 if an error occurred.
    // The parameter size is an upper limit of the amount of bytes to read.
    // It is ok for it to be too big.
    //
    FILE *shader;
    char name[100];
    int count;

    strcpy(name, fileName);



    //
    // Open the file
    //
    shader = fopen(name, "r");
    if (!shader)
        return -1;

    //
    // Get the shader from a file.
    //
    fseek(shader, 0, SEEK_SET);
    count = fread(shaderText, 1, size, shader);
    shaderText[count] = '\0';

    if (ferror(shader))
        count = 0;
    else count = 1;

    fclose(shader);
    return count;
}


int TGLShader::readShaderSource(char *fileName, GLcharARB **Shader)
{
    int Size;

    //
    // Allocate memory to hold the source of our shaders.
    //
    Size = shaderSize(fileName);

    if (Size == -1) 
    {

        return 0;
    }

    *Shader = (GLcharARB *) malloc(Size);

    //
    // Read the source code
    //
    if (!readShader(fileName, *Shader, Size))
    {

        return 0;
    }

    return 1;
}

void TGLShader::install_shader(const GLcharARB* VertexShaderSource, const GLcharARB* FragmentShaderSource)
{
	GLint       vertCompiled, fragCompiled;    						//status values

	std::cerr << "compile shaders" << std::endl; 
	volumeVS = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);				//creating the shader Objects
	volumeFS = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);

	glShaderSourceARB(volumeVS, 1, &VertexShaderSource, NULL);
	//setting the sources for shader Objects
	glCompileShaderARB(volumeVS);//compile the shader Objects

	glGetObjectParameterivARB(volumeVS,GL_OBJECT_COMPILE_STATUS_ARB, &vertCompiled);	//check for succesful Compilation
	if (!vertCompiled) {
		std::cerr << "Vertexshader not compiled" << std::endl;
		GLcharARB log[2048]; 
		GLsizei msg_length = 0; 
		glGetInfoLogARB(volumeVS, 2048, &msg_length, log);
		std::cerr << log << std::endl;
		
	}
	
	glShaderSourceARB(volumeFS, 1, &FragmentShaderSource, NULL);
	glCompileShaderARB(volumeFS);

	glGetObjectParameterivARB(volumeFS,GL_OBJECT_COMPILE_STATUS_ARB, &fragCompiled);
	if (!fragCompiled) {
		std::cerr << "Fragmentshader not compiled" << std::endl;
		GLcharARB log[2048]; 
		GLsizei msg_length = 0; 
		glGetInfoLogARB(volumeFS, 2048, &msg_length, log);
		std::cerr << log << std::endl;
	
	}


	ShadeProgram = glCreateProgramObjectARB();						//create the shading program


}

void TGLShader::useShader()
{
	GLint linked;						//status value
	
	glAttachObjectARB(ShadeProgram, volumeVS);		//attache the loaded shaders
	glAttachObjectARB(ShadeProgram, volumeFS);

	glLinkProgramARB(ShadeProgram);				//link the shading programm
  
	glGetObjectParameterivARB(ShadeProgram,GL_OBJECT_LINK_STATUS_ARB, &linked);	//check for succesful linking
	if (!linked ) {
		std::cerr << "ShadeProgram not linked\n";
		std::cerr << gluErrorString(glGetError()) << std::endl;
	}                     
	glUseProgramObjectARB(ShadeProgram);			//use the shading program
}

void TGLShader::stopShader()
{
	GLint linked;
	
	glDetachObjectARB(ShadeProgram, volumeVS);		//detach the shader Objects
	glDetachObjectARB(ShadeProgram, volumeFS);

	glLinkProgramARB(ShadeProgram);				//link an empty shade program
  
	glGetObjectParameterivARB(ShadeProgram,GL_OBJECT_LINK_STATUS_ARB, &linked);		//check for linking
	if (!linked ) std::cerr << "ShadeProgram not linked" << std::endl;
	
	glUseProgramObjectARB(ShadeProgram);			//use the empty shader program
}

/**************************************************************************************************************/


//************** creating a plane based on 3 points ******************

C4DFVector CreatePlaneFromPoints(C3DFVector P0, C3DFVector P1, C3DFVector P2){

	P1.x = P1.x - P0.x;
	P1.y = P1.y - P0.y;
	P1.z = P1.z - P0.z;
		
	P2.x = P2.x - P0.x;
	P2.y = P2.y - P0.y;
	P2.z = P2.z - P0.z;

	C4DFVector Plane;

	Plane.x = P1.y*P2.z-P1.z*P2.y;
	Plane.y = P1.z*P2.x-P1.x*P2.z;
	Plane.z = P1.x*P2.y-P1.y*P2.x;
	Plane.a=-(Plane.x*P0.x+Plane.y*P0.y+Plane.z*P0.z);

	return Plane;
};

//************* creating a plane ortogonal to a line **********

C4DFVector CreatePlaneOnLine(C3DFVector Start, C3DFVector Direction, GLfloat t){


	Start.x=Start.x+t*Direction.x;
	Start.y=Start.y+t*Direction.y;
	Start.z=Start.z+t*Direction.z;
	
	C4DFVector Plane;

	Plane.x = Direction.x;
	Plane.y = Direction.y;
	Plane.z = Direction.z;
	Plane.a=-(Plane.x*Start.x+Plane.y*Start.y+Plane.z*Start.z);

	return Plane;
};

//************* calculating an intersection between a line and a plane ******************

C3DFVector IntersectPlaneLine(C4DFVector Plane, C3DFVector LinePoint1, C3DFVector LinePoint2, GLfloat dividend){

	LinePoint2.x = LinePoint2.x - LinePoint1.x;
	LinePoint2.y = LinePoint2.y - LinePoint1.y;
	LinePoint2.z = LinePoint2.z - LinePoint1.z;
	
	if(dividend==0.0f) dividend=(Plane.x*LinePoint2.x+Plane.y*LinePoint2.y+Plane.z*LinePoint2.z);
	
	C3DFVector IntersectPoint;

	if (dividend!=0.0) 
	{
		GLfloat t = -(Plane.x*LinePoint1.x+Plane.y*LinePoint1.y+Plane.z*LinePoint1.z+Plane.a)/dividend;
		
		{
			IntersectPoint.x=LinePoint1.x+t*LinePoint2.x;
			IntersectPoint.y=LinePoint1.y+t*LinePoint2.y;
			IntersectPoint.z=LinePoint1.z+t*LinePoint2.z;
					
		}
	}
	return IntersectPoint;
};

//************ testing of an intersection between line and plane ******************

GLfloat DoesIntersectPlaneLine(C4DFVector Plane, C3DFVector LinePoint1, C3DFVector LinePoint2){

	LinePoint2.x = LinePoint2.x - LinePoint1.x;
	LinePoint2.y = LinePoint2.y - LinePoint1.y;
	LinePoint2.z = LinePoint2.z - LinePoint1.z;
	
	GLfloat dividend =(Plane.x*LinePoint2.x+Plane.y*LinePoint2.y+Plane.z*LinePoint2.z);

	return dividend;
};

//************ calculating the first and last samplepoint and the sampledistance *****************

void FirstAndLastSamplePoint(C3DFVector& Peye, C3DFVector& Ptarget, C3DFVector& Direction, GLfloat& distance)
{
	GLfloat PointsOfBox[8][3] =		//definig all points of the box
	{
	{0.0f,0.0f,0.0f},
	{0.0f,0.0f,256.0f},
	{0.0f,256.0f,0.0f},
	{0.0f,256.0f,256.0f},
	{256.0f,0.0f,0.0f},
	{256.0f,0.0f,256.0f},
	{256.0f,256.0f,0.0f},
	{256.0f,256.0f,256.0f}
	};
	

	
	C3DFVector VertexResults[8],Raypoint; 	 //VertexResults will contain the intersections with the viewing ray, Raypoint is the actual Point of the Box
	int PointsFound=0;			//0 points found yet; can be less then 8
	
	for(int corner=0;corner<8;corner++){
	
		Raypoint.x=PointsOfBox[corner][0];	//source point is a boxcorner
		Raypoint.y=PointsOfBox[corner][1];
		Raypoint.z=PointsOfBox[corner][2];
		
		C4DFVector Plane = CreatePlaneOnLine(Raypoint, Direction, 0);	//creating a plane on the corner orthogonal to the ray

		GLfloat Intersection = DoesIntersectPlaneLine(Plane,Peye,Ptarget);	//checking fot intersection
				
		if(Intersection!=0.0)					//if intersection takes place, add the point to vertexlist
		{
			C3DFVector Pintersect = IntersectPlaneLine(Plane,Peye,Ptarget,Intersection);	//calculating fot intersection
			
			VertexResults[PointsFound].x=Pintersect.x;		//adding the intersection
			VertexResults[PointsFound].y=Pintersect.y;
			VertexResults[PointsFound].z=Pintersect.z;

			PointsFound++;
			
		}
	}
	
	//loop over all intersections
	
	GLfloat max = 0,min = 0;	//helpvalues
	int maxp = 0, minp = 0;
	
	for(int i=0; i<PointsFound;i++){
		
		distance = sqrt(pow(Peye.x-VertexResults[i].x,2)+pow(Peye.y-VertexResults[i].y,2)+pow(Peye.z-VertexResults[i].z,2));	//calculating the distance between point of eye and intersection
		
		if(i==0) {max=distance; min=distance; minp=0; maxp=0;}		//first check
			
		if(distance>max) {max=distance;	maxp=i;}		//keep max and mins as value and point
		if(distance<min) {min=distance; minp=i;}
	}
	
	
	distance = 1/(max-min); 				//the sampledistance for the box
	
	Peye.x=VertexResults[minp].x;				//new point of eye is first samplepoint
	Peye.y=VertexResults[minp].y;
	Peye.z=VertexResults[minp].z;
	
	Direction.x=VertexResults[maxp].x-VertexResults[minp].x;	//a new direction, "normalized" for loop
	Direction.y=VertexResults[maxp].y-VertexResults[minp].y;
	Direction.z=VertexResults[maxp].z-VertexResults[minp].z;
};
//************ computing the intersections of the boundingbox and the slices *******************

void IntersectionOfPlaneAndBox(C4DFVector& Plane, C3DFVector* VertexResults, int& PointsFound)
{
	//defining the edges of the bounding box
	
	GLfloat LinesOfBox[12][6] = 
	{
	{256.0f,256.0f,256.0f, 0.0f,256.0f,256.0f},
	{256.0f,256.0f,256.0f, 256.0f,0.0f,256.0f},
	{256.0f,256.0f,256.0f, 256.0f,256.0f,0.0f},

	{0.0f,0.0f,0.0f, 256.0f,0.0f,0.0f},
	{0.0f,0.0f,0.0f, 0.0f,256.0f,0.0f},
	{0.0f,0.0f,0.0f, 0.0f,0.0f,256.0f},

	{0.0f,256.0f,256.0f, 0.0f,0.0f,256.0f},
	{0.0f,256.0f,256.0f, 0.0f,256.0f,0.0f},

	{256.0f,0.0f,256.0f, 0.0f,0.0f,256.0f},
	{256.0f,0.0f,256.0f, 256.0f,0.0f,0.0f},

	{256.0f,256.0f,0.0f, 0.0f,256.0f,0.0f},
	{256.0f,256.0f,0.0f, 256.0f,0.0f,0.0f}};
	
	PointsFound=0;	//no points found yet
	
	//loop over all edges
	
	for(int i=0; (i<12)&&(PointsFound<6); i++)
	{
		C3DFVector Pintersect;		//helping variables
		C3DFVector Pline0;
		C3DFVector Pline1;
		GLfloat Intersection;
			
		Pline0.x=LinesOfBox[i][0];		//creating the line of points from box
		Pline0.y=LinesOfBox[i][1];
		Pline0.z=LinesOfBox[i][2];
		Pline1.x=LinesOfBox[i][3];
		Pline1.y=LinesOfBox[i][4];
		Pline1.z=LinesOfBox[i][5];

		Intersection=DoesIntersectPlaneLine(Plane,Pline0,Pline1);	//checking for intersection
				
		if(Intersection!=0.0)					//if intersection takes place, add the point to vertexlist
		{
		
		Pintersect=IntersectPlaneLine(Plane,Pline0,Pline1,Intersection);	//calculating fot intersection

		if((-0.000001<=Pintersect.x)&&(Pintersect.x<=256.00001)&&(-0.000001<=Pintersect.y)&&(Pintersect.y<=256.000001)&&(-0.000001<=Pintersect.z)&&(Pintersect.z<=256.000001))		//check if point within errorlimits
			{
				VertexResults[PointsFound].x=Pintersect.x;		//adding the points to the list
				VertexResults[PointsFound].y=Pintersect.y;
				VertexResults[PointsFound].z=Pintersect.z;

				PointsFound++;
			}
		}
	}

}
//*********** computing an averagepoint if slice consists of more than one triangle *************

void AddingAveragePoint(C3DFVector* VertexResults, int& PointsFound)
{
	VertexResults[PointsFound].x=0.0;	//reset the addidional point
	VertexResults[PointsFound].y=0.0;
	VertexResults[PointsFound].z=0.0;
	for(int i=0; i<PointsFound;i++){		//sum up the values of all points as the added point
		VertexResults[PointsFound].x=VertexResults[PointsFound].x+VertexResults[i].x;
		VertexResults[PointsFound].y=VertexResults[PointsFound].y+VertexResults[i].y;
		VertexResults[PointsFound].z=VertexResults[PointsFound].z+VertexResults[i].z;
		}
	VertexResults[PointsFound].x=VertexResults[PointsFound].x/GLfloat(PointsFound);		//average the point
	VertexResults[PointsFound].y=VertexResults[PointsFound].y/GLfloat(PointsFound);
	VertexResults[PointsFound].z=VertexResults[PointsFound].z/GLfloat(PointsFound);
}
//*********** sorting the points for trianglecreation ****************

void SortThePoints(C3DFVector* VertexResults, int& PointsFound, int* Polylist)
{
	const double PICONST=3.14159265358979323846;
	GLfloat VertexAngles[6];		//list with the angles of the points
	GLfloat TempAng;
	int TempPol;


	//calculate the angle for every point around the average point
	for(int i=0;i<PointsFound;i++)
	{
		VertexAngles[i]=atan((VertexResults[i].y-VertexResults[PointsFound].y)/(VertexResults[i].x-VertexResults[PointsFound].x));	//angle around the average point
		if(VertexResults[i].x<VertexResults[PointsFound].x) 	VertexAngles[i]=PICONST+VertexAngles[i];	//turn by 90° if the point is lying below 0 on x axis
	}
		
		//sorting the points around the center
		int pos;
	
		for(int h=0;h<PointsFound-1;h++)
		{
		pos=h;
		for(int j=h;j<PointsFound;j++) if(VertexAngles[j]>VertexAngles[pos]) pos=j;
			
		TempAng=VertexAngles[h];
		VertexAngles[h]=VertexAngles[pos];
		VertexAngles[pos]=TempAng;
		TempPol=Polylist[h];
		Polylist[h]=Polylist[pos];
		Polylist[pos]=TempPol;
		
		}
	
}
//********** computing the gradient of the volumedataset *************

void get_gradient(GLubyte* volumetexture, C3DBounds in_size)
{
	for(size_t zdir=0;zdir<in_size.z;zdir++)			//loop over all elements of the volumedata and getting values of the neighbours
	{
		
		for(size_t ydir=0; ydir<in_size.y;ydir++)
		{
		
			for(size_t xdir=0; xdir<in_size.x; xdir++)
			{
			GLint vecval[6];
			
			if(zdir==0) 
			{
				vecval[0]=0;
				vecval[1]=volumetexture[(4*(zdir+1)*in_size.y*in_size.x)+(4*ydir*in_size.x)+(4*(xdir))+3];
			} else 
			
				if(zdir==in_size.z-1) 
				{
					vecval[1]=0;
					vecval[0]=volumetexture[(4*(zdir-1)*in_size.y*in_size.x)+(4*ydir*in_size.x)+(4*(xdir))+3];
				 }else
				
					{ 
					vecval[0]=volumetexture[(4*(zdir-1)*in_size.y*in_size.x)+(4*ydir*in_size.x)+(4*(xdir))+3];
					vecval[1]=volumetexture[(4*(zdir+1)*in_size.y*in_size.x)+(4*ydir*in_size.x)+(4*(xdir))+3];
					}
			
			if(ydir==0) 
			{
				vecval[2]=0;
				vecval[3]=volumetexture[(4*zdir*in_size.y*in_size.x)+(4*(ydir+1)*in_size.x)+(4*(xdir))+3];
			} else 
			
				if(ydir==in_size.y-1) 
				{
					vecval[3]=0;
					vecval[2]=volumetexture[(4*zdir*in_size.y*in_size.x)+(4*(ydir-1)*in_size.x)+(4*(xdir))+3];
				 }else
				
					{ 
					vecval[2]=volumetexture[(4*zdir*in_size.y*in_size.x)+(4*(ydir-1)*in_size.x)+(4*(xdir))+3];
					vecval[3]=volumetexture[(4*zdir*in_size.y*in_size.x)+(4*(ydir+1)*in_size.x)+(4*(xdir))+3];
					}
			
			if(xdir==0) 
			{
				vecval[4]=0;
				vecval[5]=volumetexture[(4*zdir*in_size.y*in_size.x)+(4*ydir*in_size.x)+(4*(xdir+1))+3];
			} else 
			
				if(xdir==in_size.x-1) 
				{
					vecval[5]=0;
					vecval[4]=volumetexture[(4*zdir*in_size.y*in_size.x)+(4*ydir*in_size.x)+(4*(xdir-1))+3];
				 }else
				
					{ 
					vecval[4]=volumetexture[(4*zdir*in_size.y*in_size.x)+(4*ydir*in_size.x)+(4*(xdir-1))+3];
					vecval[5]=volumetexture[(4*zdir*in_size.y*in_size.x)+(4*ydir*in_size.x)+(4*(xdir+1))+3];
					}
					
			//creating negative vectors
			vecval[0]*=-1;		
			vecval[2]*=-1;
			vecval[4]*=-1;
			
			//computing and range compressing
			volumetexture[(4*zdir*in_size.y*in_size.x)+(4*ydir*in_size.x)+(4*(xdir))+2]=((vecval[0]+vecval[1])/2)+128;
			volumetexture[(4*zdir*in_size.y*in_size.x)+(4*ydir*in_size.x)+(4*(xdir))+1]=((vecval[2]+vecval[3])/2)+128;
			volumetexture[(4*zdir*in_size.y*in_size.x)+(4*ydir*in_size.x)+(4*(xdir))]=((vecval[4]+vecval[5])/2)+128;
			}
		}
	}
}


/*******************************************************************************************************
the volume renderer class functions
*/

TVolumeRenderer::TVolumeRenderer(const string& name, const C3DUBImage& image):TDrawable(name),_M_volumen_data(image)
{
	transparent_on();
}

TVolumeRenderer::~TVolumeRenderer() 
{

}

const char* TVolumeRenderer::get_classname() const
{
	return VOLUMERENDERER_CLASSNAME;
}

void TVolumeRenderer::get_classname_list(list<string> *classlist) const
{
	if (classlist) {
		classlist->push_front(VOLUMERENDERER_CLASSNAME);
		TDrawable::get_classname_list(classlist);
	}
}	

bool TVolumeRenderer::do_handle_key_event(int key) 
{
	
	return TDrawable::do_handle_key_event(key); 
}	

bool TVolumeRenderer::do_handle_command_event(PEventInfo info)
{
	//if (__data->do_handle_command_event(info))
	//	return true;
	
	return TDrawable::do_handle_command_event(info);
}

void TVolumeRenderer::pass_transfer_table(const TransferTable& transfertable)
{
	GLint texLoc2;									//location value

	glActiveTexture(GL_TEXTURE1);							//activate textur 1
	glBindTexture(GL_TEXTURE_1D,transfertexid);
	
	glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);			//set the filters for 1D textures
	glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_WRAP_S, GL_CLAMP);
	
	glTexImage1D(GL_TEXTURE_1D,0, GL_RGBA, transfertable.size(), 0, GL_RGBA, GL_FLOAT, &transfertable[0]);	//create the texture
	
	texLoc2 = glGetUniformLocationARB(Shader->ShadeProgram, "TransferTable");	//locate the uniform in the shader
	
	if (texLoc2==-1) std::cerr << "TransferTable not found" << std::endl;		//check for existance
	
	glUniform1iARB(texLoc2, 1);							//pass the location to the shader
}

void TVolumeRenderer::gl_attach()
{
	glEnable(GL_ALPHA_TEST);					//enable alphatest for threshold
	
	glGenTextures(1, &volumetexid);					//generate a texture
	glGenTextures(1, &transfertexid);
	
	glEnable(GL_TEXTURE_3D);					//enable 3dtextures
	
	glEnable(GL_TEXTURE_1D);					//enable 1dtextures
	
	glActiveTexture(GL_TEXTURE0);					//activate texture 0
	glBindTexture(GL_TEXTURE_3D,volumetexid);
	
	glTexParameteri(GL_TEXTURE_3D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);	//set the filters
	glTexParameteri(GL_TEXTURE_3D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

	
	C3DBounds in_size = _M_volumen_data.get_size();			//get the size of the volume data
	C3DBounds size(1,1,1);
	
	while (size.x < in_size.x) size.x <<= 1;			//calculate the size for a surrounding 2*n texture
	while (size.y < in_size.y) size.y <<= 1;
	while (size.z < in_size.z) size.z <<= 1;
	
	if((size.x>size.y)&&(size.x>size.z)) texsize=size.x;			//finding the biggest size
		else if(size.y>size.z) texsize=size.y; else texsize=size.z;
	
	
	int in_texsize = in_size.x*in_size.y*in_size.z;			//original volumedata size
	
	volumetexture = new GLubyte[in_texsize*4];			//create storage for volumedata
	
	C3DUBImage::const_iterator textsrc=_M_volumen_data.begin();			//get the begining of the data
		
	for(int alpha=0; alpha<in_texsize;alpha++, ++textsrc) 			//loop to copy the ubyte texture to the float texture
	{
		volumetexture[(4*alpha)+3] = *textsrc;
	}
	


	get_gradient(volumetexture, in_size);				//calculate the gradient
	
	glTexImage3D(GL_TEXTURE_3D,0,4, size.x, size.y, size.z,0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);	//set the surroundig texture
	
	glTexSubImage3D(GL_TEXTURE_3D,0,0,0,0,in_size.x, in_size.y, in_size.z,GL_RGBA, GL_UNSIGNED_BYTE,volumetexture
);		//volume texture becomes subtexture
	
	delete volumetexture;						//delete the texture for creation
	
	Shader = new TGLShader();	//create an new shader
	
	Shader->useShader();							//use the shader
	
	GLint texLoc;								//texture location

	texLoc = glGetUniformLocationARB(Shader->ShadeProgram, "VolumeTexture");	//passing the volumetextur location to shader
	glUniform1iARB(texLoc, 0);
	
	TransferTable tt(256);
	TransferTable::iterator i = tt.begin();
	
	for(size_t k=0;k<tt.size(); k++,++i) 
	{
		float value = k / (tt.size() - 1.0); 
		*i = C4DFVector(value, value, value, value);
	}
	
	pass_transfer_table(tt);
	
	threshold=0.2f;					//init the threshold
	slice_multiplier=2.0f;				//init the slice amount
}

void TVolumeRenderer::gl_detach()
{
glDeleteTextures(1,&volumetexid);		//delete the volumetexture
glDeleteTextures(1,&transfertexid);		//delete the volumetexture
delete Shader;					//delete the shader

}

void TVolumeRenderer::do_gl_draw(const TCamera& c) const
{
	C4DFVector Plane;		//Plane for Calculations
	C3DFVector Peye;		//Eyepoint
	C3DFVector Ptarget;		//Targetpoint
	C3DFVector Direction;		//Direction of Viewingray
	C3DFVector VertexResults[7];	//Results for the Points of the Triangles
	int i, PointsFound;		//interation	
	GLfloat sampledistance;		//Sampledistance
	
	/*************************** setting up the eye and target point ******************************/
	
	
	
	Peye = c.get_location();		//eyepoint (position) from camera
	
	Ptarget = c.get_mid_shift();		//targetpoint from camera
	
	Direction.x=Ptarget.x-Peye.x;		//direction of the viewingray
	Direction.y=Ptarget.y-Peye.y;
	Direction.z=Ptarget.z-Peye.z;
	
	
	/*************************** passing values to the shader ******************************/
		
	GLint var;
	var = glGetUniformLocationARB(Shader->ShadeProgram, "LightDirection");	//getting the location for LigntDirection
		
	GLfloat LightPos[4];
	glGetLightfv(GL_LIGHT0,GL_POSITION,LightPos);				//getting the light position
	
	glUniform3fARB(var, LightPos[0], LightPos[1], LightPos[2]);
	
	/* *** Passing a Light at EyePoint*****
	   glUniform3fARB(var, -Direction.x, -Direction.y, -Direction.z);
	*/
	
	/**************************** setting the threshold ****************************************/
	
	
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable (GL_ALPHA_TEST);
	glAlphaFunc(GL_GEQUAL,threshold);

//**************************** getting first and last samplepoint ******************************

	FirstAndLastSamplePoint(Peye, Ptarget, Direction, sampledistance);
	
	sampledistance = sampledistance*(256.0f/(GLfloat(texsize)*slice_multiplier));

//**************************** loop over all the sampling points of the viewing ray ************
	
	for(GLfloat Distance=0;Distance<1;Distance=Distance+sampledistance)		
	{

	Plane=CreatePlaneOnLine(Peye, Direction, Distance);			//creating a slicing plane at the samplepoint of the ray
	
	IntersectionOfPlaneAndBox(Plane, VertexResults, PointsFound);		//getting all intersectionpoints with the box

	
	//if more than 3 points have been found an order must been given
	
	if(PointsFound>3) {
	
		AddingAveragePoint(VertexResults, PointsFound);		//a point in the middle must be added to create triangles
		
		int Polylist[7]={0,1,2,3,4,5,6};		//representing the order of the points
		
		SortThePoints(VertexResults, PointsFound, Polylist);		//bringing an order to the points
	
		//rendering the triangles in their order
		int next;
		glBindTexture(GL_TEXTURE_3D,volumetexid);
		glBegin(GL_TRIANGLES);
			for(i=0;i<PointsFound;i++)
			{
				
			glTexCoord3f(VertexResults[Polylist[i]].x/256.0f,VertexResults[Polylist[i]].y/256.0f,VertexResults[Polylist[i]].z/256.0f);
			glVertex3f(VertexResults[Polylist[i]].x,VertexResults[Polylist[i]].y,VertexResults[Polylist[i]].z);
			
			glTexCoord3f(VertexResults[PointsFound].x/256.0f,VertexResults[PointsFound].y/256.0f,VertexResults[PointsFound].z/256.0f);
			glVertex3f(VertexResults[PointsFound].x,VertexResults[PointsFound].y,VertexResults[PointsFound].z);
			
			if(i==PointsFound-1) next=0; else next=i+1;
			
			glTexCoord3f(VertexResults[Polylist[next]].x/256.0f,VertexResults[Polylist[next]].y/256.0f,VertexResults[Polylist[next]].z/256.0f);
			glVertex3f(VertexResults[Polylist[next]].x,VertexResults[Polylist[next]].y,VertexResults[Polylist[next]].z);
			}
		glEnd();	
		
		}
		else
		{	
			//rendering only the triangles
			glBindTexture(GL_TEXTURE_3D,volumetexid);
			glBegin(GL_TRIANGLES);
				//glColor4f(0.0f,0.5f,0.5f,0.05f);
				
				glTexCoord3f(VertexResults[0].x/256.0f,VertexResults[0].y/256.0f,VertexResults[0].z/256.0f);
				glVertex3f(VertexResults[0].x,VertexResults[0].y,VertexResults[0].z);
				
				glTexCoord3f(VertexResults[1].x/256.0f,VertexResults[1].y/256.0f,VertexResults[1].z/256.0f);
				glVertex3f(VertexResults[1].x,VertexResults[1].y,VertexResults[1].z);
				
				glTexCoord3f(VertexResults[2].x/256.0f,VertexResults[2].y/256.0f,VertexResults[2].z/256.0f);
				glVertex3f(VertexResults[2].x,VertexResults[2].y,VertexResults[2].z);
			glEnd();
		}
		
	}
	

}


const char TGLShader::frag_shader[] = 
	"varying vec3 LightVec;\n"
	"uniform sampler3D VolumeTexture;\n"
	"uniform sampler1D TransferTable;\n"
	"const float diffuseContrib = 0.9;\n"
	"const float ambientContrib = 1.0 - diffuseContrib;\n"
	"void main(void)\n"
	"{\n"
	"  vec4 Color = vec4(texture3D(VolumeTexture, vec3(gl_TexCoord[0])));   //getting the Normal/Value Values\n"
	"  vec4 transfer = vec4(texture1D(TransferTable,Color.a));		//getting Color and Alpha Values\n"
	"  vec3 Norm = normalize(vec3((Color.rgb-0.5)*2.0));			//computing SurfaceNormal\n"
	"  float LightIntens =  diffuseContrib * max(dot(LightVec, Norm),0.0);	//computing Light Intensity\n"
	"  gl_FragColor = vec4( (LightIntens + ambientContrib) * transfer.rgb , transfer.a);	//setting Fragmentcolor\n"
	"}\n";


const char TGLShader::vert_shader[] = "varying vec3 LightVec;\n"
	"uniform vec3 LightDirection;\n"
	"void main(void)\n"
	"{\n"
	"  LightVec = normalize(LightDirection);\n"
	"  gl_TexCoord[0]=gl_MultiTexCoord0;\n"
	"  gl_Position = ftransform();\n"
	"}\n";






