/*********************************************************************//**
*		Modul pro detekci kolizi a provadeni pohybu entit, ktere koliduji se scenou.
*	V tomto modulu jsou implementovany vsechny potrebne funkce
*	pro vypocty pohybu entit. Pri takovemto pohybu je nutne spocitat,
*	jestli hrac nenarazil do zdi a take provadet detekci chuze do schodu
*	nebo po zebriku, vypocitat, jestli se hrac pohybuje po zebriku
*	a tento pohyb provest, vylezani z vody a nakonec vypocet reakce
*	na pohybujici se objekty. Dalsi funkci je ray tracing, kdy je nutne
*	napriklad pro strely vypocitat, kam dopadly.
*
*	author: Michal Jirous
*	date: 13.02.2009
*	file: level_collision.cpp
**********************************************************************/


#include "level_collision.h"
#include "level_loader.h"

LevelCollision collisionSystem;

/** @param position Testovany bod.
*	@param hull_type Typ HULL rozsireni (0-3).
*	@return Obsah listu, kde lezi bod (see all CONTENTS in BSP file definition).
*/
int LevelCollision::clippingHullCollision( const Point &position, const int hull_type )
{

	return clippingHullCollision( position, levelLoader.m_LevelData.models[0], hull_type );
}

/** @param position Bod, pro ktery se ma najit list BSP stromu pro model 0, kde lezi.
*	@return Cislo listu, kde bod lezi.
*/
int LevelCollision::findLeaf( const Point &position )
{
	LevelData &scene = levelLoader.m_LevelData;

	dmodel_t &model = scene.models[0];

	int nodeIdx = model.headnode[0];	//zjistime index prvniho uzlu bsp stromu (koren)
	while( nodeIdx > -1 )	//zaporne cislo uzlu znaci, ze jde o list (prochazime az do listu)
	{
		BSPNode &node = scene.nodes[nodeIdx];
		Plane &plane = scene.planes[ node.planenum ];	//potrebujeme znat rovinu
		float v;
		if( plane.m_type <= PLANE_Z )	//pro axialni rovinu je vypocet jednoduchy
			v = position[plane.m_type] + plane.m_fDistance;
		else
			v = plane.m_vecNormal.multiply( position ) + plane.m_fDistance;
		
		if( v >= 0 )
			nodeIdx = node.children[0];	//front
		else
			nodeIdx = node.children[1];	//back
	}

	return -(nodeIdx+1);
}

/** @param position Testovany bod.
*	@param model Identifikacni cislo modelu, kde se ma tato detekce kolizi testovat.
*	@param hull_type Typ HULL rozsireni (0-3).
*	@return Obsah listu, kde lezi bod (see all CONTENTS in BSP file definition).
*/
int LevelCollision::clippingHullCollision( const Point &position, size_t model, int hull_type )
{
	return clippingHullCollision( position,	levelLoader.m_LevelData.models[model], hull_type );
}	

/** @param position Testovany bod.
*	@param model Model, kde se ma tato detekce kolizi testovat.
*	@param hull_type Typ HULL rozsireni (0-3).
*	@return Obsah listu, kde lezi bod (see all CONTENTS in BSP file definition).
*/
int LevelCollision::clippingHullCollision( const Point &position, const dmodel_t &model, int hull_type )
{
	LevelData &scene = levelLoader.m_LevelData;

	if( hull_type == HULL_REALWORLD )
	{
		BSPLeaf &leaf = scene.leaves[ findLeaf( position ) ];
		return leaf.contents;
	}
	else if( hull_type > HULL_PLAYER_DUCKED )
		return 0;
	

	int nodeIdx = model.headnode[hull_type];	//zjistime index prvniho uzlu bsp stromu (koren)
	while( nodeIdx > -1 )	//zaporne cislo uzlu znaci, ze jde o list (prochazime az do listu)
	{
		dclipnode_t &node = scene.clip_nodes[nodeIdx];
		Plane &plane = scene.planes[ node.planenum ];	//potrebujeme znat rovinu
		float v;
		if( plane.m_type <= PLANE_Z )	//pro axialni rovinu je vypocet jednoduchy
			v = position[plane.m_type] + plane.m_fDistance;
		else
			v = plane.m_vecNormal.multiply( position ) + plane.m_fDistance;
		
		if( v >= 0 )
			nodeIdx = node.children[0];	//front
		else
			nodeIdx = node.children[1];	//back
	}

	return nodeIdx;
}

#include "ent_basemoveable.h"
#include "game.h"
/**	@param entityList Seznam entit, u kterych se ma provadet analyza pohybu. */
void LevelCollision::processMoving( std::list<CBasePhysics*> &entityList)
{
	

	std::list<CBaseEntity*> usedLightForceVectors;

	//kolize aktivnich s pevnymi i aktivnimi
	for( std::list<CBasePhysics*>::iterator iterCreature = entityList.begin(); iterCreature != entityList.end(); ++iterCreature )
	{
		bool stairsMovementTested = false;
		usedLightForceVectors.clear();
		(*iterCreature)->prepareForCollision();	//spocita vysledny vektor rychlosti
		if( (*iterCreature) == game.m_pPlayer )
			game.movement = (*iterCreature)->m_Model.m_vecMovement;

		bool lastAirValue = (*iterCreature)->isInAir();
		(*iterCreature)->m_bIsInAir = true;
		(*iterCreature)->m_bStairsMovement = false;
		Vector lightForceMovement, nextLightForceMovement;
		bool bLadderTested = false;
		CollisionData collData( (*iterCreature)->m_Model );
		bool nextSet = false;
		Vector startOrigin( (*iterCreature)->m_Model.m_topCenterPoint );
		/*=======================================
		*	1. Clipping hull test, kdyz to jde
		*======================================*/
		for( int i = 0; i < 5; ++i)
		{
			if( nextSet )
			lightForceMovement = nextLightForceMovement;
			nextSet = false;
			//nextLightForceMovement.clear();
			const float distance = (*iterCreature)->m_Model.m_vecMovement.absolute();
			Vector move( (*iterCreature)->m_Model.m_vecMovement, distance + 1.0f );

			//yeah!!! toto vubec nefunguje :))))) (teda..nejak to funguje, ale bez dokumentace to nezjistim)
			//float u = Vector( 1,1,0).scalarMultiply( Vector(-1,-1,-1) );
			/*int result = clippingHullCollision( (*iterCreature)->m_Model.m_middleCenterPoint + move, (*iterCreature)->getHullType() );
			if( result == CONTENTS_EMPTY )
			{
				(*iterCreature)->acceptVector( (*iterCreature)->m_Model.m_vecMovement );
				break;		
			}*/
			/*bool h = true;
			Point test(0,0,0);
			while( h )
			{
				int result = clippingHullCollision( test, 1 );
				result = result;
			}*/


			/*=======================================
			*	2. Pripravime kolizni data
			*======================================*/
			
			
			collData.highestPoint = collData.model.m_StaticBounds.m_fBounds[MIN_Z];
			collData.maxdistance = distance + lightForceMovement.absolute() + 1.0f;
			collData.distance = collData.maxdistance;
			collData.initiator = (*iterCreature);
			collData.face = NULL;

			if( (collData.maxdistance < 0.01f) )
				break;



			/*=======================================
			*	3. Pohyb po zebriku pouze pro hrace
			*======================================*/
			if( (*iterCreature) == game.m_pPlayer )
			{
				//test zachyceni se zebriku pri pohybu shora dolu
				//testuje se pouze jednou
				if( lastAirValue && game.m_pPlayer->m_bPosibleLadderDownMovement )
				{
					CollisionData ladderCollData( collData.model );
					Vector backup = ladderCollData.model.m_vecMovement;
					ladderCollData.model.m_vecMovement = - (ladderCollData.model.m_vecMovement + ladderCollData.model.m_vecMovement);	//2x
					
		
					ladderCollData.maxdistance = distance + distance;	//2x distance
					ladderCollData.distance = ladderCollData.maxdistance;
					ladderCollData.model.m_vecMovement.z = 0.0f;	//pouze horizontalni vektor
					ladderCollData.model.createMovedModel();

					game.m_pPlayer->m_bPosibleLadderDownMovement = false;
					if( game.m_pPlayer->m_pLadder->collisionDetection( ladderCollData ) )
					{
						Vector horizontal = Vector( ladderCollData.normal.x, ladderCollData.normal.y, 0 );
						if( horizontal != Vector() && horizontal.scalarMultiply( ladderCollData.normal ) < COLL_LADDER_NORMAL_MAX_ANGLE )
						{
							ladderCollData.normal.normalize();
							setLadderParameters( ladderCollData.distance, ladderCollData.normal, game.m_pPlayer->m_pLadder );
							break;
						}
					}

					collData.model.m_vecMovement = backup;
				}

				//Test pritomnosti na zebriku
				//ted musime otestovat jestli jsme stale na zebriku
				if( !bLadderTested && game.m_pPlayer->m_bIsOnLadder )	//nejprve test, zda minule kolo byl hrac na zebriku
				{
					CollisionData ladderCollData( collData.model );
					bLadderTested = true;
					
					ladderCollData.maxdistance = game.m_pPlayer->m_fLastLadderDistance + 1/*ALG_COLLISION_DETECT_2ND_PHASE_BOUNDS_RANGE*/;
					ladderCollData.distance = ladderCollData.maxdistance;
					ladderCollData.highestPoint = ladderCollData.model.m_StaticBounds.m_fBounds[MIN_Z];
					
					Vector backup = ladderCollData.model.m_vecMovement;
					ladderCollData.model.m_vecMovement = -Vector( game.m_pPlayer->m_vecLastLadderNormal, ladderCollData.distance );	//mozna je to tady zaporny

					ladderCollData.model.createMovedModel();
					
					if( game.m_pPlayer->m_pLadder->collisionDetection( ladderCollData ) )
					{
						ladderCollData.normal.normalize();
						//jsem stale na zebriku a vykonam pohyb po nem
						Vector finnishedVector = transformLadderMovement( backup, ladderCollData.normal ); //TODO_VEC
					
						Vector v = finnishedVector * ladderCollData.normal;
						Vector resultsMove = ladderCollData.normal * v;
						//TODO_VEC

						ladderCollData.model.m_vecMovement = resultsMove;
						
						//game.m_pPlayer->setMoveVector( resultsMove );	//ano, je tam potreba opacny smer //HMMM
						continue;	//po vykonu pohybu po zebriku musi pokracovat detekce kolizi dalsim krokem
					}
					else
						game.m_pPlayer->m_bIsOnLadder = false;	//konec pohybu po zebriku a pokracujeme normalni detekci

					ladderCollData.model.m_vecMovement = backup;
				}
			}

			

			/*=======================================
			*	4. Detekujeme kolize
			*======================================*/
			collData.model.m_vecMovement += lightForceMovement;
			collData.model.createMovedModel();
			collData.target = NULL;

			bool isColliding = collisionDetection( collData );
			
			/*Uint32 start = SDL_GetTicks();

			for( int i = 0; i < game.iteration; i++ )
			{
				collData.distance = collData.maxdistance;
				collisionDetection( collData );
			
			}

			cout << game.iteration << " " << SDL_GetTicks() - start << endl;*/
			
			if( !isColliding )
			{
				(*iterCreature)->acceptVector( (*iterCreature)->m_Model.m_vecMovement );

				

				break;
			}
		

			CBaseEntity *pEntity = (CBaseEntity*)collData.target;

			if( pEntity )
			{
				pEntity->onCollide( collData );


				if( pEntity->getProperties() & ENT_PARM_FORCE_MOVING )
				{
					bool founded = false;
					for( std::list<CBaseEntity*>::iterator iter = usedLightForceVectors.begin(); iter != usedLightForceVectors.end(); ++iter )
						if( (*iter) == pEntity )
						{
							founded = true;
							break;
						}

					if( !founded )
					{
						if( collData.normal.scalarMultiply( Vector( 0,0,1) ) < 30.0f )
						{
							nextLightForceMovement =((CBaseMoveable*)pEntity)->getNextMovement();
							usedLightForceVectors.push_back( pEntity );
							nextSet = true;
						}
					}
				}
			}


			/*=======================================
			*	5. Pouze pro hrace
			*		Test, zda hrac nenarazil do zebriku
			*======================================*/
			if( (*iterCreature) == game.m_pPlayer )
			{
				if( collData.target && ((CBaseEntity*)(collData.target))->getClassName() == "func_ladder" )
				{
					
					/*	Musime otestovat uhel normaloveho vektoru kolizni roviny zebriku
					*	vzhledem k horizontalni rovine..vysledny uhel musi splnovat danny limit, aby
					*	bylo mozne se pohybovat po zebriku.
					*/
					Vector vecHorizontal = Vector( collData.normal.x, collData.normal.y, 0 );
					if( vecHorizontal != Vector() && vecHorizontal.scalarMultiply( collData.normal ) < COLL_LADDER_NORMAL_MAX_ANGLE )
					{
						//nastavime parapetry pohybu po zebriku
						setLadderParameters( collData.distance, collData.normal, reinterpret_cast<FuncLadder*>( (CBaseEntity*)(collData.target)) );
						game.m_pPlayer->m_bPosibleLadderDownMovement = false;
						
						//dovolime pohyb do prvni kolize
						if( collData.distance > 0.1f )
							(*iterCreature)->acceptVector( Vector( (*iterCreature)->m_Model.m_vecMovement, collData.distance-0.1f  ) );

						break;
					}
					else if( game.m_pPlayer->m_bIsOnLadder == false )
					{	
						//Pripad uhlu vetsiho nez limit natoceni zebriku. V tom pripade je mozne,
						//ze nastane pohyb po zebriku dolu
						game.m_pPlayer->m_bPosibleLadderDownMovement = true;
						game.m_pPlayer->m_pLadder = reinterpret_cast<FuncLadder*>( (CBaseEntity*)(collData.target));
					}
				}
				else
					game.m_pPlayer->m_bPosibleLadderDownMovement = false;
			}

			//collData.model.m_vecMovement += lightForceMovement;
			Vector finalVector = resultsVectors( (*iterCreature)->m_Model.m_vecMovement - lightForceMovement, collData, (*iterCreature) );
			
			lightForceMovement = resultsVectors( lightForceMovement, collData, NULL );
			/*=======================================
			*	6. Test pohybu do schodu
			*		Test, zda hrac nenarazil do zebriku
			*======================================*/
			if( !stairsMovementTested )
			{
				const float minStairs = collData.model.m_StaticBounds.m_fBounds[ MIN_Z ];
				if( collData.highestPoint + FLOAT_COMPARE_ACCURACY_HIGH > minStairs && collData.highestPoint < minStairs + COLL_STAIRS_ADDITIONAL_HEIGHT &&
					!compareFloats(collData.normal[2],0.0f, 0.1f) )
				{
					stairsMovementTested = true;
					const float value = collData.highestPoint - minStairs;	//Rozdil vysek = cilova vzdalenost
					const alg::Vector stairsVectorUp( 0, 0, value + 1.0f );

					//lets see if this works - no its not === TODO
					int result = clippingHullCollision( (*iterCreature)->m_Model.m_middleCenterPoint + stairsVectorUp, (*iterCreature)->getHullType() );
					if( result == CONTENTS_EMPTY )
					{
						const float backup_distance = collData.distance;
						const Vector backup_normal = collData.normal;
						const Vector backup_movement = collData.model.m_vecMovement;
						collData.model.translateStatic( stairsVectorUp );
						collData.distance = collData.maxdistance;
						collData.model.m_vecMovement.z = 0.0f;
						collData.model.createMovedModel();
	
						isColliding = collisionDetection( collData );

						if( !isColliding )
						{
							//collData.model.translateStatic( -stairsVectorUp );
							(*iterCreature)->acceptVector( (*iterCreature)->m_Model.m_vecMovement/*+stairsVectorUp*/ );
							startOrigin.z += value;
							break;
						}
						else if( isColliding && collData.distance > backup_distance && collData.distance > 0.1f )
						{
							(*iterCreature)->m_bStairsMovement = true;
							(*iterCreature)->acceptVector( Vector( collData.model.m_vecMovement, collData.distance-0.11f  ) );
							(*iterCreature)->setMoveVector( collData.model.m_vecMovement - lightForceMovement ); //TODO_VEC
							startOrigin.z += value;
							continue;
						}
						else
						{
							//mozna staci static translate - TODO
							collData.model.translateStatic( -stairsVectorUp );
							collData.distance = backup_distance;
							collData.normal = backup_normal;
							collData.model.m_vecMovement = backup_movement;
						}
					}

				}
	//			else if( iLastWaterLevel > LEGS_IN_WATER && fStairsMax + 0.000001 > minStairs			&&
	//				fStairsMax < minStairs + PLAYER_HEIGHT + COLL_JUMP_OUT_OF_WATER_ADDITIONAL_HEIGHT	&&
	//				!compareFloats( vecNormal[2], 0.0f, 0.1f) )
	//			{
	//				m_pPlayer->m_bJumpoutOfWater = true;
	//			}
			}



			if( isColliding )
			{
				float ratio = (collData.distance-0.1f) / collData.maxdistance;

				if( collData.distance > 0.1f )
				{
					(*iterCreature)->acceptVector( Vector( (*iterCreature)->m_Model.m_vecMovement, std::max(0.0f,collData.distance-0.11f)  ) );
					lightForceMovement = lightForceMovement * (1.0f - ratio);
				}

				
				(*iterCreature)->setMoveVector( finalVector ); //TODO_VEC
			}

		}




		Vector bckMove = collData.model.m_vecMovement;

		for( std::list<CBaseMoveable*>::iterator iterForceMovingObject = levelLoader.m_Entities.m_ForceColliding.begin();
			iterForceMovingObject != levelLoader.m_Entities.m_ForceColliding.end(); ++iterForceMovingObject )
		{

			if( collData.model.m_DynamicBounds.compare( (*iterForceMovingObject)->getCollisionMovedBox() ) )
			{
				Vector thisMove = /*m_pPlayer->m_vecMove +*/ -(*iterForceMovingObject)->getNextMovement(); //TODO_VEC
			
				collData.maxdistance = thisMove.absolute();
				collData.model.m_vecMovement = thisMove;
				collData.distance = collData.maxdistance;
				collData.initiator = (*iterCreature);
				collData.model.createMovedModel();
				
				levelLoader.m_LevelData.current_session++;	//zvysime cislo aktualniho kola :) protoze jinak to muzeme zabalit

				if( (*iterForceMovingObject)->forceCollisionDetection( collData ) )
				{
					collData.normal.normalize();
					
					Vector forceVector = resultsVectorsMove( thisMove, collData.normal, (*iterForceMovingObject)->getNextMovement(), (*iterCreature) ) + (*iterForceMovingObject)->getNextMovement();	
					bckMove += forceVector;
					collData.model.m_vecMovement = forceVector;
					collData.model.createMovedModel();
					collData.maxdistance = forceVector.absolute();
					collData.distance = collData.maxdistance;
			
					(*iterForceMovingObject)->blockCollisionDetection();

					bool bIsColliding = collisionDetection( collData );
					(*iterForceMovingObject)->unblockCollisionDetection();
					if( !bIsColliding )
						(*iterCreature)->acceptVector( forceVector ); //TODO_VEC
					else
						(*iterForceMovingObject)->blockMovement( collData );
				}
			}
		}
		

		//(*iterCreature)->setMoveVector( bckMove ); //TODO_VEC
		
//
//	//Vector hhh = m_pPlayer->m_vecMove;
//	//bool f = bTargetCanAcceptVector;
//	////nyni nastava faze, kdy je treba spocitat kolize s pohyblivymi objekty
//	//Vector forceVector;
//	//m_bLockForceMovingObjects = true;
//	//for( std::list<CBaseForceMovingObject*>::iterator tmpForceMovingObject = m_ForceMovingObjects.begin();
//	//	tmpForceMovingObject != m_ForceMovingObjects.end(); tmpForceMovingObject++ )
//	//{
//	//	//jako vektor pohybu pouzijeme vektor pohybu hrace secteny s opacnym vektorem pohybu objektu
//	//	Vector thisMove = /*m_pPlayer->m_vecMove +*/ (*tmpForceMovingObject)->getForcedVector(); //TODO_VEC
//	//	
//	//	m_pPlayer->m_ActiveBrush.createMovedBrush( thisMove ); //TODO_VEC
//	//	
//	//	fDistance = thisMove.absolute();
//	//	if( (*tmpForceMovingObject)->collisionDetection( m_pPlayer, vecNormal, fDistance , fStairsMax ) )
//	//	{
//	//		bTargetCanAcceptVector = false;
//
//	//		forceVector = resultsVectorsMove( thisMove, vecNormal, (*tmpForceMovingObject)->getForcedVector() ) - (*tmpForceMovingObject)->getForcedVector();	
//	//		m_pPlayer->m_vecMove = forceVector; //TODO_VEC
//	//		
//	//		m_pPlayer->m_ActiveBrush.createMovedBrush( m_pPlayer->m_vecMove ); //TODO_VEC
//	//		fDistance = m_pPlayer->m_vecMove.absolute();
//	//		
//	//		bIsColliding = octreeCollisionDetection( m_pPlayer, vecNormal, fDistance, fStairsMax );
//	//		if(! bIsColliding || (*tmpForceMovingObject) == m_pCollisionSubject )
//	//			m_pPlayer->acceptVector( forceVector ); //TODO_VEC
//	//		else
//	//			(*tmpForceMovingObject)->forceMoveCollision( m_pPlayer );
//	//		//bIsColliding = octreeCollisionDetection( m_pPlayer, vecNormal, fDistance, fStairsMax );
//	//	}
//	//	else
//	//		m_bLockForceMovingObjects = m_bLockForceMovingObjects;
//	//}


































	}
}


Vector LevelCollision::resultsVectorsMove( Vector &move, Vector &normal, Vector &forceVector, CBasePhysics* creature )
{
	Vector v = move * normal;
	Vector f = normal * v;

	float x = normal.scalarMultiply(alg::Vector(0.0f, 0.0f, 1.0f));
	
	/* pokud je uhel mezi osou x a vyslednym normalovym vektorem mensi 
	nez 40, tak hrac stoji na zemi a neni ve vzduchu */
	if( x < COLL_GROUND_DETECT_ANGLE )
	{
		if( creature )
		{
			creature->m_bIsInAir = false;
		}
		
		Vector xyz( f );
		xyz.z = 0.0f;

		float angle = f.scalarMultiply( xyz );
		float size = xyz.absolute() / mycos( angle );
		f = Vector( f, size );


		/*Vector abc = move * Vector(0.0f, 0.0f, 1.0f);
		Vector ggg = Vector(0.0f, 0.0f, 1.0f) * abc;

		Vector xyz = normal * abc;

		float angle = f.scalarMultiply( forceVector );
		float size = forceVector.absolute() / mycos( angle );
		f = Vector( f, size );
		f = Vector( f, xyz.absolute() );
		f = xyz;*/

	}



return f;
	/*Vector v = move * normal;
	Vector f = normal * v;

	float x = normal.scalarMultiply( forceVector );
	if(x < 30)
	{
		return Vector();
	}
	else
	{
		float angle = f.scalarMultiply( -forceVector );
		float size = forceVector.absolute() / mycos( angle );
		return f = Vector( f, size );
	}*/
		
}



void LevelCollision::setLadderParameters( float fDistance, Vector &vecLadderNormal, FuncLadder *pLadder )
{
	game.m_pPlayer->m_fLastLadderDistance = fDistance;
	game.m_pPlayer->m_bIsOnLadder = true;
	game.m_pPlayer->m_vecLastLadderNormal = vecLadderNormal;
	game.m_pPlayer->m_pLadder = pLadder;
	game.m_pPlayer->setMoveVector( Vector() );
}



bool LevelCollision::collisionDetection( CollisionData &collData )
{
	/*=======================================
	*	1. Pro solid model 0 budeme prochazet
	*		bsp tree.
	*======================================*/

	levelLoader.m_LevelData.current_session++;
	bool isColliding = BSPTreeCollision( collData, levelLoader.m_LevelData.nodes[ levelLoader.m_LevelData.models[0].headnode[HULL_REALWORLD] ] );


	/*=======================================
	*	2. Pote pocitame detekci pro vsechny
	*		ostatni modely (entity) krome 
	*		iniciatoru pohybu. (sam se sebou)
	*======================================*/
	for( list<CBaseEntity*>::iterator iterEntity = levelLoader.m_Entities.m_CollisionEntities.begin(); iterEntity != levelLoader.m_Entities.m_CollisionEntities.end(); ++iterEntity )
	{
		if( (*iterEntity) != collData.initiator )
		{
			if( (*iterEntity)->collisionDetection( collData ) )
			{
				collData.target = (*iterEntity);
				isColliding = true;
			}
		}
	}

	if( isColliding )
		collData.normal.normalize();


	return isColliding;
}

/** @param collData Kolizni data pro aktualni detekci kolizi.
*	@param currentNode Aktualni uzel, kde se provede detekce.
*	@return True, pokud doslo ke kolizi, jinak False.
*/
bool LevelCollision::BSPTreeCollision( CollisionData &collData, BSPNode &currentNode )
{
	if( !currentNode.bounds.compare( collData.model.m_DynamicBounds ) )
		return false;

	bool isColliding = false;
	short front = currentNode.children[0];
	if( front < 0 )	//leaf
		isColliding = BSPLeafCollision( collData, levelLoader.m_LevelData.leaves[ -(front+1) ] );
	else
		isColliding = BSPTreeCollision( collData, levelLoader.m_LevelData.nodes[front] );

	short back = currentNode.children[1];
	if( back < 0 )	//leaf
		isColliding |= BSPLeafCollision( collData, levelLoader.m_LevelData.leaves[ -(back+1) ] );
	else
		isColliding |= BSPTreeCollision( collData, levelLoader.m_LevelData.nodes[back] );

	return isColliding;
}

#define SIMPLE_COLLISION

bool LevelCollision::BSPLeafCollision( CollisionData &collData, BSPLeaf &leaf )
{
	if( leaf.contents == CONTENTS_SOLID )
		return false;

	if( !leaf.bounds.compare( collData.model.m_DynamicBounds ) )
		return false;


	bool isColliding = false;
	unsigned short *marked_surface = &levelLoader.m_LevelData.marked_faces[ leaf.first_mark_surface ];
	for( unsigned short i = 0; i < leaf.mark_surfaces_count; ++i )
	{
		Face &face = levelLoader.m_LevelData.faces[ marked_surface[i] ];
		if( face.m_Bounds.compare( collData.model.m_DynamicBounds ) )
		{
			bool collide = false;
#ifdef SIMPLE_COLLISION
			if( face.flags & FACE_SIMPLE_COLLISION )
			{
				if( face.session_key != levelLoader.m_LevelData.current_session )
				{
					face.session_key = levelLoader.m_LevelData.current_session;
					collide = boundingBoxCollisionDetection( face.m_Bounds, collData );
				}
			}
			else
#endif
			collide = face.collideWith( collData );

			/*float d = collData.distance;
			float h = collData.highestPoint;
			Uint32 start = SDL_GetTicks();
			for( int i = 0; i < game.iteration; i++ )
			{
				face.session_key--;
				face.collideWith( collData );
			}

			cout << game.iteration << " " << SDL_GetTicks() - start << endl;
			collData.highestPoint = h;
			collData.distance = d;*/
			if( collide )
			{
				collData.face = &face;
				isColliding = true;
			}
		}
		
	}
	return isColliding;
}


#include "damage_types.h"

/*	Tato funkce pocita tzv. sliding efekt, neboli 
*	klouzajici pohyb po koliznich rovinach.
*/

Vector LevelCollision::resultsVectors( const Vector &move, CollisionData &collData, CBasePhysics *pPlayer  )
{
	Vector v = move * collData.normal;
	Vector f = collData.normal * v;

	float x = collData.normal.scalarMultiply(alg::Vector(0.0f, 0.0f, 1.0f));
	
	/* pokud je uhel mezi osou x a vyslednym normalovym vektorem mensi 
	nez 40, tak hrac stoji na zemi a neni ve vzduchu */
	if( x < COLL_GROUND_DETECT_ANGLE )
	{
		Vector abc = move * Vector(0.0f, 0.0f, 1.0f);
		Vector ggg = Vector(0.0f, 0.0f, 1.0f) * abc;

		Vector xyz = collData.normal * abc;
		if( pPlayer )
		{
			pPlayer->m_bIsInAir = false;
			pPlayer->onCollide_Initiator_this( collData, true );
		}
		if( pPlayer == game.m_pPlayer )
		{
			
			((CPlayer*)pPlayer)->m_bIsOnLadder = false;	//konec ladder pohybu
		}

		

		f = Vector( f, xyz.absolute() );
		f = xyz;

		xyz.z = 0;
		float angle = f.scalarMultiply( xyz );
		float size = xyz.absolute() / mycos( angle );
		f = Vector( f, size );

/*
		float hhh = ggg.scalarMultiply( normal );
		if( hhh > 90.0f )
		{
			f = ggg;
		}*/
		//pokud padal rychle, pad mu zpusobi zraneni
		if( pPlayer && move.absolute() > PLAYER_FALL_DAMAGE_SPEED_LIMIT )
		{
			float fFallDamage = gamevarsLibrary::getfData( vardef::FALLDAMAGE_NAME );
			int iDamage = (int)((move.absolute() - PLAYER_FALL_DAMAGE_SPEED_LIMIT) * fFallDamage);
			AttackInfo attackInfo;
			attackInfo.m_iDamage = iDamage;
			attackInfo.m_iDamageType = dmg::FALL;

			pPlayer->onAttack( attackInfo );
			
			//cout << move.absolute() << " = " << iDamage << " " << endl;
		}
	}
	if( pPlayer )
		pPlayer->onCollide_Initiator_this( collData );


	return f;
}








//bool LevelCollision::rayTrace( RayTraceData &rayData )
//{
//	tmpRayData = &rayData;
//	levelLoader.m_LevelData.current_session++;
//	myPos = findLeaf( rayData.ray );
//	bool collide = BSPTreeRayTracing( levelLoader.m_LevelData.nodes[ levelLoader.m_LevelData.models[0].headnode[0] ] ); 
//
//	return collide;
//}
//
//
//bool LevelCollision::BSPTreeRayTracing( BSPNode &currentNode ) const
//{
//	float tmpDistance;
//
//	if( !currentNode.bounds.isPointInBoundingBox( tmpRayData->ray ) )
//	{
//		bool intersect = currentNode.bounds.lineIntersectDistance( tmpRayData->ray.m_vecDirection, tmpRayData->ray, tmpDistance );
//		
//		if( !intersect )
//			return false;
//		else if( tmpDistance > tmpRayData->distance )
//			return false;
//	}
//
//	bool isColliding;
//	
//	const Plane &plane = levelLoader.m_LevelData.planes[ currentNode.planenum];
//
//	float d,s;
//	if( plane.m_type <= PLANE_Z )
//	{
//		d = tmpRayData->ray[PLANE_Z] + plane.m_fDistance;
//		s = tmpRayData->vzdaleny[PLANE_Z] + plane.m_fDistance;
//	}
//	else
//	{
//		d = plane.m_vecNormal.multiply( tmpRayData->ray ) + plane.m_fDistance;
//		s = plane.m_vecNormal.multiply( tmpRayData->vzdaleny ) + plane.m_fDistance;
//	}
//
//	short front;
//	short back;
//	bool second = true;
//	if( d >= 0.0f )	//front je bliz
//	{
//		front = currentNode.children[0];
//		back = currentNode.children[1];
//		if( s >= 0.0f )
//			second = false;
//	}
//	else
//	{
//		front = currentNode.children[1];
//		back = currentNode.children[0];
//		if( s < 0.0f )
//			second = false;
//	}
//		
//
//	if( front < 0 )	//leaf
//		isColliding = BSPLeafRayTracing( levelLoader.m_LevelData.leaves[ -(front+1) ] );
//	else
//		isColliding = BSPTreeRayTracing( levelLoader.m_LevelData.nodes[ front ] );
//
//	if( !isColliding /*&& second*/ )	//pokud sme nenasli reseni
//	{
//		if( back < 0 )	//leaf
//			isColliding = BSPLeafRayTracing( levelLoader.m_LevelData.leaves[ -(back+1) ] );
//		else
//			isColliding = BSPTreeRayTracing( levelLoader.m_LevelData.nodes[ back ] );
//	}
//				
//
//
//	return isColliding;
//
//}




/************************************************************
*
*		RAY TRACING
*
************************************************************/








bool LevelCollision::BSPLeafRayTracing( BSPLeaf &leaf ) const
{
	if( leaf.contents == CONTENTS_SOLID )
		return false;

	/*bool visible = false;
	for( int i = 0; i < leaf.visible_leafs_count; ++i )
	{
		if( leaf.visible_leafs[i] == myPos )
		{
			visible = true;
			break;
		}
	}

	if( !visible )
		return false;*/

	
	float tmpDistance;
	Point result;
	unsigned short *marked_surface = &levelLoader.m_LevelData.marked_faces[ leaf.first_mark_surface ];
	for( unsigned short i = 0; i < leaf.mark_surfaces_count; ++i )
	{
		Face &face = levelLoader.m_LevelData.faces[ marked_surface[i] ];
		//if( face.m_Bounds.compare( collData.model.m_DynamicBounds ) )
		
		if( face.session_key == levelLoader.m_LevelData.current_session )
			continue;

		face.session_key = levelLoader.m_LevelData.current_session;

		if( !face.m_Bounds.isPointInBoundingBox( tmpRayData->ray ) )
		{
			bool intersect = face.m_Bounds.lineIntersectDistance( tmpRayData->ray.m_vecDirection, tmpRayData->ray, result, tmpDistance );
		
			if( !intersect )
				continue;
			else if( tmpDistance > tmpRayData->distance )
				continue;
		}


		if( face.intersectByLine( tmpRayData->ray, result ) )
		{
			if( tmpRayData->ray.m_vecDirection.multiply( result ) + tmpRayData->planeDistance > 0.0f )
			{
				tmpDistance = Vector(result - tmpRayData->ray).absolute();
				if( tmpDistance < tmpRayData->distance )
				{
					tmpRayData->distance = tmpDistance;
					tmpRayData->face = &face;
					tmpRayData->intersection = result;
					return true;
				}
			}
		}
	}
	return false;

}



//bool LevelCollision::RayTrace( int node, float min, float max )
//{
//	if( node == -1 )	//SOLID node (no faces)
//		return false;
//
//	if( node < 0 )
//	{
//		BSPLeaf &leaf = levelLoader.m_LevelData.leaves[ -(node+1) ];
//
//
//
//		float tmpDistance;
//		if( !leaf.bounds.isPointInBoundingBox( tmpRayData->ray ) )
//		{
//			bool intersect = leaf.bounds.lineIntersectDistance( tmpRayData->ray.m_vecDirection, tmpRayData->ray, tmpDistance );
//			
//			if( !intersect )
//				return false;
//			else if( tmpDistance > tmpRayData->distance )
//				return false;
//		}
//
//
//
//
//
//
//
//		return BSPLeafRayTracing( leaf );
//	}
//	
//	BSPNode *nodet = &levelLoader.m_LevelData.nodes[ node ];
//	Plane *plane = &levelLoader.m_LevelData.planes[ nodet->planenum ];
//	Point result;
//
//	planeLineIntersect( tmpRayData->ray, *plane, result );
//	float dist = Vector(result - tmpRayData->ray).absolute();
//	
//	if( tmpRayData->ray.m_vecDirection.multiply( result ) + tmpRayData->planeDistance < 0.0f )
//		dist = -dist;	
//
//	int nearN;
//	int farN;
//	if( plane->m_vecNormal.multiply( tmpRayData->ray ) + plane->m_fDistance > 0.0f )
//	{
//		nearN = nodet->children[0]; farN = nodet->children[1];
//	}
//	else
//	{
//		nearN = nodet->children[1]; farN = nodet->children[0];
//	}
//	
//	if( dist > max || dist < 0 )
//		return RayTrace( nearN, min, max );
//	else if( dist < min )
//		return RayTrace( farN, min, max );
//	else
//	{
//		if( RayTrace( nearN, min, dist ) )
//			return true;
//		return RayTrace( farN, dist, max );
//	}
//}


/** @param rayData Aktualni data ray tracingu.
*	@return True, pokud doslo k pruniku, jinak False.
*/
bool LevelCollision::rayTrace(  RayTraceData &rayData )
{
	LevelData &scene = levelLoader.m_LevelData;
	scene.current_session++;

	bool intersection = rayTrace( rayData, levelLoader.m_LevelData.models[0].headnode[0] );

	for( entityList_t::iterator iter = levelLoader.m_Entities.m_CollisionEntities.begin(); iter != levelLoader.m_Entities.m_CollisionEntities.end(); ++iter )
	{
		if( (*iter) != rayData.initiator && (*iter)->rayTrace( rayData ) )
		{
			intersection = true;
			rayData.target = (*iter);
		}
	}

	return intersection;
}

/** @param rayData Aktualni data ray tracingu.
*	@param startNode Startovni uzel ray tracingu.
*	@return True, pokud doslo k pruniku, jinak False.
*/
bool LevelCollision::rayTrace( RayTraceData &rayData, int startNode )
{
	tmpRayData = &rayData;

	return RayTrace( startNode, 0.0f, 1.0f, rayData.ray, rayData.vzdaleny );
}





/* TATO FUNKCE JE PREVZATA Z GAME TUTORIALS 
*	http://www.gametutorials.com/
*/

/* Tohle je zajimavy. */
const float kEpsilon =  0.03125f;		// This is our small number to compensate for float errors

bool LevelCollision::RayTrace(int nodeIndex, float startRatio, float endRatio, Vector vStart, Vector vEnd)
{
	// Remember, the nodeIndices are stored as negative numbers when we get to a leaf, so we 
	// check if the current node is a leaf, which holds brushes.  If the nodeIndex is negative,
	// the next index is a leaf (note the: nodeIndex + 1)
	if(nodeIndex < 0)
	{
		// If this node in the BSP is a leaf, we need to negate and add 1 to offset
		// the real node index into the m_pLeafs[] array.  You could also do [~nodeIndex].
		BSPLeaf *pLeaf = &levelLoader.m_LevelData.leaves[-(nodeIndex + 1)];
		
		float tmpDistance;
		if( !pLeaf->bounds.isPointInBoundingBox( tmpRayData->ray ) )
		{
			Point result;
			bool intersect = pLeaf->bounds.lineIntersectDistance( tmpRayData->ray.m_vecDirection, tmpRayData->ray, result, tmpDistance );
			
			if( !intersect )
				return false;
			else if( tmpDistance > tmpRayData->distance )
				return false;
		}
		
		
		
		
		return BSPLeafRayTracing( *pLeaf );
		// We have a leaf, so let's go through all of the brushes for that leaf
		//for(int i = 0; i < pLeaf->numOfLeafBrushes; i++)
		//{
		//	// Get the current brush that we going to check
		//	tBSPBrush *pBrush = &m_pBrushes[m_pLeafBrushes[pLeaf->leafBrush + i]];

		//	// This is kind of an important line.  First, we check if there is actually
		//	// and brush sides (which store indices to the normal and plane data for the brush).
		//	// If not, then go to the next one.  Otherwise, we also check to see if the brush
		//	// is something that we want to collide with.  For instance, there are brushes for
		//	// water, lava, bushes, misc. sprites, etc...  We don't want to collide with water
		//	// and other things like bushes, so we check the texture type to see if it's a solid.
		//	// If the textureType can be binary-anded (&) and still be 1, then it's solid,
		//	// otherwise it's something that can be walked through.  That's how Quake chose to
		//	// do it.

		//	// Check if we have brush sides and the current brush is solid and collidable
		//	if((pBrush->numOfBrushSides > 0) && (m_pTextures[pBrush->textureID].textureType & 1))
		//	{
		//		// Now we delve into the dark depths of the real calculations for collision.
		//		// We can now check the movement vector against our brush planes.
		//		CheckBrush(pBrush, vStart, vEnd);
		//	}
		//}

		// Since we found the brushes, we can go back up and stop recursing at this level

	}

	// If we haven't found a leaf in the node, then we need to keep doing some dirty work
	// until we find the leafs which store the brush information for collision detection.

	// Grad the next node to work with and grab this node's plane data
	BSPNode *pNode = &levelLoader.m_LevelData.nodes[nodeIndex];
	Plane *pPlane = &levelLoader.m_LevelData.planes[pNode->planenum];
	
	// Now we do some quick tests to see which side we fall on of the node in the BSP

	// Here we use the plane equation to find out where our initial start position is
	// according the the node that we are checking.  We then grab the same info for the end pos.
	float startDistance = vStart.multiply( pPlane->m_vecNormal ) + pPlane->m_fDistance;
	float endDistance = vEnd.multiply( pPlane->m_vecNormal ) + pPlane->m_fDistance;
	float offset = 0.0f;

	// If we are doing any type of collision detection besides a ray, we need to change
	// the offset for which we are testing collision against the brushes.  If we are testing
	// a sphere against the brushes, we need to add the sphere's offset when we do the plane
	// equation for testing our movement vector (start and end position).  * More Info * For
	// more info on sphere collision, check out our tutorials on this subject.

	// If we are doing sphere collision, include an offset for our collision tests below
	//if(m_traceType == TYPE_SPHERE)
	//	offset = m_traceRadius;

	// Below we just do a basic traversal down the BSP tree.  If the points are in
	// front of the current splitter plane, then only check the nodes in front of
	// that splitter plane.  Otherwise, if both are behind, check the nodes that are
	// behind the current splitter plane.  The next case is that the movement vector
	// is on both sides of the splitter plane, which makes it a bit more tricky because we now
	// need to check both the front and the back and split up the movement vector for both sides.

	// Here we check to see if the start and end point are both in front of the current node.
	// If so, we want to check all of the nodes in front of this current splitter plane.
	if(startDistance >= offset && endDistance >= offset)
	{
		// Traverse the BSP tree on all the nodes in front of this current splitter plane
		return RayTrace(pNode->children[0], startDistance, endDistance, vStart, vEnd);
	}
	// If both points are behind the current splitter plane, traverse down the back nodes
	else if(startDistance < -offset && endDistance < -offset)
	{
		// Traverse the BSP tree on all the nodes in back of this current splitter plane
		return RayTrace(pNode->children[1], startDistance, endDistance, vStart, vEnd);
	}	
	else
	{
		// If we get here, then our ray needs to be split in half to check the nodes
		// on both sides of the current splitter plane.  Thus we create 2 ratios.
		float Ratio1 = 1.0f, Ratio2 = 0.0f, middleRatio = 0.0f;
		Vector vMiddle;	// This stores the middle point for our split ray

		// Start of the side as the front side to check
		int side = pNode->children[0];

		// Here we check to see if the start point is in back of the plane (negative)
		if(startDistance < endDistance)
		{
			// Since the start position is in back, let's check the back nodes
			side = pNode->children[1];

			// Here we create 2 ratios that hold a distance from the start to the
			// extent closest to the start (take into account a sphere and epsilon).
			// We use epsilon like Quake does to compensate for float errors.  The second
			// ratio holds a distance from the other size of the extents on the other side
			// of the plane.  This essential splits the ray for both sides of the splitter plane.
			float inverseDistance = 1.0f / (startDistance - endDistance);
			Ratio1 = (startDistance - offset - kEpsilon) * inverseDistance;
			Ratio2 = (startDistance + offset + kEpsilon) * inverseDistance;
		}
		// Check if the starting point is greater than the end point (positive)
		else if(startDistance > endDistance)
		{
			// This means that we are going to recurse down the front nodes first.
			// We do the same thing as above and get 2 ratios for split ray.
			// Ratio 1 and 2 are switched in contrast to the last if statement.
			// This is because the start is starting in the front of the splitter plane.
			float inverseDistance = 1.0f / (startDistance - endDistance);
			Ratio1 = (startDistance + offset + kEpsilon) * inverseDistance;
			Ratio2 = (startDistance - offset - kEpsilon) * inverseDistance;
		}

		// Make sure that we have valid numbers and not some weird float problems.
		// This ensures that we have a value from 0 to 1 as a good ratio should be :)
		if (Ratio1 < 0.0f) Ratio1 = 0.0f;
        else if (Ratio1 > 1.0f) Ratio1 = 1.0f;

        if (Ratio2 < 0.0f) Ratio2 = 0.0f;
        else if (Ratio2 > 1.0f) Ratio2 = 1.0f;

		// Just like we do in the Trace() function, we find the desired middle
		// point on the ray, but instead of a point we get a middleRatio percentage.
		// This isn't the true middle point since we are using offset's and the epsilon value.
		// We also grab the middle point to go with the ratio.
		middleRatio = startRatio + ((endRatio - startRatio) * Ratio1);
		vMiddle = vStart + ((vEnd - vStart) * Ratio1);

		// Now we recurse on the current side with only the first half of the ray
		bool intersect = RayTrace(side, startRatio, middleRatio, vStart, vMiddle);

		// Now we need to make a middle point and ratio for the other side of the node
		middleRatio = startRatio + ((endRatio - startRatio) * Ratio2);
		vMiddle = vStart + ((vEnd - vStart) * Ratio2);

		// Depending on which side should go last, traverse the bsp with the
		// other side of the split ray (movement vector).
		if(side == pNode->children[1])
			return intersect | RayTrace(pNode->children[0], middleRatio, endRatio, vMiddle, vEnd);
		else
			return intersect | RayTrace(pNode->children[1], middleRatio, endRatio, vMiddle, vEnd);
	}
}




void LevelCollision::processPlayerUsage()
{
	RayTraceData rayData;
	rayData.initiator = game.m_pPlayer;
	rayData.distance = gamevarsLibrary::getfData( vardef::USE_DISTANCE_NAME );
	rayData.face = NULL;
	rayData.target = NULL;
	rayData.ray = Line( game.m_pPlayer->getRenderOrigin(), game.vecDir);
	rayData.entity_types = ENT_PARM_INTERACTIVE;
	rayData.vzdaleny = rayData.ray + Vector( game.vecDir, rayData.distance );
	rayData.determineDistance();

	if( rayTrace( rayData ) && rayData.target != NULL )
	{
		CBaseEntity *entity = (CBaseEntity*)rayData.target;
		entity->onUsage( rayData );	
	}
}
	
void LevelCollision::processPlayerPointing()
{
	RayTraceData rayData;
	rayData.initiator = game.m_pPlayer;
	rayData.distance = gamevarsLibrary::getfData( vardef::POINTING_DISTANCE_NAME );
	rayData.face = NULL;
	rayData.target = NULL;
	*((Point*)&rayData.ray) = game.m_pPlayer->getRenderOrigin();
	rayData.ray.m_vecDirection = game.vecDir;
	rayData.entity_types = ENT_PARM_POINTING;
	rayData.determineDistance();
	rayData.vzdaleny = rayData.ray + rayData.ray.m_vecDirection * rayData.distance;

	if( rayTrace( rayData ) && rayData.target != NULL )
	{
		CBaseEntity *entity = (CBaseEntity*)rayData.target;
		entity->onPointing( rayData );	
	}

	game.onPointingFinnished( rayData );
}

bool setMaterial( AttackInfo &attackInfo, CBaseEntity *entity, RayTraceData rayData )
{
	attackInfo.m_iMaterial = entity->getMaterial();
	if( attackInfo.m_iMaterial == MATERIAL_FACE_BASED )
	{
		if( rayData.face )
		{
			attackInfo.m_iMaterial = ((Face*)rayData.face)->getMaterial();
		}
		else
			return false;
	}
	return true;
}


/** @param attackInfo Informace o projektilu. */	
bool LevelCollision::processShooting( AttackInfo &attackInfo )
{
	RayTraceData rayData;
	rayData.initiator = attackInfo.m_pInitiator;
	rayData.distance = attackInfo.m_fDistance;
	rayData.face = NULL;
	rayData.target = NULL;
	*((Point*)&rayData.ray) = attackInfo.m_Start;
	rayData.ray.m_vecDirection = attackInfo.m_vecDirection;
	rayData.entity_types = ENT_PARM_SHOOTABLE;
	rayData.determineDistance();
	rayData.vzdaleny = rayData.ray + attackInfo.m_vecDirection * rayData.distance;

	if( rayTrace( rayData )  )
	{
		attackInfo.m_End = rayData.intersection;
		attackInfo.face = rayData.face;
		if( rayData.target )
		{
			CBaseEntity *entity = (CBaseEntity*)rayData.target;
			setMaterial( attackInfo, entity, rayData );
			entity->onAttack( attackInfo );	

		}
		else //shot to wall = default 
		{
			if( rayData.face )
				attackInfo.m_iMaterial = ((Face*)rayData.face)->getMaterial();
			else
				attackInfo.m_iMaterial = MATERIAL_CONCRETE;
		
		}

	}

	if( rayData.face != NULL || rayData.target != NULL )
		return true;
	return false;
}

/** @param box AABB , ke kteremu se ma najit nejblizsi box (box presne vymezuje prostor, ve kterem se ma hledat.
*	@param dist Vzdalenost od facu (na zacatku je to maximalni mozna vzdalenost.
*	@return Face, ktery je nejblizsi, jinak NULL.
*/
Face *LevelCollision::findNearestFace( const BoundingBox &box, float &dist ) const
{
	Face *face = NULL;
	for( size_t i = 0; i < levelLoader.m_LevelData.models_count; ++i )
	{
		Face *pFace = BSPTreeFindNearestBox( levelLoader.m_LevelData.models[i].headnode[HULL_REALWORLD], box, dist );
		if( pFace )
			face = pFace;
	}
	return face;
}



Face *LevelCollision::BSPTreeFindNearestBox( int node, const BoundingBox &box, float &dist  ) const
{
	BSPNode &bspNode = levelLoader.m_LevelData.nodes[node];
	
	if( !bspNode.bounds.compare( box ) )
		return NULL;
	
	Face *pFace = NULL;
	for( unsigned short i = 0; i < bspNode.numfaces; ++i )
	{
		Face &face = levelLoader.m_LevelData.faces[ bspNode.firstface + i];
		
		if( !face.m_Bounds.compare( box ) )
			continue;

		Plane &plane = levelLoader.m_LevelData.planes[ face.planenum ];
		Vector normal = plane.m_vecNormal;
		if( face.side )
			normal = -normal;
		
		Point centerPoint = box.getCenterPoint();

		
		float d = normal.x * centerPoint.x + normal.y * centerPoint.y + normal.z * centerPoint.z + plane.m_fDistance;

		if( d > 0.0f && d < dist )
		{
			dist = d;
			pFace = &face;
		}
	}

	Face *pNewFace = NULL;
	for( int i = 0; i < 2; ++i )
	{
		if( bspNode.children[i] >= 0 )	//node
		{
			pNewFace = BSPTreeFindNearestBox( bspNode.children[i], box, dist );
			if( pNewFace )
				pFace = pNewFace;
		}
	}

	return pFace;
}

