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

	Program:		Range Imaging Toolkit (RITK)
	Module:			ritkOpenGLRImageEntity.cxx
	Version:		$Revision: 2869 $
	Date:			$Date: 2012-03-30 16:27:19 +0200 (Fr, 30 Mrz 2012) $

	Copyright (c) 2010-2011
	Pattern Recognition Lab, 
	University Erlangen-Nuremberg, Germany
	All rights reserved.

	This program 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 RITKCopyright.txt for more information.

	http://www5.informatik.uni-erlangen.de/research/software/ritk

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

#include "ritkDebugManager.h"

#include <QDataStream>
#include <QVariant>
#include <QDockWidget>
#include <QApplication>
#include <QComboBox>

#include "ThirdParty/GLee.h"

#include "ritkOpenGLRImageEntity.h"
#include "ritkLUTs.h"
#include "ritkShaders.h"

#include <itkRescaleIntensityImageFilter.h>

#define CHECK_GL_ERROR()												\
{																		\
	GLenum errCode = glGetError();										\
	if ( errCode != GL_NO_ERROR )										\
	{																	\
	std::cout << "An OpenGL error occured: " << errCode << std::endl;	\
	std::cout << gluErrorString(errCode) << std::endl;					\
	std::cout << __FILE__ << " Line " << __LINE__  << std::endl;		\
	}																	\
}

#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")

//#define PERFORMANCE_CHECK
//#define USE_SUB_TEX
#define USE_INDEX_VBO


namespace ritk
{

//----------------------------------------------------------------------------
OpenGLRImageEntity::OpenGLRImageEntity()
{
	// Not initialized by default
	m_InitFlag = false;
	m_SizeDirtyFlag = true;

	// Initialize containers
	m_CurrentSize.Fill(0);
	m_TextureCoordsContainer = GenericImportImageContainer<unsigned long, itk::Vector<float,2> >::New();
	m_TriangleStripsIndicesContainer = GenericImportImageContainer<unsigned long, unsigned int>::New();
	m_PointsIndicesContainer = GenericImportImageContainer<unsigned long, unsigned int>::New();

	// Invalid GL interop adaptor
	m_CurrentWorldCoordGraphicsInteropContainer = NULL;

	// Initialize primary payload members
	m_PrimaryPayloadLUTTexture = 0;
	m_PrimaryPayloadTexture = 0;

	// Initialize secondary payload members
	m_SecondaryPayloadLUTTexture = 0;
	m_SecondaryPayloadTexture = 0;

	// VBOs not initialized by default
	m_VBOInitialized = false;
	m_VBOTriangleStripsIndices = 0;
	m_VBOVertices = 0;
	m_VBOTexCoords = 0;

	// No textures by default
	m_FlagTexture = 0;
	m_WorldCoordTexture = 0;

	// Std blending parameters and no shader program by default
	m_Alpha = 0.5;
	m_ShaderProgram = 0;
	m_RenderPoints = false;

	// Discard defect pixels by default
	m_DiscardInvalidFragments = true;

	// Interpolate Textures by default
	m_InterpolateTexture = true;
	m_TriangleInterpolation = true;

	// Create our control unit
	m_ControlUnit = new OpenGLRImageControlUnit();

	// Connect the control widget's signals to our slots
	connect(this->m_ControlUnit->m_AlphaSlider,					SIGNAL(sliderMoved(int)),																this,	SLOT(SetLUTAlpha(int))														);
	connect(this->m_ControlUnit->m_RadioButtonPoints,			SIGNAL(clicked()),																		this,	SLOT(SetRenderTypePoints())													);
	connect(this->m_ControlUnit->m_RadioButtonTriangles,		SIGNAL(clicked()),																		this,	SLOT(SetRenderTypeTriangles())												);
	connect(this->m_ControlUnit->m_ValidFlagsCheckBox,			SIGNAL(toggled(bool)),																	this,	SLOT(SetShowValidFlagsOnly(bool))											);
	connect(this->m_ControlUnit->m_InterpolateTextureCheckBox,	SIGNAL(toggled(bool)),																	this,	SLOT(SetInterpolateTexture(bool))											);
	connect(this->m_ControlUnit->m_TriangleInterpolation,		SIGNAL(toggled(bool)),																	this,	SLOT(SetTriangleInterpolation(bool))											);
	connect(this->m_ControlUnit,								SIGNAL(PrimaryPayloadVisualizationConfigChanged(ImagePayloadVisualizationConfig)),		this,	SLOT(UpdatePrimaryPayloadVisualizationConfiguration(ImagePayloadVisualizationConfig))		);
	connect(this->m_ControlUnit,								SIGNAL(SecondaryPayloadVisualizationConfigChanged(ImagePayloadVisualizationConfig)),	this,	SLOT(UpdateSecondaryPayloadVisualizationConfiguration(ImagePayloadVisualizationConfig))		);
	connect(this->m_ControlUnit->m_PPClampPushButton,			SIGNAL(clicked()),																		this,	SLOT(ClampPrimaryPayloadToCurrentInterval()));
	connect(this->m_ControlUnit->m_SPClampPushButton,			SIGNAL(clicked()),																		this,	SLOT(ClampSecondaryPayloadToCurrentInterval()));

	// Connect our signals to the control widget's slots
	connect(this,	SIGNAL(AvailableRangeImagePayloadsChanged(std::vector<ImagePayloadVisualizationConfig>)),	this->m_ControlUnit,	SLOT(ResetGUI(std::vector<ImagePayloadVisualizationConfig>))	);


#if 0
	connect(this->m_ControlUnit->m_RangeIntervalMinSpinBox,			SIGNAL(valueChanged(double)),																			this,	SLOT(SetMinRangeClamping(double))																			);
	connect(this->m_ControlUnit->m_RangeIntervalMaxSpinBox,			SIGNAL(valueChanged(double)),																			this,	SLOT(SetMaxRangeClamping(double))																			);
	connect(this->m_ControlUnit->m_RangeIntervalClampPushButton,	SIGNAL(clicked()),																						this,	SLOT(ClampRangeInterval())																					);

	connect(this,	SIGNAL(SetMinSignal(double)),																				this->m_ControlUnit->m_RangeIntervalMinSpinBox,			SLOT(setValue(double))															);
	connect(this,	SIGNAL(SetMaxSignal(double)),																				this->m_ControlUnit->m_RangeIntervalMaxSpinBox,			SLOT(setValue(double))															);
	connect(this,	SIGNAL(SetRangeIntervalMinSpinBoxEnabled(bool)),															this->m_ControlUnit->m_RangeIntervalMinSpinBox,			SLOT(setEnabled(bool))															);
	connect(this,	SIGNAL(SetRangeIntervalMaxSpinBoxEnabled(bool)),															this->m_ControlUnit->m_RangeIntervalMaxSpinBox,			SLOT(setEnabled(bool))															);
	connect(this,	SIGNAL(SetRangeIntervalClampPushButtonEnabled(bool)),														this->m_ControlUnit->m_RangeIntervalClampPushButton,	SLOT(setEnabled(bool))															);
#endif

#ifdef PERFORMANCE_CHECK
	// Initialize timers
	for ( int i = 0; i < 10; i++ )
	{
		m_Counters[i] = 0;
		m_Timers[i] = 0;
	}
#endif
}

//----------------------------------------------------------------------------
OpenGLRImageEntity::~OpenGLRImageEntity()
{
	// Clean up
	CloseGL();

	if ( m_ControlUnit )
		delete m_ControlUnit;
}


//----------------------------------------------------------------------------
void 
OpenGLRImageEntity::DetermineAvailableRangeImagePayloads(RImageF2::ConstPointer RI)
{
	// Initialize the vector holding the available payloads
	std::vector<ImagePayloadVisualizationConfig> AvailablePayloads;

	// Loop over the range image's payloads
	for ( unsigned int i = 0; i < RI->GetNumberOfPayloads(); i++ )
	{
		// Get the payload container and its actual payload object.
		GenericPayloadContainer::ConstPointer PayloadContainer = m_CurrentFrame->GetPayloadContainer(i);
		const GenericPayloadContainer::PayloadType *Payload = (const GenericPayloadContainer::PayloadType*)PayloadContainer->GetPayload();

		// Create a visualization config with default parameters. However, do not add it to the available payloads as we might not be able to handle the type!
		ImagePayloadVisualizationConfig VisualizationConfig;
		VisualizationConfig.ValidFlag = true;
		VisualizationConfig.PayloadIdentifier = PayloadContainer->GetIdentifier();
		VisualizationConfig.PayloadId = i;
		VisualizationConfig.UseLUT = false;
		VisualizationConfig.LUTID = 0;

		// Cast the payload an unsigned char image. If successful, add the payload to the available payloads
		typedef itk::Image<unsigned char,2> ImageTypeUC;
		ImageTypeUC::ConstPointer ImageUC = dynamic_cast<const ImageTypeUC*>(Payload);
		if ( ImageUC )
		{
			// Set the image type
			VisualizationConfig.ImageType = 0;

			// Clamping is available
			VisualizationConfig.UseClamping = true;
			typedef itk::RescaleIntensityImageFilter<ImageTypeUC> FilterType;
			FilterType::Pointer Filter = FilterType::New();
			Filter->SetInput(ImageUC);
			Filter->SetOutputMinimum(0);
			Filter->SetOutputMaximum(255);
			Filter->Update();
			VisualizationConfig.ClampBounds[0] = Filter->GetInputMinimum();
			VisualizationConfig.ClampBounds[1] = Filter->GetInputMaximum();
		
			// Add to the available payloads
			AvailablePayloads.push_back(VisualizationConfig);

			// continue as this is the only possible cast
			continue;
		}

		// Cast the payload to an itk::Vector<unsigned char,3> image. If successful, add the payload to the available payloads
		typedef itk::Image<itk::Vector<unsigned char,3>,2> ImageTypeUC3;
		ImageTypeUC3::ConstPointer ImageUC3 = dynamic_cast<const ImageTypeUC3*>(Payload);
		if ( ImageUC3 )
		{
			// Set the image type
			VisualizationConfig.ImageType = 1;

			// Do not use clamping
			VisualizationConfig.UseClamping = false;

			// Add to the available payloads
			AvailablePayloads.push_back(VisualizationConfig);

			// continue as this is the only possible cast
			continue;
		}

		// Cast the payload to a float image. If successful, add the payload to the available payloads
		typedef itk::Image<float,2> ImageTypeF;
		ImageTypeF::ConstPointer ImageF = dynamic_cast<const ImageTypeF*>(Payload);
		if ( ImageF )
		{
			// Set the image type
			VisualizationConfig.ImageType = 2;

			// Clamping is available
			VisualizationConfig.UseClamping = true;
			typedef itk::RescaleIntensityImageFilter<ImageTypeF> FilterType;
			FilterType::Pointer Filter = FilterType::New();
			Filter->SetInput(ImageF);
			Filter->SetOutputMinimum(0);
			Filter->SetOutputMaximum(255);
			Filter->Update();
			VisualizationConfig.ClampBounds[0] = Filter->GetInputMinimum();
			VisualizationConfig.ClampBounds[1] = Filter->GetInputMaximum();

			// Add to the available payloads
			AvailablePayloads.push_back(VisualizationConfig);

			// continue as this is the only possible cast
			continue;
		}

#if 0
		// Cast the payload to an itk::Vector<float,3> image. If successful, add the payload to the available payloads
		typedef itk::Image<itk::Vector<float,3>,2> ImageTypeF3;
		ImageTypeF3::ConstPointer ImageF3 = dynamic_cast<const ImageTypeF3*>(Payload);
		if ( ImageF3 )
		{
			std::cout << "OpenGLRImageEntity: found a ImageF3..." << std::endl;

			// Set the image type
			VisualizationConfig.ImageType = 3;

			// Add to the available payloads
			AvailablePayloads.push_back(VisualizationConfig);

			// continue as this is the only possible cast
			continue;
		}
#endif
	}

	// Emit the signal indicating that the payloads that can be visualized has changed
	emit AvailableRangeImagePayloadsChanged(AvailablePayloads);
}


//----------------------------------------------------------------------------
void
OpenGLRImageEntity::UpdatePayloadTexture(ImagePayloadVisualizationConfig Config, GLuint PayloadTexID)
{
	m_CurrentFrameMutex.lock();

	if ( !m_CurrentFrame )
	{
		LOG_DEB("Cannot set primary payload without a valid frame...");
		m_CurrentFrameMutex.unlock();
		return;
	}

	// Get the payload container and its actual payload object. Do not forget to cast the smart pointer into a regular pointer
	GenericPayloadContainer::ConstPointer PayloadContainer = m_CurrentFrame->GetPayloadContainer(Config.PayloadId);
	const GenericPayloadContainer::PayloadType *Payload = (const GenericPayloadContainer::PayloadType*)PayloadContainer->GetPayload();

#if 0
	// Actions based on the image type
	switch ( Config.ImageType )
	{
		case 0:
		{
			itk::Image<unsigned char,2>::ConstPointer ImageUC = (const itk::Image<unsigned char,2>*)Payload;
			itk::Image<unsigned char,2>::SizeType Size = ImageUC->GetBufferedRegion().GetSize();
			glBindTexture(GL_TEXTURE_2D, PayloadTexID);
			glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, Size[0], Size[1], 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, ImageUC->GetBufferPointer());
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
			break;
		}
		case 1:
		{
			itk::Image<itk::Vector<unsigned char,3>,2>::ConstPointer ImageUC3 = (const itk::Image<itk::Vector<unsigned char,3>,2>*)Payload;
			itk::Image<itk::Vector<unsigned char,3>,2>::SizeType Size = ImageUC3->GetBufferedRegion().GetSize();
			glBindTexture(GL_TEXTURE_2D, PayloadTexID);
			glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, Size[0], Size[1], 0, GL_RGB, GL_UNSIGNED_BYTE, ImageUC3->GetBufferPointer());
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
			break;
		}
		case 2:
		{
			itk::Image<float,2>::ConstPointer ImageF = (const itk::Image<float,2>*)Payload;
			itk::Image<float,2>::SizeType Size = ImageF->GetBufferedRegion().GetSize();
			glBindTexture(GL_TEXTURE_2D, PayloadTexID);
			glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE32F_ARB, Size[0], Size[1], 0, GL_LUMINANCE, GL_FLOAT, ImageF->GetBufferPointer());
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
			break;
		}
		default:
		{
			LOG_DEB("Invalid image payload type detected...");
		}
	}
#else
	// Cast the payload an unsigned char image
	typedef itk::Image<unsigned char,2> ImageTypeUC;
	ImageTypeUC::ConstPointer ImageUC = dynamic_cast<const ImageTypeUC*>(Payload);
	if ( ImageUC )
	{
		// Determine image size
		ImageTypeUC::SizeType Size = ImageUC->GetBufferedRegion().GetSize();

		// Bind the UC texture
		glBindTexture(GL_TEXTURE_2D, PayloadTexID);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, Size[0], Size[1], 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, ImageUC->GetBufferPointer());
		if(m_InterpolateTexture)
		{
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		}
		else
		{
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
		}
		m_CurrentFrameMutex.unlock();
		return;
	}

	// Cast the payload to an itk::Vector<unsigned char,3> image
	typedef itk::Image<itk::Vector<unsigned char,3>,2> ImageTypeUC3;
	ImageTypeUC3::ConstPointer ImageUC3 = dynamic_cast<const ImageTypeUC3*>(Payload);
	if ( ImageUC3 )
	{
		// Determine image size
		ImageTypeUC3::SizeType Size = ImageUC3->GetBufferedRegion().GetSize();

		// Bind the UC3 texture
		glBindTexture(GL_TEXTURE_2D, PayloadTexID);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, Size[0], Size[1], 0, GL_RGB, GL_UNSIGNED_BYTE, ImageUC3->GetBufferPointer());
		if(m_InterpolateTexture)
		{
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		}
		else
		{
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
		}
		m_CurrentFrameMutex.unlock();
		return;
	}

	// Cast the payload to a float image
	typedef itk::Image<float,2> ImageTypeF;
	ImageTypeF::ConstPointer ImageF = dynamic_cast<const ImageTypeF*>(Payload);
	if ( ImageF )
	{
		// Determine image size
		ImageTypeF::SizeType Size = ImageF->GetBufferedRegion().GetSize();

		// Bind the UC texture
		// GL_LUMINANCE32F_ARB
		glBindTexture(GL_TEXTURE_2D, PayloadTexID);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE32F_ARB, Size[0], Size[1], 0, GL_LUMINANCE, GL_FLOAT, ImageF->GetBufferPointer());
		if(m_InterpolateTexture)
		{
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		}
		else
		{
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
			glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
		}
		m_CurrentFrameMutex.unlock();
		return;
	}

	// Cast the payload to an itk::Vector<float,3> image
	typedef itk::Image<itk::Vector<float,3>,2> ImageTypeF3;
	ImageTypeF3::ConstPointer ImageF3 = dynamic_cast<const ImageTypeF3*>(Payload);
	if ( ImageF3 )
	{
		LOG_DEB("Setting float3 images not supported atm...");
	}
#endif

	m_CurrentFrameMutex.unlock();
}


//----------------------------------------------------------------------------
void
OpenGLRImageEntity::UpdatePrimaryPayloadVisualizationConfiguration(ImagePayloadVisualizationConfig Config)
{
	m_CurrentFrameMutex.lock();

	// Update the config member
	m_PrimaryPayloadVisualizationConfig = Config;

	// Update the LUT
	unsigned int LUTID = m_PrimaryPayloadVisualizationConfig.LUTID;
	unsigned int NumLUTElems = 0;
	const GLfloat *LUT = 0;
	if ( LUTID == 0 )
		LUT = ritk::GetLUT("GrayScale", &NumLUTElems);
	else if ( LUTID == 1 )
		LUT = ritk::GetLUT("FireStorm1",&NumLUTElems);
	else if ( LUTID == 2 )
		LUT = ritk::GetLUT("FireStorm2",&NumLUTElems);
	else if ( LUTID == 3 )
		LUT = ritk::GetLUT("ColdFusion",&NumLUTElems);
	else if ( LUTID == 4 )
		LUT = ritk::GetLUT("HylaArborea",&NumLUTElems);
	else if ( LUTID == 5 )
		LUT = ritk::GetLUT("Jet", &NumLUTElems);
	else
	{
		LOG_DEB("Invalid LUTID: " << LUTID << " Reverting to default grayscale...");
		LUT = ritk::GetLUT("GrayScale", &NumLUTElems);
	}
	glBindTexture(GL_TEXTURE_1D, m_PrimaryPayloadLUTTexture);
	glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP);
	glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, NumLUTElems, 0, GL_RGBA, GL_FLOAT, LUT);
	glBindTexture(GL_TEXTURE_1D, 0);
	CHECK_GL_ERROR();
	m_CurrentFrameMutex.unlock();

	// Emit the signal indicating that our internal status changed and a redraw is necessary
	emit StatusChanged();
}


//----------------------------------------------------------------------------
void
OpenGLRImageEntity::UpdateSecondaryPayloadVisualizationConfiguration(ImagePayloadVisualizationConfig Config)
{
	m_CurrentFrameMutex.lock();

	// Update the config member
	m_SecondaryPayloadVisualizationConfig = Config;

	// Update the LUT
	unsigned int LUTID = m_SecondaryPayloadVisualizationConfig.LUTID;
	unsigned int NumLUTElems = 0;
	const GLfloat *LUT = 0;
	if ( LUTID == 0 )
		LUT = ritk::GetLUT("GrayScale", &NumLUTElems);
	else if ( LUTID == 1 )
		LUT = ritk::GetLUT("FireStorm1",&NumLUTElems);
	else if ( LUTID == 2 )
		LUT = ritk::GetLUT("FireStorm2",&NumLUTElems);
	else if ( LUTID == 3 )
		LUT = ritk::GetLUT("ColdFusion",&NumLUTElems);
	else if ( LUTID == 4 )
		LUT = ritk::GetLUT("HylaArborea",&NumLUTElems);
	else if ( LUTID == 5 )
		LUT = ritk::GetLUT("Jet", &NumLUTElems);
	else
	{
		LOG_DEB("Invalid LUTID: " << LUTID << " Reverting to default grayscale...");
		LUT = ritk::GetLUT("GrayScale", &NumLUTElems);
	}
	glBindTexture(GL_TEXTURE_1D, m_SecondaryPayloadLUTTexture);
	glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP);
	glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, NumLUTElems, 0, GL_RGBA, GL_FLOAT, LUT);
	glBindTexture(GL_TEXTURE_1D, 0);
	CHECK_GL_ERROR();

	m_CurrentFrameMutex.unlock();

	// Emit the signal indicating that our internal status changed and a redraw is necessary
	emit StatusChanged();
}


//----------------------------------------------------------------------------
void
OpenGLRImageEntity::ClampPrimaryPayloadToCurrentInterval()
{
	QMutexLocker locker(&m_CurrentFrameMutex);
	itk::Vector<float, 2> range = DetermineMinMaxRange(m_CurrentFrame, m_DiscardInvalidFragments);
	locker.unlock();
	m_ControlUnit->m_PPClampMinSpinBox->setValue(range[0]);
	m_ControlUnit->m_PPClampMaxSpinBox->setValue(range[1]);
}


//----------------------------------------------------------------------------
void
OpenGLRImageEntity::ClampSecondaryPayloadToCurrentInterval()
{
	QMutexLocker locker(&m_CurrentFrameMutex);
	itk::Vector<float, 2> range = DetermineMinMaxRange(m_CurrentFrame, m_DiscardInvalidFragments);
	locker.unlock();
	m_ControlUnit->m_SPClampMinSpinBox->setValue(range[0]);
	m_ControlUnit->m_SPClampMaxSpinBox->setValue(range[1]);
}


//----------------------------------------------------------------------------
itk::Vector<float ,2>
OpenGLRImageEntity::DetermineMinMaxRange(RImageF2::ConstPointer img, bool discardInvalids){
	itk::Vector<float, 2> erg;
	
	RImageF2::FlagImageConstPointer flagImg = img->GetFlagImage();
	RImageF2::RangeImageConstPointer rangeImg = img->GetRangeImage();

	int sizeX = rangeImg->GetBufferedRegion().GetSize()[0];
	int sizeY = rangeImg->GetBufferedRegion().GetSize()[1];
	int sizeXf = flagImg->GetBufferedRegion().GetSize()[0];
	int sizeYf = flagImg->GetBufferedRegion().GetSize()[1];
	
	if(sizeX != sizeXf || sizeY != sizeYf){
		erg[0] = -1.0f;
		erg[1] = -1.0f;
		return erg;
	}

	float max = -1e-16;
	float min = 1e16;
	bool init = false;

	for(int y = 0; y < sizeY; y++){
		for(int x = 0; x < sizeX; x++){
			if(discardInvalids){
				if(((flagImg->GetBufferPointer()[y*sizeX + x]) & RImageF2::VALID) != RImageF2::VALID)
					continue;
			}

			if(!init || rangeImg->GetBufferPointer()[y*sizeX + x] > max){
				max = rangeImg->GetBufferPointer()[y*sizeX + x];
			}
			if(!init || rangeImg->GetBufferPointer()[y*sizeX + x] < min){
				min = rangeImg->GetBufferPointer()[y*sizeX + x];
			}

			if(init == false){
				init = true;
			}
		}
	}

	if (init == false )
	{
		erg[0] = -1.0f;
		erg[1] = -1.0f;
		return erg;
	}

	erg[0] = min;
	erg[1] = max;
	
	return erg;

}



//----------------------------------------------------------------------------
void
OpenGLRImageEntity::SetLUTAlpha(int value)
{
	m_Mutex.Lock();

	m_Alpha = value/100.0f;

	m_Mutex.Unlock();

	emit StatusChanged();
}


//----------------------------------------------------------------------------
void
OpenGLRImageEntity::SetRenderTypePoints()
{
	m_Mutex.Lock();

	m_RenderPoints = true;

	m_Mutex.Unlock();

	emit StatusChanged();
}


//----------------------------------------------------------------------------
void
OpenGLRImageEntity::SetRenderTypeTriangles()
{
	m_Mutex.Lock();

	m_RenderPoints = false;

	m_Mutex.Unlock();

	emit StatusChanged();
}


//----------------------------------------------------------------------------
void 
OpenGLRImageEntity::SetShowValidFlagsOnly(bool b)
{
	// Lock the mutex
	m_Mutex.Lock();

	m_DiscardInvalidFragments = b;

	// Unlock
	m_Mutex.Unlock();

	// Due to non-rendering of invalid points
	SetRangeData(m_CurrentFrame);
}


//----------------------------------------------------------------------------
void 
OpenGLRImageEntity::SetInterpolateTexture(bool b)
{
	// Lock the mutex
	m_Mutex.Lock();

	m_InterpolateTexture = b;

	// Unlock
	m_Mutex.Unlock();

	// Due to non-rendering of invalid points
	SetRangeData(m_CurrentFrame);
}

//----------------------------------------------------------------------------
void 
OpenGLRImageEntity::SetTriangleInterpolation(bool b)
{
	// Lock the mutex
	m_Mutex.Lock();

	m_TriangleInterpolation = b;

	// Unlock
	m_Mutex.Unlock();

	// Due to non-rendering of invalid points
	SetRangeData(m_CurrentFrame);
}


//----------------------------------------------------------------------------
void
OpenGLRImageEntity::SetRangeData(RImageF2::ConstPointer Data)
{
	// Lock ourself
	m_Mutex.Lock();

#ifdef PERFORMANCE_CHECK
	// Initialize timer
	LONGLONG Frequency;
	QueryPerformanceFrequency((LARGE_INTEGER*)&Frequency);

	LONGLONG C1;
	QueryPerformanceCounter((LARGE_INTEGER*)&C1);
#endif

	// Determine if we have to initialize ourself. 
	// This may be due to (1) the first frame ever, (2) a new RImage type, (3) the payload changed and (4) a new image size
	bool InitRequired = false;
	if ( !m_CurrentFrame )
		InitRequired = true;
	if ( m_CurrentFrame && strcmp(m_CurrentFrame->GetNameOfClass(),Data->GetNameOfClass()) != 0 )
		InitRequired = true;
	if ( m_CurrentFrame && m_CurrentFrame->GetPayloadHash() != Data->GetPayloadHash() )
		InitRequired = true;
	if ( Data->GetRequestedRegion().GetSize()[0] != m_CurrentSize[0] || Data->GetRequestedRegion().GetSize()[1] != m_CurrentSize[1] )
		InitRequired = true;

	// Update the current frame
	m_CurrentFrame = Data;

	// For convenience
	long SizeX = m_CurrentFrame->GetWorldCoordImage()->GetRequestedRegion().GetSize()[0];
	long SizeY = m_CurrentFrame->GetWorldCoordImage()->GetRequestedRegion().GetSize()[1];

	// Allocate memory
	if ( InitRequired )
	{
		// Allocate memory for indices and texture coords
		m_TriangleStripsIndicesContainer->Reserve(SizeX*SizeY + SizeX*(SizeY-2));
		m_PointsIndicesContainer->Reserve(SizeX*SizeY);
		m_TextureCoordsContainer->Reserve(SizeX*SizeY);

		// Set up triangle strip indices, point indices and texture coordinates
		GLuint *TrinagleStripsIndexPtr = m_TriangleStripsIndicesContainer->GetBufferPointer();
		GLuint *PointsIndexPtr = m_PointsIndicesContainer->GetBufferPointer();
		GLfloat *TexCoordPtr = (float*)m_TextureCoordsContainer->GetBufferPointer();
		for ( unsigned int y = 0; y < SizeY; y++ )
		{
			for ( unsigned int x = 0; x < SizeX; x++ )
			{
				// Triangle strip indices
				// |00|01|02|03|
				// |04|05|06|07|
				// |08|09|10|11|
				// ->
				// |00|04|01|05|02|06|03|07|  |07|11|06|10|05|09|04|08| ...
				
				if(y < SizeY - 1){
					if ( y%2 == 0 )
					{
						TrinagleStripsIndexPtr[0] = y*SizeX+x;
						TrinagleStripsIndexPtr[1] = (y+1)*SizeX+x;
					}
					else
					{
						TrinagleStripsIndexPtr[0] = y*SizeX+(SizeX-1-x);
						TrinagleStripsIndexPtr[1] = (y+1)*SizeX+(SizeX-1-x);
					}
					TrinagleStripsIndexPtr += 2;
				}

				// Point indices
				PointsIndexPtr[0] = y*SizeX+x;
				PointsIndexPtr++;
				
				// Texture coordinates
				TexCoordPtr[0] = (x+0.5f)/(SizeX*1.f);
				TexCoordPtr[1] = (y+0.5f)/(SizeY*1.f);
				TexCoordPtr += 2;
			}
		}

		// Update our control unit
		DetermineAvailableRangeImagePayloads(m_CurrentFrame);
	}

	// Remember the texture size
	m_CurrentSize[0] = SizeX;
	m_CurrentSize[1] = SizeY;

	// Set the dirty flag
	if ( InitRequired )
		m_SizeDirtyFlag = InitRequired;

	// Mark the VBO as invalid
	m_VBOInitialized = false;

	// Unlock ourself
	m_Mutex.Unlock();

	emit StatusChanged();

#ifdef PERFORMANCE_CHECK
	LONGLONG C2;
	QueryPerformanceCounter((LARGE_INTEGER*)&C2);
	double Diff = (((double)(C2-C1))/((double)Frequency))*1000;
	m_Timers[0] += Diff;
	if ( m_Counters[0]++ == 100 )
	{
		std::cout << "Avg SetRangeData: " << (m_Timers[0]/100) << "ms" << std::endl;
		m_Timers[0] = 0;
		m_Counters[0] = 0;
	}
#endif
}


//----------------------------------------------------------------------------
void
OpenGLRImageEntity::SetRangeData(RImageF2::Pointer Data)
{
	RImageF2::ConstPointer ConstData = (const RImageF2*)Data;
	SetRangeData(ConstData);
}


//----------------------------------------------------------------------------
OpenGLRImageEntity::BoundingBoxType
OpenGLRImageEntity::GetBoundingBox() const
{
	m_Mutex.Lock();

	// Sanity check
	if ( !m_CurrentFrame )
	{
		BoundingBoxType Dummy;
		Dummy.Fill(0);
		m_Mutex.Unlock();
		return Dummy;
	}

	// Initialize BB
	BoundingBoxType BB;
	BB[0] = 1e16;
	BB[1] = 1e16;
	BB[2] = 1e16;
	BB[3] = -1e16;
	BB[4] = -1e16;
	BB[5] = -1e16;
	
	// Loop and compute the BB
	long NumVertices = m_CurrentFrame->GetWorldCoordImage()->GetRequestedRegion().GetNumberOfPixels();
	float *CoordPtr = (float*)m_CurrentFrame->GetWorldCoordImage()->GetBufferPointer();
	for ( long l = 0; l < NumVertices; l++, CoordPtr += 3 )
	{
		// Skip invalid coords
		if ( fabsf(*(CoordPtr+2)) < 1e-6 )
			continue;

		// Min
		if ( *CoordPtr < BB[0] )
			BB[0] = *CoordPtr;
		if ( *(CoordPtr+1) < BB[1] )
			BB[1] = *(CoordPtr+1);
		if ( *(CoordPtr+2) < BB[2] )
			BB[2] = *(CoordPtr+2);

		// Max
		if ( *CoordPtr > BB[3] )
			BB[3] = *CoordPtr;
		if ( *(CoordPtr+1) > BB[4] )
			BB[4] = *(CoordPtr+1);
		if ( *(CoordPtr+2) > BB[5] )
			BB[5] = *(CoordPtr+2);
	}

	m_Mutex.Unlock();

	// Return the result
	return BB;
}


//----------------------------------------------------------------------------
void 
OpenGLRImageEntity::UpdateVBO()
{
	// The number of vertices that we have to process
	int SizeX = m_CurrentFrame->GetWorldCoordImage()->GetRequestedRegion().GetSize()[0];
	int SizeY = m_CurrentFrame->GetWorldCoordImage()->GetRequestedRegion().GetSize()[1];

	// Activate the texture coordinate VBO and update its data
	if ( m_SizeDirtyFlag )
	{
#ifdef USE_INDEX_VBO
		// Triangle strip indices
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VBOTriangleStripsIndices);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int)*(SizeX*SizeY + SizeX*(SizeY-2)), m_TriangleStripsIndicesContainer->GetBufferPointer(), GL_STATIC_DRAW);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
#endif

		// Texture coordinates
		glBindBuffer(GL_ARRAY_BUFFER, m_VBOTexCoords);
		glBufferData(GL_ARRAY_BUFFER, sizeof(float)*(SizeX*SizeY)*2, (float*)m_TextureCoordsContainer->GetBufferPointer(), GL_STATIC_DRAW);
		glBindBuffer(GL_ARRAY_BUFFER, 0);

		// Vertex data
		glBindBuffer(GL_ARRAY_BUFFER, m_VBOVertices);
		glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*SizeX*SizeY, NULL, GL_DYNAMIC_DRAW); // GL_DYNAMIC_DRAW
		glBindBuffer(GL_ARRAY_BUFFER, 0);

		// Textures
		glBindTexture(GL_TEXTURE_2D, m_WorldCoordTexture);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, SizeX, SizeY, 0, GL_RGB, GL_FLOAT, NULL);
		glBindTexture(GL_TEXTURE_2D, 0);

		glBindTexture(GL_TEXTURE_2D, m_FlagTexture);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, SizeX, SizeY, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, NULL);
	}

#ifdef PERFORMANCE_CHECK
	// Initialize timer
	LONGLONG Frequency;
	QueryPerformanceFrequency((LARGE_INTEGER*)&Frequency);

	LONGLONG C1;
	QueryPerformanceCounter((LARGE_INTEGER*)&C1);
#endif

	// Buffer our world coords
	glBindBuffer(GL_ARRAY_BUFFER, m_VBOVertices);
	WorldCoordGraphicsInteropContainerConstPointer Adaptor = dynamic_cast<const WorldCoordGraphicsInteropContainerType*>(m_CurrentFrame->GetWorldCoordImage()->GetPixelContainer());
	if ( Adaptor && false )
	{
		try
		{
			// Make the old adaptor to unregister and the new adaptor to register the resource
			if ( m_CurrentWorldCoordGraphicsInteropContainer != Adaptor )
			{
				// Sanity check
				if ( m_CurrentWorldCoordGraphicsInteropContainer )
				{
					//std::cout << "Unregistering VBO for Container@" << m_CurrentWorldCoordGraphicsInteropContainer << std::endl;
					m_CurrentWorldCoordGraphicsInteropContainer->UnregisterVBO(m_VBOVertices);
					//std::cout << "[OK]" << std::endl;
				}
				m_CurrentWorldCoordGraphicsInteropContainer = Adaptor;
				m_CurrentWorldCoordGraphicsInteropContainer->RegisterVBO(m_VBOVertices);
			}
		
			// Actual copy operation
			m_CurrentWorldCoordGraphicsInteropContainer->CopyToVBO(m_VBOVertices, sizeof(float)*3*SizeX*SizeY);
		}
		catch ( itk::ExceptionObject &e )
		{
			std::cerr << e << std::endl;
			LOG_DEB(e);
		}
	}
	else
	{
		// Version1 (reallocates the whole buffer, should be slow in theory)
		//glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*SizeX*SizeY, (float*)m_CurrentFrame->GetWorldCoordImage()->GetBufferPointer(), GL_DYNAMIC_DRAW);
		// Version2 (just copy the data. No need to reallocate the buffer. Should be faster in theory and according to the OpenGL spec
		glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float)*3*SizeX*SizeY, (float*)m_CurrentFrame->GetWorldCoordImage()->GetBufferPointer());
		// Version3 (...)
		/*void *BufferData = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
		if ( BufferData )
			memcpy(BufferData, (float*)m_CurrentFrame->GetWorldCoordImage()->GetBufferPointer(), sizeof(float)*3*SizeX*SizeY);
		else
			std::cout << "Could not map buffer..." << std::endl;
		glUnmapBuffer(GL_ARRAY_BUFFER);*/
	}
	

#ifdef PERFORMANCE_CHECK
	CHECK_GL_ERROR();
	LONGLONG C2;
	QueryPerformanceCounter((LARGE_INTEGER*)&C2);
#endif

	// Bind the flag texture
	glBindTexture(GL_TEXTURE_2D, m_FlagTexture);
	if(m_InterpolateTexture)
	{
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	}
	else
	{
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
	}
#ifdef USE_SUB_TEX
	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, SizeX, SizeY, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_CurrentFrame->GetFlagImage()->GetBufferPointer());
#else
	glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, SizeX, SizeY, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_CurrentFrame->GetFlagImage()->GetBufferPointer());
#endif

	// Bind the world coord texture
	glBindTexture(GL_TEXTURE_2D, m_WorldCoordTexture);
	if ( m_InterpolateTexture )
	{
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	}
	else
	{
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
	}
	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_VBOVertices);
#ifdef USE_SUB_TEX
	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, SizeX, SizeY, GL_RGB, GL_FLOAT, NULL);
#else
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, SizeX, SizeY, 0, GL_RGB, GL_FLOAT, NULL);
#endif
	glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);

	// Update the data
	if ( m_PrimaryPayloadVisualizationConfig.ValidFlag )
		UpdatePayloadTexture(m_PrimaryPayloadVisualizationConfig, m_PrimaryPayloadTexture);
	else
		std::cerr << "No valid primary payload visualization config found..." << std::endl;
	if ( m_SecondaryPayloadVisualizationConfig.ValidFlag )
		UpdatePayloadTexture(m_SecondaryPayloadVisualizationConfig, m_SecondaryPayloadTexture);
	else
		std::cerr << "No valid secondary payload visualization config found..." << std::endl;

	// Synchronize
	m_VBOInitialized = true;

	// Unset
	m_SizeDirtyFlag = false;

#ifdef PERFORMANCE_CHECK
	LONGLONG C3;
	QueryPerformanceCounter((LARGE_INTEGER*)&C3);

	// Wait for all OpenGL operations to finish
	glFinish();

	LONGLONG C4;
	QueryPerformanceCounter((LARGE_INTEGER*)&C4);

	double Diff1 = (((double)(C2-C1))/((double)Frequency))*1000;
	double Diff2 = (((double)(C3-C2))/((double)Frequency))*1000;
	double Diff3 = (((double)(C4-C3))/((double)Frequency))*1000;

	m_Timers[1] += Diff1;
	m_Timers[2] += Diff2;
	m_Timers[3] += Diff3;

	if ( m_Counters[1]++ == 100 )
	{
		std::cout << "Avg world coords vbo : " << (m_Timers[1]/100) << "ms" << std::endl;
		m_Timers[1] = 0;
		m_Counters[1] = 0;
	}

	if ( m_Counters[2]++ == 100 )
	{
		std::cout << "Avg texture : " << (m_Timers[2]/100) << "ms" << std::endl;
		m_Timers[2] = 0;
		m_Counters[2] = 0;
	}

	if ( m_Counters[3]++ == 100 )
	{
		std::cout << "Avg gl update: " << (m_Timers[3]/100) << "ms" << std::endl;
		m_Timers[3] = 0;
		m_Counters[3] = 0;
	}
#endif
}


//----------------------------------------------------------------------------
void
OpenGLRImageEntity::InitGL()
{
	if ( m_InitFlag )
	{
		return;
	}

	// Create the VBOs
	glGenBuffers(1, &m_VBOVertices);
	glGenBuffers(1, &m_VBOTexCoords);
	glGenBuffers(1, &m_VBOTriangleStripsIndices);
	CHECK_GL_ERROR();

	// Create textures
	glGenTextures(1, &m_FlagTexture);
	glGenTextures(1, &m_WorldCoordTexture);

	glGenTextures(1, &m_PrimaryPayloadTexture);
	glGenTextures(1, &m_SecondaryPayloadTexture);

	glGenTextures(1, &m_PrimaryPayloadLUTTexture);
	glGenTextures(1, &m_SecondaryPayloadLUTTexture);
	CHECK_GL_ERROR();

	// Shaders
	LoadShader(OpenGLRImageEntity_VertexShader, true);
	LoadShader(OpenGLRImageEntity_FragementShader, false);
	glLinkProgram(m_ShaderProgram);
	CHECK_GL_ERROR();

	// Std OpenGL
	glClearColor(0.0, 0.0, 0.0, 0.0);
	glEnable(GL_DEPTH_TEST);
	CHECK_GL_ERROR();

	// We're initialized right now. Update the flag
	m_InitFlag = true;
}


//----------------------------------------------------------------------------
void
OpenGLRImageEntity::CloseGL()
{
	// Clean up VBOs
	if ( m_VBOVertices )
	{
		glDeleteBuffers(1, &m_VBOVertices);
		m_VBOVertices = 0;
	}

	if ( m_VBOTexCoords )
	{
		glDeleteBuffers(1, &m_VBOTexCoords);
		m_VBOTexCoords = 0;
	}

	if ( m_VBOTriangleStripsIndices )
	{
		glDeleteBuffers(1, &m_VBOTriangleStripsIndices);
		m_VBOTriangleStripsIndices = 0;
	}

	// Delete textures
	if ( m_FlagTexture )
	{
		glDeleteTextures(1, &m_FlagTexture);
		m_FlagTexture = 0;
	}
	if ( m_WorldCoordTexture )
	{
		glDeleteTextures(1, &m_WorldCoordTexture);
		m_WorldCoordTexture = 0;
	}
	if ( m_PrimaryPayloadTexture )
	{
		glDeleteTextures(1, &m_PrimaryPayloadTexture);
		m_PrimaryPayloadTexture = 0;
	}
	if ( m_SecondaryPayloadTexture )
	{
		glDeleteTextures(1, &m_SecondaryPayloadTexture);
		m_SecondaryPayloadTexture = 0;
	}
	if ( m_PrimaryPayloadLUTTexture )
	{
		glDeleteTextures(1, &m_PrimaryPayloadLUTTexture);
		m_PrimaryPayloadLUTTexture = 0;
	}
	if ( m_SecondaryPayloadLUTTexture )
	{
		glDeleteTextures(1, &m_SecondaryPayloadLUTTexture);
		m_SecondaryPayloadLUTTexture = 0;
	}

	// Shader
	for ( unsigned int i = 0; i < m_ShaderHandles.size(); i++ )
	{
		glDetachShader(m_ShaderProgram, m_ShaderHandles[i]);
		glDeleteShader(m_ShaderHandles[i]);
	}
	m_ShaderHandles.clear();

	if ( m_ShaderProgram )
	{
		glDeleteProgram(m_ShaderProgram); 
		m_ShaderProgram = 0;
	}

	m_InitFlag = false;
}


//----------------------------------------------------------------------------
QVector2D
OpenGLRImageEntity::GetPayloadDimensions()
{
	QVector2D erg;
	m_Mutex.Lock();

	if ( !m_CurrentFrame )
	{
		m_Mutex.Unlock();
		return QVector2D(0.0f, 0.0f);
	}

	int SizeX = m_CurrentFrame->GetBufferedRegion().GetSize()[0];
	int SizeY = m_CurrentFrame->GetBufferedRegion().GetSize()[1];
	erg = QVector2D(SizeX, SizeY);

	m_Mutex.Unlock();
	
	return erg;

}


//----------------------------------------------------------------------------
void
OpenGLRImageEntity::Render2D(const OpenGLVisualizationWidget *Widget, QVector2D ImgDimensions)
{
	

	m_Mutex.Lock();

	if ( !m_CurrentFrame )
	{
		m_Mutex.Unlock();
		return;
	}

	// Check for previous OpenGL errors
	CHECK_GL_ERROR();

	// Initialize our GL members. This should return immediately once we're initialized
	InitGL();
	

	// First, update the VBOs
	UpdateVBO();

	// Enable shader program
	glUseProgram(m_ShaderProgram);

	// Discarding of invalid fragments
	GLint DiscardInvalidFragmentsLocation = glGetUniformLocation(m_ShaderProgram, "m_DiscardInvalidFragments");
	glUniform1i(DiscardInvalidFragmentsLocation, m_DiscardInvalidFragments);

	// The range image texture
	GLint WorldCoordTextureLocation = glGetUniformLocation(m_ShaderProgram, "m_WorldCoordTexture");
	glActiveTexture(GL_TEXTURE0); 
	glBindTexture(GL_TEXTURE_2D, m_WorldCoordTexture);
	glEnable(GL_TEXTURE_2D);
	glUniform1i(WorldCoordTextureLocation, 0);
	glDisable(GL_TEXTURE_2D);

	// The flag image texture
	GLint FlagTextureLocation = glGetUniformLocation(m_ShaderProgram, "m_FlagTexture");

	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, m_FlagTexture);
	glEnable(GL_TEXTURE_2D);
	glUniform1i(FlagTextureLocation, 1);
	glDisable(GL_TEXTURE_2D);

	// ===================== //
	// == Primary Payload == //
	// ===================== //


	// Primary payload clamping
	GLint PrimaryPayloadClampEnabledLocation = glGetUniformLocation(m_ShaderProgram, "m_PrimaryPayloadClampEnabled");
	glUniform1i(PrimaryPayloadClampEnabledLocation, m_PrimaryPayloadVisualizationConfig.UseClamping);

	// Primary payload min clamping
	GLint PrimaryPayloadClampMinLocation = glGetUniformLocation(m_ShaderProgram, "m_PrimaryPayloadClampMin");
	glUniform3f(PrimaryPayloadClampMinLocation, m_PrimaryPayloadVisualizationConfig.ClampBounds[0], m_PrimaryPayloadVisualizationConfig.ClampBounds[0],  m_PrimaryPayloadVisualizationConfig.ClampBounds[0]);

	// Primary payload max clamping
	GLint PrimaryPayloadClampMaxLocation = glGetUniformLocation(m_ShaderProgram, "m_PrimaryPayloadClampMax");
	glUniform3f(PrimaryPayloadClampMaxLocation, m_PrimaryPayloadVisualizationConfig.ClampBounds[1], m_PrimaryPayloadVisualizationConfig.ClampBounds[1],  m_PrimaryPayloadVisualizationConfig.ClampBounds[1]);

	// Primary payload use LUT
	GLint PrimaryPayloadUseLUTLocation = glGetUniformLocation(m_ShaderProgram, "m_PrimaryPayloadUseLUT");
	glUniform1i(PrimaryPayloadUseLUTLocation, m_PrimaryPayloadVisualizationConfig.UseLUT);

	// Primary payload texture
	GLint PrimaryPayloadTextureLocation = glGetUniformLocation(m_ShaderProgram, "m_PrimaryPayloadTexture");
	glActiveTexture(GL_TEXTURE2); 
	glBindTexture(GL_TEXTURE_2D, m_PrimaryPayloadTexture);
	glEnable(GL_TEXTURE_2D);
	glUniform1i(PrimaryPayloadTextureLocation, 2);
	glDisable(GL_TEXTURE_2D);


	// Secondary payload LUT texture
	GLint PrimaryPayloadLUTTextureLocation = glGetUniformLocation(m_ShaderProgram, "m_PrimaryPayloadLUTTexture");

	glActiveTexture(GL_TEXTURE3);

	glBindTexture(GL_TEXTURE_1D, m_PrimaryPayloadLUTTexture);
	glEnable(GL_TEXTURE_1D);
	glUniform1i(PrimaryPayloadLUTTextureLocation, 3);
	glDisable(GL_TEXTURE_1D);

	
	


	// ======================= //
	// == Secondary Payload == //
	// ======================= //

	// Secondary payload clamping
	GLint SecondaryPayloadClampEnabledLocation = glGetUniformLocation(m_ShaderProgram, "m_SecondaryPayloadClampEnabled");
	glUniform1i(SecondaryPayloadClampEnabledLocation, m_SecondaryPayloadVisualizationConfig.UseClamping);

	// Secondary payload min clamping
	GLint SecondaryPayloadClampMinLocation = glGetUniformLocation(m_ShaderProgram, "m_SecondaryPayloadClampMin");
	glUniform3f(SecondaryPayloadClampMinLocation, m_SecondaryPayloadVisualizationConfig.ClampBounds[0], m_SecondaryPayloadVisualizationConfig.ClampBounds[0],  m_SecondaryPayloadVisualizationConfig.ClampBounds[0]);

	// Secondary payload max clamping
	GLint SecondaryPayloadClampMaxLocation = glGetUniformLocation(m_ShaderProgram, "m_SecondaryPayloadClampMax");
	glUniform3f(SecondaryPayloadClampMaxLocation, m_SecondaryPayloadVisualizationConfig.ClampBounds[1], m_SecondaryPayloadVisualizationConfig.ClampBounds[1],  m_SecondaryPayloadVisualizationConfig.ClampBounds[1]);

	// Secondary payload use LUT
	GLint SecondaryPayloadUseLUTLocation = glGetUniformLocation(m_ShaderProgram, "m_SecondaryPayloadUseLUT");
	glUniform1i(SecondaryPayloadUseLUTLocation, m_SecondaryPayloadVisualizationConfig.UseLUT);

	// Secondary payload texture
	GLint SecondaryPayloadTextureLocation = glGetUniformLocation(m_ShaderProgram, "m_SecondaryPayloadTexture");
	glActiveTexture(GL_TEXTURE4); 
	glBindTexture(GL_TEXTURE_2D, m_SecondaryPayloadTexture);
	glEnable(GL_TEXTURE_2D);
	glUniform1i(SecondaryPayloadTextureLocation, 4);
	glDisable(GL_TEXTURE_2D);

	// Secondary payload LUT texture
	GLint SecondaryPayloadLUTTextureLocation = glGetUniformLocation(m_ShaderProgram, "m_SecondaryPayloadLUTTexture");
	glActiveTexture(GL_TEXTURE5);
	glBindTexture(GL_TEXTURE_1D, m_SecondaryPayloadLUTTexture);
	glEnable(GL_TEXTURE_1D);
	glUniform1i(SecondaryPayloadLUTTextureLocation, 5);
	glDisable(GL_TEXTURE_1D);


	// Render without Lighting
	GLint EnableLighting = glGetUniformLocation(m_ShaderProgram, "m_EnableLighting");
	glUniform1i(EnableLighting, false);
	
	
	// Render without Triangle Texture Interpolation (3D interpolation)
	GLint EnableTriangleTextureInterpolation = glGetUniformLocation(m_ShaderProgram, "m_EnableTriangleTextureInterpolation");
	glUniform1i(EnableTriangleTextureInterpolation, false);
	
	
	// The current alpha value for blending
	GLint AlphaLocation = glGetUniformLocation(m_ShaderProgram, "m_Alpha");
	glUniform1f(AlphaLocation, m_Alpha);



	// Draw
	int SizeX = m_CurrentFrame->GetBufferedRegion().GetSize()[0];
	int SizeY = m_CurrentFrame->GetBufferedRegion().GetSize()[1];

	//glUseProgram(0);
	

	glBegin(GL_QUADS);
	glTexCoord2f(0.0f, 0.0f); glVertex3f(0.0f, SizeY,  0.0f);  // Bottom Left Of The Texture and Quad
    glTexCoord2f(1.0f, 0.0f); glVertex3f(SizeX, SizeY,  0.0f);  // Bottom Right Of The Texture and Quad
    glTexCoord2f(1.0f, 1.0f); glVertex3f(SizeX,  0.0f,  0.0f);  // Top Right Of The Texture and Quad
    glTexCoord2f(0.0f, 1.0f); glVertex3f(0.0f,  0.0f,  0.0f);
	glEnd();

	// Set to default texture to prevent VERY ANNOYING bugs.
	glActiveTexture(GL_TEXTURE0);

	// Disable all previously enabled client states
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glDisableClientState(GL_VERTEX_ARRAY);

	// Unload shader program
	glUseProgram(0);

	// Check for GL errors
	CHECK_GL_ERROR();

	m_Mutex.Unlock();
}

//----------------------------------------------------------------------------
void
OpenGLRImageEntity::Render3D(const OpenGLVisualizationWidget *Widget)
{
	
	m_Mutex.Lock();

	if ( !m_CurrentFrame )
	{
		m_Mutex.Unlock();
		return;
	}

	// Check for previous OpenGL errors
	CHECK_GL_ERROR();

	// Initialize our GL members. This should return immediately once we're initialized
	InitGL();

	// First, update the VBOs
	UpdateVBO();

	// Default drawing color and point size
	glColor3f(1,1,1);
	glPointSize(3);

	// Enable shader program
	glUseProgram(m_ShaderProgram);

	// Discarding of invalid fragments
	GLint DiscardInvalidFragmentsLocation = glGetUniformLocation(m_ShaderProgram, "m_DiscardInvalidFragments");
	glUniform1i(DiscardInvalidFragmentsLocation, m_DiscardInvalidFragments);

	// The range image texture
	GLint WorldCoordTextureLocation = glGetUniformLocation(m_ShaderProgram, "m_WorldCoordTexture");
	glActiveTexture(GL_TEXTURE0); 
	glBindTexture(GL_TEXTURE_2D, m_WorldCoordTexture);
	glEnable(GL_TEXTURE_2D);
	glUniform1i(WorldCoordTextureLocation, 0);
	glDisable(GL_TEXTURE_2D);

	// The flag image texture
	GLint FlagTextureLocation = glGetUniformLocation(m_ShaderProgram, "m_FlagTexture");

	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, m_FlagTexture);
	glEnable(GL_TEXTURE_2D);
	glUniform1i(FlagTextureLocation, 1);
	glDisable(GL_TEXTURE_2D);

	// ===================== //
	// == Primary Payload == //
	// ===================== //

	// Primary payload clamping
	GLint PrimaryPayloadClampEnabledLocation = glGetUniformLocation(m_ShaderProgram, "m_PrimaryPayloadClampEnabled");
	glUniform1i(PrimaryPayloadClampEnabledLocation, m_PrimaryPayloadVisualizationConfig.UseClamping);

	// Primary payload min clamping
	GLint PrimaryPayloadClampMinLocation = glGetUniformLocation(m_ShaderProgram, "m_PrimaryPayloadClampMin");
	glUniform3f(PrimaryPayloadClampMinLocation, m_PrimaryPayloadVisualizationConfig.ClampBounds[0], m_PrimaryPayloadVisualizationConfig.ClampBounds[0],  m_PrimaryPayloadVisualizationConfig.ClampBounds[0]);

	// Primary payload max clamping
	GLint PrimaryPayloadClampMaxLocation = glGetUniformLocation(m_ShaderProgram, "m_PrimaryPayloadClampMax");
	glUniform3f(PrimaryPayloadClampMaxLocation, m_PrimaryPayloadVisualizationConfig.ClampBounds[1], m_PrimaryPayloadVisualizationConfig.ClampBounds[1],  m_PrimaryPayloadVisualizationConfig.ClampBounds[1]);

	// Primary payload use LUT
	GLint PrimaryPayloadUseLUTLocation = glGetUniformLocation(m_ShaderProgram, "m_PrimaryPayloadUseLUT");
	glUniform1i(PrimaryPayloadUseLUTLocation, m_PrimaryPayloadVisualizationConfig.UseLUT);

	// Primary payload texture
	GLint PrimaryPayloadTextureLocation = glGetUniformLocation(m_ShaderProgram, "m_PrimaryPayloadTexture");
	glActiveTexture(GL_TEXTURE2); 
	glBindTexture(GL_TEXTURE_2D, m_PrimaryPayloadTexture);
	glEnable(GL_TEXTURE_2D);
	glUniform1i(PrimaryPayloadTextureLocation, 2);
	glDisable(GL_TEXTURE_2D);

	// Secondary payload LUT texture
	GLint PrimaryPayloadLUTTextureLocation = glGetUniformLocation(m_ShaderProgram, "m_PrimaryPayloadLUTTexture");
	glActiveTexture(GL_TEXTURE3);
	glBindTexture(GL_TEXTURE_1D, m_PrimaryPayloadLUTTexture);
	glEnable(GL_TEXTURE_1D);
	glUniform1i(PrimaryPayloadLUTTextureLocation, 3);
	glDisable(GL_TEXTURE_1D);

	// ======================= //
	// == Secondary Payload == //
	// ======================= //

	// Secondary payload clamping
	GLint SecondaryPayloadClampEnabledLocation = glGetUniformLocation(m_ShaderProgram, "m_SecondaryPayloadClampEnabled");
	glUniform1i(SecondaryPayloadClampEnabledLocation, m_SecondaryPayloadVisualizationConfig.UseClamping);

	// Secondary payload min clamping
	GLint SecondaryPayloadClampMinLocation = glGetUniformLocation(m_ShaderProgram, "m_SecondaryPayloadClampMin");
	glUniform3f(SecondaryPayloadClampMinLocation, m_SecondaryPayloadVisualizationConfig.ClampBounds[0], m_SecondaryPayloadVisualizationConfig.ClampBounds[0],  m_SecondaryPayloadVisualizationConfig.ClampBounds[0]);

	// Secondary payload max clamping
	GLint SecondaryPayloadClampMaxLocation = glGetUniformLocation(m_ShaderProgram, "m_SecondaryPayloadClampMax");
	glUniform3f(SecondaryPayloadClampMaxLocation, m_SecondaryPayloadVisualizationConfig.ClampBounds[1], m_SecondaryPayloadVisualizationConfig.ClampBounds[1],  m_SecondaryPayloadVisualizationConfig.ClampBounds[1]);

	// Secondary payload use LUT
	GLint SecondaryPayloadUseLUTLocation = glGetUniformLocation(m_ShaderProgram, "m_SecondaryPayloadUseLUT");
	glUniform1i(SecondaryPayloadUseLUTLocation, m_SecondaryPayloadVisualizationConfig.UseLUT);

	// Secondary payload texture
	GLint SecondaryPayloadTextureLocation = glGetUniformLocation(m_ShaderProgram, "m_SecondaryPayloadTexture");
	glActiveTexture(GL_TEXTURE4); 
	glBindTexture(GL_TEXTURE_2D, m_SecondaryPayloadTexture);
	glEnable(GL_TEXTURE_2D);
	glUniform1i(SecondaryPayloadTextureLocation, 4);
	glDisable(GL_TEXTURE_2D);

	// Secondary payload LUT texture
	GLint SecondaryPayloadLUTTextureLocation = glGetUniformLocation(m_ShaderProgram, "m_SecondaryPayloadLUTTexture");
	glActiveTexture(GL_TEXTURE5);
	glBindTexture(GL_TEXTURE_1D, m_SecondaryPayloadLUTTexture);
	glEnable(GL_TEXTURE_1D);
	glUniform1i(SecondaryPayloadLUTTextureLocation, 5);
	glDisable(GL_TEXTURE_1D);



	// Render with Lighting
	GLint EnableLighting = glGetUniformLocation(m_ShaderProgram, "m_EnableLighting");
	glUniform1i(EnableLighting, true);

	// Render with Triangle Texture Interpolation 
	bool triangleInterpolation = m_TriangleInterpolation;
	if(!m_InterpolateTexture) triangleInterpolation = false;
	GLint EnableTriangleTextureInterpolation = glGetUniformLocation(m_ShaderProgram, "m_EnableTriangleTextureInterpolation");
	glUniform1i(EnableTriangleTextureInterpolation, triangleInterpolation);

	// The current alpha value for blending
	GLint AlphaLocation = glGetUniformLocation(m_ShaderProgram, "m_Alpha");
	glUniform1f(AlphaLocation, m_Alpha);

	// Texture coords
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	glBindBuffer(GL_ARRAY_BUFFER, m_VBOTexCoords);
	glTexCoordPointer(2, GL_FLOAT, 0, NULL);
	glBindBuffer(GL_ARRAY_BUFFER,0);

	// The vertices
	glEnableClientState(GL_VERTEX_ARRAY);
	glBindBuffer(GL_ARRAY_BUFFER, m_VBOVertices);
	glVertexPointer(3, GL_FLOAT, 0, NULL);
	glBindBuffer(GL_ARRAY_BUFFER,0);

	// Draw
	int SizeX = m_CurrentFrame->GetBufferedRegion().GetSize()[0];
	int SizeY = m_CurrentFrame->GetBufferedRegion().GetSize()[1];

	//glUseProgram(0);

	if(m_RenderPoints)
	{
		glDrawElements(GL_POINTS, SizeX*SizeY, GL_UNSIGNED_INT, m_PointsIndicesContainer->GetBufferPointer());
	}
	else
	{
		// The indices
#ifdef USE_INDEX_VBO
		/*glEnableClientState(GL_INDEX_ARRAY);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VBOTriangleStripsIndices);
		glDrawElements(GL_TRIANGLE_STRIP, SizeX*SizeY + SizeX*(SizeY-2), GL_UNSIGNED_INT, NULL);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
		glDisableClientState(GL_INDEX_ARRAY);*/

		/*int MaxNumVertices = -1;
		int MaxNumIndices = -1;
		glGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &MaxNumVertices);
		glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &MaxNumIndices);
		std::cout << "MaxNumVertices=" << MaxNumVertices << " MaxNumIndices=" << MaxNumIndices << std::endl;*/
		int NumIndices = SizeX*SizeY + SizeX*(SizeY-2);
		int Step = NumIndices / 2;
		glEnableClientState(GL_INDEX_ARRAY);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VBOTriangleStripsIndices);
		glDrawRangeElements(GL_TRIANGLE_STRIP, 0, NumIndices-1, Step + 2 /*2 indices need te be drawn twice */, GL_UNSIGNED_INT, ((char*)NULL)+0*Step*sizeof(unsigned int));
		glDrawRangeElements(GL_TRIANGLE_STRIP, 0, NumIndices-1, Step, GL_UNSIGNED_INT, ((char*)NULL)+1*Step*sizeof(unsigned int));
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
		glDisableClientState(GL_INDEX_ARRAY);
#else
		glDrawElements(GL_TRIANGLE_STRIP, SizeX*SizeY + SizeX*(SizeY-2), GL_UNSIGNED_INT, m_TriangleStripsIndicesContainer->GetBufferPointer());
#endif
	}
	
	// Set to default texture to prevent VERY ANNOYING bugs.
	glActiveTexture(GL_TEXTURE0);
	
	// Disable all previously enabled client states
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	glDisableClientState(GL_VERTEX_ARRAY);

	// Unload shader program
	glUseProgram(0);

	// Check for GL errors
	CHECK_GL_ERROR();
		
	m_Mutex.Unlock();
}


//----------------------------------------------------------------------------
GLuint
OpenGLRImageEntity::LoadShader(const char *Program, bool VertexFlag)
{
	// No valid program
	if ( !Program )
	{
		std::cerr << "No valid program provided!" << std::endl;
		return 0;
	}

	// Create shader. The type is set by the user
	GLuint ShaderHandle = 0;
	if ( VertexFlag )
		ShaderHandle = glCreateShader(GL_VERTEX_SHADER);
	else
		ShaderHandle = glCreateShader(GL_FRAGMENT_SHADER);

	// Set source
	glShaderSource(ShaderHandle, 1, &Program, NULL);

	// Compile shader
	glCompileShader(ShaderHandle);

	// If the shader program does not exist, create it
	if ( !m_ShaderProgram )
		m_ShaderProgram = glCreateProgram();

	// Finally attach the shader to the shader program and save the handle
	glAttachShader(m_ShaderProgram,ShaderHandle);

	m_ShaderHandles.push_back(ShaderHandle);

	// Some debug info
	int infologLength = 0;
	int charsWritten  = 0;
	char *infoLog;
	glGetShaderiv(ShaderHandle, GL_INFO_LOG_LENGTH, &infologLength);

	if (infologLength > 0)
	{
		infoLog = (char *)malloc(infologLength);
		glGetShaderInfoLog(ShaderHandle, infologLength, &charsWritten, infoLog);

		//printf("ShaderInfo: %s\n",infoLog);
		free(infoLog);
	}
	return ShaderHandle;
}


//----------------------------------------------------------------------------
QDockWidget*
OpenGLRImageEntity::GetControlUnit() const
{
	return m_ControlUnit;	
}


//----------------------------------------------------------------------------
RImageF2::ConstPointer
OpenGLRImageEntity::getCurrentFrame() const
{

	return m_CurrentFrame;

}


//----------------------------------------------------------------------------
OpenGLRImageControlUnit*
OpenGLRImageEntity::GetRImageControlUnit() const
{
	return m_ControlUnit;	
}


//----------------------------------------------------------------------------
void
OpenGLRImageEntity::PrintSelf(std::ostream& os, itk::Indent indent) const
{
	std::cout << "OpenGLRImageEntity" << std::endl;	
}


} // namespace ritk
