/*
 * scene.cc: classes for objects in scene
 *
 * This file is part of Pyrit Ray Tracer.
 *
 * Copyright 2006, 2007, 2008  Radek Brich
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "scene.h"

void Camera::rotate(const Quaternion &q)
{
	/*
	//non-optimized
	Quaternion res;
	res = q * Quaternion(u) * conjugate(q);
	u = res.toVector();
	res = q * Quaternion(v) * conjugate(q);
	v = res.toVector();
	res = q * Quaternion(p) * conjugate(q);
	p = res.toVector();
	*/

	// optimized
	Float t2 =   q.a*q.b;
	Float t3 =   q.a*q.c;
	Float t4 =   q.a*q.d;
	Float t5 =  -q.b*q.b;
	Float t6 =   q.b*q.c;
	Float t7 =   q.b*q.d;
	Float t8 =  -q.c*q.c;
	Float t9 =   q.c*q.d;
	Float t10 = -q.d*q.d;
	Float x,y,z;
	x = 2*( (t8 + t10)*p.x + (t6 -  t4)*p.y + (t3 + t7)*p.z ) + p.x;
	y = 2*( (t4 +  t6)*p.x + (t5 + t10)*p.y + (t9 - t2)*p.z ) + p.y;
	z = 2*( (t7 -  t3)*p.x + (t2 +  t9)*p.y + (t5 + t8)*p.z ) + p.z;
	p = Vector3(x,y,z);
	x = 2*( (t8 + t10)*u.x + (t6 -  t4)*u.y + (t3 + t7)*u.z ) + u.x;
	y = 2*( (t4 +  t6)*u.x + (t5 + t10)*u.y + (t9 - t2)*u.z ) + u.y;
	z = 2*( (t7 -  t3)*u.x + (t2 +  t9)*u.y + (t5 + t8)*u.z ) + u.z;
	u = Vector3(x,y,z);
	x = 2*( (t8 + t10)*v.x + (t6 -  t4)*v.y + (t3 + t7)*v.z ) + v.x;
	y = 2*( (t4 +  t6)*v.x + (t5 + t10)*v.y + (t9 - t2)*v.z ) + v.y;
	z = 2*( (t7 -  t3)*v.x + (t2 +  t9)*v.y + (t5 + t8)*v.z ) + v.z;
	v = Vector3(x,y,z);
	p.normalize();
	u.normalize();
	v.normalize();
}

void Camera::move(const Float fw, const Float left, const Float up)
{
	eye = eye + fw*p + left*u + up*v;
}

/* http://www.siggraph.org/education/materials/HyperGraph/raytrace/rtinter3.htm */
bool BBox::intersect(const Ray &ray, Float &a, Float &b)
{
	register Float tnear = -Inf;
	register Float tfar = Inf;
	register Float t1, t2;

	for (int i = 0; i < 3; i++)
	{
		if (ray.dir[i] == 0) {
			/* ray is parallel to these planes */
			if (ray.o[i] < L[i] || ray.o[i] > H[i])
				return false;
		} else
		{
			/* compute the intersection distance of the planes */
			t1 = (L[i] - ray.o[i]) / ray.dir[i];
			t2 = (H[i] - ray.o[i]) / ray.dir[i];

			if (t1 > t2)
				swap(t1, t2);

			if (t1 > tnear)
				tnear = t1; /* want largest Tnear */
			if (t2 < tfar)
				tfar = t2; /* want smallest Tfar */
			if (tnear > tfar || tfar < 0)
				return false; /* box missed; box is behind ray */
		}
	}

	a = tnear;
	b = tfar;
	return true;
}
