//------------------------------------------------------------------------------
// Lamp : Open source game middleware
// Copyright (C) 2004  Junpei Ohtani ( Email : junpee@users.sourceforge.jp )
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library 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 the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//------------------------------------------------------------------------------

/** @file
 * 軸沿いボックス交差実装
 * @author Junpee
 */

#include "LampBasic.h"
#include "Geometry/Intersection/AxisAlignedBoxIntersection.h"

namespace Lamp{

//------------------------------------------------------------------------------
// 点
//------------------------------------------------------------------------------
// 点交差
bool AxisAlignedBoxIntersection::intersect(
	const AxisAlignedBox& aab, const Vector3& point){
	const Vector3& max = aab.getMaximum();
	const Vector3& min = aab.getMinimum();
	if(point.x < min.x){ return false; }
	if(point.y < min.y){ return false; }
	if(point.z < min.z){ return false; }
	if(point.x > max.x){ return false; }
	if(point.y > max.y){ return false; }
	if(point.z > max.z){ return false; }
	return true;
}
//------------------------------------------------------------------------------
// 軸沿いボックス
//------------------------------------------------------------------------------
// 軸沿いボックス交差
bool AxisAlignedBoxIntersection::intersect(
	const AxisAlignedBox& aab0, const AxisAlignedBox& aab1){
	const Vector3& max0 = aab0.getMaximum();
	const Vector3& min0 = aab0.getMinimum();
	const Vector3& max1 = aab1.getMaximum();
	const Vector3& min1 = aab1.getMinimum();
	// 軸の最大、最小で比較して範囲が被っていなければ交差しない
	if(max0.x < min1.x){ return false; }
	if(max0.y < min1.y){ return false; }
	if(max0.z < min1.z){ return false; }
	if(min0.x > max1.x){ return false; }
	if(min0.y > max1.y){ return false; }
	if(min0.z > max1.z){ return false; }
	return true;
}
//------------------------------------------------------------------------------
// カプセル
//------------------------------------------------------------------------------
// カプセル交差
bool AxisAlignedBoxIntersection::intersect(
	const AxisAlignedBox& aab, const Capsule& capsule){
	Assert(false);
	return false;
}
//------------------------------------------------------------------------------
// コーン
//------------------------------------------------------------------------------
// コーン交差
bool AxisAlignedBoxIntersection::intersect(
	const AxisAlignedBox& aab, const Cone& cone){
	Assert(false);
	return false;
}
//------------------------------------------------------------------------------
// ライン
//------------------------------------------------------------------------------
// ライン交差
bool AxisAlignedBoxIntersection::intersect(
	const AxisAlignedBox& aab, const Line& line){
	const Vector3& max = aab.getMaximum();
	const Vector3& min = aab.getMinimum();
	const Vector3& direction = line.getDirection();
	const Vector3& origin = line.getOrigin();
	float totalMinT = -Limit::floatMax;
	float totalMaxT = Limit::floatMax;
	// 各軸に関して調べる
	for(int i = 0; i < 3; i++){
		// ラインの方向が軸に垂直か調べる
		float axisDirection = direction.array[i];
		if(Math::abs(axisDirection) <= Math::epsilon){
			// 軸の値が変化しないのでラインの原点がその軸の範囲内に無いと交差しない
			if((origin.array[i] < min.array[i]) ||
				(origin.array[i] > max.array[i])){ return false; }
			continue;
		}
		// 最小値と最大値を投影する
		float minT = (min.array[i] - origin.array[i]) / axisDirection;
		float maxT = (max.array[i] - origin.array[i]) / axisDirection;
		if(minT > maxT){ Math::swap(minT, maxT); }
		// トータルの最小値、最大値の更新
		if(minT > totalMinT){ totalMinT = minT; }
		if(maxT < totalMaxT){ totalMaxT = maxT; }
		// Tの範囲をチェックする
		if(totalMinT > totalMaxT){
			return false;
		}
	}
	return true;
}
//------------------------------------------------------------------------------
// 指向性ボックス
//------------------------------------------------------------------------------
// 指向性ボックス交差
bool AxisAlignedBoxIntersection::intersect(
	const AxisAlignedBox& aab, const OrientedBox& ob){
	Assert(false);
	return false;
}
//------------------------------------------------------------------------------
// 平面
//------------------------------------------------------------------------------
// 平面交差
bool AxisAlignedBoxIntersection::intersect(
	const AxisAlignedBox& aab, const Plane& plane){
	// 最初のコーナーが面のどちら側にあるのか調べる
	float distance = plane.dotProduct(aab.getCorner(0));
	if(distance > 0.f){
		// 平面の負方向に頂点があれば交差している
		for(int i = 1; i < 8; i++){
			distance = plane.dotProduct(aab.getCorner(i));
			if(distance <= 0.f){ return true; }
		}
	}else if(distance < 0.f){
		// 平面の正方向に頂点があれば交差している
		for(int i = 1; i < 8; i++){
			distance = plane.dotProduct(aab.getCorner(i));
			if(distance >= 0.f){ return true; }
		}
	}else{
		// サイズが0でなければ交差している
		return (aab.getSize() != Vector3::zero);
	}
	return false;
}
//------------------------------------------------------------------------------
// レイ
//------------------------------------------------------------------------------
// レイ交差
bool AxisAlignedBoxIntersection::intersect(
	const AxisAlignedBox& aab, const Ray& ray){
	const Vector3& max = aab.getMaximum();
	const Vector3& min = aab.getMinimum();
	const Vector3& direction = ray.getDirection();
	const Vector3& origin = ray.getOrigin();
	float totalMinT = -Limit::floatMax;
	float totalMaxT = Limit::floatMax;
	// 各軸に関して調べる
	for(int i = 0; i < 3; i++){
		// レイの方向が軸に垂直か調べる
		float axisDirection = direction.array[i];
		if(Math::abs(axisDirection) <= Math::epsilon){
			// 軸の値が変化しないのでレイの原点がその軸の範囲内に無いと交差しない
			if((origin.array[i] < min.array[i]) ||
				(origin.array[i] > max.array[i])){ return false; }
			continue;
		}
		// 最小値と最大値を投影する
		float minT = (min.array[i] - origin.array[i]) / axisDirection;
		float maxT = (max.array[i] - origin.array[i]) / axisDirection;
		if(minT > maxT){ Math::swap(minT, maxT); }
		// トータルの最小値、最大値の更新
		if(minT > totalMinT){ totalMinT = minT; }
		if(maxT < totalMaxT){ totalMaxT = maxT; }
		// Tの範囲をチェックする
		if(totalMinT > totalMaxT){ return false; }
		if(totalMaxT < 0.f){ return false; }
	}
	return true;
}
//------------------------------------------------------------------------------
// セグメント
//------------------------------------------------------------------------------
// セグメント交差
bool AxisAlignedBoxIntersection::intersect(
	const AxisAlignedBox& aab, const Segment& segment){
	const Vector3& max = aab.getMaximum();
	const Vector3& min = aab.getMinimum();
	const Vector3& direction = segment.getDirection();
	const Vector3& origin = segment.getOrigin();
	float totalMinT = -Limit::floatMax;
	float totalMaxT = Limit::floatMax;
	// 各軸に関して調べる
	for(int i = 0; i < 3; i++){
		// セグメントの方向が軸に垂直か調べる
		float axisDirection = direction.array[i];
		if(Math::abs(axisDirection) <= Math::epsilon){
			// 軸の値が変化しないのでセグメントの原点がその軸の範囲内に無いと交差しない
			if((origin.array[i] < min.array[i]) ||
				(origin.array[i] > max.array[i])){ return false; }
			continue;
		}
		// 最小値と最大値を投影する
		float minT = (min.array[i] - origin.array[i]) / axisDirection;
		float maxT = (max.array[i] - origin.array[i]) / axisDirection;
		if(minT > maxT){ Math::swap(minT, maxT); }
		// トータルの最小値、最大値の更新
		if(minT > totalMinT){ totalMinT = minT; }
		if(maxT < totalMaxT){ totalMaxT = maxT; }
		// Tの範囲をチェックする
		if(totalMinT > totalMaxT){ return false; }
		if(totalMaxT < 0.f){ return false; }
		if(totalMinT > 1.f){ return false; }
	}
	return true;
}
//------------------------------------------------------------------------------
// 球
//------------------------------------------------------------------------------
// 球交差
bool AxisAlignedBoxIntersection::intersect(
	const AxisAlignedBox& aab, const Sphere& sphere){
	const Vector3& max = aab.getMaximum();
	const Vector3& min = aab.getMinimum();
	const Vector3& center = sphere.getCenter();
	float radius = sphere.getRadius();

	// 各軸のはみ出した距離の総和が半径以上なら交差していない
	float totalDitance = 0.f;
	float distance;

	distance = min.x - center.x;
	if(distance > 0.f){ totalDitance += distance * distance; }
	distance = center.x - max.x;
	if(distance > 0.f){ totalDitance += distance * distance; }

	distance = min.y - center.y;
	if(distance > 0.f){ totalDitance += distance * distance; }
	distance = center.y - max.y;
	if(distance > 0.f){ totalDitance += distance * distance; }

	distance = min.z - center.z;
	if(distance > 0.f){ totalDitance += distance * distance; }
	distance = center.z - max.z;
	if(distance > 0.f){ totalDitance += distance * distance; }

	return (totalDitance <= (radius * radius));
}
//------------------------------------------------------------------------------
// 三角
//------------------------------------------------------------------------------
// 三角交差
bool AxisAlignedBoxIntersection::intersect(
	const AxisAlignedBox& aab, const Triangle& triangle){
	Assert(false);
	return false;
}
//------------------------------------------------------------------------------
} // End of namespace Lamp
//------------------------------------------------------------------------------
