#include "RendererUtil.h"
#include "MaterialCache.h"
#include "kernel.h"
#include "imageHDR.h"
#include "imagecommon.h"
#include "scenemanager.h"
#include "3dmath.h"

using namespace VRUT;

void MaterialCache::GetCubeTexCoords(const Ray& ray, int tw, int th, size_t& pos)
{
	unsigned int offset = tw * th * 3;
	GLfloat s = 0, r = 0;
	if (fabs(ray.direction.x) > fabs(ray.direction.y))
	{
		if (fabs(ray.direction.x) > fabs(ray.direction.z))
		{
			if (ray.direction.x > 0)
			{
				// biggest positive X
				offset *= 0;
				r = ray.direction.y / (ray.direction.x);
				//r = -r;
				s = -ray.direction.z / (ray.direction.x);
			}
			else
			{
				// biggest negative X
				offset *= 1;
				r = (float) ray.direction.y / fabs(ray.direction.x);
				s = (float) ray.direction.z / fabs(ray.direction.x);
			}
		}
		else if (ray.direction.z > 0)
		{
			// biggest positive Z
			offset *= 4;
			r = ray.direction.y / (ray.direction.z);
			s = ray.direction.x / (ray.direction.z);
		}
		else
		{
			//biggest negative Z
			offset *= 5;
			r = ray.direction.x / fabs(ray.direction.z);
			s = ray.direction.y / fabs(ray.direction.z);
		}
	}
	else if (fabs(ray.direction.y) > fabs(ray.direction.z))
	{
		if (ray.direction.y > 0)
		{
			// biggest positive Y
			offset *= 2;
			// stejne s OpenGL - pro cubeTexturu u Fabia2 nesedi spravne
			r = ray.direction.z / (ray.direction.y);
			s = ray.direction.x / (ray.direction.y);
		}
		else
		{
			// biggest negative Y
			offset *= 3;
			// stejne s OpenGL - pro cubeTexturu u Fabia2 nesedi spravne
			r = -ray.direction.z / fabs(ray.direction.y);
			s = ray.direction.x / fabs(ray.direction.y);
		}
	}
	else if (ray.direction.z > 0)
	{
		// biggest positive Z
		offset *= 4;
		r = ray.direction.y / (ray.direction.z);
		s = ray.direction.x / (ray.direction.z);
	}
	else
	{
		//biggest negative Z
		offset *= 5;
		s = ray.direction.x / fabs(ray.direction.z);
		r = ray.direction.y / fabs(ray.direction.z);
	}

	s = (s + 1) / 2.0f;
	r = (r + 1) / 2.0f;

	int x = tw * fabs(s);
	int y = th * fabs(r);

	pos = (y * tw + x) * 3 + offset;	
}

Vector4 MaterialCache::GetCubeTexColor(const Ray& ray, unsigned int tw, unsigned int th, float * data, bool linear)
{
	unsigned int choseSide = 0;
	GLfloat s = 0, r = 0;
	if (fabs(ray.direction.x) > fabs(ray.direction.y))
	{
		if (fabs(ray.direction.x) > fabs(ray.direction.z))
		{
			if (ray.direction.x > 0)
			{
				// biggest positive X
				choseSide = 0;
				r = ray.direction.y / (ray.direction.x);
				s = -ray.direction.z / (ray.direction.x);
				//r = - r;
			}
			else
			{
				// biggest negative X
				choseSide = 1;
				r = (float) ray.direction.y / fabs(ray.direction.x);
				s = (float) ray.direction.z / fabs(ray.direction.x);
				//r = - r;
			}
		}
		else if (ray.direction.z > 0)
		{
			// biggest positive Z
			choseSide = 4;
			r = ray.direction.y / (ray.direction.z);
			s = ray.direction.x / (ray.direction.z);
			//r =  - r;
		}
		else
		{
			//biggest negative Z
			choseSide = 5;
			r = ray.direction.y / fabs(ray.direction.z);
			s = -ray.direction.x / fabs(ray.direction.z);
		}
	}
	else if (fabs(ray.direction.y) > fabs(ray.direction.z))
	{
		if (ray.direction.y < 0)
		{
			// biggest positive Y
			choseSide = 2;
			// stejne s OpenGL - pro cubeTexturu u Fabia2 nesedi spravne
			r = -ray.direction.z / (ray.direction.y);
			s = -ray.direction.x / (ray.direction.y);
		}
		else
		{
			// biggest negative Y
			choseSide = 3;
			// stejne s OpenGL - pro cubeTexturu u Fabia2 nesedi spravne
			r = -ray.direction.z / fabs(ray.direction.y);
			s = ray.direction.x / fabs(ray.direction.y);
		}
	}
	else if (ray.direction.z > 0)
	{
		// biggest positive Z
		choseSide = 4;
		r = ray.direction.y / (ray.direction.z);
		s = ray.direction.x / (ray.direction.z);
		//r = -r;
	}
	else
	{
		//biggest negative Z
		choseSide = 5;
		r = ray.direction.y / fabs(ray.direction.z);
		s = -ray.direction.x / fabs(ray.direction.z);

	}

	s = (s + 1) / 2.0f;
	r = (r + 1) / 2.0f;
	float fs = (tw-1) * fabs(s);
	float fr = (th-1) * fabs(r);

	unsigned int x = fs;
	unsigned int y = fr;
	unsigned int oneSide = tw * th * 3;	
	if (!linear)
	{
		int pos = (y * tw + x) * 3 + oneSide * choseSide;
		return Vector4(data[pos], data[pos + 1], data[pos + 2], 1.0f);
	}


	float a, b;
	Vector4 Q11, Q12, Q21, Q22;
	unsigned int Q11p, Q12p, Q21p, Q22p;
	a = fs - x;
	b = fr - y;

	switch (choseSide)
	{
	case 0:
		{
			if ((x >= tw - 1) && (y < th - 1))
			{
				Q11p = ( y * tw + x ) * 3;
				Q21p = ( (y + 1) * tw + x ) * 3;
				Q12p = ( y * tw + (th - x) ) * 3 + oneSide * 5;
				Q22p = ( (y + 1) * tw + (th - (x + 1)) ) * 3  + oneSide * 5;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);
			}
			else if ((x < tw - 1) && (y >= th - 1))
			{
				Q11p = ( y * tw + x ) * 3;
				Q21p = ( x * tw + (th - 1) ) * 3  + oneSide * 3;
				Q12p = ( y * tw + (x + 1) ) * 3;
				Q22p = ( (x + 1) * tw + (th - 1) ) * 3  + oneSide * 3;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);
			}
			//roh
			else if (x >= (tw - 1) && y >= (th - 1))
			{
				Q11p = ( y * tw + x ) * 3;
				Q21p = ( x * tw + (th - 1) ) * 3  + oneSide * 3;
				Q12p = ( y * tw + (th - x) ) * 3 + oneSide * 5;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);

				Q22 = (Q21 + Q12);
				Q22 /= 2.0f;

			}
			else
			{
				Q11p = ( y * tw + x ) * 3;
				Q21p = ( (y + 1) * tw + x ) * 3;
				Q12p = ( y * tw + (x + 1) ) * 3;
				Q22p = ( (y + 1) * tw + (x + 1) ) * 3;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);
			}
			break;
		}
	case 1:
		{
			if ((x >= tw - 1) && (y < th - 1))
			{
				Q11p = ( y * tw + x ) * 3 + oneSide;
				Q21p = ( (y + 1) * tw + x ) * 3 + oneSide;
				Q12p = ( y * tw ) * 3 + oneSide * 4;
				Q22p = ( (y + 1) * tw ) * 3  + oneSide * 4;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);
			}
			else if ((x < tw - 1) && (y >= th - 1))
			{
				Q11p = ( y * tw + x ) * 3 + oneSide /** 1*/;
				Q21p = ( (tw - x ) * tw /*+ (0) */) * 3  + oneSide * 3;
				Q12p = ( y * tw + (x + 1) ) * 3 + oneSide /** 1*/;
				Q22p = ( (tw - (x + 1)) * tw /*+ (0) */) * 3  + oneSide * 3;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);
			}
			//roh
			else if (x >= (tw - 1) && y >= (th - 1))
			{
				Q11p = ( y * tw + x ) * 3 + oneSide;
				Q21p = ( (tw - x ) * tw) * 3  + oneSide * 3;
				Q12p = ( y * tw) * 3 + oneSide * 4;				

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);

				Q22 = (Q21 + Q12);
				Q22 /= 2.0f;

			}
			else
			{
				Q11p = ( y * tw + x ) * 3 + oneSide;
				Q21p = ( (y + 1) * tw + x ) * 3 + oneSide;
				Q12p = ( y * tw + (x + 1) ) * 3 + oneSide;
				Q22p = ( (y + 1) * tw + (x + 1) ) * 3 + oneSide;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);
			}
			break;
		}
	case 2:
		{
			if ((x >= tw - 1) && (y < th - 1))
			{
				Q11p = ( y * tw + x ) * 3 + oneSide * 2;
				Q21p = ( (y + 1) * tw + x ) * 3 + oneSide * 2;
				Q12p = ( (th - y) ) * 3;
				Q22p = ( (th - (y + 1)) ) * 3;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);
			}
			else if ((x < tw - 1) && (y >= th - 1))
			{
				Q11p = ( y * tw + x ) * 3 + oneSide * 2;
				Q21p = ( /*(0) * tw +*/ (x ) ) * 3  + oneSide * 4;
				Q12p = ( y * tw + (x + 1) ) * 3  + oneSide * 2;
				Q22p = ( /*(0) * tw +*/ (x + 1) ) * 3  + oneSide * 4;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);
			}
			//roh
			else if (x >= (tw - 1) && y >= (th - 1))
			{
				Q11p = ( y * tw + x ) * 3 + oneSide * 2;
				Q21p = x * 3  + oneSide * 4;
				Q12p = ( (th - y) ) * 3;				

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);

				Q22 = (Q21 + Q12);
				Q22 /= 2.0f;

			}
			else
			{
				Q11p = ( y * tw + x ) * 3 + oneSide * 2;
				Q21p = ( (y + 1) * tw + x ) * 3 + oneSide * 2;
				Q12p = ( y * tw + (x + 1) ) * 3  + oneSide * 2;
				Q22p = ( (y + 1) * tw + (x + 1) ) * 3  + oneSide * 2;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);
			}
			break;
		}
	case 3:
		{
			if ((x >= tw - 1) && (y < th - 1))
			{
				Q11p = ( y * tw + x ) * 3 + oneSide * 3;
				Q21p = ( (y + 1) * tw + x ) * 3 + oneSide * 3;
				Q12p = ( (tw - 1) * tw + (y) ) * 3;
				Q22p = ( (tw - 1) * tw + (y + 1) ) * 3;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);

			}
			else if ((x < tw - 1) && (y >= th - 1))
			{
				Q11p = ( y * tw + x ) * 3 + oneSide * 3;
				Q21p = ( (th - 1) * tw + (tw - (x)) ) * 3  + oneSide * 5;
				Q12p = ( y * tw + (x + 1) ) * 3  + oneSide * 3;
				Q22p = ( (th - 1) * tw  + (tw -(x + 1)) ) * 3  + oneSide * 5;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);
			}
			//roh
			else if (x >= (tw - 1) && y >= (th - 1))
			{
				Q11p = ( y * tw + x ) * 3 + oneSide * 3;
				Q21p = ( (th - 1) * tw + (tw - (x)) ) * 3  + oneSide * 5;
				Q12p = ( (tw - 1) * tw + (y) ) * 3;				

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);

				Q22 = (Q21 + Q12);
				Q22 /= 2.0f;

			}
			else
			{
				Q11p = ( y * tw + x ) * 3 + oneSide * 3;
				Q21p = ( (y + 1) * tw + x ) * 3 + oneSide * 3;
				Q12p = ( y * tw + (x + 1) ) * 3 + oneSide * 3;
				Q22p = ( (y + 1) * tw + (x + 1) ) * 3 + oneSide * 3;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);
			}
			break;
		}
	case 4:
		{
			if ((x >= tw - 1) && (y < th - 1))
			{
				Q11p = ( y * tw + x ) * 3 + oneSide * 4;
				Q21p = ( (y + 1) * tw + x ) * 3 + oneSide * 4;
				Q12p = ( (y) * tw) * 3;
				Q22p = ( (y + 1) * tw) * 3;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);
			}
			else if ((x < tw - 1) && (y >= th - 1))
			{
				Q11p = ( y * tw + x ) * 3 + oneSide * 4;
				Q21p = ( ( x) ) * 3  + oneSide * 3;
				Q12p = ( y * tw + (x + 1) ) * 3  + oneSide * 4;
				Q22p = ( (x + 1) ) * 3  + oneSide * 3;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);
			}
			//roh
			else if (x >= (tw - 1) && y >= (th - 1))
			{
				Q11p = ( y * tw + x ) * 3 + oneSide * 4;
				Q21p = (( x) ) * 3  + oneSide * 3;
				Q12p = ( (y) * tw) * 3;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);

				Q22 = (Q21 + Q12);
				Q22 /= 2.0f;

			}
			else
			{
				Q11p = ( y * tw + x ) * 3 + oneSide * 4;
				Q21p = ( (y + 1) * tw + x ) * 3 + oneSide * 4;
				Q12p = ( y * tw + (x + 1) ) * 3 + oneSide * 4;
				Q22p = ( (y + 1) * tw + (x + 1) ) * 3 + oneSide * 4;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);
			}					
			break;
		}
	case 5:
		{
			if ((x >= tw - 1) && (y < th - 1))
			{
				Q11p = ( y * tw + x ) * 3 + oneSide * 5;
				Q21p = ( (y + 1) * tw + x ) * 3 + oneSide * 5;
				Q12p = ( (y) * tw) * 3 + oneSide;
				Q22p = ( (y + 1) * tw) * 3  + oneSide;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);
			}
			else if ((x < tw - 1) && (y >= th - 1))
			{
				Q11p = ( y * tw + x ) * 3 + oneSide * 5;
				Q21p = ( (th - 1) * tw + (tw - x) ) * 3  + oneSide * 3;
				Q12p = ( y * tw + (x + 1) ) * 3  + oneSide * 5;
				Q22p = ( (th - 1) * tw +  (tw - (x + 1)) ) * 3  + oneSide * 3;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);
			}
			//roh
			else if (x >= (tw - 1) && y >= (th - 1))
			{
				Q11p = ( y * tw + x ) * 3 + oneSide * 5;
				Q21p = ( (th - 1) * tw + (tw - x) ) * 3  + oneSide * 3;
				Q12p = ( (y) * tw) * 3 + oneSide;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);

				Q22 = (Q21 + Q12);
				Q22 /= 2.0f;

			}
			else
			{
				Q11p = ( y * tw + x ) * 3 + oneSide * 5;
				Q21p = ( (y + 1) * tw + x ) * 3 + oneSide * 5;
				Q12p = ( y * tw + (x + 1) ) * 3 + oneSide * 5;
				Q22p = ( (y + 1) * tw + (x + 1) ) * 3 + oneSide * 5;

				Q11 = Vector4( data[Q11p], data[Q11p + 1], data[Q11p + 2], 1.0);
				Q21 = Vector4( data[Q21p], data[Q21p + 1], data[Q21p + 2], 1.0);	
				Q12 = Vector4( data[Q12p], data[Q12p + 1], data[Q12p + 2], 1.0);
				Q22 = Vector4( data[Q22p], data[Q22p + 1], data[Q22p + 2], 1.0);
			}					
			break;
		}
	}
	return a * ( b * Q22 + (1 - b) * Q12 ) + (1 - a) * ( b * Q21 + (1 - b) * Q11 );
}

void MaterialCache::TransformTexCoords(const RTMaterial &material, int texWidth, int texHeight, float & x, float & y) const
{
	if ((x >= 1) || (x < 0))
	{
		if (!(material.flags & RTMaterial::TEX_UWRAP_CLAMP))
		{
			// repeat in u direction
			x -= (int)x;
			if (x < 0)
				x += 1.0f;
			x *= texWidth;
		}
		else
		{
			// no-repeat - clamp
			x = (x >= 1.0f) ? texWidth - 1 : 0;
		}
	}
	else
		x *= texWidth;
	
	if ((y >= 1) || (y < 0))
	{
		if (!(material.flags & RTMaterial::TEX_VWRAP_CLAMP))
		{
			// repeat in v direction
			y -= (int)y;
			if (y < 0)
				y += 1.0f;
			y *= texHeight;
		}
		else
		{
			// no-repeat - clamp
			y = (y >= 1.0) ? texHeight - 1 : 0;
		}
	}
	else
		y *= texHeight;
}

void MaterialCache::GetTriangleTexCoords(const RayIntersectionInfo & rayInfo, const Vector2 * texCoord1, const Vector2 * texCoord2, const Vector2 * texCoord3, float & x, float & y)
{
	float u = fabs(rayInfo.u);
	float v = fabs(rayInfo.v);
	x = (u * (texCoord1->x - texCoord2->x) + v * (texCoord3->x - texCoord2->x) + texCoord2->x);
	y = (u * (texCoord1->y - texCoord2->y) + v * (texCoord3->y - texCoord2->y) + texCoord2->y);
}

void MaterialCache::GetSphereTexCoords(const RayIntersectionInfo & rayInfo, const Material * material, float & x, float & y)
{
	x = 0.5f + atan2(-rayInfo.normal.z, -rayInfo.normal.x) / (2 * M_PI);
	y = 0.5f + asin(-rayInfo.normal.x) / M_PI;
}

void MaterialCache::GetMaterialDiffuseColor(const RTMaterial &material, const Ray & ray, const RayIntersectionInfo & rayInfo, float x, float y, float rayOffset, Vector4 &color, bool & usedTex, Vector4 & texColor)
{
	if (usedTex)
	{
		usedTex = false;
		if ((material.flags & RTMaterial::HAS_TEXTURE) /*&& !(material.flags & RTMaterial::TEX_GEN_FUNC_V_SPHERE)*/)
		{
			// material has texture
			const Image * img = material.texture.getPtr();
			if (img)
			{
				int texWidth = img->GetWidth();
				int texHeight = img->GetHeight();

				switch (img->GetType())
				{
				case Image::WXIMAGE:// texture is wxImage type
					{
						TransformTexCoords(material, texWidth, texHeight, x, y);
						ImageCommon * imgCommon = (ImageCommon *) img;
						const wxImage * wxImg = imgCommon->GetWxImage();
						usedTex = true;
						if (material.flags & RTMaterial::TEX_MAG_FILTER_LINEAR)
							texColor = SampleTexture2DBilinear(wxImg, x, y);
						else
							texColor = SampleTexture2DPoint(wxImg, x, y);

						break;

					}
				case Image::DDSIMAGE://texture if DDSimage
					{
/*						size_t size;
						GLfloat * data = oglTextureMap.GetData(material->imageName, size);

						if (data == nullptr)
						{
							break;
						}

						if (img->IsCube())
						{
							if (material->texFlags & Material::TEX_GEN_REFLECTION)
							{
								Ray r(ray.origin, Vector3(ray.direction.x, -ray.direction.y, ray.direction.z));
								texColor = GetCubeTexColor(r, texWidth, texHeight, data, FLAG2B(material->texFlags, Material::TEX_MAG_FILTER_LINEAR, Material::TEX_MAG_FILTER_MASK));
								usedTex = true;
							}
							else if (material->texFlags & Material::TEX_GEN_NORMAL)
							{
								texColor = GetCubeTexColor(Ray(Vector3(), rayInfo.normal), texWidth, texHeight,data, FLAG2B(material->texFlags, Material::TEX_MAG_FILTER_LINEAR, Material::TEX_MAG_FILTER_MASK));
								usedTex = true;
							}
							else
							{
#if 0
								static int counter = 0;
								if (counter++%100000 == 0)
									LOG(wxT("CUBE MAP"));
#endif
								texColor = GetCubeTexColor(ray, texWidth, texHeight, data, FLAG2B(material->texFlags, Material::TEX_MAG_FILTER_LINEAR, Material::TEX_MAG_FILTER_MASK));
								usedTex = true;
							}
						}*/
						break;
					}
				case Image::HDRIMAGE:// texture is HDR type
					{
						TransformTexCoords(material, texWidth, texHeight, x, y);
						ImageHDR * imgHDR = (ImageHDR *) img;
						/*if (imgHDR->IsCube())
							LOG(wxT("Image HDR is cube - NOT supported yet"));*/
						usedTex = true;
						if (material.flags & RTMaterial::TEX_MAG_FILTER_LINEAR)
						{
							//merge point
							int xx = (int)x;
							int yy = (int)y;
							float a = x - xx;
							float b = y - yy;
							xx = xx % texWidth;
							yy = yy % texHeight;

							Vector4 Q11(imgHDR->GetRGBA(xx, yy));
							Vector4 Q21(imgHDR->GetRGBA((xx + 1) % texWidth, yy));
							Vector4 Q22(imgHDR->GetRGBA((xx + 1) % texWidth, (yy + 1) % texHeight));
							Vector4 Q12(imgHDR->GetRGBA(xx, (yy + 1) % texHeight));

							texColor = a * ( b * Q22 + (1 - b) * Q21) + (1 - a) * ( b * Q12 + ( 1 - b) * Q11);
						}
						else
							texColor = imgHDR->GetRGBA(x, y);
						break;
					}
				default:
					break;

				}
			}
		}
	}
	color = material.diffuse;

	if (usedTex)
	{
		if (material.flags & RTMaterial::TEX_ENV_INTERPOLATE)
		{
			color = (1-material.textureMix)*color + material.textureMix*texColor;
			ClampColor(color);
		}
		else if (material.flags & RTMaterial::TEX_ENV_MODULATE)
			color *= texColor;
		else if (material.flags & RTMaterial::TEX_ENV_REPLACE)
			color = texColor;
		else if (material.flags & RTMaterial::TEX_ENV_DECAL)
		{
			// TODO
			color = texColor;
		}
		else if (material.flags & RTMaterial::TEX_ENV_BLEND)
		{
			// TODO
			color *= texColor;
		}
		else if (material.flags & RTMaterial::TEX_ENV_ADD)
		{
			color += texColor;
			ClampColor(color);
		}
		else //unknown mode so we will do modulate
			color *= texColor;
	}
}

void MaterialCache::GetEnvironmentColor(const Ray & ray, const RayIntersectionInfo & rayInfo, float rayOffset, Vector4 & color)
{
	float x = (atan2f(ray.direction.x, ray.direction.y) / M_PI + 1.0f) / 2.0f;
	float y = (ray.direction.z + 1.0f) / 2.0f;
	bool usedTex = true;
	Vector4 texColor;
	GetMaterialDiffuseColor(enviromentMaterial, ray, rayInfo, x, y, rayOffset, color, usedTex, texColor);
	if (usedTex)
	{
		color.x += enviromentMaterial.emission.x * enviromentMaterial.emission.w * texColor.x;
		color.y += enviromentMaterial.emission.y * enviromentMaterial.emission.w * texColor.y;
		color.z += enviromentMaterial.emission.z * enviromentMaterial.emission.w * texColor.z;
	}
	else
	{
		color.x += enviromentMaterial.emission.x * enviromentMaterial.emission.w;
		color.y += enviromentMaterial.emission.y * enviromentMaterial.emission.w;
		color.z += enviromentMaterial.emission.z * enviromentMaterial.emission.w;
	}
}

void MaterialCache::InvalidateImageCacheEntry(unsigned int index)
{
	if (index < rtMaterials.size())
		rtMaterials[index].Invalidate();
}

void MaterialCache::CacheEnvironmentalMap(SceneShadow * sceneShadow)
{
	enviromentMaterial.SetMaterial(nullptr);
	if (sceneShadow)
	{
		smart_pointer_v2<Scene> &scene = sceneShadow->GetScene();
		std::vector<smart_pointer<BackgroundNode> > EM;
		scene->GetNodes(EM, SceneNode::BACKGROUND);
		for (auto &E : EM)
		{
			if (sceneShadow->IsActive(E->GetID()))
			{
				smart_pointer<Material> envMat;
				scene->GetMaterial(envMat, E->GetMaterialID());
				oglTextureMap.CopyDDSTexDataToRayTracerTexData(envMat.getPtr());
				enviromentMaterial.SetMaterial(envMat.getPtr());
				break;
			}
		}
	}
}

RTMaterial* MaterialCache::GetOrCreateRTMaterial(MATERIAL_ID mid, smart_pointer_v2<Scene> &scene, smart_pointer<Geometry> geo)
{
	if (geo && geo->IsOfType(Geometry::GEOMETRY_TEXT))
	{
		auto _gn = (GeometryText*)geo.getPtr();
		wxFont font = _gn->GetFont();
		//font.SetPointSize(12); //Copied from FontGlyphsTextureOpenGL::GetOrCreatefont

		//font name is the same as material name
		const wxString fontName = _gn->GetFontName();

		RTMaterial gtm;
		if (mid < rtMaterials.size())
		{
			if (!rtMaterials[mid].IsValid())
			{
				smart_pointer<Material> mat;
				scene->GetMaterial(mat, mid);
				rtMaterials[mid].SetMaterial(mat.getPtr());
			}
			gtm = rtMaterials[mid];
		}
		else
			gtm.diffuse = Vector4(1.0f, 1.0f, 1.0f, 1.0f);

		//Check font texture presence
		//If not, create & load it
		gtm.texture.assign(const_cast<Image*>(GetFontCacheImage(font)));
		if (!gtm.texture)
		{
			//Load font texture and cache it
			//font.SetPointSize(12); //Copied from FontGlyphsTextureOpenGL::GetOrCreatefont
			const auto glyphs = KERNEL->GetOrCreateFont(font);

			//Assemble the texture
			//Convert float font texture to RGBA
			const int dims[] = { 32, 32 };
			const size_t effective_char_count = std::min(glyphs->GetCharCount(), size_t(dims[0] * dims[1]));
			const size_t tex_size = glyphs->GetTexSize();

			unsigned char* texture = static_cast<unsigned char*>(malloc(tex_size * tex_size * effective_char_count * 3));
			memset(texture, 0, tex_size * tex_size * effective_char_count * 3);
			unsigned char* alphaChannel = static_cast<unsigned char*>(malloc(tex_size * tex_size * effective_char_count));
			memset(alphaChannel, 0, tex_size * tex_size * effective_char_count);

			for (size_t c = 0; c < effective_char_count; c++)
			{
				int tex[2] = { int(c % dims[0]), int((c / dims[0]) % dims[1]) };

				//Copy subimage to alpha channel
				const auto texels = glyphs->GetTexData(c);

				const unsigned int tSize = tex_size * tex_size;
				const unsigned int startX = tex[0] * tex_size;
				const unsigned int startY = tex[1] * tex_size;
				const unsigned int pitchSize = dims[0] * tex_size;

				for (unsigned int dy = 0; dy < tex_size; ++dy)
				{
					for (unsigned int dx = 0; dx < tex_size; ++dx)
					{
						float val = texels[dy * tex_size + dx] * 255.0f;

						if (val != 0)
						{
							texture[(startY + dy) * 3 * pitchSize + 3 * startX + 3 * dx + 0] = 255;
							texture[(startY + dy) * 3 * pitchSize + 3 * startX + 3 * dx + 1] = 255;
							texture[(startY + dy) * 3 * pitchSize + 3 * startX + 3 * dx + 2] = 255;
							alphaChannel[(startY + dy) * pitchSize + startX + dx] = static_cast<unsigned char>(val);
						}
					}
				}
			}

			wxImage* fontTexture = new wxImage;
			fontTexture->Create(tex_size * dims[0], tex_size * dims[1], texture, alphaChannel);

			//--
			//fontTexture->SaveFile(wxT("textura.png"));
			//--

			Image* image = new ImageCommon(fontTexture);
			image->SetTransparencyAndSetAlphaUsage(false);

			const_smart_pointer<Image> imgptr = image;
			AddFontCacheImage(imgptr, font);
			gtm.texture.assign(const_cast<Image*>(image));
		}

		//Override material texture
		//Multi-texturing with fonts not supported, font texture used only
		gtm.flags = RTMaterial::ALPHA_TEST | RTMaterial::TEX_MAG_FILTER_LINEAR | RTMaterial::HAS_TEXTURE | RTMaterial::TEX_ENV_MODULATE;

		geometryTextRTMaterialsMap[geo->GetID()] = gtm;
		return &geometryTextRTMaterialsMap[geo->GetID()];
	}
	if (mid < rtMaterials.size())
	{
		if (!rtMaterials[mid].IsValid())
		{
			smart_pointer<Material> mat;
			scene->GetMaterial(mat, mid);
			rtMaterials[mid].SetMaterial(mat.getPtr());
		}
		return &rtMaterials[mid];
	}
	else
		return &defaultMaterial;
}

void MaterialCache::AddFontCacheImage(const_smart_pointer<Image> img, wxFont font)
{
	fontImageCache.push_back(std::make_pair(font, img));
}

const Image* MaterialCache::GetFontCacheImage(wxFont &font)
{
	for (int i = 0; i < fontImageCache.size(); ++i)
		if (fontImageCache[i].first == font)
			return fontImageCache[i].second.getPtr();

	return nullptr;
}

void MaterialCache::ResizeRTMaterials(MATERIAL_ID maxMid)
{
	if (rtMaterials.size() < maxMid)
		rtMaterials.resize(maxMid + 1);
}

Vector4 MaterialCache::SampleTexture2DPoint(const wxImage* texture, int u, int v) const
{
	Vector4 texColor;
	const unsigned int texWidth = texture->GetWidth();
	const unsigned int texHeight = texture->GetHeight();

	texColor.x = texture->GetRed(u % texWidth, v % texHeight) / 255.0f;
	texColor.y = texture->GetGreen(u % texWidth, v % texHeight) / 255.0f;
	texColor.z = texture->GetBlue(u % texWidth, v % texHeight) / 255.0f;

	if (texture->HasAlpha())
		texColor.w = texture->GetAlpha(u % texWidth, v % texHeight) / 255.0f;
	else
		texColor.w = 1.0f;

	return texColor;
}

Vector4 MaterialCache::SampleTexture2DBilinear(const wxImage* texture, float u, float v) const
{
	int xx = (int)u;
	int yy = (int)v;
	Vector4  Q11, Q21, Q12, Q22;
	const float a = u - xx;
	float b = v - yy;
	xx = xx % texture->GetWidth();
	yy = yy % texture->GetHeight();

	Q11 = SampleTexture2DPoint(texture, xx, yy);
	Q21 = SampleTexture2DPoint(texture, xx+1, yy);
	Q12 = SampleTexture2DPoint(texture, xx, yy+1);
	Q22 = SampleTexture2DPoint(texture, xx+1, yy+1);

	return  a * (b * Q22 + (1 - b) * Q21) + (1 - a) * (b * Q12 + (1 - b) * Q11);
}

Vector3 MaterialCache::SampleNormalMap(const RTMaterial &material, float x, float y) const
{
	float bumpScale = material.bumpScale;
	const Image * img = material.bumpTexture.getPtr();

	Vector3 sampledValue = Vector3(0, 0, 0);

	if (img)
	{
		// we have a bump map to use
		const int texWidth = img->GetWidth();
		const int texHeight = img->GetHeight();

		switch (img->GetType())
		{
		case Image::WXIMAGE: // texture is wxImage type
			{
				TransformTexCoords(material, texWidth, texHeight, x, y);
				ImageCommon * imgCommon = (ImageCommon *)img;
				const wxImage * wxImg = imgCommon->GetWxImage();

				sampledValue = 2.0f * Vector3(SampleTexture2DBilinear(wxImg, x, y)) - Vector3(1, 1, 1);
			}
			default:
				break;
		}
	}
	
	return Vector3(sampledValue.x*bumpScale, sampledValue.y*bumpScale, sampledValue.z).Normalize();
}