uniform sampler3D volume;
uniform sampler1D pal;

uniform float width;

uniform vec3 cam_pos;
uniform vec3 cam_dir;
uniform vec3 cam_up;
uniform vec3 cam_right;

uniform float ptime;

const float jump_size = 1.0;
const float tiny_size = 0.25;
const float norm_size = 0.1;

float vox_step = 1.0 / width;
float big_step = vox_step * jump_size;
float small_step = vox_step * tiny_size;
float norm_step = vox_step * norm_size;

const float fixed_small_step = 64.0;

float far_d = 0.0;
float near_d = 0.0;

vec3 fpos;

bool bbox_check(vec3 origin, vec3 dir)
{
	float left = 0.0;
	float right = 1.0;
	float down = 0.0;
	float up = 1.0;
	float back = 0.0;
	float front = 1.0;

	float tNearX, tFarX;
	float tNearY, tFarY;
	float tNearZ, tFarZ;

	// Slab X
	if (dir.x >= 0.0)
	{
		tNearX = (left - origin.x) / dir.x;
		tFarX = (right - origin.x) / dir.x;
	}
	else if (dir.x < 0.0)
	{
		tNearX = (right - origin.x) / dir.x;
		tFarX = (left - origin.x) / dir.x;
	}

	// Slab Y
	if (dir.y >= 0.0)
	{
		tNearY = (down - origin.y) / dir.y;
		tFarY = (up - origin.y) / dir.y;
	}
	else if (dir.y < 0.0)
	{
		tNearY = (up - origin.y) / dir.y;
		tFarY = (down - origin.y) / dir.y;
	}

	// Slab Z
	if (dir.z >= 0.0)
	{
		tNearZ = (back - origin.z) / dir.z;
		tFarZ = (front - origin.z) / dir.z;
	}
	else if (dir.z < 0.0)
	{
		tNearZ = (front - origin.z) / dir.z;
		tFarZ = (back - origin.z) / dir.z;
	}

	// We have to find now the the largest of the Near and the the smallest of the Far

	float tNearMax, tFarMin;

	if (tNearX > tNearY)
		tNearMax = tNearX;
	else
		tNearMax = tNearY;
	if (tNearZ > tNearMax)
		tNearMax = tNearZ;

	if (tFarX < tFarY)
		tFarMin = tFarX;
	else
		tFarMin = tFarY;
	if (tFarZ < tFarMin)
		tFarMin = tFarZ;

	far_d = tFarMin;
	near_d = tNearMax;

	// Final test
	return (tNearMax < tFarMin);
}

float eval(vec3 pos)
{
//	vec4 vb = texture3D(volume, pos);
//	float c = vb.r;

	pos *= 512.0;
	float c = sin(pos.x / 45.0) + sin((pos.y - ptime) / 26.0) + sin(pos.z / 30.0) + sin((pos.x + pos.y + ptime + ptime) / 74.0) + sin((pos.x + pos.z) / 34.0) + sin((pos.y + pos.z - ptime - ptime - ptime) / 116.0) + sin((pos.x + pos.y + pos.z + ptime) / 48.0);
	if (c > 0.99) c = 0.99;
	if (c < 0.0) c = 0.0;

/*	int num = 1;
	float s = 0.0;

	float energy = 0.025;

	for (int i=0; i<num; i++)
	{
//		float xp = sin((i + ptime) / 27.0) * 0.25 + 0.5;
//		float yp = sin((i + ptime) / 47.0) * 0.35 + 0.45;
//		float zp = sin((i + ptime) / 77.0) * 0.45 + 0.35;

	float xp = 0.5;
	float yp = 0.5;
	float zp = 0.5;

		s += energy / pow((pos.x - xp) * (pos.x - xp) + (pos.y - yp) * (pos.y - yp) + (pos.z - zp) * (pos.z - zp),2.0);
	}

	float c = s;

	if (c < 0.0) c = 0.0;
	if (c > 0.99) c = 0.99;
*/
	return c;
}


vec4 calc_val(vec3 pos, vec3 dir, float steps)
{
	vec4 rgba_sum = vec4(0.0);

	for (float i=0.0; i<steps; i++)
	{
		float c = eval(pos);
		vec4 rgba_read = texture1D(pal, c);

		rgba_sum += (1.0 - rgba_sum.a) * rgba_read;

		if (rgba_sum.a > 0.95)
			break;

		pos = pos + dir;
	}

	fpos = pos;

	return rgba_sum;
}



void main()
{
	float xt = gl_TexCoord[0].x - 0.5;
	float yt = gl_TexCoord[0].y - 0.5;

	vec3 pscreen = xt * cam_right + yt * cam_up + cam_dir;

	vec3 dir = normalize(pscreen);
	vec3 dirstep = dir * big_step;
	vec3 smlstep = dir * small_step;

	vec3 pos = cam_pos + vec3(0.5, 0.5, 0.5);


	bool bchk = bbox_check(pos, dirstep);
	if (!bchk)
	{
	 	gl_FragColor = vec4(0.0, 0.0, 0.5, 1.0);
		return;
	}


	if (near_d > 0.0)
	{
		vec3 mdir = dirstep * near_d;
		pos = pos + mdir;
	}
	else near_d = 0;

	int f_num_steps = int(far_d - near_d);


	float c;
	vec4 vgfx = vec4(0.0);

  for (int i=0; i<f_num_steps; i++)
	{
		fpos = pos;
		c = eval(pos);

		if (c!=0)
		{
			vgfx = calc_val(pos, smlstep, fixed_small_step);
			break;
		}


		pos = pos + dirstep;
	}

	float c0 = eval(fpos);
	float cx = eval(fpos + vec3(norm_step, 0.0, 0.0));
	float cy = eval(fpos + vec3(0.0, norm_step, 0.0));
	float cz = eval(fpos + vec3(0.0, 0.0, norm_step));

	vec3 n = normalize(vec3(c0 - cx, c0 - cy, 0.01));
	vec3 l = vec3(0.0, 0.0, 1.0);
	float shade = dot(n,l) + pow(dot(n,l),4.0);

//	gl_FragColor = vgfx;
	gl_FragColor = vgfx * shade;
}
