/*
 **************************************************************************************************
 *	For camera visibility tests.
 **************************************************************************************************
 */
 
#define D_CineInfo_StructureDefine
 
#include "AIUseCPA.h"
#include "GEO.h"
#include "GLI.h"
#include "COL.h"
#include "PO.h"
/*XB980821*/
#ifndef D_THROW_IPO
#include "IPO.h"
#endif /* D_THROW_IPO */
/*End XB*/
/* Shaitan Clean Env {*/
/*#include "ENV.h"*/
/*End Shaitan Clean Env }*/
#include "SND.h"
/* Shaitan Clean Env {*/
/*#include "SRF.h"*/
/*End Shaitan Clean Env }*/
#include "SCT.h"
#include "GAM.h"
 
#include "CAM_Base.h"
#include "CAM_Vis.h"
#include "CAM_Tool.h"

#include <ASSERT.h> 
/*XB*/
#if defined(U64)
void qsort(void *,size_t,size_t,int (*)(const void *,const void *));
#endif
/*End XB*/
 
/*
 **************************************************************************************************
 **************************************************************************************************
	VISIBILITY : FILL ARRAY OF SIN AND COS
 **************************************************************************************************
 **************************************************************************************************
 */
 
/* Array that contains angles to test in both normal and failure mode */
unsigned short		guw_NbVectorArrayCpt = 0;
typedef struct
{
	MTH3D_tdstVector	stDir;
	MTH_tdxReal			xPound;
} tdst_PosPound;
tdst_PosPound gast_VectorArrayCpt[256];
 
/* To compute pound : Contains the ideal parameters */
struct tdstInternalStructurCineinfo_		gst_Ideal;
 
/* To indicate that the ray to test visibility can test static hierarchy */
char	cCanTestStatic = 1;
 
/*
 **************************************************************************************************
 **************************************************************************************************
	VISIBILITY : BASIC TEST OF VISIBILITY
 **************************************************************************************************
 **************************************************************************************************
 */
 
 
/*
 *=================================================================================================
 *	Is sector is in list ?
 *=================================================================================================
 */
char CAM_fn_cIsSectorInCollisionList(HIE_tdxHandleToSuperObject	_hFirstSector, HIE_tdxHandleToSuperObject _hTestSector)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	int												i;
	SECT_tdxHandleOfElementLstCollisionInteraction	hList;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	SECT_M_ForEachCollisionNodeInCollisionInteractionList(_hFirstSector, hList, i)
	{
		if(SECT_GetSectorInCollisionList(hList) == _hTestSector)
			return 1;
	}
	return 0;
}
 
/*
 *=================================================================================================
 * Truncate a vector element to avoid computing errors to change the validity of a segment.
 *=================================================================================================
 */
#define M_Trunc(Vector, Type)\
{\
	int _iTemp_;\
	_iTemp_ = (int) MTH_M_xMul(MTH3D_M_xGet##Type##ofVector(&Vector), 1000);\
	MTH3D_M_vSet##Type##ofVector(&Vector, MTH_M_xDiv((MTH_tdxReal) _iTemp_, 1000));\
}
 
/*
 *=================================================================================================
 *	Visibility test with a segment in dynamic world.
 *	We only test actors which priority is fewer than camera one.
 *=================================================================================================
 */
char CAM_fn_cSendSegmentInDynamicWorld
(
	MTH3D_tdstVector			*_p_stSource, 
	MTH3D_tdstVector			*_p_stDest,
	HIE_tdxHandleToSuperObject  _hSector,
	HIE_tdxHandleToSuperObject  _hCamera
)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	long								j;
	SECT_tdxHandleOfElementLstCharacter hCharacterList;
	HIE_tdxHandleToSuperObject			hTreatCharacter;
	unsigned char						ucCameraPriority;
	MTH3D_tdstVector					stTempVector;
	MS_tdxHandleToCollSet				hCollset;
	MS_tdxHandleToStandardGame			hStdGame;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/* Get camera priority to test character one with */
	ucCameraPriority = CS_fn_ucGetCharacterPriority(M_GetMSHandle(_hCamera, CollSet));
	MTH3D_M_vSubVector(&stTempVector, _p_stDest, _p_stSource);
	SECT_M_ForEachCharListInSector(_hSector, hCharacterList, j)
	{
		hTreatCharacter = SECT_GetCharacterInList(hCharacterList);
		hCollset = M_GetMSHandle(hTreatCharacter, CollSet);
		hStdGame = M_GetMSHandle(hTreatCharacter, StandardGame);

		if ( (hCollset && (CS_fn_ucGetCharacterPriority(hCollset) == ucCameraPriority)) ||
			 (hStdGame && (fn_ulStandardGameGetCustomBits(hStdGame) & GAM_C_CustBitCameraHit)) )
		/*if (!hCollset || (CS_fn_ucGetCharacterPriority(hCollset) == ucCameraPriority))*/
		{
			/*if (CS_fn_ucGetCharacterPriority(hCollset) == ucCameraPriority)*/
			{
				/* Perso must have an anim, so must have some children */
				if(HIE_fn_lGetNumberOfChildren(hTreatCharacter))
				{
					if
					(
						HIE_bDetectIntersectSegmentWithSuperObject
						(
							_p_stSource,
/*							_p_stDest,*/
							&stTempVector,
							hTreatCharacter
						)
					)
						return 1;
				}
			}
		}
	}
	return 0;
}
 
/*
 *=================================================================================================
 * Visibility test with a segment in static world and dynamic world.
 * We test :
 * - In current actor sector
 * - In current camera sector
 * - In the sectors of collision list of actor
 * - In the sectors of collision list of camera
 *=================================================================================================
 */
char CAM_fn_cSendSegment
(
	CAM_tdstUpdateCamera	*_p_stStruct,
	MTH3D_tdstVector		*_p_stSource, 
	MTH3D_tdstVector		*_p_stDest
)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector								stTempVector;
	int												i;
	MS_tdxHandleToSectInfo							hPersoCineinfo;
	MS_tdxHandleToSectInfo							hCamCineinfo;
	SECT_tdxHandleOfElementLstCollisionInteraction	hList;
	MTH3D_tdstVector								stSource, stDest;
	HIE_tdxHandleToSuperObject						hPersoTargeted;
	HIE_tdxHandleToSuperObject						hCamera;
	HIE_tdxHandleToSuperObject						hCurrentSectorPerso;
	HIE_tdxHandleToSuperObject						hCurrentSectorCamera;
	HIE_tdxHandleToSuperObject						hSectorInCollision;
	char											cVisibilityWithDynWorld;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	if(MTH3D_M_bEqualVector(_p_stSource, _p_stDest))
		return 0;
 
	/*
	 * Determin if we must test in dynamic world.
	 */
	cVisibilityWithDynWorld = _p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_NoVisibilityWithDynHie ? 0 : 1;
 
	/*
	 * We truncate source and destination vector to avoid computing errors.
	 * Positions that are closer from each other are then considered to be equal.
	 */
	/*MTH3D_M_vCopyVector(&stSource, _p_stSource);
	MTH3D_M_vCopyVector(&stDest, _p_stDest);*/
	stSource = *_p_stSource;
	stDest = *_p_stDest;
	M_Trunc(stSource, X);
	M_Trunc(stSource, Y);
	M_Trunc(stSource, Z);
	M_Trunc(stDest, X);
	M_Trunc(stDest, Y);
	M_Trunc(stDest, Z);
 
	/*
	 *	Retrieve sector of camera and targeted perso.
	 */
	hCamera = _p_stStruct->hSuperObjCamera;
	hCamCineinfo = M_GetMSHandle(hCamera, SectInfo);
	hCurrentSectorPerso = hCurrentSectorCamera = fn_h_SectInfoGetCurrentSector(hCamCineinfo);
 
	hPersoTargeted = _p_stStruct->hCineinfoWork->hSuperObjectTargeted;
	if(hPersoTargeted)
	{
		hPersoCineinfo = M_GetMSHandle(hPersoTargeted, SectInfo);
		hCurrentSectorPerso = fn_h_SectInfoGetCurrentSector(hPersoCineinfo);
	}
 
	/* 
	 *	Compute segment.
	 */
	MTH3D_M_vSubVector(&stTempVector, &stDest, &stSource);
 
	/* 
	 * Current actor sector
	 *----------------------
	 */
 
	/* STATIC */
	if
	(
		cCanTestStatic
	&&	HIE_bDetectIntersectSegmentWithSuperObject
		(
			&stSource,
/*			&stDest,*/
			&stTempVector,
			hCurrentSectorPerso
		)
	)
		return 1;
 
	/* DYNAMIC */
	if
	(	
		(cVisibilityWithDynWorld)
	&&	CAM_fn_cSendSegmentInDynamicWorld
		(
			&stSource,
			&stDest,
			hCurrentSectorPerso,
			hCamera
		)
	)
		return 1;
 
	/* 
	 * Current camera sector (if it's not the same than actor one)
	 *-------------------------------------------------------------
	 */
	if(hCurrentSectorPerso != hCurrentSectorCamera)
	{
		/* STATIC */
		if		
		(
			cCanTestStatic
		&&	HIE_bDetectIntersectSegmentWithSuperObject
			(
				&stSource,
/*				&stDest,*/
				&stTempVector,
				hCurrentSectorCamera
			)
		)
			return 1;
 
		/* DYNAMIC */
		if
		(
			(cVisibilityWithDynWorld)
		&&	CAM_fn_cSendSegmentInDynamicWorld
			(
				&stSource,
				&stDest,
				hCurrentSectorCamera,
				hCamera
			)
		)
			return 1;
	}
 
	/* 
	 * Collision sectors of perso
	 *----------------------------
	 */
	SECT_M_ForEachCollisionNodeInCollisionInteractionList
	(
		hCurrentSectorPerso,
		hList,
		i
	)
	{
		hSectorInCollision = SECT_GetSectorInCollisionList(hList);
 
		/* Sector is not camera one (already treated) */
		if(hSectorInCollision != hCurrentSectorCamera)
		{
			/* STATIC */
			if 
			(
				cCanTestStatic
			&&	HIE_bDetectIntersectSegmentWithSuperObject
				(
					&stSource,
/*					&stDest,*/
					&stTempVector,
					hSectorInCollision
				)
			)
				return 1;
 
			/* DYNAMIC */
			if
			(
				(cVisibilityWithDynWorld)
			&&	CAM_fn_cSendSegmentInDynamicWorld
				(
					&stSource,
					&stDest,
					hSectorInCollision,
					hCamera
				)
			)
				return 1;
		}
	}
 
	/* 
	 * Collision sectors of camera
	 *-----------------------------
	 */
	if(hCurrentSectorPerso != hCurrentSectorCamera)
	{
		SECT_M_ForEachCollisionNodeInCollisionInteractionList
		(
			hCurrentSectorCamera,
			hList,
			i
		)
		{
			hSectorInCollision = SECT_GetSectorInCollisionList(hList);
			if
			(
				/* Sector is not in perso list */
				!CAM_fn_cIsSectorInCollisionList	
				(
					hCurrentSectorPerso,
					hSectorInCollision
				)
				/* Sector is not perso one */
			&&	(hSectorInCollision != hCurrentSectorPerso)
			)
			{
				/* STATIC */
				if
				(
					cCanTestStatic
				&&	HIE_bDetectIntersectSegmentWithSuperObject
					(
						&stSource,
/*						&stDest,*/
						&stTempVector,
						hSectorInCollision
					)
				)
					return 1;
 
				/* DYNAMIC */
				if
				(
					(cVisibilityWithDynWorld)
				&&	CAM_fn_cSendSegmentInDynamicWorld
					(
						&stSource,
						&stDest,
						hSectorInCollision,
						hCamera
					)
				)
					return 1;
			}
		}
	}
 
	return 0;
}
 
/*
 *=================================================================================================
 *	Visibility with multi segment.
 *	This function send "_lNbRay" segment and failed with the first one.
 *=================================================================================================
 */       
char CAM_fn_cSendMultiSegment
(
	CAM_tdstUpdateCamera	*_p_stStruct,
	MTH3D_tdstVector		*_p_stSourcePos,
	MTH3D_tdstVector		*_p_stTargetPos,
	MTH_tdxReal				_xRadiusCamera,
	MTH_tdxReal				_xRadiusTarget,
	long					_lNbRay
)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector		stDirection;
	MTH3D_tdstVector		stPositionSource, stPositionSource1;
	MTH3D_tdstVector		stPositionTarget, stPositionTarget1;
	MTH_tdstRotation		stRotation;
	MTH3D_tdstVector		stAxisZ, stVector;
	long					i;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/*
	 *	Center if just one ray.
	 */
	if(_lNbRay == 1)
	{
		return CAM_fn_cSendSegment
		(
			_p_stStruct, 
			_p_stSourcePos, 
			_p_stTargetPos
		);
	}
 
	/*
	 * Compute the first vector (ray of a sphere from center of source to _xRadiusCamera).
	 */
	MTH3D_M_vSubVector(&stDirection, _p_stTargetPos, _p_stSourcePos);
	MTH3D_M_vNormalizeVector(&stDirection, &stDirection);
 
	MTH3D_M_vSetBaseKVector(&stAxisZ);
	MTH3D_M_vCrossProductVectorWithoutBuffer(&stVector, &stDirection, &stAxisZ);
	if(MTH3D_M_bIsNullVector(&stVector))
	{
		MTH3D_M_vSetBaseIVector(&stAxisZ);
		MTH3D_M_vCrossProductVectorWithoutBuffer(&stVector, &stDirection, &stAxisZ);
	}
	MTH3D_M_vNormalizeVector(&stVector, &stVector);
	MTH3D_M_vMulScalarVector(&stVector, _xRadiusCamera, &stVector);
 
	/* Rotation axis is vector source to target */
	MTH_M_RotationSetAxis(&stRotation, &stDirection);
 
	/* Normal rotation angle is 2Pi / Nb ray */
	MTH_M_xRotationSetAngle(&stRotation, MTH_M_xDiv(MTH_C_2Pi, _lNbRay));
 
	for(i = 0; i < _lNbRay; i++)
	{
		/* Compute source point position */
		/*MTH3D_M_vCopyVector(&stPositionSource, &stVector);
		MTH3D_M_vMulScalarVector(&stPositionSource, _xRadiusCamera, &stPositionSource);*/
		MTH3D_M_vMulScalarVector(&stPositionSource, _xRadiusCamera, &stVector); /* remplace les 2 lignes du dessus*/
		MTH3D_M_vAddVector(&stPositionSource1, &stPositionSource, _p_stSourcePos);
 
		/* Compute target point position */
		/*MTH3D_M_vCopyVector(&stPositionTarget, &stVector);
		MTH3D_M_vMulScalarVector(&stPositionTarget, _xRadiusTarget, &stPositionTarget);*/
		MTH3D_M_vMulScalarVector(&stPositionTarget, _xRadiusTarget, &stVector); /* remplace les 2 lignes du dessus*/
		MTH3D_M_vAddVector(&stPositionTarget1, &stPositionTarget, _p_stTargetPos);
 
		/* Throw a ray */
		if
		(
			CAM_fn_cSendSegment
			(
				_p_stStruct, 
				&stPositionSource1, 
				&stPositionTarget1
			)
		) 
			return 1;
 
		/* Rotate to find the new position */
		MTH_p_stRotationMulVector(&stRotation, &stVector, &stVector);
	}
	return 0;
}
 
 
/*
 *=================================================================================================
 * Compute impact point of camera from a source to a dest point.
 * There must be an impact !!! (don't call this function to test a collision, but when a collision
 * occured to retreive impact point).
 *=================================================================================================
 */
 
#ifndef _FIRE_DEADCODE_U64_ /* Added by RUC */
char CAM_fn_cComputeImpactPointWithCameraSphere
(
	CAM_tdstUpdateCamera	*_p_stStruct,
	MTH3D_tdstVector		*_p_stSource,
	MTH3D_tdstVector		*_p_stTarget,
	MTH3D_tdstVector		*_p_stImpact
)
{
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
	POS_tdstCompletePosition	stMatrix1, stMatrix2;
	/*HIE_tdxHandleToSuperObject	hTmpSector;*/
	char						cRes1;
	DNM_tdstMecObstacle			stObstacle;
	MTH_tdxReal					xNorm;
	/*MS_tdxHandleToSectInfo hSectInfo = M_GetMSHandle(_p_stStruct->hSuperObjCamera, SectInfo);*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 
	if(_p_stImpact == NULL)
		return 1;
 
	/*
	 * Test with camera sphere
	 *-------------------------
	 */
	POS_fn_vSetIdentityMatrix(&stMatrix1);
	POS_fn_vSetIdentityMatrix(&stMatrix2);
	POS_fn_vSetTranslationVector(&stMatrix1, _p_stSource);
	POS_fn_vSetTranslationVector(&stMatrix2, _p_stTarget);
 
	/* Save sector */ /*INUTILE CAR LE TEST DE COLLISION NE MET PLUS A JOUR LES INFOS DE SECTEUR*/
	/*hTmpSector = fn_h_SectInfoGetCurrentSector(hSectInfo);*/
	cRes1 = (char) CAM_lHaveITakeSThgIntoTheMug
	(
		&stObstacle,
		_p_stStruct->hSuperObjCamera,            
		&stMatrix1,  
		&stMatrix2
	);
 
	/* Restore sector */
	/*fn_vSectInfoSetCurrentSector(hSectInfo, hTmpSector);*/
	if(!cRes1)
	{
		MTH3D_M_vNullVector(_p_stImpact);
		return 0;
	}
 
	/*
	 * Compute impact point
	 *----------------------
	 */
	MTH3D_M_vSubVector(_p_stImpact, _p_stTarget, _p_stSource);
	MTH3D_M_vMulScalarVector(_p_stImpact, DNM_M_xObstacleGetRate(&stObstacle), _p_stImpact);
	xNorm =  MTH_M_xSub(MTH3D_M_xNormVector(_p_stImpact), MTH_M_xFloatToReal(0.02f));
	if (MTH_M_bLessZero(xNorm))
	{
		MTH3D_M_vCopyVector(_p_stImpact,_p_stSource);
	}
	else
	{
		MTH3D_M_vNormalizeVector(_p_stImpact, _p_stImpact);
		MTH3D_M_vMulScalarVector(_p_stImpact, xNorm, _p_stImpact);
		MTH3D_M_vAddVector(_p_stImpact, _p_stImpact, _p_stSource);
	}
	return 1;
}
#endif /* _FIRE_DEADCODE_U64_ */ /* Added by RUC */
 
 
 
/*
 **************************************************************************************************
 **************************************************************************************************
	VISIBILITY : MORE HIGH LEVEL TEST OF VISIBILITY
 **************************************************************************************************
 **************************************************************************************************
 */
 
 
/*
 *=================================================================================================
 *	Can't camera go to a given point ?
 *=================================================================================================
 */       
char CAM_fn_cCameraCantGo
(
	CAM_tdstUpdateCamera	*_p_stStruct,
	MTH3D_tdstVector		*_p_stSourcePos,
	MTH3D_tdstVector		*_p_stTargetPos
)
{
	return CAM_fn_cSendMultiSegment
	(
		_p_stStruct,
		_p_stSourcePos,
		_p_stTargetPos,
		CAM_C_xRayCameraSphereToGo1,
		CAM_C_xRayCameraSphereToGo2,
		CAM_C_xNumRayCameraCanGo
	);
}
 
/*
 *=================================================================================================
 *	Can't camera go to a given point ? For failure, we must be sure that this will be correct.
 *=================================================================================================
 */       
char CAM_fn_cCameraCantGoFailure
(
	CAM_tdstUpdateCamera	*_p_stStruct,
	MTH3D_tdstVector		*_p_stSourcePos,
	MTH3D_tdstVector		*_p_stTargetPos
)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector	stTempVector;
	MTH_tdxReal			xNorm;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/* First the destination point is farest with the ZDM ray of camera */
	MTH3D_M_vSubVector(&stTempVector, _p_stTargetPos, _p_stSourcePos);
	xNorm = MTH3D_M_xNormVector(&stTempVector);
	xNorm = MTH_M_xAdd(xNorm, MTH_M_xFloatToReal(0.35f));
	MTH3D_M_vNormalizeVector(&stTempVector, &stTempVector);
	MTH3D_M_vMulScalarVector(&stTempVector, xNorm, &stTempVector);
	MTH3D_M_vAddVector(&stTempVector, &stTempVector, _p_stSourcePos);
 
	/* First the few ray */
	return CAM_fn_cSendMultiSegment
	(
		_p_stStruct,
		_p_stSourcePos,
		&stTempVector,
		CAM_C_xRayCameraSphereToGo1,
		CAM_C_xRayCameraSphereToGo2,
		CAM_C_xNumRayCameraCanGo
	);
}
 
/*
 *=================================================================================================
 *	Can't camera see a given point ?
 *=================================================================================================
 */       
char CAM_fn_cCameraCantSee
(
	CAM_tdstUpdateCamera	*_p_stStruct,
	MTH3D_tdstVector		*_p_stSourcePos,
	MTH3D_tdstVector		*_p_stTargetPos
)
{
	return CAM_fn_cSendMultiSegment
	(
		_p_stStruct,
		_p_stSourcePos,
		_p_stTargetPos,
		CAM_C_xRayCameraSphereToSee1,
		CAM_C_xRayCameraSphereToSee2,
		CAM_C_xNumRayCameraCanSee
	);
}
 
/*
 *=================================================================================================
 *	Is wanted pos not good ?
 *	This function determis if a given position is correct.
 *=================================================================================================
 */       
char CAM_fn_cIsWantedPosIncorrect
(
	CAM_tdstUpdateCamera	*_p_stStruct, 
	MTH3D_tdstVector		*_p_stWantedPos,
	char					_cMode
)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector	stTempVector;
	char				cCant;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/* 
	 *	Wanted too short from target : Exit 
	 */
	MTH3D_M_vSubVector(&stTempVector, _p_stWantedPos, &_p_stStruct->stTargetedPersoRealPos);
	if(MTH_M_bLess(MTH3D_M_xNormVector(&stTempVector), CAM_C_xMinDistPersoToComputePos))
		return 1;
 
	/*
	 *	Test visibility : Target to real wanted 
	 *------------------------------------------
	 */
	if(_cMode & C_mode_MUSTSEE)
	{
		/*
		 *	In failure mode, we send a ray cause we want to be sure that position 
		 *	is not already in collision.
		 *	Else we send a normal ray.
		 */
		cCant = ((_cMode & C_mode_FAILURE) ? CAM_fn_cCameraCantGoFailure : CAM_fn_cCameraCantSee) (_p_stStruct, &_p_stStruct->stTargetedPersoRealPos, _p_stWantedPos);
		/*EQUIVALENT A CECI, MAIS GENERE NORMALEMENT MOINS DE CODE
		if(_cMode & C_mode_FAILURE)
			cCant = CAM_fn_cCameraCantGoFailure(_p_stStruct, &_p_stStruct->stTargetedPersoRealPos, _p_stWantedPos);
		else
			cCant = CAM_fn_cCameraCantSee(_p_stStruct, &_p_stStruct->stTargetedPersoRealPos, _p_stWantedPos);*/
 
		if(cCant)
			return 1;
	}
 
	/*
	 *	Test visibility : Current to real wanted position 
	 *----------------------------------------------------
	 */
	if(_cMode & C_mode_MUSTGO) 
	{
		cCant = CAM_fn_cCameraCantGo(_p_stStruct, &_p_stStruct->stCurrentCameraPos, _p_stWantedPos);
 
		if(cCant)
			return 1;
	}
 
	return 0;
}
 
 
/*
 **************************************************************************************************
 **************************************************************************************************
 COMPUTE A NEW GOOD IDEAL POS
 **************************************************************************************************
 **************************************************************************************************
 */
 
 
/*
 *=================================================================================================
 *	Compute a good position with a given direction.
 *=================================================================================================
 */
char CAM_fn_cFindCorrectPosWithDir
(
	CAM_tdstUpdateCamera	*_p_stStruct, 
	MTH3D_tdstVector		*_p_stDir, 
	MTH3D_tdstVector		*_p_stCompute, 
	char					_cMode
)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector	stTestedPos;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/* 
	 *	Compute new pos with direction
	 */
	MTH3D_M_vAddVector(&stTestedPos, _p_stDir, &_p_stStruct->stTargetedPersoPos);
 
	/* 
	 *	Test position. 
	 */
	if(CAM_fn_cIsWantedPosIncorrect(_p_stStruct, &stTestedPos, _cMode))
		return 0;
	/* 
	 *	Position correct : Success.
	 */
	else
	{
		MTH3D_M_vCopyVector(_p_stCompute, &stTestedPos);
		return 1;
	}
}
 
/*
 *=================================================================================================
 * Compute the pound of a position. Less it is, better it is.
 *=================================================================================================
 */
MTH_tdxReal	CAM_fnx_ComputePosPound(CAM_tdstUpdateCamera *_pst_Struct, MTH3D_tdstVector *_pst_Dir)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH_tdxReal								x_Pound, x_Angle, x_Angle1;
	MTH_tdxReal								x_P1, x_P2, x_P3;
	MTH3D_tdstVector						st_TempVector;
	struct tdstInternalStructurCineinfo_	*pst_Memo;
	struct tdstInternalStructurCineinfo_	st_Temp;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/*
	 *	Compute requested position.
	 */
	MTH3D_M_vAddVector(&st_TempVector, &_pst_Struct->stTargetedPersoPos, _pst_Dir);
 
	/* 
	 *	Compute parameters of camera for that position. We compute the parameters in a private
	 *	internal structure (cause we don't want to save them). 
	 */
	pst_Memo = _pst_Struct->hCineinfoWork;
 
	st_Temp.hSuperObjectTargeted = _pst_Struct->hCineinfoWork->hSuperObjectTargeted;
	st_Temp.hSecondSuperObjectTargeted = _pst_Struct->hCineinfoWork->hSecondSuperObjectTargeted;
	st_Temp.cChannel = _pst_Struct->hCineinfoWork->cChannel;
 
	_pst_Struct->hCineinfoWork = &st_Temp;
	CAM_fn_vComputeCineinfoWorkForAPosition(_pst_Struct, &st_TempVector);
	_pst_Struct->hCineinfoWork = pst_Memo;
 
	/*
	 *	Compute pound of that position depending on ideal parameters.
	 */
 
	/* Alpha */
	x_Angle = MTH_M_bGreater(gst_Ideal.xAngleAlpha, MTH_C_Pi) ? MTH_M_xSub(MTH_C_2Pi, gst_Ideal.xAngleAlpha) : gst_Ideal.xAngleAlpha;
	x_Angle1 = MTH_M_bGreater(st_Temp.xAngleAlpha, MTH_C_Pi) ? MTH_M_xSub(MTH_C_2Pi, st_Temp.xAngleAlpha) : st_Temp.xAngleAlpha;
	x_P1 = MTH_M_xAbs(MTH_M_xSub(x_Angle, x_Angle1));
 
	x_P1 = MTH_M_xLongToReal(MTH_M_xRealToLongRound(MTH_M_xMul(x_P1, MTH_M_xFloatToReal(100))));
	x_P1 = MTH_M_xMul(x_P1, MTH_M_xFloatToReal(0.01f));
 
	/* Theta */
	x_Angle = MTH_M_bGreater(gst_Ideal.xAngleTheta, MTH_C_Pi) ? MTH_M_xSub(MTH_C_2Pi, gst_Ideal.xAngleTheta) : gst_Ideal.xAngleTheta;
	x_Angle1 = MTH_M_bGreater(st_Temp.xAngleTheta, MTH_C_Pi) ? MTH_M_xSub(st_Temp.xAngleTheta, MTH_C_2Pi) : st_Temp.xAngleTheta;
	x_P2 = MTH_M_xAbs(MTH_M_xSub(x_Angle, x_Angle1));
 
	x_P2 = MTH_M_xLongToReal(MTH_M_xRealToLongRound(MTH_M_xMul(x_P2, MTH_M_xFloatToReal(100))));
//	x_P2 = MTH_M_xMul(x_P2, MTH_M_xFloatToReal(0.01f));
	x_P2 = MTH_M_xMul(x_P2, MTH_M_xFloatToReal(0.10f));
 
	/* Distance to target */
	x_P3 = MTH_M_xAbs(MTH_M_xSub(gst_Ideal.xDistMin, st_Temp.xDistMin));
 
	x_P3 = MTH_M_xLongToReal(MTH_M_xRealToLongRound(MTH_M_xMul(x_P3, MTH_M_xFloatToReal(100))));
	x_P3 = MTH_M_xMul(x_P3, MTH_M_xFloatToReal(0.01f));
 
	/*
	 * Compute final pound.
	 */
	x_Pound = MTH_M_xAdd(x_P1, x_P2);
	x_Pound = MTH_M_xAdd(x_Pound, MTH_M_xMul(x_P3, MTH_M_xFloatToReal(0.1f)));
 
	return x_Pound;
}
 
/*
 *=================================================================================================
 * Determin if a vector need to be eliminate.
 *=================================================================================================
 */
char CAM_fnc_IsDirectionCorrect
(
	CAM_tdstUpdateCamera	*_p_stStruct, 
	MTH3D_tdstVector		*_pst_Dir, 
	MTH_tdxReal				_x_Pound, 
	MTH_tdxReal				_x_PoundRef,
	char					_c_Mode
)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector	stTempVector;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/* Compute real position */
	MTH3D_M_vAddVector(&stTempVector, _pst_Dir, &_p_stStruct->stTargetedPersoRealPos);
 
	/* 
	 * If we want a "more correct" position, assure that this new 
	 * position is better than current one.
	 *------------------------------------------------------------
	 */
	if(_c_Mode & C_mode_MORE_CORRECT)
	{
		/* More bad than current pos */
		if(MTH_M_bGreaterEqual(_x_Pound, _x_PoundRef))
			return 0;
 
		/* Too close from current pos */
		_x_PoundRef = MTH_M_xSub(_x_PoundRef, _x_Pound);
		if(MTH_M_bLess(_x_PoundRef, MTH_M_xFloatToReal(0.05f)))
			return 0;
 
		/* Position must reset last failed wanted */
		if(!CAM_fn_cCanResetConstraintMoveVisFailure(_p_stStruct, &stTempVector))
			return 0;
	}
 
	/*
	 * Norm of vector too small (to near from perso)
	 *-----------------------------------------------
	 */
 
	if(MTH_M_bLess(MTH3D_M_xNormVector(_pst_Dir), CAM_C_xMinDistPersoToComputePos))
		return 0;
 
	/*
	 * Vectors too near from wanted camera position
	 *-----------------------------------------------
	 */
	if
	(
		(!(_c_Mode & C_mode_FAILURE))
	&&	CAM_fn_bEqualVectorWithEpsilon
		(
			&stTempVector,
			&_p_stStruct->stWantedCameraPos,
			CAM_C_xEpsilonForEqualVectors
		)
	)
		return 0;
 
	/*
	 * Vectors too near from current camera position
	 *-----------------------------------------------
	 */
	if
	(
		((!(_c_Mode & C_mode_FAILURE)) || (_c_Mode & C_mode_MORE_CORRECT))
	&&	CAM_fn_bEqualVectorWithEpsilon
		(
			&stTempVector,
			&_p_stStruct->stCurrentCameraPos,
			CAM_C_xEpsilonForEqualVectors
		)
	)
		return 0;
 
	return 1;
}
 
/*
 *=================================================================================================
 * Compare two positions.
 * To sort vectors depending on their pounds (less to greater).
 *=================================================================================================
 */
int CAM_fni_ComparePositions(const void *_pv_Elem1, const void *_pv_Elem2)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH_tdxReal		xPound1, xPound2;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	xPound1 = ((tdst_PosPound *) _pv_Elem1)->xPound;
	xPound2 = ((tdst_PosPound *) _pv_Elem2)->xPound;
	return (MTH_M_bGreater(xPound2, xPound1)) ? -1 : (MTH_M_bEqual(xPound2, xPound1)) ? 0 : 1;
}
 
/*
 *=================================================================================================
 * Fill array gast_VectorArrayCpt to set angles to test.
 *=================================================================================================
 */
void CAM_fn_vFillVectorArray(CAM_tdstUpdateCamera *_p_stStruct, char _cMode)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector						stAxis, stTempVector, *p_stDir;
	MTH_tdxReal								xAngle, xBaseAngle, xNormIdeal, xSaveTheta;
	unsigned long							ulCounter, ulMax, ulAngle;
	struct tdstInternalStructurCineinfo_	stTemp;
	CAM_tdstUpdateCamera					stStruct;
	enum e_ucCameraStates_					eState;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/*
	 *	Init structures for compute.
	 */
	CAM_fn_vInitCameraStructure(_p_stStruct->hSuperObjCamera, &stStruct);
	stStruct.hCineinfoWork = &stTemp;
	memcpy(&stTemp, &gst_Ideal, sizeof(struct tdstInternalStructurCineinfo_));
	CAM_fn_vComputeReferencePoint(&stStruct);
 
	/*
	 *	Get ideal norm.
	 */
	MTH3D_M_vSubVector(&stTempVector, &_p_stStruct->stRealWantedCameraPos, &_p_stStruct->stTargetedPersoPos);
	xNormIdeal = MTH3D_M_xNormVector(&stTempVector);
 
	/*
	 *	Get axis of face perso is walking on.
	 */
	if(!CAM_fn_bPersoOnFace(_p_stStruct->hCineinfoWork->hSuperObjectTargeted, &stAxis))
	{
		MTH3D_M_vSetVectorElements(&stAxis, MTH_C_ZERO, MTH_C_ZERO, MTH_C_ONE);
	}
 
	/*
	 *	Base rotation angle depending on failure mode or not.
	 */
	xBaseAngle = (_cMode & C_mode_FAILURE) ? CAM_C_xBaseAngleComputePosFailure : CAM_C_xBaseAngleComputePosNormal;
	/* EQUIVALENT A:
		if(_cMode & C_mode_FAILURE)
		xBaseAngle = CAM_C_xBaseAngleComputePosFailure;
	else
		xBaseAngle = CAM_C_xBaseAngleComputePosNormal;*/
 
	/*
	 * Fill array with all vectors computed with angles.
	 *--------------------------------------------------
	 */
	ulCounter = 0;
	ulMax = MTH_M_xDiv(MTH_C_2Pi, xBaseAngle);
	xAngle = stTemp.xAngleAlpha;
 
	/* !!! To force ComputeMovePos to take walues from work and not from current ! */
	eState = stStruct.hCineinfo->eState;
	stStruct.hCineinfo->eState = CAM_e_State_GoToOptimal;
 
	xSaveTheta = stTemp.xAngleTheta;
	for ( ulAngle = 0; ulAngle < ulMax; ulAngle ++ )
	{
		/* Compute wanted position with a given alpha */
		stTemp.xAngleAlpha = xAngle;
		stTemp.xAngleAlpha = CAM_fn_xSetAngleInInterval02Pi(stTemp.xAngleAlpha);
		stTemp.xAngleTheta = xSaveTheta;
		stStruct.hCineinfoWork->uwIAFlags |= C_IAFlags_NoShiftUntilPosReached;
		CAM_fn_vComputeMovePosWithDynTheta(&stStruct);
 
		/* Compute direction with good dist */
		p_stDir = &gast_VectorArrayCpt[ulCounter].stDir;
		MTH3D_M_vSubVector(p_stDir, &stStruct.stWantedCameraPos, &stStruct.stTargetedPersoPos);
		gast_VectorArrayCpt[ulCounter].xPound = CAM_fnx_ComputePosPound(_p_stStruct, p_stDir);
		ulCounter++;
 
		/* The same dir with a different norm : We took xNormIdeal/2 */
		if(!CONSTANT_NODISTVIS)
		{
			if(_cMode & C_mode_FAILURE)
			{
				p_stDir = &gast_VectorArrayCpt[ulCounter].stDir;
				MTH3D_M_vSubVector(p_stDir, &stStruct.stWantedCameraPos, &stStruct.stTargetedPersoPos);
				MTH3D_M_vNormalizeVector(p_stDir, p_stDir);
				MTH3D_M_vMulScalarVector(p_stDir, MTH_M_xMul(xNormIdeal, MTH_M_xFloatToReal(0.5f)), p_stDir);
				gast_VectorArrayCpt[ulCounter].xPound = CAM_fnx_ComputePosPound(_p_stStruct, p_stDir);
				ulCounter++;
				if(_cMode & C_mode_MORE_CORRECT)
				{
					p_stDir = &gast_VectorArrayCpt[ulCounter].stDir;
					MTH3D_M_vSubVector(p_stDir, &stStruct.stWantedCameraPos, &stStruct.stTargetedPersoPos);
					MTH3D_M_vNormalizeVector(p_stDir, p_stDir);
					MTH3D_M_vMulScalarVector(p_stDir, MTH_M_xMul(xNormIdeal, MTH_M_xFloatToReal(0.33f)), p_stDir);
					gast_VectorArrayCpt[ulCounter].xPound = CAM_fnx_ComputePosPound(_p_stStruct, p_stDir);
					ulCounter++;
				}
			}
		}
 
		/* WITH THETA = 0 */
		stTemp.xAngleTheta = MTH_C_ZERO;
		stStruct.hCineinfoWork->uwIAFlags |= C_IAFlags_NoShiftUntilPosReached;
		CAM_fn_vComputeMovePosWithDynTheta(&stStruct);
 
		/* Compute direction with good dist */
		p_stDir = &gast_VectorArrayCpt[ulCounter].stDir;
		MTH3D_M_vSubVector(p_stDir, &stStruct.stWantedCameraPos, &stStruct.stTargetedPersoPos);
		gast_VectorArrayCpt[ulCounter].xPound = CAM_fnx_ComputePosPound(_p_stStruct, p_stDir);
		ulCounter++;
 
		/* WITH THETA = Pi/4 */
		stTemp.xAngleTheta = MTH_C_PiBy4;
		stStruct.hCineinfoWork->uwIAFlags |= C_IAFlags_NoShiftUntilPosReached;
		CAM_fn_vComputeMovePosWithDynTheta(&stStruct);
 
		/* Compute direction with good dist */
		p_stDir = &gast_VectorArrayCpt[ulCounter].stDir;
		MTH3D_M_vSubVector(p_stDir, &stStruct.stWantedCameraPos, &stStruct.stTargetedPersoPos);
		gast_VectorArrayCpt[ulCounter].xPound = CAM_fnx_ComputePosPound(_p_stStruct, p_stDir);
		ulCounter++;
 
		/* WITH THETA = -Pi/4 */
		stTemp.xAngleTheta = -MTH_C_PiBy4;
		stStruct.hCineinfoWork->uwIAFlags |= C_IAFlags_NoShiftUntilPosReached;
		CAM_fn_vComputeMovePosWithDynTheta(&stStruct);
 
		/* Compute direction with good dist */
		p_stDir = &gast_VectorArrayCpt[ulCounter].stDir;
		MTH3D_M_vSubVector(p_stDir, &stStruct.stWantedCameraPos, &stStruct.stTargetedPersoPos);
		gast_VectorArrayCpt[ulCounter].xPound = CAM_fnx_ComputePosPound(_p_stStruct, p_stDir);
		ulCounter++;
 
 
		xAngle += xBaseAngle;
	}
	guw_NbVectorArrayCpt = ulCounter;
 
	stStruct.hCineinfo->eState = eState;
}
 
/*
 *=================================================================================================
 *	Compute a new good ideal pos depending on real wanted position.
 *	_p_stComputeVector will receive computed position (if algo didn't fail).
 *	Returns:	1 in success
 *				0 if failed
 *=================================================================================================
 */
char CAM_fn_cFindNewGoodIdealPos
(
	CAM_tdstUpdateCamera	*_p_stStruct, 
	MTH3D_tdstVector		*_p_stComputeVector, 
	char					_cMode
)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	unsigned long		ulCounter;
	MTH3D_tdstVector	stTempVector, *p_stDir;
	MTH_tdxReal			xPoundRef;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	MTH3D_M_vNullVector(_p_stComputeVector);
 
	/* 
	 *	Compute pound of current wanted pos. When we want a better position, this is
	 *	the reference pound (all positions with a greater poud will be eliminate).
	 */
	xPoundRef = MTH_C_ZERO;
	if(_cMode & C_mode_MORE_CORRECT)
	{
		if(_cMode & C_mode_FAILURE)
		{
			MTH3D_M_vSubVector(&stTempVector, &_p_stStruct->stCurrentCameraPos, &_p_stStruct->stTargetedPersoPos);
		}
		else
		{
			MTH3D_M_vSubVector(&stTempVector, &_p_stStruct->stWantedCameraPos, &_p_stStruct->stTargetedPersoPos);
		}
		xPoundRef = CAM_fnx_ComputePosPound(_p_stStruct, &stTempVector);
	}
 
	/*
	 * Fill array of vectors to test.
	 */
	CAM_fn_vFillVectorArray(_p_stStruct, _cMode);
 
	/*
	 * Sort array by priority.
	 */
	qsort
	(
		gast_VectorArrayCpt,
		guw_NbVectorArrayCpt,
		sizeof(tdst_PosPound),
		CAM_fni_ComparePositions
	);
 
	/*
	 * Compute vector to test, and test...
	 */
	for ( ulCounter = 0; ulCounter < guw_NbVectorArrayCpt; ulCounter ++ )
	{
		p_stDir = &gast_VectorArrayCpt[ulCounter].stDir;
		if
		(
			CAM_fnc_IsDirectionCorrect
			(
				_p_stStruct, 
				p_stDir, 
				gast_VectorArrayCpt[ulCounter].xPound,
				xPoundRef,
				_cMode
			)
			&&
			CAM_fn_cFindCorrectPosWithDir
			(
				_p_stStruct, 
				p_stDir,
				_p_stComputeVector,
				_cMode
			)
		)
		{
			if(ulCounter == 0)
				return 1;
			return 2;
		}
	}
 
	return 0;
}
 
 
/*
 **************************************************************************************************
 **************************************************************************************************
 **************************************************************************************************
 **************************************************************************************************
 */
 
 
/*
 *=================================================================================================
 *	We must find a position to force the camera, cause we can't go to a new computed position
 *	without collision or visibility. 
 *	We search a new good position but don't test if we can go to it.
 *=================================================================================================
 */
void CAM_fn_vForceFailureVisibility(CAM_tdstUpdateCamera *_p_stStruct)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector	stComputeVector, stTempVector;
	char				cMode;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/*
	 *	Find a new position but we don't care if we can go to. 
	 */
	cMode = C_mode_MUSTSEE|C_mode_FAILURE;
	if(CONSTANT_FORCEBESTPOS)
		cMode |= C_mode_MORE_CORRECT;
 
	if(!CAM_fn_cFindNewGoodIdealPos(_p_stStruct, &stComputeVector, cMode))
		return;
 
	/*
	 *	Determin if we cut or not. If camera can see the computed one, or if distance if
	 *	short enought, don't cut.
	 */
	MTH3D_M_vSubVector(&stTempVector, &stComputeVector, &_p_stStruct->stCurrentCameraPos);
	_p_stStruct->hCineinfo->eState = CAM_e_State_FailureVisibility;
 
	/*
	 *	To remember the position where we want to go.
	 */
	MTH3D_M_vCopyVector(&_p_stStruct->hCineinfo->stLastFailedWanted, &stComputeVector);
}
 
/*
 *=================================================================================================
 *	Compute the move after a failure visibility.
 *=================================================================================================
 */
void CAM_fn_vComputeFailureVisibility(CAM_tdstUpdateCamera *_p_stStruct)
{
	if(_p_stStruct->hCineinfo->eState == CAM_e_State_FailureVisibility)
	{
		/* 
		 * Force camera to pos at stLastFailedWanted.
		 * Force target.
		 */
		CAM_fn_vForceCameraPos(_p_stStruct->hSuperObjCamera, &_p_stStruct->hCineinfo->stLastFailedWanted);
		CAM_fn_vForceCameraTgt(_p_stStruct->hSuperObjCamera, &_p_stStruct->stCptPos.stTarget, &_p_stStruct->stCptPos.stRefAxisZ);
		MTH3D_M_vCopyVector(&_p_stStruct->stCurrentCameraPos, &_p_stStruct->hCineinfo->stLastFailedWanted);
 
		/*
		 * This is the new ideal position.
		 */
		CAM_fn_vMakeAsNewIdealPosition(_p_stStruct, &_p_stStruct->hCineinfo->stLastFailedWanted);
	}
}
 
 
/*
 **************************************************************************************************
 **************************************************************************************************
 **************************************************************************************************
 **************************************************************************************************
 */
 
 
/*
 *=================================================================================================
 *	Call to check if perso if not closest from camera.
 *	If it's a case, we try to find a new good ideal position.
 *=================================================================================================
 */
void CAM_fn_vTestBadDistance(CAM_tdstUpdateCamera *_p_stStruct)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector	stTempVector, stComputeVector;
	char				cMode;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/*
	 *	State must be optimal of computed optimal. We make the test cause a previous function
	 *	can have change the state to force to failure.
	 */
	if
	(
		(_p_stStruct->hCineinfo->eState != CAM_e_State_GoToOptimal)
	&&	(_p_stStruct->hCineinfo->eState != CAM_e_State_GoToComputedOptimal)
	)
		return;
 
	/*
	 *	Flags must be correct.
	 */
	if
	(
		(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_TargetIsAlreadyComputed)
	||	(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_PositionIsAlreadyComputed)
	||	(_p_stStruct->hCineinfo->ucVolIAFlags & C_VolIAFlags_InitJustCopiedInCurrent)
	)
		return;
 
	/* 
	 *	Get vector of current camera pos to real target to get distance.
	 */
	MTH3D_M_vSubVector(&stTempVector, &_p_stStruct->stCurrentCameraPos, &_p_stStruct->stTargetedPersoRealPos);
 
	/*
	 * Failed in distance if :
	 * - Distance is less than minimal one.
	 * - We are in collision or camera doesn't move.
	 */
	if
	(
		(MTH_M_bGreater(MTH3D_M_xNormVector(&stTempVector), CAM_C_xMinDistPerso))
	||	(
			(!(DNM_M_bReportCollision(_p_stStruct->p_stReport)))
		&&	(!(_p_stStruct->hCineinfo->ucVolIAFlags & C_VolIAFlags_CameraNotMove))
		)
	)
		return;
 
	/* 
	 * If current wanted position is correct, don't search, that's it !
	 */
	if(!CAM_fn_cIsWantedPosIncorrect(_p_stStruct, &_p_stStruct->stWantedCameraPos, C_mode_MUSTGO|C_mode_MUSTSEE))
		return;
 
	/* 
	 * We try to find another correct position. 
	 * If not, failure.
	 * If yes, this is the new ideal one.
	 */
/* ANNECY MT - 03/08/99 { */
	/* _p_stStruct->hCineinfo->wCounter1 = 0; */
	_p_stStruct->hCineinfo->xCounter1 = MTH_C_ZERO;
/* END ANNECY MT } */
 
	cMode = C_mode_MUSTGO|C_mode_MUSTSEE;
	if(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_NoVisibility)
		cMode &= ~C_mode_MUSTSEE;
 
	if(!CAM_fn_cFindNewGoodIdealPos(_p_stStruct, &stComputeVector, cMode))
		CAM_fn_vForceFailureVisibility(_p_stStruct);
	else
	{
		CAM_fn_vMakeAsNewIdealPosition(_p_stStruct, &stComputeVector);
		_p_stStruct->hCineinfo->ucPerIAFlags |= C_PerIAFlags_ConstraintMoveVisFailure;
	}
}
 
/*
 *=====================================================================================================
 *	Test visibility of camera.
 *	Returns 1 if camera must pass in a special state cause of unvisibility.
 *	Returns 0 else.
 *=====================================================================================================
 */       
void CAM_fn_vTestBadVisibility(CAM_tdstUpdateCamera	*_p_stStruct)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector	stComputeVector, stTempVector;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	/*
	 *	State must be optimal of computed optimal. We make the test cause a previous function
	 *	can have change the state to force to failure.
	 */
	if
	(
		(_p_stStruct->hCineinfo->eState != CAM_e_State_GoToOptimal)
	&&	(_p_stStruct->hCineinfo->eState != CAM_e_State_GoToComputedOptimal)
	)
		return;
 
	/*
	 *	Flags must be correct.
	 */
	if
	(
		(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_NoVisibility)
//	||	(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_TargetIsAlreadyComputed)
	||	(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_PositionIsAlreadyComputed)
	||	(_p_stStruct->hCineinfo->ucVolIAFlags & C_VolIAFlags_InitJustCopiedInCurrent)
	)
		return;
 
	/*
	 *	Counter must be correct to test visibility.
	 */
/* ANNECY MT - 03/08/99 { */
	/* _p_stStruct->hCineinfo->wCounter2++; */
	_p_stStruct->hCineinfo->xCounter2 = MTH_M_xAdd(_p_stStruct->hCineinfo->xCounter2,MTH_M_xFloatToReal(g_stEngineStructure.stEngineTimer.ulUsefulDeltaTime));

	/* if(_p_stStruct->hCineinfo->wCounter2 < CAM_C_xTickTestVisibility) */
	if(MTH_M_bLess(_p_stStruct->hCineinfo->xCounter2 , CAM_C_xTickTimeTestVisibility))
		return;


	/* _p_stStruct->hCineinfo->wCounter2 = 0; */
	_p_stStruct->hCineinfo->xCounter2 = MTH_C_ZERO;

	/* OPTIM RAY : Only if last trame, we don't failed */
	/* if(_p_stStruct->hCineinfo->wCounter3 == 0) */

	if(MTH_M_bIsNull(_p_stStruct->hCineinfo->xCounter3))
	{
		/* If camera doesn't move, don't test static hierarchy */
		/* if(_p_stStruct->hCineinfo->wCounter4 > 10) */
		if(MTH_M_bGreater(_p_stStruct->hCineinfo->xCounter4 , MTH_M_xMul(MTH_M_xFloatToReal(10000.0f),g_stEngineStructure.stEngineTimer.xFrameLength)))
/* END ANNECY MT } */
//		{
//			if(_p_stStruct->hCineinfo->wCounter4)
				cCanTestStatic = 0;
			else
			{
				MTH3D_tdstVector stVectorNull;
 
 				MTH3D_M_vNullVector(&stVectorNull);

				/* If last move is quiet equal to ray, don't test static */
				MTH3D_M_vCopyVector(&stComputeVector, DNM_M_p_stReportGetLastMove(_p_stStruct->p_stReport));
				MTH3D_M_vSubVector(&stTempVector, &_p_stStruct->stTargetedPersoRealPos, &_p_stStruct->stCurrentCameraPos);

				if 
				(
					(!CAM_fn_bEqualVectorWithEpsilon(&stComputeVector, &stVectorNull,MTH_M_xFloatToReal(0.0001f)))
				&&	(!CAM_fn_bEqualVectorWithEpsilon(&stTempVector, &stVectorNull,MTH_M_xFloatToReal(0.0001f)))
				)
				{
					MTH3D_M_vNormalizeVector(&stComputeVector, &stComputeVector);
					MTH3D_M_vNormalizeVector(&stTempVector, &stTempVector);
					if(MTH_M_bGreater(MTH_M_xAbs(MTH3D_M_xDotProductVector(&stTempVector, &stComputeVector)), MTH_M_xFloatToReal(0.9f)))
						cCanTestStatic = 0;
				}
			}
//		}
	}
 
	/*
	 *	Determin if we can see target real position.
	 *	If we see it, exit cause no failed.
	 */
/* ANNECY MT - 03/08/99 { */
	if(!CAM_fn_cCameraCantSee(_p_stStruct, &_p_stStruct->stTargetedPersoRealPos, &_p_stStruct->stCurrentCameraPos))
	{
		/* _p_stStruct->hCineinfo->wCounter3 = 0; */
		_p_stStruct->hCineinfo->xCounter3 = MTH_C_ZERO;
		return;
	}
 
	/*
	 * FAILURE.
	 * Check for a too long time without seeing target.
	 */
	/* _p_stStruct->hCineinfo->wCounter3++; */
	_p_stStruct->hCineinfo->xCounter3 = MTH_M_xAdd(_p_stStruct->hCineinfo->xCounter3,MTH_M_xFloatToReal(g_stEngineStructure.stEngineTimer.ulUsefulDeltaTime));

	/* if(MTH_M_bGreater(_p_stStruct->hCineinfo->wCounter3, CAM_C_xTickFailureCantSee)) */
	if(MTH_M_bGreater(_p_stStruct->hCineinfo->xCounter3 , CAM_C_xTickTimeFailureCantSee))
	{
		/* _p_stStruct->hCineinfo->wCounter3 = 0; */
		_p_stStruct->hCineinfo->xCounter3 = MTH_C_ZERO;
		CAM_fn_vForceFailureVisibility(_p_stStruct);
		return;
	}
/* END ANNECY MT } */
 
	/* 
	 *	Test if we can go to wanted position. If yes, exit cause no failed.
	 */
	if(!CAM_fn_cIsWantedPosIncorrect(_p_stStruct, &_p_stStruct->stWantedCameraPos, C_mode_MUSTGO|C_mode_MUSTSEE))
		return;
 
	/* 
	 * We try to find another correct position. 
	 * If not, failure.
	 * If yes, this is the new ideal one.
	 */
/* ANNECY MT - 03/08/99 { */
	/*
	_p_stStruct->hCineinfo->wCounter1 = 0;
	_p_stStruct->hCineinfo->wCounter3 = 0;
	*/
	_p_stStruct->hCineinfo->xCounter1 = MTH_C_ZERO;
	_p_stStruct->hCineinfo->xCounter3 = MTH_C_ZERO;
/* END ANNECY MT } */
 
	if(!CAM_fn_cFindNewGoodIdealPos(_p_stStruct, &stComputeVector, C_mode_MUSTGO|C_mode_MUSTSEE))
		CAM_fn_vForceFailureVisibility(_p_stStruct);
	else
	{
		CAM_fn_vMakeAsNewIdealPosition(_p_stStruct, &stComputeVector);
		_p_stStruct->hCineinfo->ucPerIAFlags |= C_PerIAFlags_ConstraintMoveVisFailure;
	}
}
 
/*
 *=================================================================================================
 *	Call to check if camera collide with a face with an opposite normal
 *	If it's a case, we try to find a new good ideal position.
 *=================================================================================================
 */
void CAM_fn_vTestOppositeCollision(CAM_tdstUpdateCamera *_p_stStruct)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector	stCameraMove;
	char				cMode;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/*
	 *	State must be optimal of computed optimal. We make the test cause a previous function
	 *	can have change the state to force to failure.
	 */
	if
	(
		(_p_stStruct->hCineinfo->eState != CAM_e_State_GoToOptimal)
	&&	(_p_stStruct->hCineinfo->eState != CAM_e_State_GoToComputedOptimal)
	)
		return;
 
	/*
	 *	Flags must be correct.
	 */
	if
	(
		(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_TargetIsAlreadyComputed)
	||	(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_PositionIsAlreadyComputed)
	||	(_p_stStruct->hCineinfo->ucVolIAFlags & C_VolIAFlags_InitJustCopiedInCurrent)
	)
		return;
 
	/*
	 *	We must be in collision.
	 */
	if (!(DNM_M_bReportCollision(_p_stStruct->p_stReport)))
		return;
 
	/*
	 *	Check a meca report for an opposite collision.
	 */
	if(!(DNM_M_bReportIsWall(_p_stStruct->p_stReport)))
		return;
 
	/* 
	 * We try to find another correct position. 
	 * If not, failure.
	 * If yes, this is the new ideal one.
	 */
/* ANNECY MT - 03/08/99 { */
	/* _p_stStruct->hCineinfo->wCounter1 = 0; */
	_p_stStruct->hCineinfo->xCounter1 = MTH_C_ZERO;
/* END ANNECY MT } */
 
	cMode = C_mode_MUSTGO|C_mode_MUSTSEE;
	if(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_NoVisibility)
		cMode &= ~C_mode_MUSTSEE;
 
	if(!CAM_fn_cFindNewGoodIdealPos(_p_stStruct, &stCameraMove, cMode))
		CAM_fn_vForceFailureVisibility(_p_stStruct);
	else
		CAM_fn_vMakeAsNewIdealPosition(_p_stStruct, &stCameraMove);
}
 
 
/*
 **************************************************************************************************
 **************************************************************************************************
 **************************************************************************************************
 **************************************************************************************************
 */
 
 
/*
 *=====================================================================================================
 *  Compute cineinfowork parameters to go to a specific position.
 *	- hCineinfoWork must be correct.
 *	- In that structure, must be correct :
 *		* super object (first and second)
 *		* channel number
 *=====================================================================================================
 */
void CAM_fn_vComputeCineinfoWorkForAPosition(CAM_tdstUpdateCamera *_p_stStruct, MTH3D_tdstVector *_p_stPosition)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector				stDir;
	MTH3D_tdstVector				stCamTargetVector;
	MTH_tdxReal						xAlphaAngle, xThetaAngle;
	MTH_tdxReal						xNorm;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/* Compute normal axis (super object direction or vector direction in cineinfo */
	CAM_fn_vComputeRefAxis(_p_stStruct, &stDir);
 
	/* Compute rotated axis */
	MTH3D_M_vSubVector(&stCamTargetVector, _p_stPosition, &_p_stStruct->stTargetedPersoPos);
	xNorm = MTH3D_M_xNormVector(&stCamTargetVector);
 
	/* Compute alpha and theta angles */
	CAM_fn_vComputeAnglesBetweenTwoVectors
	(
		_p_stStruct->hCineinfoWork, 
		&stDir,  
		&stCamTargetVector, 
		&xAlphaAngle, 
		&xThetaAngle,
		&_p_stStruct->stCptPos.stRefAxisZ
	);
 
	/*
	 *  Set new parameters
	 */
	_p_stStruct->hCineinfoWork->xDistMin = xNorm;
	_p_stStruct->hCineinfoWork->xDistMax = xNorm;
	_p_stStruct->hCineinfoWork->xAngleAlpha = xAlphaAngle;
	_p_stStruct->hCineinfoWork->xAngleTheta = xThetaAngle;
}
void CAM_fn_vComputeCineinfoCurrentForAPosition(CAM_tdstUpdateCamera *_p_stStruct, MTH3D_tdstVector *_p_stPosition)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	struct tdstInternalStructurCineinfo_	*p_stMemo;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	p_stMemo = _p_stStruct->hCineinfoWork;
	_p_stStruct->hCineinfoWork = _p_stStruct->hCineinfoCurrent;
	CAM_fn_vComputeCineinfoWorkForAPosition(_p_stStruct, _p_stPosition);
	_p_stStruct->hCineinfoWork = p_stMemo;
}
 
/*
 *=================================================================================================
 *	Compute a new ideal position.
 *=================================================================================================
 */
void CAM_fn_vMakeAsNewIdealPosition(CAM_tdstUpdateCamera *_p_stStruct, MTH3D_tdstVector *_p_stPosition)
{
	/* 
	 *	Now, cut angle can go in the sens it wants...
	 */
	_p_stStruct->hCineinfo->cLastCutAngleSens = 0;
 
	/*
	 *	This is now a computed ideal position. Remember actual current cineinfo
	 *	(just the first time, look at function).
	 */
/*	if(!(_p_stStruct->hCineinfo->ucPerIAFlags & C_PerIAFlags_VisibilityCopied))*/
/*	{*/
		CAM_fn_vSetCineinfoVisibilityFromCurrent(_p_stStruct->hCineinfo);
/*		memcpy(_p_stStruct->hCineinfo->hVisibility, _p_stStruct->hCineinfo->hWork, sizeof(struct tdstInternalStructurCineinfo_));*/
/*		_p_stStruct->hCineinfo->ucPerIAFlags |= C_PerIAFlags_VisibilityCopied;*/
/*	}*/
 
	/*
	 *	Compute parameters for that position.
	 */
	CAM_fn_vComputeCineinfoCurrentForAPosition(_p_stStruct, _p_stPosition);
 
	/*
	 *	New state.
	 */
	_p_stStruct->hCineinfo->eState = CAM_e_State_GoToComputedOptimal;
 
	/*
	 *	When we compute a position, we don't want shift alpha and theta to live...
	 */
	_p_stStruct->hCineinfo->ucPerIAFlags |= C_PerIAFlags_NoShift;
 
	/*
	 *	Compute for now. We copy in work the filed of cineinfcurrent that have been
	 *	modified by SetCineinfo...
	 */
	_p_stStruct->hCineinfoWork->xAngleAlpha = _p_stStruct->hCineinfoCurrent->xAngleAlpha;
	_p_stStruct->hCineinfoWork->xAngleTheta = _p_stStruct->hCineinfoCurrent->xAngleTheta;
	_p_stStruct->hCineinfoWork->xDistMin = _p_stStruct->hCineinfoCurrent->xDistMin;
	_p_stStruct->hCineinfoWork->xDistMax = _p_stStruct->hCineinfoCurrent->xDistMax;
 
	/*
	 *	Compute move.
	 */
	CAM_fn_vComputeMovePos(_p_stStruct);
 
	/*
	 * Remember positions.
	 */
	MTH3D_M_vCopyVector(&_p_stStruct->hCineinfo->stLastFailedOrigin, &_p_stStruct->stCurrentCameraPos);
	MTH3D_M_vCopyVector(&_p_stStruct->hCineinfo->stLastFailedWanted, _p_stPosition);
}
 
 
/*
 **************************************************************************************************
 **************************************************************************************************
 **************************************************************************************************
 **************************************************************************************************
 */
 
 
/*
 *=====================================================================================================
 *	Determin if we can reset stLastFailedWanted.
 *=====================================================================================================
 */
char CAM_fn_cCanResetConstraintMoveVisFailure(CAM_tdstUpdateCamera *_p_stStruct, MTH3D_tdstVector *_p_stWanted)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector	stTempVector, stTempVector1;
	MTH_tdxReal			xNorm, xNorm1;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */

	/*
	 * Normal state : Ok.
	 */
	if(_p_stStruct->hCineinfo->eState == CAM_e_State_GoToOptimal)
		return 1;
 
	/*
	 *	If camera doesn't move for a too long time, we can reset.
	 */
/* ANNECY MT - 03/08/99 { */
	/* if(_p_stStruct->hCineinfo->wCounter4 >= CAM_C_xTickFindBetterPosIfNoMove) */
	if(MTH_M_bGreaterEqual(_p_stStruct->hCineinfo->xCounter4,CAM_C_xTickTimeFindBetterPosIfNoMove))
		return 1;
/* END ANNECY MT } */
 
	/*
	 *	Special process after a failure.
	 *	If we are in a special mode, we must test the end of the mode.
	 */
	if(!(_p_stStruct->hCineinfo->ucPerIAFlags & C_PerIAFlags_ConstraintMoveVisFailure))
		return 1;
 
	/*
	 *	If current camera wanted position is outside the circle (center = last failed origin, ray = 
	 *	first shifting), we force last failed wanted to be NULL to tell that we can move now. 
	 *	There's a little epsilon on the ray not to be too short.
	 */
	MTH3D_M_vSubVector
	(
		&stTempVector, 
		_p_stWanted,
		&_p_stStruct->hCineinfo->stLastFailedOrigin
	);
	xNorm = MTH3D_M_xNormVector(&stTempVector);
 
	MTH3D_M_vSubVector
	(
		&stTempVector1, 
		&_p_stStruct->hCineinfo->stLastFailedWanted,
		&_p_stStruct->hCineinfo->stLastFailedOrigin
	);
	xNorm1 = MTH3D_M_xNormVector(&stTempVector1);
/*	if(MTH_M_bGreater(xNorm1, MTH_M_xFloatToReal(5.0f)))*/
/*		xNorm1 = MTH_M_xFloatToReal(5.0f);*/
 
	if(MTH_M_bLessEqual(xNorm, MTH_M_xAdd(xNorm1, 0.5)))
		return 0;
 
	/*
	 *	We can reset vector.
	 */
	return 1;
}
 
/*
 *=====================================================================================================
 *	To restore the normal state.
 *=====================================================================================================
 */
void CAM_fn_vRestoreNormalState(CAM_tdstUpdateCamera *_p_stStruct)
{
	/* Return to normal state */
	_p_stStruct->hCineinfo->eState = CAM_e_State_GoToOptimal;
 
	/* Restore visibility cineinfo structure to current one to restore parameters of ideal position */
	CAM_fn_vSetCineinfoCurrentFromVisibility(_p_stStruct->hCineinfo);
	CAM_fn_vSetCineinfoWorkFromVisibility(_p_stStruct->hCineinfo);
 
	/* 
	 *	No shift until ideal pos is reached.
	 */
	_p_stStruct->hCineinfo->ucPerIAFlags |= C_PerIAFlags_NoShift;
	_p_stStruct->hCineinfo->ucPerIAFlags &= ~C_PerIAFlags_ConstraintMoveVisFailure;
 
	/* 
	 *	Compute position for this new position.
	 */
	CAM_fn_vComputeMovePosWithDynTheta(_p_stStruct);
}
 
/*
 *=====================================================================================================
 *	Try to find a better position than current one.
 *=====================================================================================================
 */
void CAM_fn_vCheckForABetterPos(CAM_tdstUpdateCamera *_p_stStruct)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector	stComputeVector;
	char				cRes;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	/*
	 *	One more trame in this new ideal position. If wCounter1 is too small, don't search a better one.
	 *	Else we try to find a better position. We try to find a better position if camera doesn't move
	 *	for a too long time too (wCounter4).
	 *--------------------------------------------------------------------------------------------------
	 */
/* ANNECY MT - 03/08/99 { */
	/* _p_stStruct->hCineinfo->wCounter1++; */
	_p_stStruct->hCineinfo->xCounter1 = MTH_M_xAdd(_p_stStruct->hCineinfo->xCounter1,MTH_M_xFloatToReal(g_stEngineStructure.stEngineTimer.ulUsefulDeltaTime));
	/* if((_p_stStruct->hCineinfo->wCounter1 >= CAM_C_xTickFindBetterPos) && (!(CONSTANT_NOBETTERPOS)) && _p_stStruct->hCineinfo->bCanDoBestPos) */
	if(MTH_M_bGreaterEqual(_p_stStruct->hCineinfo->xCounter1 , CAM_C_xTickTimeFindBetterPos) && (!(CONSTANT_NOBETTERPOS)) && _p_stStruct->hCineinfo->bCanDoBestPos)
	{
		/* _p_stStruct->hCineinfo->wCounter1 = 0; */
		_p_stStruct->hCineinfo->xCounter1 = MTH_C_ZERO;
/* END ANNECY MT } */
 
		/*
		 * Test for a better position.
		 */
		cRes = CAM_fn_cFindNewGoodIdealPos(_p_stStruct, &stComputeVector, C_mode_MUSTGO|C_mode_MUSTSEE|C_mode_MORE_CORRECT);
		if(cRes)
		{
			if(cRes == 1)
			{
				CAM_fn_vRestoreNormalState(_p_stStruct);
			}
			else
			{
				CAM_fn_vMakeAsNewIdealPosition(_p_stStruct, &stComputeVector);
				if(_p_stStruct->hCineinfo->ucPerIAFlags & C_PerIAFlags_ConstraintMoveVisFailure)
					cJustBetterPos = 1;
				else
					cJustBetterPos = 0;
				_p_stStruct->hCineinfo->ucPerIAFlags &= ~C_PerIAFlags_ConstraintMoveVisFailure;	
			}
			_p_stStruct->hCineinfo->bCanDoBestPos = FALSE;
		}
	}
}
