#pragma once

#include "volumeshop.h"

#include <list>
#include <set>

#include "RendererPlugin.h"
#include "PluginInstance.h"
#include "Volume.h"
#include "Renderer.h"
#include "Vector.h"
#include "Quaternion.h"
#include "Matrix.h"
#include "Shader.h"
#include "Color.h"
#include "Timer.h"
#include "Label.h"
#include "Exception.h"
#include "Primitive.h"
#include "Mesh.h"
#include "Font.h"
#include "Plugin.h"
#include "Image.h"
#include "Volume.h"
#include "Voxel.h"
#include "Transformer.h"
#include "Evaluator.h"

#include <iostream>
#include <fstream>
#include <string>

class RendererMesh : public Renderer, public PluginInstance
{

public:

	RendererMesh(Plugin & pluPlugin) : PluginInstance(pluPlugin), m_uWidth(1), m_uHeight(1),
		//m_uVertexBuffer(0), m_uIndexBuffer(0), m_bGenerateBounds(true), m_bLoaded(false),
		//m_vboVertexArrayId(0), m_vboNormalArrayId(0), m_vboTexCoordArrayId(0), m_vboIndexArrayId(0),
		m_uColormapFront(0), m_uColormapBack(0),
		m_shaVolume(pluPlugin.GetPluginDirectory()+"plugin_renderer_mesh_volume.glsl"),
		m_shaDepthPeel(pluPlugin.GetPluginDirectory()+"plugin_renderer_mesh_depthPeel.glsl"),
		m_shaColormap(pluPlugin.GetPluginDirectory()+"plugin_renderer_mesh_colorMap.glsl")
	{
		if (!GLEW_ARB_fragment_shader)
			throw Exception("GL_ARB_fragment_shader is not supported by your hardware or driver.");
		if (!GLEW_ARB_vertex_shader)
			throw Exception("GL_ARB_fragment_shader is not supported by your hardware or driver.");
		if (!GLEW_EXT_framebuffer_object)
			throw Exception("GL_EXT_framebuffer_object is not supported by your hardware or driver.");
		if (!GLEW_ARB_vertex_buffer_object)
			throw Exception("GL_ARB_vertex_buffer_object is not supported by your hardware or driver.");

		m_modVariantObserverGenerate.connect(this,&RendererMesh::propertyModifiedGenerate);
		m_modVariantObserverUpdate.connect(this,&RendererMesh::propertyModifiedUpdate);
		m_modVariantObserverLoadMesh.connect(this, &RendererMesh::propertyUpdateMesh);

		glGenBuffers(1, &m_uVertexBuffer );
		glGenBuffers(1, &m_uIndexBuffer );
		glGenFramebuffersEXT(1, &m_uVolumeFramebuffer);
		glGenFramebuffersEXT(1, &m_uColorCubeFramebuffer);
		glGenTextures(1, &m_uColormapFront);
		glGenTextures(1, &m_uColormapBack);
		glGenTextures(2, m_uNormalMap);
		glGenTextures(2, m_uFinalTexture);
		glGenTextures(4, m_depthTexture);

		GetPlugin().GetProperty("Mesh").require(Variant::TypeFilename());
		GetPlugin().GetProperty("Mesh").addObserver(&m_modVariantObserverLoadMesh);

		GetPlugin().GetProperty("Volume").require(Variant::TypeHandle());
		GetPlugin().GetProperty("Volume").addObserver(&m_modVariantObserverGenerate);

		GetPlugin().GetProperty("Projection Transformation").require(Variant::TypeMatrix());
		GetPlugin().GetProperty("Projection Transformation").addObserver(&m_modVariantObserverUpdate);

		GetPlugin().GetProperty("Viewing Transformation").require(Variant::TypeMatrix());
		GetPlugin().GetProperty("Viewing Transformation").addObserver(&m_modVariantObserverUpdate);

		GetPlugin().GetProperty("Mesh Transformation").require(Variant::TypeMatrix());
		GetPlugin().GetProperty("Mesh Transformation").addObserver(&m_modVariantObserverUpdate);

		GetPlugin().GetProperty("Render Passes").require(Variant::TypeInteger(0, 10));
		GetPlugin().GetProperty("Render Passes").addObserver(&m_modVariantObserverUpdate);

		GetPlugin().GetProperty("Mesh Edge").require(Variant::TypeColor());
		GetPlugin().GetProperty("Mesh Edge").addObserver(&m_modVariantObserverUpdate);

		GetPlugin().GetProperty("Mesh Material").require(Variant::TypeColor());
		GetPlugin().GetProperty("Mesh Material").addObserver(&m_modVariantObserverUpdate);

		GetPlugin().GetProperty("Transfer Function").require(Variant::TypeHandle());
		GetPlugin().GetProperty("Transfer Function").addObserver(&m_modVariantObserverGenerate);

		GetPlugin().GetProperty("Sample Distance").require(Variant::TypeFloat(0.5f,0.25f,2.0f));
		GetPlugin().GetProperty("Sample Distance").addObserver(&m_modVariantObserverUpdate);

		GetPlugin().GetProperty("Cut Volume").require(Variant::TypeBoolean(false));
		GetPlugin().GetProperty("Cut Volume").addObserver(&m_modVariantObserverUpdate);

		m_bLoaded = false;
	};
 
	virtual ~RendererMesh()
	{
		//Mesh stuff from Ivan
		if (m_vboVertexArrayId)			glDeleteBuffers(1 ,&m_vboVertexArrayId);
		if (m_vboNormalArrayId)			glDeleteBuffers(1 ,&m_vboNormalArrayId);
		if (m_vboTexCoordArrayId)		glDeleteBuffers(1 ,&m_vboTexCoordArrayId);
		if (m_vboIndexArrayId)			glDeleteBuffers(1 ,&m_vboIndexArrayId);
		if (m_uVertexBuffer)			glDeleteBuffers(1, &m_uVertexBuffer);
		if (m_uIndexBuffer)				glDeleteBuffers(1, &m_uIndexBuffer);

		if (m_uColormapFront)			glDeleteTextures(1, &m_uColormapFront);
		if (m_uColormapBack)			glDeleteTextures(1, &m_uColormapBack);
		if (m_uVolumeFramebuffer)		glDeleteFramebuffersEXT(1, &m_uVolumeFramebuffer);
		if (m_uColorCubeFramebuffer)	glDeleteFramebuffersEXT(1, &m_uColorCubeFramebuffer);
		glDeleteTextures(2, m_uNormalMap);
		glDeleteTextures(2, m_uFinalTexture);
		glDeleteTextures(4, m_depthTexture);
	};

	virtual void idle()
	{
	};

	virtual void underlay()
	{

	};

	virtual void overlay()
	{
	}; 

	virtual void display(Canvas & canCanvas)
	{
		if (!m_bLoaded) {
			std::string meshFile = GetPlugin().GetProperty("Mesh").require(Variant::TypeFilename());
			if (readObjFile(meshFile.c_str())) {
				createVBO();
				m_bLoaded = true;
			}
		}

		renderVolumeMesh(canCanvas);
	};
	
	virtual void reshape(const unsigned int uWidth, const unsigned int uHeight)
	{
		m_uWidth = uWidth;
		m_uHeight = uHeight;

		setupTexture2D(m_uColormapFront, m_uWidth, m_uHeight, GL_RGBA16F_ARB, GL_RGBA, GL_FLOAT, NULL);
		setupTexture2D(m_uColormapBack, m_uWidth, m_uHeight, GL_RGBA16F_ARB, GL_RGBA, GL_FLOAT, NULL);
		setupTexture2D(m_uFinalTexture[0], m_uWidth, m_uHeight, GL_RGBA16F_ARB, GL_RGBA, GL_FLOAT, NULL);
		setupTexture2D(m_uFinalTexture[1], m_uWidth, m_uHeight, GL_RGBA16F_ARB, GL_RGBA, GL_FLOAT, NULL);
		setupTexture2D(m_uNormalMap[0], m_uWidth, m_uHeight, GL_RGBA16F_ARB, GL_RGBA, GL_FLOAT, NULL);
		setupTexture2D(m_uNormalMap[1], m_uWidth, m_uHeight, GL_RGBA16F_ARB, GL_RGBA, GL_FLOAT, NULL);

		//Setup depth textures
		for(int i=0; i<4; i++) {
			glBindTexture( GL_TEXTURE_RECTANGLE_EXT, m_depthTexture[i] );
			glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_DEPTH24_STENCIL8_EXT, m_uWidth, m_uHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
			glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
			glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
			glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
			glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
			glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
			glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_COMPARE_FUNC_ARB, GL_GREATER);
			glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 0);
		} 

		GetPlugin().update();
	};

	virtual void setupTexture2D(GLuint textureId, int width, int height, GLuint internalFormat, GLuint format, GLuint type, unsigned char *data) {
		glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, textureId);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); 
		glTexImage2D(GL_TEXTURE_2D,0,internalFormat, width, height,0,format,type,data);
	};

	virtual void button(const Controller & conController)
	{
	};

	virtual void cursor(const Controller & conController)
	{
	};

protected:

	virtual void renderVolumeMesh(Canvas & canCanvas) {
		const Handle hanVolume = GetPlugin().GetProperty("Volume").require(Variant::TypeHandle());
		if (!hanVolume.IsValid())
			return;

		const Handle hanImage = GetPlugin().GetProperty("Transfer Function").require(Variant::TypeHandle());
		if (!hanImage.IsValid())
			return;

		const Matrix matVolumeTransformation = hanVolume->GetProperty("Transformation").require(Variant::TypeMatrix());
		const Matrix matProjectionTransformation = GetPlugin().GetProperty("Projection Transformation").require(Variant::TypeMatrix());
		const Matrix matViewingTransformation = GetPlugin().GetProperty("Viewing Transformation").require(Variant::TypeMatrix());
		const Matrix matMeshTransformation = GetPlugin().GetProperty("Mesh Transformation").require(Variant::TypeMatrix());
		const int renderPasses = GetPlugin().GetProperty("Render Passes").require(Variant::TypeInteger());
		const Color meshEdge = GetPlugin().GetProperty("Mesh Edge").require(Variant::TypeColor());
		const Color meshMaterial = GetPlugin().GetProperty("Mesh Material").require(Variant::TypeColor());
		const float fSampleDistance = GetPlugin().GetProperty("Sample Distance").require(Variant::TypeFloat(0.5f,0.25f,2.0f));

		Matrix matDataTransformation;
		const Vector vecMaximum = hanVolume->GetBounds().GetMaximum();
		const float fDimension = hanVolume->GetSize().GetMaximum();
		matDataTransformation *= matVolumeTransformation;
		matDataTransformation.scale(Vector(1.0f / fDimension,1.0f / fDimension,1.0f / fDimension));
		matDataTransformation.translate(vecMaximum * -0.5f);

		Matrix matTextureTransformation;
		matTextureTransformation = hanVolume->GetTextureTransformation() * (matViewingTransformation*matDataTransformation).GetInverse();

		Matrix matGeometryTransformation;
		matGeometryTransformation = (matTextureTransformation * matViewingTransformation) * (matProjectionTransformation * matViewingTransformation).GetInverse();

		Matrix matModelViewTransformation = matViewingTransformation*matDataTransformation;
		Matrix matInverseModelViewTransformation = matModelViewTransformation.GetInverse();

		Matrix matModelViewProjectionTransformation = matProjectionTransformation * matModelViewTransformation;
		Matrix matInverseModelViewProjectionTransformation = matModelViewProjectionTransformation.GetInverse();

		//Find depth / length ratio. Near is not correct, should probably be fixed. Will not affect rendering in most cases.
		const Vector nearVec = matProjectionTransformation.GetInverse() * Vector(0.0f, 0.0f, 0.0f);
		const Vector farVec = matProjectionTransformation.GetInverse() * Vector(0.0f, 0.0f, 1.0f);
		float zFar = farVec.GetZ();
		float zNear = nearVec.GetZ();
		float depthDivisor = -zFar + zNear;

		glPushAttrib(GL_ALL_ATTRIB_BITS);
	
		glMatrixMode(GL_PROJECTION);
		//glPushMatrix();
		glLoadMatrixf(matProjectionTransformation.Get());

		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		glLoadMatrixf(matViewingTransformation.Get());
		glMultMatrixf(matDataTransformation.Get());

		//Draw volume colormaps
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_uColorCubeFramebuffer);
		glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_uColormapFront, 0);
		glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, m_uColormapBack, 0);
		
		// Render front faces
		glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_EXT, m_depthTexture[2], 0);
		glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_EXT, m_depthTexture[2], 0);
		glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);

		glClearColor(0.0f,0.0f,0.0f,1.0f);
		glClearDepth(1.0f);

		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
		glEnable(GL_STENCIL_TEST);
		glStencilFunc(GL_ALWAYS,1,1);
		glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);

		glEnable(GL_DEPTH_TEST);
		glDepthFunc(GL_LESS);

		glEnable(GL_CULL_FACE);
		glCullFace(GL_FRONT);

		glColorMask(GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE);
		renderBounds();
		glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
		m_shaColormap.bind();
		glUniformMatrix4fv(m_shaColormap.GetUniformLocation("matTexture"),1,false,matTextureTransformation.Get());
		glUniform1fARB(m_shaColormap.GetUniformLocation("depthDivisor"),depthDivisor);

		glStencilFunc(GL_ALWAYS,0,1);
		glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE);

		glCullFace(GL_BACK);
		glDepthFunc(GL_LEQUAL);

		renderBounds();

		glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);
		glStencilFunc(GL_EQUAL,1,1);
		glEnable(GL_DEPTH_CLAMP_NV);
		glDepthFunc(GL_ALWAYS);
		const Vector vecNear0 = matInverseModelViewProjectionTransformation * Vector(-1.0f,-1.0f,-1.0f);
		const Vector vecNear1 = matInverseModelViewProjectionTransformation * Vector( 1.0f,-1.0f,-1.0f);
		const Vector vecNear2 = matInverseModelViewProjectionTransformation * Vector( 1.0f, 1.0f,-1.0f);
		const Vector vecNear3 = matInverseModelViewProjectionTransformation * Vector(-1.0f, 1.0f,-1.0f);
		glBegin(GL_QUADS);
		glVertex3fv(vecNear0.Get());
		glVertex3fv(vecNear1.Get());
		glVertex3fv(vecNear2.Get());
		glVertex3fv(vecNear3.Get());
		glEnd();
		glDisable(GL_STENCIL_TEST);

		// Render back faces
		glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_EXT, m_depthTexture[3], 0);
		glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_EXT, m_depthTexture[3], 0);
		glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);
		glClearColor(0.0f,0.0f,0.0f,0.0f);
		glClearDepth(0.0);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glEnable(GL_DEPTH_TEST);
		glEnable(GL_CULL_FACE);
		glDepthFunc(GL_GREATER);
		glCullFace(GL_FRONT);		
		renderBounds();
		m_shaColormap.release();
		//End colormap

		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

		glMatrixMode(GL_MODELVIEW);
		glPopMatrix();

		//Init vertex buffer
		glPushClientAttrib( GL_CLIENT_ALL_ATTRIB_BITS );
		glEnableClientState( GL_VERTEX_ARRAY );
		glBindBuffer( GL_ARRAY_BUFFER, m_vboVertexArrayId );
		glVertexPointer( 3, GL_FLOAT, 0, bufferObjectPtr( 0 ) );
		if (m_vboNormalArrayId!=0) {
			glEnableClientState(GL_NORMAL_ARRAY);
			glBindBuffer( GL_ARRAY_BUFFER, m_vboNormalArrayId );
			glNormalPointer(GL_FLOAT, 0, bufferObjectPtr( 0 ) );
		}
		if (m_vboTexCoordArrayId!=0) {
			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
			glBindBuffer( GL_ARRAY_BUFFER, m_vboTexCoordArrayId );
			glTexCoordPointer(3, GL_FLOAT, 0, bufferObjectPtr( 0 ) );
		}
		glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_vboIndexArrayId );

		const Vector vecViewVolume = matViewingTransformation.GetInverse().GetRotated(Vector(0.0f,0.0f,1.0f)).GetNormalized();
		const Vector vecLightVolume = matViewingTransformation.GetInverse().GetRotated(Vector(0.0f,0.0f,1.0f)).GetNormalized();
		const Vector vecHalfVolume = (vecLightVolume + vecViewVolume).GetNormalized();

		const Vector vecViewMesh = matMeshTransformation.GetInverse().GetRotated(Vector(0.0f,0.0f,1.0f)).GetNormalized();
		const Vector vecLightMesh = matMeshTransformation.GetInverse().GetRotated(Vector(0.0f,0.0f,1.0f)).GetNormalized();
		const Vector vecHalfMesh = (vecLightMesh + vecViewMesh).GetNormalized();

		glEnable(GL_DEPTH_TEST);
		glDepthFunc(GL_LEQUAL);
		glEnable(GL_TEXTURE_RECTANGLE_EXT);
		//Bind textures
		glActiveTexture(GL_TEXTURE3);
		hanVolume->bind();
		glActiveTexture(GL_TEXTURE4);
		hanImage->bind();
		glActiveTexture(GL_TEXTURE5);
		glBindTexture(GL_TEXTURE_2D, m_uColormapFront);
		glActiveTexture(GL_TEXTURE6);
		glBindTexture(GL_TEXTURE_2D, m_uColormapBack);
		glActiveTexture(GL_TEXTURE7);
		glBindTexture(GL_TEXTURE_RECTANGLE_EXT, m_depthTexture[2]);
		glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
		glActiveTexture(GL_TEXTURE8);
		glBindTexture(GL_TEXTURE_RECTANGLE_EXT, m_depthTexture[3]);
		glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);

		glClearColor( 0.0, 0.0, 0.0, 0.0 );
		glDisable(GL_CULL_FACE);

		//Init framebuffer for volume & mesh rendering
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_uVolumeFramebuffer);

		//Fill first depth layer with 0.0
		glClearDepth(0.0);
		glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_uFinalTexture[0], 0);
		glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_EXT, m_depthTexture[0], 0);
		glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
		glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
		glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_EXT, m_depthTexture[2], 0);
		glClear(GL_DEPTH_BUFFER_BIT);
		glClearDepth(1.0); 

		glLoadMatrixf(matMeshTransformation.Get());
		glScalef(0.001f, 0.001f, 0.001f);

		for(int i=0; i<renderPasses; i++) {
			//Set current render target and bind previous
			glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_RECTANGLE_EXT, m_depthTexture[(i+1)%2], 0);
			glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_uFinalTexture[(i+1)%2], 0);
			glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D, m_uNormalMap[(i+1)%2], 0);

			//Update back depth buffer
			glActiveTexture(GL_TEXTURE1);
			glBindTexture(GL_TEXTURE_RECTANGLE_EXT, m_depthTexture[i%2]);
			glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
			glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_COMPARE_FUNC_ARB, GL_GREATER);
			if (i != 0) {
				m_shaDepthPeel.SetOption("first", 0);
				m_shaDepthPeel.bind();
				glUniform1iARB(m_shaDepthPeel.GetUniformLocation("prevDepthTexture"), 1);
			} else {
				m_shaDepthPeel.SetOption("first", 1);
				m_shaDepthPeel.bind();
			}
			glUniform1fARB(m_shaDepthPeel.GetUniformLocation("depthDivisor"),depthDivisor);
			glUniformMatrix4fv(m_shaDepthPeel.GetUniformLocation("matView"), 1, false, matMeshTransformation.Get());
			glUniformMatrix4fv(m_shaDepthPeel.GetUniformLocation("matTexture"), 1, false, matTextureTransformation.Get());
			glDrawBuffer(GL_COLOR_ATTACHMENT1_EXT);
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			glDrawRangeElements(GL_TRIANGLES, 0, m_vecIndices.size() - 1, m_vecIndices.size(), GL_UNSIGNED_INT, bufferObjectPtr(0) );
			if (i != 0) m_shaDepthPeel.release();

			glDisable(GL_DEPTH_TEST);
			//Bind depth textures
			glActiveTexture(GL_TEXTURE1);
			glBindTexture(GL_TEXTURE_RECTANGLE_EXT, m_depthTexture[(i+1)%2]);
			glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);
			glActiveTexture(GL_TEXTURE2);
			glBindTexture(GL_TEXTURE_RECTANGLE_EXT, m_depthTexture[i%2]);
			glTexParameterf(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_COMPARE_MODE_ARB, GL_NONE);

			//Setup shader & render square
			glActiveTexture(GL_TEXTURE0);
			glBindTexture(GL_TEXTURE_2D, m_uFinalTexture[i%2]);
			glActiveTexture(GL_TEXTURE9);
			glBindTexture(GL_TEXTURE_2D, m_uNormalMap[i%2]);
			glActiveTexture(GL_TEXTURE10);
			glBindTexture(GL_TEXTURE_2D, m_uNormalMap[(i+1)%2]);

			//Start ray-casting
			glPushAttrib(GL_ALL_ATTRIB_BITS);

			const bool cutVolume = GetPlugin().GetProperty("Cut Volume").require(Variant::TypeBoolean());
			const Matrix matProjectionTransformation = GetPlugin().GetProperty("Projection Transformation").require(Variant::TypeMatrix());

			int isIn = i%2;
			int renderMesh = 0;
			int renderVolume = 1;
			int passType = isIn;
			if (!cutVolume) passType += 2;
			if (isIn) {
				renderMesh = 1;
				if (cutVolume) renderVolume=0;
			}

			m_shaVolume.SetOption("components",int(hanVolume->GetElementComponents()));
			m_shaVolume.SetOption("passType", passType);
			m_shaVolume.SetOption("renderMesh", renderMesh);
			m_shaVolume.SetOption("renderVolume", renderVolume);

			m_shaVolume.bind();
			glUniform4f (m_shaVolume.GetUniformLocation("samVecMeshEdge"), (float)meshEdge.GetRed()/255.0f, (float)meshEdge.GetGreen()/255.0f, (float)meshEdge.GetBlue()/255.0f, (float)meshEdge.GetAlpha()/255.0f);
			glUniform4f(m_shaVolume.GetUniformLocation("samVecMeshMaterial"), (float)meshMaterial.GetRed()/255.0f, (float)meshMaterial.GetGreen()/255.0f, (float)meshMaterial.GetBlue()/255.0f, (float)meshMaterial.GetAlpha()/255.0f);
			glUniform1iARB(m_shaVolume.GetUniformLocation("samResult"), 0);
			glUniform1iARB(m_shaVolume.GetUniformLocation("samMeshDepthBack"), 1);	
			glUniform1iARB(m_shaVolume.GetUniformLocation("samMeshDepthFront"), 2);
			glUniform1iARB(m_shaVolume.GetUniformLocation("samVolume"), 3);
			glUniform1iARB(m_shaVolume.GetUniformLocation("samTransferFunction"), 4);
			glUniform1iARB(m_shaVolume.GetUniformLocation("samFront"), 5);
			glUniform1iARB(m_shaVolume.GetUniformLocation("samBack"), 6);
			glUniform1iARB(m_shaVolume.GetUniformLocation("samVolumeDepthFront"), 7);
			glUniform1iARB(m_shaVolume.GetUniformLocation("samVolumeDepthBack"), 8);
			glUniform1iARB(m_shaVolume.GetUniformLocation("samNormalFront"), 9);
			glUniform1iARB(m_shaVolume.GetUniformLocation("samNormalBack"), 10);
			//glUniform1iARB(m_shaVolume.GetUniformLocation("samNoise"), 4);
			glUniform1fARB(m_shaVolume.GetUniformLocation("depthDivisor"),depthDivisor);

			glUniform1f(m_shaVolume.GetUniformLocation("fUnitStep"), 1.0f / hanVolume->GetTextureSize().GetMaximum());
			glUniform3fvARB(m_shaVolume.GetUniformLocation("vecSteps"), 1, hanVolume->GetTextureSize().GetInverse().Get());
			glUniform1f(m_shaVolume.GetUniformLocation("fSampleDistance"), fSampleDistance);
			glUniform3fvARB(m_shaVolume.GetUniformLocation("vecLightVolume"), 1, vecLightVolume.Get());
			glUniform3fvARB(m_shaVolume.GetUniformLocation("vecHalfVolume"), 1, vecHalfVolume.Get());
			glUniform3fvARB(m_shaVolume.GetUniformLocation("vecLightMesh"), 1, vecLightMesh.Get());
			glUniform3fvARB(m_shaVolume.GetUniformLocation("vecHalfMesh"), 1, vecHalfMesh.Get());
			glUniformMatrix4fv(m_shaVolume.GetUniformLocation("matVolume"),1,false,matVolumeTransformation.GetInverse().Get());

			glMatrixMode(GL_PROJECTION);
			glPushMatrix();
			glLoadIdentity();

			glMatrixMode(GL_MODELVIEW);
			glPushMatrix();
			glLoadIdentity();

			//Render to framebuffer
			glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);
			glColorMask(GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE);
			glDisable(GL_DEPTH_TEST);
			glDepthMask(GL_FALSE);
			glClear(GL_COLOR_BUFFER_BIT);
			glBegin(GL_QUADS);
			glTexCoord2f(0.0f,0.0f);	glVertex3f(-1.0f,-1.0f,1.0f);
			glTexCoord2f(1.0f,0.0f);	glVertex3f(1.0f,-1.0f,1.0f);
			glTexCoord2f(1.0f,1.0f);	glVertex3f(1.0f,1.0f,1.0f);
			glTexCoord2f(0.0f,1.0f);	glVertex3f(-1.0f,1.0f,1.0f);
			glEnd();

			m_shaVolume.release();

			glMatrixMode(GL_PROJECTION);
			glPopMatrix();

			glMatrixMode(GL_MODELVIEW);
			glPopMatrix();

			glPopAttrib();
			//Ray-casting done

			glEnable(GL_DEPTH_TEST);
		}

		//Disable everything
		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

		glBindBuffer( GL_ARRAY_BUFFER, 0 );
		glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );

		if (m_vboTexCoordArrayId!=0) glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		if (m_vboNormalArrayId!=0) glDisableClientState(GL_NORMAL_ARRAY);
		glDisableClientState( GL_VERTEX_ARRAY );

		glPopClientAttrib();

		//Release
		glActiveTexture(GL_TEXTURE0);		//Release m_uFinalRenderTexture[*]
		glBindTexture(GL_TEXTURE_2D, 0);
		glActiveTexture(GL_TEXTURE1);		//Release m_uDepth 0
		glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 0);
		glActiveTexture(GL_TEXTURE2);		//Release m_uDepth 1
		glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 0);
		glActiveTexture(GL_TEXTURE3);		//Release hanVolume
		hanVolume->release();
		glActiveTexture(GL_TEXTURE4);		//Release hanImage
		hanImage->release();
		glActiveTexture(GL_TEXTURE5);		//Release m_uColormapFront
		glBindTexture(GL_TEXTURE_2D, 0);
		glActiveTexture(GL_TEXTURE6);		//Release m_uColormapBack
		glBindTexture(GL_TEXTURE_2D, 0);
		glActiveTexture(GL_TEXTURE7);		//Release volume depth texture 0
		glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 0);
		glActiveTexture(GL_TEXTURE8);		//Release volume depth texture 1
		glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 0);
		glActiveTexture(GL_TEXTURE9);		//Release m_uNormalMap 0
		glBindTexture(GL_TEXTURE_2D, 0);
		glActiveTexture(GL_TEXTURE10);		//Release m_uNormalMap 1
		glBindTexture(GL_TEXTURE_2D, 0);
		glDisable(GL_TEXTURE_2D);
		glDisable(GL_TEXTURE_RECTANGLE_EXT);

//		glMatrixMode(GL_PROJECTION);
//		glPopMatrix();

//		glMatrixMode(GL_MODELVIEW);
//		glPopMatrix();

		glPopAttrib();

		//Print rendered texture
		canCanvas.bind();
		glEnable(GL_BLEND); 
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 

		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();

		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		glLoadIdentity();

		glActiveTexture(GL_TEXTURE0);
		glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, m_uFinalTexture[renderPasses%2]);

		// Show rendered image
		glBegin(GL_QUADS);
		glTexCoord2f(0.0f,0.0f);	glVertex3f(-1.0f,-1.0f,1.0f);
		glTexCoord2f(1.0f,0.0f);	glVertex3f(1.0f,-1.0f,1.0f);
		glTexCoord2f(1.0f,1.0f);	glVertex3f(1.0f,1.0f,1.0f);
		glTexCoord2f(0.0f,1.0f);	glVertex3f(-1.0f,1.0f,1.0f);
		glEnd();

		glBindTexture(GL_TEXTURE_2D,0);
		
		glMatrixMode(GL_PROJECTION);
		glPopMatrix();

		glMatrixMode(GL_MODELVIEW);
		glPopMatrix();
		
		canCanvas.release();
	};

	//old mesh-loader code from Ivan
	void createVBO()
	{
		if (glGetError() != GL_NO_ERROR) std::cout << " gl error" <<  std::endl;
		m_vboVertexArrayId = 0;
		m_vboNormalArrayId = 0;
		m_vboTexCoordArrayId = 0;
		m_vboIndexArrayId = 0;

		if (m_vecVertices.size()>0)
		{
			glGenBuffers(1, &m_vboVertexArrayId);
			if (!glIsBuffer(m_vboVertexArrayId)) std::cout << " glGenBuffers error" <<  std::endl;
		} else throw Exception("Mesh does not contain any vertices!");

		if (m_vecNormals.size()>0)
		{
			glGenBuffers(1, &m_vboNormalArrayId);
			if (!glIsBuffer(m_vboNormalArrayId)) std::cout << " glGenBuffers error" <<  std::endl;
		}

		if (m_vecTexCoords.size()>0)
		{
			glGenBuffers(1, &m_vboTexCoordArrayId);
			if (!glIsBuffer(m_vboTexCoordArrayId)) std::cout << " glGenBuffers error" <<  std::endl;
		}

		if (m_vecIndices.size()>0)
		{
			glGenBuffers(1, &m_vboIndexArrayId);
			if (!glIsBuffer(m_vboIndexArrayId)) std::cout << " glGenBuffers error" <<  std::endl;
		} else throw Exception("The index array is empty!");

		// Bind the buffer object, OpenGL initially creates it empty.
		if (m_vboVertexArrayId!=0)
		{
			glBindBuffer(GL_ARRAY_BUFFER, m_vboVertexArrayId);
			glBufferData( GL_ARRAY_BUFFER, m_vecVertices.size()*sizeof(GLfloat), &(m_vecVertices.front()),GL_STATIC_DRAW ); // Tell OpenGL to copy data from the 'vertices' pointer into   the buffer object.
			//m_vecVertices.clear();
		}

		if (m_vboNormalArrayId!=0)
		{
			glBindBuffer(GL_ARRAY_BUFFER, m_vboNormalArrayId);
			glBufferData( GL_ARRAY_BUFFER, m_vecNormals.size()*sizeof(GLfloat), &(m_vecNormals.front()),GL_STATIC_DRAW );
			//m_vecNormals.clear();
		}

		if (m_vboTexCoordArrayId!=0)
		{
			glBindBuffer(GL_ARRAY_BUFFER, m_vboTexCoordArrayId);
			glBufferData( GL_ARRAY_BUFFER, m_vecTexCoords.size()*sizeof(GLfloat), &(m_vecTexCoords.front()),GL_STATIC_DRAW );
			//m_vecTexCoords.clear();
		}

		if (m_vboIndexArrayId!=0)
		{
			glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_vboIndexArrayId );
			glBufferData( GL_ELEMENT_ARRAY_BUFFER, m_vecIndices.size()*sizeof(GLuint), &(m_vecIndices.front()), GL_STATIC_DRAW );
			//m_vecIndices.clear();
		}

		glBindBuffer( GL_ARRAY_BUFFER, 0 );
		glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );

		if (glGetError() != GL_NO_ERROR) std::cout << " gl error" <<  std::endl;
	};

	bool readObjFile(const char* cFilename)
	{
		std::string line;
		std::ifstream objfile (cFilename);

		std::vector<GLfloat> vecVertices;
		std::vector<GLfloat> vecNormals;
		std::vector<GLfloat> vecTexCoords;
		std::vector<GLuint> vecVertexIndices;
		std::vector<GLuint> vecNormalIndices;
		std::vector<GLuint> vecTexCoordIndices;

		if (objfile.is_open())
		{
			vecVertices.clear();
			vecNormals.clear();
			vecTexCoords.clear();
			vecVertexIndices.clear();
			vecNormalIndices.clear();
			vecTexCoordIndices.clear();

			m_vecVertices.clear();
			m_vecNormals.clear();
			m_vecTexCoords.clear();
			m_vecIndices.clear();

			GLfloat fX, fY, fZ;
			GLuint uV[3], uVt[3], uVn[3];

			while (! objfile.eof() )
			{
				getline (objfile,line);
				//std::cout << line << std::endl;
				
				fX=0.0f; fY=0.0f; fZ=0.0f; uV[0]=0; uV[1]=0; uV[2]=0; uVt[0]=0; uVt[1]=0; uVt[2]=0; uVn[0]=0; uVn[1]=0; uVn[2]=0;

				// Trimming left spaces (should not appear but still...)
				std::string::iterator i;
				for (i = line.begin(); i != line.end(); i++) if (!isspace(*i)) break;
				if (i == line.end()) line.clear();
				else line.erase(line.begin(), i);

				// Normals
				if (line.find("vn") == 0)
				{
					sscanf_s(line.c_str(), "vn %f %f %f", &fX, &fY, &fZ);
					vecNormals.push_back(fX);
					vecNormals.push_back(fY);
					vecNormals.push_back(fZ);
					//std::cout << line << " vn " << fX << " " << fY << " " << fZ << std::endl;

					continue;
				}

				// Texture coordinates 
				if (line.find("vt") == 0)
				{
					sscanf_s(line.c_str(), "vt %f %f %f", &fX, &fY, &fZ);
					vecTexCoords.push_back(fX);
					vecTexCoords.push_back(1.0f-fY); //rotate the image
					vecTexCoords.push_back(fZ);
					//std::cout << line << " vt " << fX << " " << fY << " " << fZ << std::endl;

					continue;
				}

				// Positions
				if (line.find("v") == 0)
				{
					sscanf_s(line.c_str(), "v %f %f %f", &fX, &fY, &fZ);
					vecVertices.push_back(fX);
					vecVertices.push_back(fY);
					vecVertices.push_back(fZ);
					//std::cout << line << " v " << fX << " " << fY << " " << fZ << std::endl;

					continue;
				}

				// Faces
				if (line.find("f") == 0)
				{
					if (vecVertices.size()==0) continue;
					//std::cout << line << " f ";
					while (i < line.end())
					{
						for (i = line.begin(); i != line.end(); i++) if (isspace(*i)) break; // deleting the previous text
						if (i == line.end()) continue;
						else line.erase(line.begin(), i);

						for (i = line.begin(); i != line.end(); i++) if (!isspace(*i)) break; // deleting spaces
						if (i == line.end()) continue;
						else line.erase(line.begin(), i);

						if (vecTexCoords.size()>0) // meaning that faces should appear after vt and vn in the obj file which is usually the case
						{
							if (vecNormals.size()>0)
							{
								// support for polygons: n != 3
								// case of n=5: 1,2,0; 2,3,0; 3,4,0; ...
								// works of course only for convex polygons
								if (uV[2]==0) sscanf_s(line.c_str(), "%d/%d/%d", &uV[2], &uVt[2], &uVn[2]); // third vertex is always the first read
								else if (uV[0]==0) sscanf_s(line.c_str(), "%d/%d/%d", &uV[0], &uVt[0], &uVn[0]); // first vertex reading
								else
								{
									if (uV[1]!=0) 
									{
										uV[0] = uV[1]; // or changing the second vertex to be the first one
										uVt[0] = uVt[1];
										uVn[0] = uVn[1];
									}
									sscanf_s(line.c_str(), "%d/%d/%d", &uV[1], &uVt[1], &uVn[1]);
									for (unsigned int j=0; j<3;j++)
									{
										vecVertexIndices.push_back(uV[j]-1); //-1 deals with the addressing in the buffer
										vecNormalIndices.push_back(uVn[j]-1);
										vecTexCoordIndices.push_back(uVt[j]-1);
										//std::cout << uV[j] << ", " << uVt[j] << ", " << uVn[j] << "; ";
									}
								}
							}
							else
							{
								if (uV[2]==0) sscanf_s(line.c_str(), "%d/%d", &uV[2], &uVt[2]); // third vertex is always the first read
								else if (uV[0]==0) sscanf_s(line.c_str(), "%d/%d", &uV[0], &uVt[0]); // first vertex reading
								else
								{
									if (uV[1]!=0) 
									{
										uV[0] = uV[1];
										uVt[0] = uVt[1];
									}
									sscanf_s(line.c_str(), "%d/%d", &uV[1], &uVt[1]);
									for (unsigned int j=0; j<3;j++)
									{
										vecVertexIndices.push_back(uV[j]-1);
										vecTexCoordIndices.push_back(uVt[j]-1);
										//std::cout << uV[j] << ", " << uVt[j] << "; ";
									}
								}
							}
						}
						else
						{
							if (vecNormals.size()>0)
							{
								if (uV[2]==0) sscanf_s(line.c_str(), "%d//%d", &uV[2], &uVn[2]);
								else if (uV[0]==0) sscanf_s(line.c_str(), "%d//%d", &uV[0], &uVn[0]);
								else
								{
									if (uV[1]!=0) 
									{
										uV[0] = uV[1];
										uVn[0] = uVn[1];
									}
									sscanf_s(line.c_str(), "%d//%d", &uV[1], &uVn[1]);
									for (unsigned int j=0; j<3;j++)
									{
										vecVertexIndices.push_back(uV[j]-1);
										vecNormalIndices.push_back(uVn[j]-1);
										//std::cout << uV[j] << ", " << uVn[j] << "; ";
									}
								}
							}
							else
							{
								if (uV[2]==0) sscanf_s(line.c_str(), "%d", &uV[2]);
								else if (uV[0]==0) sscanf_s(line.c_str(), "%d", &uV[0]);
								else
								{
									if (uV[1]!=0) uV[0] = uV[1];
									sscanf_s(line.c_str(), "%d", &uV[1]);
									for (unsigned int j=0; j<3;j++) vecVertexIndices.push_back(uV[j]-1);
								}
							}
						}
						if (i == line.end()) continue;
						i++;
					}
					//std::cout << std::endl;
					continue;
				}

				// TODO support for materials
				// TODO support for groups
			}
			objfile.close();
			//std::cout << "until end of reading!\n";

			unsigned int uTri;
			for (unsigned int i=0; i<vecVertexIndices.size();i++)
			{
				uTri = 3*vecVertexIndices[i];
				m_vecVertices.push_back(vecVertices[uTri]);
				m_vecVertices.push_back(vecVertices[uTri+1]);
				m_vecVertices.push_back(vecVertices[uTri+2]);

				if (vecNormalIndices.size()>0)
				{
			//std::cout << "in normals\n";
					uTri = 3*vecNormalIndices[i];
					m_vecNormals.push_back(vecNormals[uTri]);
					m_vecNormals.push_back(vecNormals[uTri+1]);
					m_vecNormals.push_back(vecNormals[uTri+2]);
				}

				if (vecTexCoordIndices.size()>0)
				{
			//std::cout << "in textures!\n";
					uTri = 3*vecTexCoordIndices[i];
					m_vecTexCoords.push_back(vecTexCoords[uTri]);
					m_vecTexCoords.push_back(vecTexCoords[uTri+1]);
					m_vecTexCoords.push_back(vecTexCoords[uTri+2]);
				}

				m_vecIndices.push_back(i);
			}

			vecVertices.clear();
			vecNormals.clear();
			vecTexCoords.clear();
			vecVertexIndices.clear();
			vecNormalIndices.clear();
			vecTexCoordIndices.clear();

			if (m_vecNormals.size()==0) // if no normals calculate at least per-face nomals
			{
				m_vecNormals.clear();
				for (unsigned int i = 0; i < m_vecVertices.size(); i+=9)
				{
					// compute two vectors in the triangle plane
					GLfloat fVector0[3], fVector1[3], fNormalVector[3];
					fVector0[0] = m_vecVertices[i+1*3] - m_vecVertices[i];
					fVector0[1] = m_vecVertices[i+1*3+1] - m_vecVertices[i+1];
					fVector0[2] = m_vecVertices[i+1*3+2] - m_vecVertices[i+2];

					fVector1[0] = m_vecVertices[i+2*3] - m_vecVertices[i];
					fVector1[1] = m_vecVertices[i+2*3+1] - m_vecVertices[i+1];
					fVector1[2] = m_vecVertices[i+2*3+2] - m_vecVertices[i+2];

					// cross product
					// X = Y*Z - Z*Y
					fNormalVector[0] = fVector0[1]*fVector1[2] - fVector0[2]*fVector1[1];
					// Y = Z*X - X*Z
					fNormalVector[1] = fVector0[2]*fVector1[0] - fVector0[0]*fVector1[2];
					// Z = X*Y - Y*X
					fNormalVector[2] = fVector0[0]*fVector1[1] - fVector0[1]*fVector1[0];

					// normalize
					GLfloat fNormalVectorSize = (float) sqrt(fNormalVector[0]*fNormalVector[0]+fNormalVector[1]*fNormalVector[1]+fNormalVector[2]*fNormalVector[2]);
					if (fNormalVectorSize)
					{
						fNormalVector[0]/=fNormalVectorSize;
						fNormalVector[1]/=fNormalVectorSize;
						fNormalVector[2]/=fNormalVectorSize;
					}
					
					// 3 times add to the normal vector stack
					// for each vertex of the triangle
					m_vecNormals.push_back(fNormalVector[0]);
					m_vecNormals.push_back(fNormalVector[1]);
					m_vecNormals.push_back(fNormalVector[2]);

					m_vecNormals.push_back(fNormalVector[0]);
					m_vecNormals.push_back(fNormalVector[1]);
					m_vecNormals.push_back(fNormalVector[2]);

					m_vecNormals.push_back(fNormalVector[0]);
					m_vecNormals.push_back(fNormalVector[1]);
					m_vecNormals.push_back(fNormalVector[2]);
				}
			}

//			std::cout << "we made it until here!\n";
		}
		else {
			std::cout << "Unable to open file"; 
			return false;
		}
		return true;
	};

	inline GLvoid* bufferObjectPtr( unsigned int idx )
	{
		return (GLvoid*)( ((char*)NULL) + idx );
	};


	void generateBounds()
	{
		if (m_bGenerateBounds)
		{
			Handle hanHandle = GetPlugin().GetProperty("Volume");

			if (!hanHandle.IsValid())
				return;

			Handle hanImage = GetPlugin().GetProperty("Transfer Function");
			Image<unsigned char,4> *pImage = dynamic_cast< Image<unsigned char,4> *>(hanImage.GetResource());

			if (!pImage)
			{
				static Image<unsigned char,4> imgImage;
				pImage = &imgImage;
			}

			std::vector<float> vecVertices;
			std::vector<unsigned int> vecIndices;

			RESOURCE_CALL(generateGeometry,*hanHandle,vecVertices,vecIndices,*pImage);

			if (vecVertices.size() > 0)
			{
				glBindBuffer(GL_ARRAY_BUFFER, m_uVertexBuffer);
				glBufferData(GL_ARRAY_BUFFER, vecVertices.size()*sizeof(float), (void*)&(vecVertices.front()), GL_STATIC_DRAW);
				glBindBuffer(GL_ARRAY_BUFFER, 0);
			}

			if (vecIndices.size() > 0)
			{
				glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_uIndexBuffer);
				glBufferData(GL_ELEMENT_ARRAY_BUFFER, vecIndices.size()*sizeof(unsigned int), (void*)&(vecIndices.front()), GL_STATIC_DRAW);
				glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
			}

			m_uElementCount = vecIndices.size();
			m_bGenerateBounds = false;
		}
	};

	void renderBounds()
	{
		generateBounds();

		glBindBuffer(GL_ARRAY_BUFFER, m_uVertexBuffer);
		glVertexPointer( 3, GL_FLOAT, 0, NULL);
		glEnableClientState(GL_VERTEX_ARRAY);	

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_uIndexBuffer);
		glIndexPointer(GL_INT,0,NULL);
		glEnableClientState(GL_INDEX_ARRAY);

		glDrawElements(GL_QUADS,m_uElementCount,GL_UNSIGNED_INT,NULL);

		glDisableClientState( GL_VERTEX_ARRAY );	
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

		glDisableClientState(GL_INDEX_ARRAY);
		glBindBuffer(GL_ARRAY_BUFFER, 0);
	};

	virtual void propertyModifiedGenerate(const Variant & varVariant, const Observable::Event & eveEvent)
	{
		m_bGenerateBounds = true;
		GetPlugin().update();
	};

	virtual void propertyModifiedUpdate(const Variant & varVariant, const Observable::Event & eveEvent)
	{
		GetPlugin().update();
	};

	virtual void propertyUpdateMesh(const Variant & varVariant, const Observable::Event & eveEvent) {

		m_bGenerateBounds = true;
		m_bLoaded = false;
		GetPlugin().update();
	}

	virtual void checkError(char *message) {
		GLuint glError = glGetError();

		if (glError != GL_NO_ERROR)
			throw Exception("%s (%s) - %d.",message, gluErrorString(glError), glError);
	};

	virtual void writeTexture(char *fileName, GLuint attachment) {
		int windowSizeX = m_uWidth;
		int windowSizeY = m_uHeight;

		void*  bmpBuffer = (void*)malloc(windowSizeX*windowSizeY*3);
		if  (!bmpBuffer)
		return;
		 
		glReadBuffer(attachment);
		glReadPixels((GLint)0, (GLint)0, (GLint)windowSizeX-1, (GLint)windowSizeY-1, GL_RGB,  GL_UNSIGNED_BYTE, bmpBuffer);
		 
		FILE *filePtr = fopen(fileName,  "wb");
		if (!filePtr)
		return;
		 
		BITMAPFILEHEADER  bitmapFileHeader;
		BITMAPINFOHEADER  bitmapInfoHeader;
		 
		bitmapFileHeader.bfType = 0x4D42;  //"BM"
		bitmapFileHeader.bfSize =  windowSizeX*windowSizeY*3;
		bitmapFileHeader.bfReserved1 =  0;
		bitmapFileHeader.bfReserved2 = 0;
		bitmapFileHeader.bfOffBits  =
		sizeof(BITMAPFILEHEADER) +  sizeof(BITMAPINFOHEADER);
		 
		bitmapInfoHeader.biSize =  sizeof(BITMAPINFOHEADER);
		bitmapInfoHeader.biWidth =  windowSizeX-1;
		bitmapInfoHeader.biHeight =  windowSizeY-1;
		bitmapInfoHeader.biPlanes = 1;
		bitmapInfoHeader.biBitCount  = 24;
		bitmapInfoHeader.biCompression =  BI_RGB;
		bitmapInfoHeader.biSizeImage = 0;
		bitmapInfoHeader.biXPelsPerMeter  = 0; // ?
		bitmapInfoHeader.biYPelsPerMeter = 0; //  ?
		bitmapInfoHeader.biClrUsed = 0;
		bitmapInfoHeader.biClrImportant =  0;
		 
		fwrite(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1,  filePtr);
		fwrite(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1,  filePtr);
		fwrite(bmpBuffer, windowSizeX*windowSizeY*3, 1,  filePtr);
		fclose(filePtr);
		 
		free(bmpBuffer);
	};

	template < template<class,unsigned int> class ResourceType, class ElementType,unsigned int N>
	void generateGeometry(const ResourceType<ElementType,N> & resResource, std::vector<float> & vecVertices, std::vector<unsigned int> & vecIndices, const Image<unsigned char,4> & imgImage)
	{

	};

	template <class ElementType,unsigned int N>
	void generateGeometry(const Volume<ElementType,N> & volVolume, std::vector<float> & vecVertices, std::vector<unsigned int> & vecIndices, const Image<unsigned char,4> & imgImage)
	{
		Volume<ElementType,N>::HierachicalIterator iter(volVolume);
		static const unsigned int vuIndices[6][4] =
		{

			{0, 1, 2, 3},
			{3, 2, 6, 7},
			{7, 6, 5, 4},
			{4, 5, 1, 0},
			{5, 6, 2, 1},
			{7, 4, 0, 3}
		};

		const unsigned int uVolumeWidth = volVolume.GetWidth();
		const unsigned int uVolumeHeight = volVolume.GetHeight();
		const unsigned int uVolumeDepth = volVolume.GetDepth();

		const unsigned int uX = iter.GetPositionX();
		const unsigned int uY = iter.GetPositionY();
		const unsigned int uZ = iter.GetPositionZ();

		const unsigned int uWidth = iter.GetWidth();
		const unsigned int uHeight = iter.GetHeight();
		const unsigned int uDepth = iter.GetDepth();

		const unsigned int uStartX = uX - uWidth/2;
		const unsigned int uStartY = uY - uHeight/2;
		const unsigned int uStartZ = uZ - uDepth/2;

		if (uStartX > volVolume.GetWidth() || uStartY > volVolume.GetHeight() || uStartZ > volVolume.GetDepth())
			return;

		const unsigned int uEndX = std::min(uVolumeWidth,uX + uWidth/2);
		const unsigned int uEndY = std::min(uVolumeHeight,uY + uHeight/2);
		const unsigned int uEndZ = std::min(uVolumeDepth,uZ + uDepth/2);

		float vfVertices[8][3];
		vfVertices[0][0] = vfVertices[1][0] = vfVertices[2][0] = vfVertices[3][0] = float(uStartX);
		vfVertices[4][0] = vfVertices[5][0] = vfVertices[6][0] = vfVertices[7][0] = float(uEndX) - 1.0f/16384.0f;
		vfVertices[0][1] = vfVertices[1][1] = vfVertices[4][1] = vfVertices[5][1] = float(uStartY);
		vfVertices[2][1] = vfVertices[3][1] = vfVertices[6][1] = vfVertices[7][1] = float(uEndY) - 1.0f/16384.0f;
		vfVertices[0][2] = vfVertices[3][2] = vfVertices[4][2] = vfVertices[7][2] = float(uStartZ);
		vfVertices[1][2] = vfVertices[2][2] = vfVertices[5][2] = vfVertices[6][2] = float(uEndZ) - 1.0f/16384.0f;


		const unsigned int uOffset = vecVertices.size() / 3;

		for (int i=0;i<24;i++)
		{
			vecVertices.push_back(*(&(vfVertices[0][0])+i));
			vecIndices.push_back(*(&(vuIndices[0][0])+i)+uOffset);
		}
	};

	template <class ElementType>
	void generateGeometry(const Volume<ElementType,1> & volVolume, std::vector<float> & vecVertices, std::vector<unsigned int> & vecIndices, const Image<unsigned char,4> & imgImage)
	{
		Volume<ElementType,1>::HierachicalIterator iter(volVolume);
		Image<unsigned char,4>::IntegralIterator intIntegral(imgImage);
		generateGeometry(volVolume,vecVertices,vecIndices,imgImage,iter);
	};

	template <class ElementType>
	void generateGeometry(const Volume<ElementType,1> & volVolume, std::vector<float> & vecVertices, std::vector<unsigned int> & vecIndices, const Image<unsigned char,4>::IntegralIterator & intIntegral, typename const Volume<ElementType,1>::HierachicalIterator & iter)
	{
		const Voxel<float,1> voxMinimum = iter.GetMinimum();
		const Voxel<float,1> voxMaximum = iter.GetMaximum();

		const unsigned int u0x = unsigned int(voxMinimum.Get(0)*float(intIntegral.GetWidth()-1));
		const unsigned int u1x = unsigned int(voxMaximum.Get(0)*float(intIntegral.GetWidth()-1));
		const Voxel<float,2> voxIntegral = intIntegral.GetIntegral(u0x,u1x);

		if (voxIntegral.Get(3) > 1.192092896e-07F)
		{
			if (iter.IsLeaf())
			{
				static const unsigned int vuIndices[6][4] =
				{

					{0, 1, 2, 3},
					{3, 2, 6, 7},
					{7, 6, 5, 4},
					{4, 5, 1, 0},
					{5, 6, 2, 1},
					{7, 4, 0, 3}
				};

				const unsigned int uVolumeWidth = volVolume.GetWidth();
				const unsigned int uVolumeHeight = volVolume.GetHeight();
				const unsigned int uVolumeDepth = volVolume.GetDepth();

				const unsigned int uX = iter.GetPositionX();
				const unsigned int uY = iter.GetPositionY();
				const unsigned int uZ = iter.GetPositionZ();

				const unsigned int uWidth = iter.GetWidth();
				const unsigned int uHeight = iter.GetHeight();
				const unsigned int uDepth = iter.GetDepth();

				const unsigned int uStartX = uX - uWidth/2;
				const unsigned int uStartY = uY - uHeight/2;
				const unsigned int uStartZ = uZ - uDepth/2;

				if (uStartX > volVolume.GetWidth() || uStartY > volVolume.GetHeight() || uStartZ > volVolume.GetDepth())
					return;

				const unsigned int uEndX = std::min(uVolumeWidth,uX + uWidth/2);
				const unsigned int uEndY = std::min(uVolumeHeight,uY + uHeight/2);
				const unsigned int uEndZ = std::min(uVolumeDepth,uZ + uDepth/2);

				float vfVertices[8][3];
				vfVertices[0][0] = vfVertices[1][0] = vfVertices[2][0] = vfVertices[3][0] = float(uStartX);
				vfVertices[4][0] = vfVertices[5][0] = vfVertices[6][0] = vfVertices[7][0] = float(uEndX) - 1.0f/16384.0f;
				vfVertices[0][1] = vfVertices[1][1] = vfVertices[4][1] = vfVertices[5][1] = float(uStartY);
				vfVertices[2][1] = vfVertices[3][1] = vfVertices[6][1] = vfVertices[7][1] = float(uEndY) - 1.0f/16384.0f;
				vfVertices[0][2] = vfVertices[3][2] = vfVertices[4][2] = vfVertices[7][2] = float(uStartZ);
				vfVertices[1][2] = vfVertices[2][2] = vfVertices[5][2] = vfVertices[6][2] = float(uEndZ) - 1.0f/16384.0f;


				const unsigned int uOffset = vecVertices.size() / 3;

				for (int i=0;i<24;i++)
				{
					vecVertices.push_back(*(&(vfVertices[0][0])+i));
					vecIndices.push_back(*(&(vuIndices[0][0])+i)+uOffset);
				}
			}
			else
			{
				for (Volume<ElementType,1>::HierachicalIterator i(iter); !i.IsAtEnd(); ++i)
					generateGeometry(volVolume,vecVertices,vecIndices,intIntegral,(*i));
			}
		}
	};

private:
	FILE *m_pFile;
	bool m_bLoaded;
	std::vector<GLfloat> m_vecVertices;
	std::vector<GLfloat> m_vecNormals;
	std::vector<GLfloat> m_vecTexCoords;
	std::vector<GLuint> m_vecIndices;

	Shader m_shaColormap;
	Shader m_shaDepthPeel;
	Shader m_shaVolume;
	unsigned int m_uWidth, m_uHeight;
	GLuint m_vboVertexArrayId;
	GLuint m_vboNormalArrayId;
	GLuint m_vboTexCoordArrayId;
	GLuint m_vboIndexArrayId;

	GLuint m_uColormapFront;	
	GLuint m_uColormapBack;
	GLuint m_uNormalMap[2];
	GLuint m_uFinalTexture[2];
	GLuint m_depthTexture[4];

	unsigned int m_uVertexBuffer;
	unsigned int m_uIndexBuffer; 
	unsigned int m_uElementCount;
	bool m_bGenerateBounds;

	GLuint m_uColorCubeFramebuffer;
	GLuint m_uVolumeFramebuffer;

	ModifiedObserver m_modVariantObserverGenerate;
	ModifiedObserver m_modVariantObserverUpdate;
	ModifiedObserver m_modVariantObserverLoadMesh;
};


DECLARE_RENDERERPLUGIN(RendererMesh)