/*
 **************************************************************************************************
 *	General functions for camera AI.
 **************************************************************************************************
 */
 
#define D_CineInfo_StructureDefine
 
#include "AIUseCPA.h"
 
#include "CAM_Base.h"
#include "CAM_Tool.h"
#include "CAM_Vis.h"
#include "Gam.h"
 
#include "safe.h"
#include "specif/aitools.h"
/*
 *	FLAGS THAT DEPEND ON ACTUAL WORK. 
 *	That flags can change the compute of moving vectors.
 *	Determin if we must take a special axis.
 */
char	cRefAxisIsAlreadyComputed;
char	cNoDynChangeTheta;
 
/* Set when we find a better position, and we just became from a failure visibility */
char	cJustBetterPos = 0;
 
extern char cCanTestStatic;
 
/*
 **************************************************************************************************
 **************************************************************************************************
	COMPUTE REFERENCE POSITION
 **************************************************************************************************
 **************************************************************************************************
 */
 
 
/*
 *=================================================================================================
 *	Compute reference camera axis to compute position.
 *=================================================================================================
 */
void CAM_fn_vComputeRefAxis(CAM_tdstUpdateCamera *_p_stStruct, MTH3D_tdstVector	*_p_stRefAxis)
{
	/*
	 *	Determin if reference axis has already been computed.
	 */
	if(cRefAxisIsAlreadyComputed)
	{
		MTH3D_tdstVector stVectorNull;
 
 
		MTH3D_M_vNullVector(&stVectorNull);
		/* Blindage ! */
/*		if(MTH3D_M_bIsNullVector(&_p_stStruct->hCineinfo->stForceRefAxisY))*/
		if (CAM_fn_bEqualVectorWithEpsilon(&_p_stStruct->hCineinfo->stForceRefAxisY, &stVectorNull,MTH_M_xFloatToReal(0.0001f)))
		{
			MTH3D_M_vSetVectorElements(&_p_stStruct->hCineinfo->stForceRefAxisY, MTH_C_ZERO, -MTH_C_ONE, MTH_C_ZERO);
			MTH3D_M_vCopyVector(_p_stRefAxis, &_p_stStruct->hCineinfo->stForceRefAxisY);
		}
		else
		{
			MTH3D_M_vNormalizeVector(_p_stRefAxis, &_p_stStruct->hCineinfo->stForceRefAxisY);
		}
		/* Blindage ! */
/*		if(MTH3D_M_bIsNullVector(&_p_stStruct->hCineinfo->stForceRefAxisZ))*/
		if (CAM_fn_bEqualVectorWithEpsilon(&_p_stStruct->hCineinfo->stForceRefAxisZ, &stVectorNull,MTH_M_xFloatToReal(0.0001f)))
		{
			MTH3D_M_vSetVectorElements(&_p_stStruct->hCineinfo->stForceRefAxisZ, MTH_C_ZERO, MTH_C_ZERO, MTH_C_ONE);
		}
		else
		{
			MTH3D_M_vNormalizeVector(&_p_stStruct->stCptPos.stRefAxisZ, &_p_stStruct->hCineinfo->stForceRefAxisZ);
		}
	}
 
	/*
	 *	Apex mode if a second superobject is defined.
	 *	Reference axis is the axis between the two persos.
	 */
	else if(_p_stStruct->hCineinfoWork->hSecondSuperObjectTargeted)
	{
		MTH3D_M_vSubVector(_p_stRefAxis, &_p_stStruct->stSecondTargetedPersoPos, &_p_stStruct->stTargetedPersoPos);
		MTH3D_M_vNormalizeVector(_p_stRefAxis, _p_stRefAxis);
		MTH3D_M_vSetVectorElements(&_p_stStruct->stCptPos.stRefAxisZ, MTH_C_ZERO, MTH_C_ZERO, MTH_C_ONE);
	}
 
	/*
	 *	Else reference axis is perso sight axis.
	 */
	else
	{
		CAM_fn_vGetPersoSightAxis(_p_stStruct, _p_stRefAxis, &_p_stStruct->stCptPos.stRefAxisZ);
		MTH3D_M_vSetVectorElements(&_p_stStruct->stCptPos.stRefAxisZ, MTH_C_ZERO, MTH_C_ZERO, MTH_C_ONE);
		MTH3D_M_vNegVector(_p_stRefAxis, _p_stRefAxis);
	}
}
 
/*
 *=================================================================================================
 *	Compute the targeted perso position.
 *	This is the position of perso return by meca (average or linear) + the stShiftPos.
 *=================================================================================================
 */
void CAM_fn_vComputeTargetedPersoPos
(
	CAM_tdstUpdateCamera		*_p_stStruct, 
	HIE_tdxHandleToSuperObject	_hPerso,
	MTH3D_tdstVector			*_p_stTargetedPersoPos,
	MTH3D_tdstVector			*_p_stTargetedPersoRealPos
)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector			stTempVector;
	unsigned char						ucChannel;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	if(_hPerso == NULL)
	{
		MTH3D_M_vNullVector(_p_stTargetedPersoPos);
		MTH3D_M_vNullVector(_p_stTargetedPersoRealPos);
		return;
	}
 
	/* Get eventual channel number for that perso */
	ucChannel = CAM_fn_cCineInfoGetChannel(_p_stStruct->hCineinfoWork);
	if(ucChannel != C_ucUnknownChannel)
	{
		/* FBF blindage acces modules {*/
#ifdef ACTIVE_EDITOR
		fn_vCheckModuleAccess(_hPerso,TRUE, &ucChannel);
#endif
		/* } fin blindage acces modules */
		_hPerso = fn_hGetSuperObjectInChannel(fn_h3dDataGetChannelSOList(M_GetMSHandle(_hPerso, 3dData)),(short) ucChannel);
	}
 
	/* Get real targeted perso position */
	POS_fn_vGetTranslationVector(HIE_fn_hGetSuperObjectGlobalMatrix(_hPerso), _p_stTargetedPersoRealPos);
 
	/* 
	 * Compute perso position that will serve to compute following camera position.
	 * In some case, the position is the real one.
	 * In other cases, position is the average one computed by meca.
	 */
	if
	(
		(_p_stStruct->hCineinfo->ucVolIAFlags & C_VolIAFlags_InitJustCopiedInCurrent)		/* It's the first trame */
	||	(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_NoAverageMoveTgtPerso)			/* We want real pos of perso */
	||	((ucChannel == C_ucUnknownChannel) && (!M_GetMSHandle(_hPerso, Dynam)))				/* Perso has no meca */
	||  (ucChannel != C_ucUnknownChannel)													/* We want a channel of perso */
	)
	{
		MTH3D_M_vCopyVector(_p_stTargetedPersoPos, _p_stTargetedPersoRealPos);
	}
	else
	{
		CAM_fn_vAveragePosition(_p_stStruct->hCineinfo, CAM_C_xAverageForComputePos, _p_stTargetedPersoPos);
	}
 
	/*
	 *	Compute shift pos. We rotate it depending on perso rotation matrix.
	 */
	MTH3D_M_vCopyVector(&stTempVector, &_p_stStruct->hCineinfoWork->stShiftPos);
 
	POS_fn_vRotateVector(&stTempVector, HIE_fn_hGetSuperObjectGlobalMatrix(_hPerso), &stTempVector);
 
	/*
	 *	Add shift pos to the 2 computed vectors.
	 */
	MTH3D_M_vAddVector
	(
		_p_stTargetedPersoPos,
		_p_stTargetedPersoPos,
		&stTempVector
	);
	MTH3D_M_vAddVector
	(
		_p_stTargetedPersoRealPos,
		_p_stTargetedPersoRealPos,
		&stTempVector
	);
}
 
 
/*
 *=================================================================================================
 *	Compute the reference position to compute target and camera position.
 *=================================================================================================
 */
void CAM_fn_vComputeReferencePoint(CAM_tdstUpdateCamera *_p_stStruct)
{
	/*
	 * If there are 2 persos, force camera to take real position instead of
	 * average cause of a wonderful optim (average in camera and not in meca).
	 */
	if(_p_stStruct->hCineinfoWork->hSecondSuperObjectTargeted)
		_p_stStruct->hCineinfoWork->uwIAFlags |= C_IAFlags_NoAverageMoveTgtPerso;
 
	/* 
	 *	Position of first perso;
	 */
	CAM_fn_vComputeTargetedPersoPos
	(
		_p_stStruct, 
		_p_stStruct->hCineinfoWork->hSuperObjectTargeted,
		&_p_stStruct->stTargetedPersoPos,
		&_p_stStruct->stTargetedPersoRealPos
	);
	
	/* 
	 *	Position of second perso. If yes, we took the middle of the axis between the
	 *	two persos.
	 */
	if(_p_stStruct->hCineinfoWork->hSecondSuperObjectTargeted)
	{
		CAM_fn_vComputeTargetedPersoPos
		(
			_p_stStruct, 
			_p_stStruct->hCineinfoWork->hSecondSuperObjectTargeted,
			&_p_stStruct->stSecondTargetedPersoPos,
			&_p_stStruct->stSecondTargetedPersoRealPos
		);
 
		MTH3D_M_vLinearInterpolVector
		(
			&_p_stStruct->stTargetedPersoPos,
			&_p_stStruct->stTargetedPersoPos,
			&_p_stStruct->stSecondTargetedPersoPos,
			MTH_M_xFloatToReal(_p_stStruct->hCineinfo->xApexRatio)
		);
		MTH3D_M_vLinearInterpolVector
		(
			&_p_stStruct->stTargetedPersoRealPos,
			&_p_stStruct->stTargetedPersoRealPos,
			&_p_stStruct->stSecondTargetedPersoRealPos,
			MTH_M_xFloatToReal(_p_stStruct->hCineinfo->xApexRatio)
		);
	}
}
 
 
/*
 **************************************************************************************************
 **************************************************************************************************
	COMPUTE TARGET
 **************************************************************************************************
 **************************************************************************************************
 */
 
 
/*
 *=================================================================================================
 * To be sure that targeted perso is visible.
 *=================================================================================================
 */
 
#ifndef _FIRE_DEADCODE_U64_ /* Added by RUC */
void CAM_fn_vDynChangeTarget(CAM_tdstUpdateCamera *_p_stStruct, MTH3D_tdstVector *_p_stTempVector)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH_tdxReal			xFocal = MTH_M_xMul(_p_stStruct->hCineinfoWork->xFocal, MTH_C_Inv2);
	MTH_tdxReal			xAngle, xNorm;
	MTH3D_tdstVector	stTempVector, stTempVector1;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/*
	 *	If no dynamic target request or first trame, don't process.
	 */
	if
	(
		(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_NoDynamicTarget)
	||  (_p_stStruct->hCineinfo->ucVolIAFlags & C_VolIAFlags_InitJustCopiedInCurrent)
	)
		return;
 
	MTH3D_M_vSubVector(&stTempVector, &_p_stStruct->stTargetedPersoPos, &_p_stStruct->stCurrentCameraPos);
	MTH3D_M_vAddVector(&stTempVector, &stTempVector, _p_stTempVector);
	MTH3D_M_vSubVector(&stTempVector1, &_p_stStruct->stTargetedPersoRealPos, &_p_stStruct->stCurrentCameraPos);
	xAngle = CAM_fn_xComputeRotation(&stTempVector, &stTempVector1, NULL);
 
	/* Add an epsilon to demi-focal angle */
	xFocal = MTH_M_xSub(xFocal, CAM_C_xEpsilonDynTgtFocalAngle);
 
	if(MTH_M_bLess(xAngle , xFocal))
		return;
 
	xNorm = MTH_M_xDiv(MTH3D_M_xNormVector(&stTempVector), MTH_M_xCos(xFocal));
	xNorm = MTH_M_xMul(MTH_M_xSin(xFocal), xNorm);
	MTH3D_M_vSubVector(&stTempVector, &_p_stStruct->stTargetedPersoRealPos, &_p_stStruct->stTargetedPersoPos);
	MTH3D_M_vAddVector(&stTempVector, &stTempVector, _p_stTempVector);
	MTH3D_M_vNormalizeVector(&stTempVector, &stTempVector);
/*	MTH3D_M_vMulScalarVector(&stTempVector, xNorm, &stTempVector);*/
/*	MTH3D_M_vCopyVector(_p_stTempVector, &stTempVector);*/
	MTH3D_M_vMulScalarVector(_p_stTempVector, xNorm, &stTempVector);
}
#endif /* _FIRE_DEADCODE_U64_ */ /* Added by RUC */
 
 
/*
 *=================================================================================================
 *	Compute the target depending on shift and persos.
 *=================================================================================================
 */
void CAM_fn_vComputeTarget(CAM_tdstUpdateCamera *_p_stStruct)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector			stTempLook;
    HIE_tdxHandleToSuperObject	h_Perso;
	unsigned char				ucChannel;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/*
	 *	If target vertex was already computed, we copy it without additional process.
	 *-------------------------------------------------------------------------------
	 */
	if(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_TargetIsAlreadyComputed)
	{
		MTH3D_M_vCopyVector(&_p_stStruct->stCptPos.stTarget, &_p_stStruct->hCineinfo->stForceTarget);
	}
 
	/*
	 *	No handle to target, and no forced target : strange...
	 *--------------------------------------------------------
	 */
	else if (_p_stStruct->hCineinfoWork->hSuperObjectTargeted == NULL)
	{
		MTH3D_M_vNullVector(&_p_stStruct->stCptPos.stTarget);
	}
 
	/*
	 *	For 2 persos, the shift target must be in the new camera axis (axis between the 2 persos).
	 *	Don't care of stShiftTarget.
	 *--------------------------------------------------------------------------------------------
	 */
	else if(_p_stStruct->hCineinfoWork->hSecondSuperObjectTargeted)
	{
		MTH3D_M_vCopyVector(&_p_stStruct->stCptPos.stTarget, &_p_stStruct->stTargetedPersoPos);
	}
 
	/*
	 *	Else we compute target depending on targeted perso position.
	 *--------------------------------------------------------------
	 */
	else
	{
		/*
		 *	Get targeted perso 
		 */
		h_Perso = _p_stStruct->hCineinfoWork->hSuperObjectTargeted;
		if ((ucChannel = CAM_fn_cCineInfoGetChannel(_p_stStruct->hCineinfoWork)) != C_ucUnknownChannel)
		{
		/* FBF blindage acces modules {*/
#ifdef ACTIVE_EDITOR
		fn_vCheckModuleAccess(h_Perso,TRUE, &ucChannel);
#endif
		/* } fin blindage acces modules */
			h_Perso = fn_hGetSuperObjectInChannel(fn_h3dDataGetChannelSOList(M_GetMSHandle(h_Perso, 3dData)), (short) ucChannel);
		}
 
		/*
		 *	Compute additional target vertex.
		 */
		MTH3D_M_vCopyVector(&stTempLook, &_p_stStruct->hCineinfoWork->stShiftTarget);
		POS_fn_vRotateVector(&stTempLook, HIE_fn_hGetSuperObjectGlobalMatrix(h_Perso), &stTempLook);
		MTH3D_M_vAddVector(&_p_stStruct->stCptPos.stTarget, &_p_stStruct->stTargetedPersoPos, &stTempLook);
 
		/*
		 *	A little change. We change the look vertex to be as max as possible from perso, so for the gameplay,
		 *	this will permit to see as possible what the perso can see.
		 */
/*		CAM_fn_vDynChangeTarget(_p_stStruct, &stTempLook);*/
 
		/*
		 *	Compute resultant look position and update field.
		 */
		MTH3D_M_vAddVector(&_p_stStruct->stCptPos.stTarget, &_p_stStruct->stTargetedPersoPos, &stTempLook);
	}
}
 
 
/*
 **************************************************************************************************
 **************************************************************************************************
	COMPUTE MOVE VECTOR
 **************************************************************************************************
 **************************************************************************************************
 */
 
 
/*
 *=================================================================================================
 *	Description   : bound angle
 *	_p_xAngleToBound  -> angle to bound (current real angle)
 *	_p_xCutAngle	  -> To receive cut angle.
 *	_xAngle			  -> angle ideal
 *	_xTolerance		  -> tolerance
 *=================================================================================================
 */
void CAM_fn_vBoundAngleWithTolerance
(
	CAM_tdstUpdateCamera	*_p_stStruct, 
	MTH_tdxReal				*_p_xAngleToBound,
	MTH_tdxReal				*_p_xCutAngle,
	MTH_tdxReal				*_p_xDeltaAngle,
	MTH_tdxReal				_xRequestAngle, 
	MTH_tdxReal				_xTolerance
)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH_tdxReal	xAngleMin;
	MTH_tdxReal	xAngleMax;
	MTH_tdxReal	xDelta;
	MTH_tdxReal	xNewAngle;
	BOOL		bInTolerance;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/*
	 *	If tolerance is greater equal than Pi, current angle is correct. Don't need to bound.
	 */
	if (MTH_M_bGreaterEqual(_xTolerance, MTH_C_Pi))
	{
		*_p_xCutAngle = *_p_xAngleToBound;
		return;
	}
 
	/*
	 *	If tolerance.
	 */
	xNewAngle = _xRequestAngle;
	if (MTH_M_bGreater(_xTolerance, MTH_C_ZERO))
	{
		/*
		 *	Compute angle min and angle max.
		 */
		_xTolerance = MTH_M_xAdd(_xTolerance, MTH_M_xFloatToReal(0.0001f));
		xAngleMin = MTH_M_xSub(_xRequestAngle, _xTolerance);
		if (MTH_M_bLessZero(xAngleMin))
			xAngleMin = MTH_M_xAdd(xAngleMin, MTH_C_2Pi);
 
		xAngleMax = MTH_M_xAdd(_xRequestAngle, _xTolerance);
		if (MTH_M_bGreater(xAngleMax, MTH_C_2Pi))
			xAngleMax = MTH_M_xSub(xAngleMax, MTH_C_2Pi);
 
		/*
		 *	Determin if current angle is between angle min and max.
		 */
		if (MTH_M_bGreater(xAngleMax, xAngleMin))
			bInTolerance = MTH_M_bInRangeEqual(*_p_xAngleToBound, xAngleMin, xAngleMax);
		else
			bInTolerance = !MTH_M_bInRange(*_p_xAngleToBound, xAngleMax, xAngleMin);
 
		/*
		 *	If current angle is not between that 2 values, we must bound it.
		 */
		if (!bInTolerance)
		{
			xDelta = MTH_M_xSub(*_p_xAngleToBound, _xRequestAngle);
			xDelta = CAM_fn_xSetAngleInIntervalPiPi(xDelta);
 
			/*
			 *	We set wanted angle to angle min or max depending on the sens.
			 */
			if (MTH_M_bGreater(xDelta, MTH_C_ZERO))
				xNewAngle = xAngleMax;
			else
				xNewAngle = xAngleMin;
		}
		else
			xNewAngle = *_p_xAngleToBound;
	}
 
	/*
	 *	Cut big angle in smaller one if necessary.
	 */
	xDelta = MTH_M_xSub(xNewAngle, *_p_xAngleToBound);
	xDelta = CAM_fn_xSetAngleInIntervalPiPi(xDelta);
	*_p_xDeltaAngle = xDelta;
	if(MTH_M_bGreaterEqual(MTH_M_xAbs(xDelta), CAM_C_xBaseCutAngle))
	{
		if (MTH_M_bGreaterZero(xDelta))
			*_p_xCutAngle = MTH_M_xAdd(*_p_xAngleToBound, CAM_C_xBaseCutAngle);
		else
			*_p_xCutAngle = MTH_M_xSub(*_p_xAngleToBound, CAM_C_xBaseCutAngle);
		*_p_xCutAngle  = CAM_fn_xSetAngleInInterval02Pi(*_p_xCutAngle);
	}
	else
		*_p_xCutAngle = xNewAngle;
 
	/*
	 *	Set new bounded angle.
	 */
	*_p_xAngleToBound = xNewAngle;
}
 
/*
 *=================================================================================================
  Description   : compute ideal angles that depend of parameters and old camera position
  _p_stStruct         -> camera data
  _p_stCameraAxis     -> new camera axis (without rotation)
  _p_stCamTargetAxis  -> old camera position to new target axis
  _p_xHorAngle		  -> horizontal angle
  _p_xVerAngle		  -> vertical angle
 *=================================================================================================
 */
void CAM_fn_vComputeCameraAxisAngles
(
	CAM_tdstUpdateCamera	*_p_stStruct,
	MTH3D_tdstVector		*_p_stCameraAxis,
	MTH3D_tdstVector		*_p_stCamTargetAxis,
	MTH_tdxReal				*_p_xHorAngle,
	MTH_tdxReal				*_p_xVerAngle
)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH_tdxReal							xTemp, xShiftAlpha, xShiftTheta;
	MTH_tdxReal							xAngleTheta;
	MS_tdxHandleToInternalCineinfo		hCineInfoWork;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	hCineInfoWork = _p_stStruct->hCineinfoWork;
	/*
	 * compute angle between old direction and new dir
	 */
	CAM_fn_vComputeAnglesBetweenTwoVectors
	(
		hCineInfoWork, 
		_p_stCameraAxis, 
		_p_stCamTargetAxis, 
		_p_xHorAngle, 
		_p_xVerAngle,
		&_p_stStruct->stCptPos.stRefAxisZ
	);
 
	/*
	 *	If no shift flag is set, no tolerance for angles.
	 */
	if
	(
		(_p_stStruct->hCineinfo->ucPerIAFlags & C_PerIAFlags_NoShift)
	||	(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_NoShiftUntilPosReached)
	)
	{
		xShiftAlpha = MTH_C_ZERO;
		xShiftTheta = MTH_C_ZERO;
	}
	else
	{
		xShiftAlpha = _p_stStruct->hCineinfoWork->xAngleShiftAlpha;
		xShiftTheta = _p_stStruct->hCineinfoWork->xAngleShiftTheta;
	}
 
	/*
	 * Tolerance on horizontal angle
	 */
	_p_stStruct->cHasCutAlpha = 0;
	CAM_fn_vBoundAngleWithTolerance
	(
		_p_stStruct, 
		_p_xHorAngle,
		&_p_stStruct->xAngleAlphaCut,
		&_p_stStruct->xAngleDeltaAlphaCut,
		_p_stStruct->hCineinfoWork->xAngleAlpha, 
		xShiftAlpha
	);
	if(!(MTH_M_bEqual(*_p_xHorAngle, _p_stStruct->xAngleAlphaCut)))
		_p_stStruct->cHasCutAlpha = 1;
 
	/*
	 * Tolerance on vertical angle
	 */
	if(_p_stStruct->hCineinfo->eState == CAM_e_State_GoToComputedOptimal)
		xAngleTheta = _p_stStruct->hCineinfoCurrent->xAngleTheta;
	else
		xAngleTheta = _p_stStruct->hCineinfoWork->xAngleTheta;
 
	CAM_fn_vBoundAngleWithTolerance
	( 
		_p_stStruct, 
		_p_xVerAngle, 
		&xTemp,
		&xTemp,
		xAngleTheta, 
		xShiftTheta
	);
}
 
/*
 *=================================================================================================
 *	Rotate camera axis with two angles (one in horizontal plane, the other in vertical plane)
 *	_p_stCameraAxis     -> axis (result axis is set in this parameter)
 *	_xHorAngle          -> rotation in horizontal plane (xOy)
 *	_xVerAngle			-> rotation in vertical plane (zOy)
 *=================================================================================================
 */
void CAM_fn_vRotateCameraAxis
(
	MTH3D_tdstVector	*_p_stCameraAxis, 
	MTH_tdxReal			_xHorAngle, 
	MTH_tdxReal			_xVerAngle,
	MTH3D_tdstVector	*_p_stRefAxisZ
)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH_tdstRotation	stRotation;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/*
	 *	Rotate axis around Z axis.
	 */
	stRotation.m_xAngle = _xHorAngle;
	MTH3D_M_vCopyVector(&stRotation.m_stAxis, _p_stRefAxisZ);
	MTH_p_stRotationMulVector(&stRotation, _p_stCameraAxis, _p_stCameraAxis);
 
	/*
	 *	Compute x Axis, and rotate axis around x axis.
	 */
	MTH3D_M_vCrossProductVectorWithoutBuffer(&stRotation.m_stAxis, _p_stCameraAxis, _p_stRefAxisZ);
	MTH3D_M_vNormalizeVector(&stRotation.m_stAxis, &stRotation.m_stAxis);
	stRotation.m_xAngle = _xVerAngle;
	MTH_p_stRotationMulVector(&stRotation, _p_stCameraAxis, _p_stCameraAxis);
 
}
 
/*
 *=================================================================================================
 *	Compute real camera wanted position.
 *=================================================================================================
 */
void CAM_fn_vComputeRealWantedPos(CAM_tdstUpdateCamera *_p_stStruct)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	CAM_tdstUpdateCamera	stTempStruct;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/*
	 *	A temporary structure.
	 */
	memcpy(&stTempStruct, _p_stStruct, sizeof(CAM_tdstUpdateCamera));
 
	/*
	 *	Real wanted position for normal state is the same that current wanted position.
	 *	We must assure that there's no shifting vertex to retreive the ideal position, so we
	 *	get current working structure (we copy it in visibility one that is not used).
	 */
	if(_p_stStruct->hCineinfo->ucPerIAFlags & C_PerIAFlags_VisibilityCopied)
	{
		memcpy(&gst_Ideal, _p_stStruct->hCineinfoVisibility, sizeof(struct tdstInternalStructurCineinfo_));
/*		gst_Ideal.xAngleTheta = _p_stStruct->hCineinfoWork->xAngleTheta;*/
 
		gst_Ideal.xDistMin = _p_stStruct->hCineinfoWork->xDistMin;
		gst_Ideal.xDistMax = _p_stStruct->hCineinfoWork->xDistMax;
	}
	else
		memcpy(&gst_Ideal, _p_stStruct->hCineinfoWork, sizeof(struct tdstInternalStructurCineinfo_));
 
	/* 
	 *	Compute position. Force to have no shift to compute the real wanted position only
	 *	depending on alpha and theta (and not shift values).
	 */
	gst_Ideal.uwIAFlags |= C_IAFlags_NoShiftUntilPosReached;
	stTempStruct.hCineinfoWork = &gst_Ideal;
	CAM_fn_vComputeMovePosWithDynTheta(&stTempStruct);
	MTH3D_M_vCopyVector(&_p_stStruct->stRealWantedCameraPos, &stTempStruct.stWantedCameraPos);
}
 
/*
 *=================================================================================================
 * Change theta depending of current targeted perso move.
 * Return 0 if theta is not changed, and 1 else.
 *=================================================================================================
 */
char CAM_fn_cDynChangeTheta(CAM_tdstUpdateCamera *_p_stStruct, MTH3D_tdstVector *_p_stTestedPos)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector		stMove, stRef, stSightAxis;
	MTH3D_tdstVector		stNormal;
	MTH_tdxReal				xAngle, xThetaAngle;
	MTH_tdxReal				xCos;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/*
	 *	Get the normal of the face the targeted perso is walking on.
	 */
	if(!CAM_fn_bPersoOnFace(_p_stStruct->hCineinfoWork->hSuperObjectTargeted, &stNormal))
		return 0;
 
	/* 
	 *	Compute projection of perso sight axis on face.
	 */
	CAM_fn_vComputeRefAxis(_p_stStruct, &stSightAxis);
	CAM_fn_vComputeProjection(&stMove, &stSightAxis, &stNormal);
	MTH3D_M_vNormalizeVector(&stMove, &stMove);
 
	/*
	 *	Compute requested theta angle.
	 */
	MTH3D_M_vSetVectorElements(&stRef, MTH_C_ZERO, MTH_C_ZERO, MTH_C_ONE);
	xAngle = CAM_fn_xComputeRotation(&stRef, &stMove, NULL);
	xAngle = MTH_M_xSub(xAngle, MTH_C_PiBy2);
 
	MTH3D_M_vSubVector(&stRef, &_p_stStruct->stTargetedPersoPos, _p_stTestedPos);
	MTH3D_M_vNormalizeVector(&stRef, &stRef);
	xCos = MTH3D_M_xDotProductVector(&stRef, &stMove);
	xAngle = MTH_M_xMul(xAngle, xCos);
 
	/*
	 *	Compute normal theta angle for tested position.
	 */
	if(_p_stStruct->hCineinfo->ucPerIAFlags & C_PerIAFlags_VisibilityCopied)
		xThetaAngle = _p_stStruct->hCineinfoVisibility->xAngleTheta;
	else
/* ANNECY MT - 09/07/99 { */
		/*xThetaAngle = _p_stStruct->hCineinfoCurrent->xAngleTheta;*/
		xThetaAngle = _p_stStruct->hCineinfoWork->xAngleTheta;
/* END ANNECY MT } */
 
	/* 
	 * Compute additional theta angle.
	 * Epsilon is current theta angle.
	 *
	 * - If actual and requested theta are opposite, we will add xAngle to current theta.
	 * - Else we compute the difference between the two angles.
	 * - Cause of current theta, new angle can be the same as wanted one.
	 */
	if(MTH_M_bLess(MTH_M_xMul(xThetaAngle, xAngle), MTH_C_ZERO))
	{
		xAngle = xAngle;
	}
	else if
	(
		MTH_M_bLessEqual
		(
			MTH_M_xAbs(xThetaAngle),
			MTH_M_xAbs(xAngle)
		)
	)
	{
		xAngle = MTH_M_xSub(xAngle, xThetaAngle);
	}
	else
	{
		xAngle = MTH_C_ZERO;
		return 0;
	}
 
	/* 
	 *	Add angle to current theta.
	 */
	_p_stStruct->hCineinfoWork->xAngleTheta = MTH_M_xAdd(xThetaAngle, xAngle);
	return 1;
}
 
/*
 *=================================================================================================
 * Description   : Take care of camera's altitude
 *=================================================================================================
 */
void CAM_fn_vTakeCareOfCameraAltitude(MS_tdxHandleToCineinfo _hCineinfo, MTH3D_tdstVector *_p_stNewCameraPosition)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH_tdxReal		xZCamera;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	xZCamera = _p_stNewCameraPosition->xZ;
	
	if (MTH_M_bGreater(xZCamera, _hCineinfo->hWork->xZMax))
		_p_stNewCameraPosition->xZ = _hCineinfo->hWork->xZMax;
			
	if (MTH_M_bLess(xZCamera, _hCineinfo->hWork->xZMin))
		_p_stNewCameraPosition->xZ = _hCineinfo->hWork->xZMin;
 
}
 
/*
 *=================================================================================================
 * Compute move vector of camera.
 *=================================================================================================
 */
void CAM_fn_vComputeMovePos(CAM_tdstUpdateCamera *_p_stStruct)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector		stDir;
	MTH3D_tdstVector		stCamTargetVector;
	MTH_tdxReal				xNorm;
	MTH_tdxReal				xDistMin;
	MTH_tdxReal				xDistMax;
	MTH_tdxReal				xAlphaAngle, xThetaAngle;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/*
	 *	Test if we have force camera position.
	 */
	if(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_PositionIsAlreadyComputed)
	{
		if(cRefAxisIsAlreadyComputed)
		{
			MTH3D_M_vNormalizeVector(&_p_stStruct->stCptPos.stRefAxisZ, &_p_stStruct->hCineinfo->stForceRefAxisZ);
		}
		else
		{
			MTH3D_M_vSetVectorElements(&_p_stStruct->stCptPos.stRefAxisZ, MTH_C_ZERO, MTH_C_ZERO, MTH_C_ONE);
		}
		/* no Dynamic Theta change when we have force camera position */
		cNoDynChangeTheta = 1; 
 
		/* Wanted position and move */
		MTH3D_M_vCopyVector(&_p_stStruct->stWantedCameraPos, &_p_stStruct->hCineinfo->stForcePosition);
		MTH3D_M_vSubVector(&_p_stStruct->stCptPos.stMovePos, &_p_stStruct->stWantedCameraPos, &_p_stStruct->stCurrentCameraPos);
	}
 
	/*
	 *	Test if the camera has a target.
	 */
	else if(!_p_stStruct->hCineinfoWork->hSuperObjectTargeted)
	{
		if(cRefAxisIsAlreadyComputed)
		{
			MTH3D_M_vNormalizeVector(&_p_stStruct->stCptPos.stRefAxisZ, &_p_stStruct->hCineinfo->stForceRefAxisZ);
		}
		else
		{
			MTH3D_M_vSetVectorElements(&_p_stStruct->stCptPos.stRefAxisZ, MTH_C_ZERO, MTH_C_ZERO, MTH_C_ONE);
		}
		/* Wanted position and move */
		CAM_fn_vComputeRefAxis(_p_stStruct, &stDir);
		MTH3D_M_vCopyVector(&_p_stStruct->stWantedCameraPos, &_p_stStruct->stCurrentCameraPos);
		MTH3D_M_vNullVector(&_p_stStruct->stCptPos.stMovePos);
	}
 
	/*
	 *	Else normal computing.
	 */
	else
	{
		/* Compute vector between old camera and new target */
		MTH3D_M_vSubVector(&stCamTargetVector, &_p_stStruct->stCurrentCameraPos, &_p_stStruct->stTargetedPersoPos);
 
		if
		(
			(stCamTargetVector.xX == _p_stStruct->stCptPos.stRefAxisZ.xX)
		&&	(stCamTargetVector.xY == _p_stStruct->stCptPos.stRefAxisZ.xY)
		)
			stCamTargetVector.xX += 0.5;

		/* Compute new axis (super object direction or vector direction in cineinfo */
		CAM_fn_vComputeRefAxis(_p_stStruct, &stDir);
 
		/* Look if old axis is in ideal angle */
		CAM_fn_vComputeCameraAxisAngles(_p_stStruct, &stDir, &stCamTargetVector, &xAlphaAngle, &xThetaAngle);
 
		/* Rotate camera system with alpha and Theta angles */
		CAM_fn_vRotateCameraAxis(&stDir, xAlphaAngle, xThetaAngle, &_p_stStruct->stCptPos.stRefAxisZ);
 
		/* Compute actual distance (between old camera and new target) */
		/*xNorm = MTH3D_M_xNormVector(&stCamTargetVector);*/
		xNorm = MTH3D_M_xSqrVector(&stCamTargetVector);
 
		/* Compute ideal distance */
		xDistMin = _p_stStruct->hCineinfoWork->xDistMin;
		xDistMax = _p_stStruct->hCineinfoWork->xDistMax;
 
		/* Test norm */
		if (MTH_M_bLess( xNorm, MTH_M_xSqr(xDistMin)))
			xNorm = xDistMin;
		else if (MTH_M_bGreater(xNorm, MTH_M_xSqr(xDistMax)))
			xNorm = xDistMax;
		else
			xNorm = MTH_M_xSqrt(xNorm);
 
		if (MTH_M_bLessEqual(xNorm,  MTH_M_xFloatToReal(0.01f)))
			xNorm = MTH_M_xFloatToReal(0.01f);
 
		/* 
		 * Compute new wanted camera position
		 */
		MTH3D_M_vMulScalarVector(&stDir, xNorm, &stDir);
 
		MTH3D_M_vAddVector(&_p_stStruct->stWantedCameraPos, &_p_stStruct->stTargetedPersoPos, &stDir);
		CAM_fn_vTakeCareOfCameraAltitude(_p_stStruct->hCineinfo, &_p_stStruct->stWantedCameraPos);
		MTH3D_M_vSubVector(&_p_stStruct->stCptPos.stMovePos, &_p_stStruct->stWantedCameraPos, &_p_stStruct->stCurrentCameraPos);
	}
}
 
/*
 *=================================================================================================
 * Compute move vector of camera with dynamic theta.
 *=================================================================================================
 */
void CAM_fn_vComputeMovePosWithDynTheta(CAM_tdstUpdateCamera *_p_stStruct)
{
	CAM_fn_vComputeMovePos(_p_stStruct);
	if
	(
		(!((_p_stStruct)->hCineinfo->ucVolIAFlags & C_VolIAFlags_InitJustCopiedInCurrent))
	&&	((_p_stStruct)->hCineinfoWork->hSuperObjectTargeted)
	&&	(!cNoDynChangeTheta)
	)
	{
		if(CAM_fn_cDynChangeTheta(_p_stStruct, &_p_stStruct->stWantedCameraPos))
			CAM_fn_vComputeMovePos(_p_stStruct);
	}
}
 
/*
 **************************************************************************************************
 **************************************************************************************************
	SPEED
 **************************************************************************************************
 **************************************************************************************************
 */
 
 
/*
 *=================================================================================================
 *	Compute the speed and accel of the camera.
 *=================================================================================================
 */
void CAM_fn_vComputeSpeed(CAM_tdstUpdateCamera *_p_stStruct)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector			stTempVector, stTempVector1, stMoveTgt;
	MTH_tdxReal					xNorm, xAngle, xDot, xNorm2;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/* 
	 * If no shift is set, this is a vis failure. We use dynamic speed with inertia
	 * of current cineinfo.
	 */
	/*
	if
	(
		(_p_stStruct->hCineinfo->ucPerIAFlags & C_PerIAFlags_NoShift)
	&&	(_p_stStruct->hCineinfo->eState == CAM_e_State_GoToComputedOptimal)
	)
	{
		_p_stStruct->hCineinfoWork->uwIAFlags |= C_IAFlags_NoDynSpeed;
		_p_stStruct->hCineinfoWork->xLinearIncreaseSpeed = _p_stStruct->hCineinfoCurrent->xLinearIncreaseSpeed;
		_p_stStruct->hCineinfoWork->xLinearDecreaseSpeed = _p_stStruct->hCineinfoCurrent->xLinearDecreaseSpeed;
		_p_stStruct->hCineinfoWork->xAngularIncreaseSpeed = _p_stStruct->hCineinfoCurrent->xAngularIncreaseSpeed;
		_p_stStruct->hCineinfoWork->xAngularDecreaseSpeed = _p_stStruct->hCineinfoCurrent->xAngularDecreaseSpeed;
		_p_stStruct->hCineinfoWork->xTargetIncreaseSpeed = _p_stStruct->hCineinfoCurrent->xTargetIncreaseSpeed;
		_p_stStruct->hCineinfoWork->xTargetDecreaseSpeed = _p_stStruct->hCineinfoCurrent->xTargetDecreaseSpeed;
	}
	*/
	/*
	 *	Speeds are the one of IA.
	 */
	if(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_NoDynSpeed)
	{
		_p_stStruct->stCptPos.xLinearSpeed = _p_stStruct->hCineinfoWork->xLinearSpeed;
		_p_stStruct->stCptPos.xAngularSpeed = _p_stStruct->hCineinfoWork->xAngularSpeed;
		_p_stStruct->stCptPos.xTargetSpeed = _p_stStruct->hCineinfoWork->xTargetSpeed;
		return;
	}
 
	/*
	 *	Linear speed.
	 *	Angular speed.
	 */
	if
	(
		(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_PositionIsAlreadyComputed)
	||	(_p_stStruct->hCineinfoWork->hSuperObjectTargeted == NULL)
	)
	{
		_p_stStruct->stCptPos.xLinearSpeed = _p_stStruct->hCineinfoWork->xLinearSpeed;
		_p_stStruct->stCptPos.xAngularSpeed = _p_stStruct->hCineinfoWork->xAngularSpeed;
	}
	else
	{
		/* LINEAR */
		xNorm = MTH_C_ZERO;
		if(!(_p_stStruct->hCineinfoWork->uwDNMFlags & DNM_CAM_C_NoLinearParsing))
		{
			MTH3D_M_vSubVector(&stTempVector, &_p_stStruct->stWantedCameraPos, &_p_stStruct->stCurrentCameraPos);
			xNorm = MTH3D_M_xNormVector(&stTempVector);
 
			xNorm = MTH_M_xMul(xNorm, CAM_C_xDynSpeed_LinearFactNorm);
			if(MTH_M_bGreater(xNorm, CAM_C_xDynSpeed_LinearMax))
				xNorm = CAM_C_xDynSpeed_LinearMax;
 
			CAM_fn_bPersoIsMoving(_p_stStruct, &stTempVector);
			xNorm2 = MTH3D_M_xNormVector(&stTempVector);
			if(MTH_M_bLess(xNorm, MTH_M_xMul(xNorm2, CAM_C_xDynSpeed_LinearMulPerso)))
				xNorm = MTH_M_xMul(xNorm2, CAM_C_xDynSpeed_LinearMulPerso);
		}
		_p_stStruct->stCptPos.xLinearSpeed = xNorm;
 
		/* ANGULAR */
		if(!(_p_stStruct->hCineinfoWork->uwDNMFlags & DNM_CAM_C_NoAngularParsing))
		{
			xNorm = MTH_C_ZERO;
			MTH3D_M_vSubVector(&stTempVector, &_p_stStruct->stTargetedPersoPos, &_p_stStruct->stCurrentCameraPos);
			if(!MTH3D_M_bIsNullVector(&stTempVector))
			{
				if (!MTH3D_M_bIsNullVector(&stTempVector))
					MTH3D_M_vNormalizeVector(&stTempVector, &stTempVector);
				MTH3D_M_vSubVector(&stTempVector1, &_p_stStruct->stTargetedPersoPos, &_p_stStruct->stWantedCameraPos);
				if (!MTH3D_M_bIsNullVector(&stTempVector1))
					MTH3D_M_vNormalizeVector(&stTempVector1, &stTempVector1);
				xNorm = CAM_fn_xComputeRotation(&stTempVector, &stTempVector1, NULL);
			}
 
			xNorm = MTH_M_xMul(xNorm, CAM_C_xDynSpeed_AngularFactNorm);
			if(MTH_M_bGreater(xNorm, CAM_C_xDynSpeed_AngularMax))
				xNorm = CAM_C_xDynSpeed_AngularMax;
 
			CAM_fn_bPersoIsTurning(_p_stStruct->hCineinfoWork->hSuperObjectTargeted, &xAngle);
			if(MTH_M_bLess(xNorm, MTH_M_xMul(xAngle, CAM_C_xDynSpeed_AngularMulPerso)))
				xNorm = xAngle;
 
			/* Decrease speed depending on distance to target (regardless of ideal distance) */
			xAngle = _p_stStruct->hCineinfoWork->xDistMin;
			MTH3D_M_vSubVector(&stTempVector, &_p_stStruct->stTargetedPersoPos, &_p_stStruct->stCurrentCameraPos);
			xAngle = MTH_M_xSub(xAngle, MTH3D_M_xNormVector(&stTempVector));
			if(MTH_M_bGreater(xAngle, MTH_C_ONE))
				xNorm = MTH_M_xDiv(xNorm, xAngle);
			else if(MTH_M_bLess(xAngle, MTH_M_xNeg(MTH_C_ONE)))
				xNorm = MTH_M_xMul(xNorm, MTH_M_xAbs(xAngle));
		}
		_p_stStruct->stCptPos.xAngularSpeed = xNorm;
	}
 
	/*
	 *	Target speed.
	 */
	if
	(
		(_p_stStruct->hCineinfo->ucPerIAFlags & C_IAFlags_TargetIsAlreadyComputed)
	||	(_p_stStruct->hCineinfoWork->hSuperObjectTargeted == NULL)
	)
	{
		_p_stStruct->stCptPos.xTargetSpeed = _p_stStruct->hCineinfoWork->xTargetSpeed;
	}
	else
	{
		xNorm = MTH_C_ZERO;
		MTH3D_M_vNullVector(&stMoveTgt);
		if(!(_p_stStruct->hCineinfoWork->uwDNMFlags & DNM_CAM_C_NoTargetParsing))
		{
			MTH3D_M_vCopyVector(&stTempVector, DNM_M_p_stCPDGetTarget(fn_p_stDynamGetParsingDatas(M_GetMSHandle(_p_stStruct->hSuperObjCamera, Dynam))));
			MTH3D_M_vSubVector
			(
				&stTempVector, 
				&_p_stStruct->stCptPos.stTarget, 
				&stTempVector
			);
			xNorm = MTH3D_M_xNormVector(&stTempVector);
			MTH3D_M_vCopyVector(&stMoveTgt, &stTempVector);
		}
 
		xNorm = MTH_M_xMul(xNorm, CAM_C_xDynSpeed_TargetFactNorm);
		if(MTH_M_bGreater(xNorm, CAM_C_xDynSpeed_TargetMax))
			xNorm = CAM_C_xDynSpeed_TargetMax;
 
		/* Norm must be greater than current perso speed */
		CAM_fn_bPersoIsMoving(_p_stStruct, &stTempVector);
		if(MTH_M_bLess(xNorm, MTH_M_xMul(MTH3D_M_xNormVector(&stTempVector), CAM_C_xDynSpeed_TargetMulPerso)))
			xNorm = MTH_M_xMul(MTH3D_M_xNormVector(&stTempVector), CAM_C_xDynSpeed_TargetMulPerso);
 
		_p_stStruct->stCptPos.xTargetSpeed = xNorm;
 
		/* 
		 * Modifs inertie.
		 */
		if((!MTH3D_M_bIsNullVector(&stMoveTgt)) && (!MTH3D_M_bIsNullVector(DNM_M_p_stReportGetLastTgtMove(_p_stStruct->p_stReport))))
		{
			xDot = MTH3D_M_xDotProductVector(&stMoveTgt, DNM_M_p_stReportGetLastTgtMove(_p_stStruct->p_stReport));
			if(MTH_M_bLess(xDot, -0.9f))
			{
				_p_stStruct->hCineinfoWork->xTargetDecreaseSpeed = 40;
				_p_stStruct->hCineinfoWork->xTargetIncreaseSpeed = 40;
			}
		}
	}
}
 
 
/*
 **************************************************************************************************
 **************************************************************************************************
	MOVE CONSTRAINT
 **************************************************************************************************
 **************************************************************************************************
 */
 
 
/*
 *=================================================================================================
 *	Determin if it's not better to not move.
 *	This function is called to avoid camera to move on the same side where the visiblity failed a
 *	short time after the failure.
 *=================================================================================================
 */
void CAM_fn_vConstraintMoveAfterVisFailure(CAM_tdstUpdateCamera *_p_stStruct)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector			stTempVector, stTempVector1;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/*
	 *	If perso is moving (relative to its father), projection of current moving camera pos to axe 
	 *	(lastfailed wanted - lastfailed origin).
	 */
	if((_p_stStruct->cTgtPersoIsMovingRelative) && (!(CONSTANT_NOVISPROJ)))
	{
		MTH3D_M_vSubVector(&stTempVector, &_p_stStruct->stWantedCameraPos, &_p_stStruct->hCineinfo->stLastFailedWanted);
		MTH3D_M_vSubVector(&stTempVector1, &_p_stStruct->stTargetedPersoPos, &_p_stStruct->hCineinfo->stLastFailedWanted);
		if((!MTH3D_M_bIsNullVector(&stTempVector)) && (!MTH3D_M_bIsNullVector(&stTempVector1)))
		{
			MTH3D_M_vNormalizeVector(&stTempVector1, &stTempVector1);
			MTH3D_M_vMulScalarVector(&stTempVector, MTH3D_M_xDotProductVector(&stTempVector, &stTempVector1), &stTempVector1);
			MTH3D_M_vAddVector(&stTempVector, &stTempVector, &_p_stStruct->hCineinfo->stLastFailedWanted);
		}
		else
		{
			MTH3D_M_vCopyVector(&stTempVector, &_p_stStruct->hCineinfo->stLastFailedWanted);
		}
	}
 
	/*
	 *	Previous wanted position became the new ideal one (so camera will go to the position computed just
	 *	after the failure).
	 */
	else
	{
		MTH3D_M_vCopyVector(&stTempVector, &_p_stStruct->hCineinfo->stLastFailedWanted);
	}
 
	/*
	 *	stTempVector is the position we want to go now. We compute movepos and we set Cineinfo current
	 *	to be at this new position.
	 */
	MTH3D_M_vSubVector(&_p_stStruct->stCptPos.stMovePos, &stTempVector, &_p_stStruct->stCurrentCameraPos);
	MTH3D_M_vCopyVector(&_p_stStruct->stWantedCameraPos, &stTempVector);
	CAM_fn_vComputeCineinfoCurrentForAPosition(_p_stStruct, &stTempVector);
 
	MTH3D_M_vCopyVector(&_p_stStruct->hCineinfo->stLastFailedWanted, &_p_stStruct->stWantedCameraPos);
}
 
/*
 *=================================================================================================
 *	Test a position with an angle.
 *=================================================================================================
 */
char CAM_fn_cConstraintMoveCutTestAngle(CAM_tdstUpdateCamera *_p_stStruct, MTH_tdxReal _xAngle)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	struct tdstInternalStructurCineinfo_	stTemp;
	CAM_tdstUpdateCamera					stStruct;
	char									cMode;
	MTH3D_tdstVector						stTempVector, stTempVector1;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/*
	 *	A temporary structure just for our compute.
	 */
	memcpy(&stTemp, _p_stStruct->hCineinfoWork, sizeof(struct tdstInternalStructurCineinfo_));
	stTemp.xAngleAlpha = _xAngle;
	CAM_fn_vInitCameraStructure(_p_stStruct->hSuperObjCamera, &stStruct);
	stStruct.hCineinfoWork = &stTemp;
 
	/*
	 *	Compute move and wanted position to take care of cut angles.
	 */
	CAM_fn_vComputeReferencePoint(&stStruct);
	CAM_fn_vComputeMovePosWithDynTheta(&stStruct);
 
	/*
	 *	If cJustBetterPos is set, we must not go to the sens of the last visibility failure.
	 */
	if(cJustBetterPos)
	{
		MTH3D_M_vSubVector(&stTempVector, &_p_stStruct->hCineinfo->stLastFailedOrigin, &_p_stStruct->stCurrentCameraPos);
		MTH3D_M_vSubVector(&stTempVector1, &_p_stStruct->hCineinfo->stLastFailedOrigin, &stStruct.stWantedCameraPos);
		if(MTH_M_bGreater(MTH3D_M_xNormVector(&stTempVector), MTH3D_M_xNormVector(&stTempVector1)))
			return 0;
	}
 
	/*
	 *	Test if position is correct. If not, requested move is not change.
	 */
	cMode = 0;
	if
	(
		(_p_stStruct->hCineinfo->ucPerIAFlags & C_PerIAFlags_NoShift) 
	&&	(_p_stStruct->hCineinfo->eState == CAM_e_State_GoToComputedOptimal)
	)
	{
		cMode = C_mode_MUSTGO|C_mode_MUSTSEE;
		if(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_NoVisibility)
			cMode &= ~C_mode_MUSTSEE;
	}
	if
	(
		CAM_fn_cIsWantedPosIncorrect
		(
			&stStruct, 
			&stStruct.stWantedCameraPos, 
			cMode
		)
	)
		return 0;
 
	/*
	 *	Else we change the position to take care of the cutted angle.
	 */
	MTH3D_M_vCopyVector(&_p_stStruct->stWantedCameraPos, &stStruct.stWantedCameraPos);
	MTH3D_M_vCopyVector(&_p_stStruct->stCptPos.stMovePos, &stStruct.stCptPos.stMovePos);
	return 1;
}
 
/*
 *=================================================================================================
 *	Function do inverse cut angle (other sens).
 *=================================================================================================
 */
MTH_tdxReal CAM_fn_xOppositeAngle(CAM_tdstUpdateCamera *_p_stStruct)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH_tdxReal		xNewAngle;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	xNewAngle = MTH_M_xMul(CAM_C_xBaseCutAngle, MTH_M_xFloatToReal(2.0f));
	if(MTH_M_bGreater(_p_stStruct->xAngleDeltaAlphaCut, MTH_C_ZERO))
		xNewAngle = MTH_M_xNeg(xNewAngle);
	xNewAngle = MTH_M_xAdd(_p_stStruct->xAngleAlphaCut, xNewAngle);
	xNewAngle = CAM_fn_xSetAngleInInterval02Pi(xNewAngle);
	return xNewAngle;
}
 
/*
 *=================================================================================================
 *	Camera will go to near from targeted perso. Try to go elsewhere.
 *=================================================================================================
 */
void CAM_fn_vConstraintMoveCutAngles(CAM_tdstUpdateCamera *_p_stStruct)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH_tdxReal		xNewAngle;
	char			cWantedSens;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	/*
	 *	If -2, the last sens is not a sens, and in fact we don't want to cut.
	 */
	if(_p_stStruct->hCineinfo->cLastCutAngleSens == -2)
		return;
 
	if(DNM_M_bReportCollision(_p_stStruct->p_stReport) && (!(CONSTANT_CUT_COL)))
	{
		_p_stStruct->hCineinfo->cLastCutAngleSens = -2;
		return;
	}
 
	if
	(
		(MTH_M_bLess(MTH_M_xMul(_p_stStruct->hCineinfo->xLastDeltaCut, _p_stStruct->xAngleDeltaAlphaCut), MTH_C_ZERO))
	&&	(MTH_M_bLess(MTH_M_xAbs(_p_stStruct->xAngleDeltaAlphaCut), MTH_M_xSub(MTH_C_Pi, CAM_C_xBaseCutAngle)))
	)
	{
		_p_stStruct->hCineinfo->cLastCutAngleSens = 0;
	}
 
	/*
	 *	Save current delta.
	 */
	if(_p_stStruct->hCineinfo->cLastCutAngleSens == 0)
		_p_stStruct->hCineinfo->xLastDeltaCut = _p_stStruct->xAngleDeltaAlphaCut;
 
	/*
	 *	There's a problem to know in which sens the cut angle will work.
	 *	We force cut angle to go in sens defined by _p_stStruct->hCineinfo->cLastCutAngleSens.
	 *	If it is equal to 0, we can test in the normal sens, and in opposite one if we want
	 *	to go to a position at the opposite of current one.
	 */
 
	xNewAngle = _p_stStruct->xAngleAlphaCut;
	if((_p_stStruct->hCineinfo->cLastCutAngleSens == -1) && (MTH_M_bGreater(_p_stStruct->xAngleDeltaAlphaCut, MTH_C_ZERO)))
		xNewAngle = CAM_fn_xOppositeAngle(_p_stStruct);
	if((_p_stStruct->hCineinfo->cLastCutAngleSens == 1) && (MTH_M_bLess(_p_stStruct->xAngleDeltaAlphaCut, MTH_C_ZERO)))
		xNewAngle = CAM_fn_xOppositeAngle(_p_stStruct);
 
	if(CAM_fn_cConstraintMoveCutTestAngle(_p_stStruct, xNewAngle))
	{
		cWantedSens = _p_stStruct->hCineinfo->cLastCutAngleSens;
		if(cWantedSens == 0)
		{
			/* Force next cut angle to work in the same sens than this one */
			if(MTH_M_bGreater(_p_stStruct->xAngleDeltaAlphaCut, MTH_C_ZERO))
				_p_stStruct->hCineinfo->cLastCutAngleSens = 1;
			else
				_p_stStruct->hCineinfo->cLastCutAngleSens = -1;
		}
		return;
	}
 
	if(_p_stStruct->hCineinfo->cLastCutAngleSens == 0)
	{
		if(MTH_M_bGreaterEqual(MTH_M_xAbs(_p_stStruct->xAngleDeltaAlphaCut), MTH_M_xSub(MTH_C_Pi, CAM_C_xBaseCutAngle)))
		{
			xNewAngle = CAM_fn_xOppositeAngle(_p_stStruct);
			if(CAM_fn_cConstraintMoveCutTestAngle(_p_stStruct, xNewAngle))
			{
				/* Force next cut angle to work in the same sens than this one */
				if(MTH_M_bGreater(_p_stStruct->xAngleDeltaAlphaCut, MTH_C_ZERO))
					_p_stStruct->hCineinfo->cLastCutAngleSens = -1;
				else
					_p_stStruct->hCineinfo->cLastCutAngleSens = 1;
				return;
			}
		}
	}
 
	/* To say that we don't want to cut angle anymore */
	_p_stStruct->hCineinfo->cLastCutAngleSens = -2;
}
 
/*
 *=================================================================================================
 *	Is it better not to move ?
 *=================================================================================================
 */
void CAM_fn_vConstraintNoMove(CAM_tdstUpdateCamera *_p_stStruct, MTH3D_tdstVector *_p_stWanted)
{
#if 0
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector	stTempVector;
	MTH_tdxReal			xCurPound, xWantPound;
	char				cMode;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	MTH3D_M_vSubVector(&stTempVector, &_p_stStruct->stCurrentCameraPos, &_p_stStruct->stTargetedPersoPos);
	xCurPound = CAM_fnx_ComputePosPound(_p_stStruct, &stTempVector);
	MTH3D_M_vSubVector(&stTempVector, _p_stWanted, &_p_stStruct->stTargetedPersoPos);
	xWantPound = CAM_fnx_ComputePosPound(_p_stStruct, &stTempVector);
	if(MTH_M_bLess(xCurPound, xWantPound))
	{
		cMode = C_mode_MUSTSEE;
		if(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_NoVisibility)
			cMode &= ~C_mode_MUSTSEE;
		if(!CAM_fn_cIsWantedPosIncorrect(_p_stStruct, &_p_stStruct->stCurrentCameraPos, cMode))
		{
			MTH3D_M_vCopyVector(&_p_stStruct->stWantedCameraPos, &_p_stStruct->stCurrentCameraPos);
			MTH3D_M_vNullVector(&_p_stStruct->stCptPos.stMovePos);
			CAM_fn_vComputeCineinfoCurrentForAPosition(_p_stStruct, &_p_stStruct->stCurrentCameraPos);
		}
	}
#endif
}
 
/*
 *=================================================================================================
 *	If camera has no moved, and we want to go to the same position than previous one, force
 *	move pos to be NULL.
 *=================================================================================================
 */
void CAM_fn_vConstraintMove(CAM_tdstUpdateCamera *_p_stStruct)
{
	/*
	 *	No constraint the first trame, and if we have force the position.
	 */
	if
	(
		(_p_stStruct->hCineinfo->ucVolIAFlags & C_VolIAFlags_InitJustCopiedInCurrent)
	||	(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_PositionIsAlreadyComputed)
	||	(_p_stStruct->hCineinfoWork->hSuperObjectTargeted == NULL)
	)
		return;
 
	/*
	 *	Determin if we can reset last failed wanted.
	 */
	if(CAM_fn_cCanResetConstraintMoveVisFailure(_p_stStruct, &_p_stStruct->stWantedCameraPos))
	{
		_p_stStruct->hCineinfo->ucPerIAFlags &= ~C_PerIAFlags_ConstraintMoveVisFailure;
	}
 
	/*
	 *	Constraint cause of a special mode.
	 */
	if(_p_stStruct->hCineinfo->ucPerIAFlags & C_PerIAFlags_ConstraintMoveVisFailure)
	{
		CAM_fn_vConstraintMoveAfterVisFailure(_p_stStruct);
		return;
	}
 
	/*
	 *	If we have cut angle, we try to go to the position defined by the cutted angles.
	 *	If we don't want to cut angle, we reset the last sens of cutting.
	 */
	if
	(
		(_p_stStruct->cHasCutAlpha) 
	&&	(!(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_NoParseCutAngle))
	)
	{
		CAM_fn_vConstraintMoveCutAngles(_p_stStruct);
	}
	else
	{
		_p_stStruct->hCineinfo->cLastCutAngleSens = 0;
	}
 
	/*
	 *	Force camera to stay in place if it is not moved
	 */
	if(_p_stStruct->hCineinfo->eState == CAM_e_State_GoToComputedOptimal)
	{
		CAM_fn_vConstraintNoMove(_p_stStruct, &_p_stStruct->stWantedCameraPos);
	}
}
 
/*
 *=================================================================================================
 *	Take care of targeted perso father.
 *=================================================================================================
 */       
void CAM_fn_vTakeCareOfFather(CAM_tdstUpdateCamera *_p_stStruct)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/*	MS_tdxHandleToDynam			h_Dynam;*/
/*	DNM_tdstDynamics			*p_stDynamics;*/
	HIE_tdxHandleToSuperObject	hFather;
/*	POS_tdstCompletePosition	stMatrix;*/
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
	hFather = CAM_fn_hGetFather(_p_stStruct->hCineinfoWork->hSuperObjectTargeted);
/*	if(hFather == NULL)*/
/*		return;*/
/*    h_Dynam = M_GetMSHandle(hFather, Dynam);*/
/*	if(h_Dynam == NULL)*/
/*		return;*/
/*	p_stDynamics = fn_p_stDynamGetDynamics(h_Dynam);  	*/
/*  if(p_stDynamics == NULL)*/
/*		return;*/
 
/*	POS_fn_vInvertMatrix(&stMatrix, DNM_M_p_stDynamicsGetPrevPreviousMatrix(p_stDynamics));*/
/*	POS_fn_vMulMatrixMatrix(&stMatrix, DNM_M_p_stDynamicsGetPreviousMatrix(p_stDynamics), &stMatrix);*/
 
/*	POS_fn_vMulMatrixVertex(&_p_stStruct->hCineinfo->stLastFailedWanted, &stMatrix, &_p_stStruct->hCineinfo->stLastFailedWanted);*/
/*	POS_fn_vMulMatrixVertex(&_p_stStruct->hCineinfo->stLastFailedOrigin, &stMatrix, &_p_stStruct->hCineinfo->stLastFailedOrigin);*/
}
 
 
/*
 **************************************************************************************************
 **************************************************************************************************
	GENERAL MANAGMENT
 **************************************************************************************************
 **************************************************************************************************
 */
 
 
/*
 *=================================================================================================
 *	Main camera function.
 *=================================================================================================
 */
void CAM_fn_vUpdateGeneralCamera(CAM_tdstUpdateCamera *_p_stStruct)
{
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	MTH3D_tdstVector	stMoveVector;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
	
recom: /* C'est ignoble, mais j'assume */
 
	cCanTestStatic = 1;
 
	/*
	 **********************************************************************************************
	 **********************************************************************************************
	 */
 
	/*
	 *	Take care of father to change last computed positions.
	 */
	CAM_fn_vTakeCareOfFather(_p_stStruct);
 
	/*
	 **********************************************************************************************
	 **********************************************************************************************
	 */
 
	/*
	 *	Test if camera is moving. wCounter4 is increased each time camera is not moving, and
	 *	reset to 0 else.
	 */
	MTH3D_M_vCopyVector(&stMoveVector, DNM_M_p_stReportGetLastMove(_p_stStruct->p_stReport));
	if(MTH_M_bLess(MTH3D_M_xNormVector(&stMoveVector), CAM_C_xMinLinearSpeedCamera))
	{

		_p_stStruct->hCineinfo->ucVolIAFlags |= C_VolIAFlags_CameraNotMove;
		/*
		 * We don't increase wCounter4 if it's greater than CAM_C_xTickFindBetterPosIfNoMove.
		 */

/* ANNECY MT - 03/08/99 { */
		/*
		if(_p_stStruct->hCineinfo->wCounter4 < CAM_C_xTickFindBetterPosIfNoMove)
		{
			_p_stStruct->hCineinfo->wCounter4++;
		}
		*/
		if(MTH_M_bLess(_p_stStruct->hCineinfo->xCounter4,CAM_C_xTickTimeFindBetterPosIfNoMove))
		{
			_p_stStruct->hCineinfo->xCounter4 = MTH_M_xAdd(_p_stStruct->hCineinfo->xCounter4,MTH_M_xFloatToReal(g_stEngineStructure.stEngineTimer.ulUsefulDeltaTime));
		}
	}
	else
	{
		/*_p_stStruct->hCineinfo->wCounter4 = 0; */
		_p_stStruct->hCineinfo->xCounter4 = MTH_C_ZERO;
	}
/* END ANNECY MT } */
 
	/*
	 * Special case in flying mode. Force some values.
	 */
	if (g_ucIsEdInGhostMode)
	{
		CAM_fn_vInitCompleteCineinfo(_p_stStruct->hCineinfo);
		memcpy(_p_stStruct->hCineinfo->hWork, _p_stStruct->hCineinfo->hCurrent, sizeof(struct tdstInternalStructurCineinfo_));
		_p_stStruct->hCineinfoWork->xDistMin = MTH_M_xFloatToReal(2.0f);
		_p_stStruct->hCineinfoWork->xDistMax = MTH_M_xFloatToReal(2.0f);
		_p_stStruct->hCineinfoWork->xAngleAlpha = MTH_C_ZERO;
		_p_stStruct->hCineinfoWork->xAngleShiftAlpha = MTH_C_ZERO;
		_p_stStruct->hCineinfoWork->xAngleTheta = MTH_C_ZERO;
		_p_stStruct->hCineinfoWork->xAngleShiftTheta = MTH_C_ZERO;
		_p_stStruct->hCineinfoWork->uwDNMFlags = DNM_CAM_C_NoLinearParsing|DNM_CAM_C_NoAngularParsing|DNM_CAM_C_NoTargetParsing|DNM_CAM_C_NoObstacle;
		_p_stStruct->hCineinfoWork->uwIAFlags = C_IAFlags_NoVisibility|C_IAFlags_NoAverageMoveTgtPerso;
		_p_stStruct->hCineinfo->ucPerIAFlags = 0;
		_p_stStruct->hCineinfo->ucVolIAFlags = 0;
		_p_stStruct->hCineinfo->eState = CAM_e_TempState_GhostMode;
	}
 
	/*
	 *	Camera for cinematic scenary.
	 */
	if
	(
		(_p_stStruct->hCineinfoWork->hSuperObjectTargeted)
	&&	(_p_stStruct->hCineinfoWork->hSecondSuperObjectTargeted)
	)
	{
		_p_stStruct->hCineinfo->ucPerIAFlags &= ~C_PerIAFlags_ConstraintMoveVisFailure;
		_p_stStruct->hCineinfoWork->uwIAFlags |= C_IAFlags_NoVisibility;
		_p_stStruct->hCineinfo->eState = CAM_e_TempState_ApexMode;
	}
 
	/*
	 **********************************************************************************************
	 *	General.
	 **********************************************************************************************
	 */
 
	/*
	 *	Every moving compute must take care of current axis requested. Even if make a position 
	 *	compute with a cineinfo without that flag, we must in fact compute the position with that
	 *	flag (it determins the actual reference axis to compute all the positions).
	 *	This is a global variable, so the computerefaxis will don't care of cineinfowork defined
	 *	in the structure passed to it (look at the function...).
	 *
	 *	The same for dyntheta.
	 */
	cRefAxisIsAlreadyComputed = _p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_RefAxisIsAlreadyComputed ? 1 : 0;
	cNoDynChangeTheta = _p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_NoDynChangeTheta ? 1 : 0;
 
	/*
	 *	- Compute position of targeted perso
	 *	- Compute target
	 *	- Compute the real IA wanted position.
	 */
	CAM_fn_vComputeReferencePoint(_p_stStruct);
	CAM_fn_vComputeTarget(_p_stStruct);
	CAM_fn_vComputeRealWantedPos(_p_stStruct);
 
	/*
	 * Compute camera move.
	 */
	CAM_fn_vComputeMovePos(_p_stStruct);
 
	if
	(
		(!((_p_stStruct)->hCineinfo->ucVolIAFlags & C_VolIAFlags_InitJustCopiedInCurrent))
	&&	((_p_stStruct)->hCineinfoWork->hSuperObjectTargeted)
	&&	(!cNoDynChangeTheta)
	&&	(!(_p_stStruct->hCineinfo->ucPerIAFlags & C_PerIAFlags_ConstraintMoveVisFailure))
	&&	(!(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_PositionIsAlreadyComputed))
	)
	{
		if(CAM_fn_cDynChangeTheta(_p_stStruct, &_p_stStruct->stWantedCameraPos))
			CAM_fn_vComputeMovePos(_p_stStruct);
	}
 
	/*
	 *	Test if we can restore initial shift alpha and theta.
	 *	When camera is to current wanted camera pos, we can restore.
	 */
	if
	(
		(_p_stStruct->hCineinfo->ucPerIAFlags & C_PerIAFlags_NoShift)
	||	(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_NoShiftUntilPosReached)
	)
	{
		if
		(
			(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_TargetIsAlreadyComputed)
		||	(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_PositionIsAlreadyComputed)
		||	(_p_stStruct->hCineinfo->ucVolIAFlags & C_VolIAFlags_InitJustCopiedInCurrent)
		||	CAM_fn_bEqualVectorWithEpsilon
			(
				&_p_stStruct->stCurrentCameraPos,
				&_p_stStruct->stWantedCameraPos,
				CAM_C_xEpsilonForEqualVectors
			)
		)
		{
			_p_stStruct->hCineinfoCurrent->uwIAFlags &= ~C_IAFlags_NoShiftUntilPosReached;
			_p_stStruct->hCineinfoWork->uwIAFlags &= ~C_IAFlags_NoShiftUntilPosReached;
			_p_stStruct->hCineinfo->ucPerIAFlags &= ~C_PerIAFlags_NoShift;
			_p_stStruct->hCineinfo->cLastCutAngleSens = 0;
		}
	}
 
	/*
	 * If current is the same as wanted, force normal state.
	 */
	if
	(
		(CONSTANT_NORMALIFJOIN) 
	&&	(_p_stStruct->hCineinfo->eState == CAM_e_State_GoToComputedOptimal)
	&&	(_p_stStruct->cTgtPersoIsMovingRelative)
	)
	{
		if
		(
			CAM_fn_bEqualVectorWithEpsilon
			(
				&_p_stStruct->stCurrentCameraPos,
				&_p_stStruct->stWantedCameraPos,
				CAM_C_xEpsilonForEqualVectors
			)
		)
		{
			if (_p_stStruct->hCineinfo->ucPerIAFlags & C_PerIAFlags_VisibilityCopied)
				CAM_fn_vSetCineinfoCurrentFromVisibility(_p_stStruct->hCineinfo);
			_p_stStruct->hCineinfo->ucPerIAFlags &= ~C_PerIAFlags_ConstraintMoveVisFailure;
			_p_stStruct->hCineinfo->ucVolIAFlags &= ~C_VolIAFlags_CurrentAlreadyCopiedInWork;
			_p_stStruct->hCineinfo->eState = CAM_e_State_GoToOptimal;
 
			goto recom;
		}
	}
 
	/*
	 **********************************************************************************************
	 *	Process the states.
	 **********************************************************************************************
	 */
 
	/*
	 * Some functions can change the current state to be in failure
	 *--------------------------------------------------------------
	 */
	switch(_p_stStruct->hCineinfo->eState)
	{
		/*
		 * - If target is not seen.
		 * - If camera distance is too near from target.
		 * - If collision is oppsition to current move.
		 */
		case CAM_e_State_GoToOptimal:
		case CAM_e_State_GoToComputedOptimal:
 
			CAM_fn_vTestBadVisibility(_p_stStruct);
			CAM_fn_vTestBadDistance(_p_stStruct);
			CAM_fn_vTestOppositeCollision(_p_stStruct);
			break;
		default:
		  break;
	}
 
	/*
	 * Process of each state.
	 *-----------------------
	 */
	switch(_p_stStruct->hCineinfo->eState)
	{
		/*
		 *	We want to go to the optimal position defined in the MS with all the
		 *	camera parameters.
		 *=========================================================================================
		 */
		case CAM_e_State_GoToOptimal:
 
			CAM_fn_vConstraintMove(_p_stStruct);
			break;
 
		case CAM_e_State_GoToComputedOptimal:
 
			CAM_fn_vCheckForABetterPos(_p_stStruct);
			CAM_fn_vConstraintMove(_p_stStruct);
			break;
 
		/*
		 *	Camera failure with visibility.
		 *=========================================================================================
		 */
		case CAM_e_State_FailureVisibility:
			CAM_fn_vComputeFailureVisibility(_p_stStruct);
			break;
 
		/*
		 *	Apex mode.
		 *=========================================================================================
		 */
 
		case CAM_e_TempState_ApexMode:
 
			_p_stStruct->hCineinfo->eState = CAM_e_State_GoToOptimal;
			break;
 
		/*
		 *	Ghost mode.
		 *=========================================================================================
		 */
		case CAM_e_TempState_GhostMode:
 
			_p_stStruct->hCineinfo->eState = CAM_e_State_GoToOptimal;
			break;

		/* XB 05/05/99 */
		default:
			break;
		/* End XB 05/05/99 */
	}
 
	/*
	 *	Some update at the end.
	 *-------------------------
	 */
 
	/* Compute speed of camera */
	CAM_fn_vComputeSpeed(_p_stStruct);
 
	/* Reset values for visibility */
	if
	(
		(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_TargetIsAlreadyComputed)
	||	(_p_stStruct->hCineinfoWork->uwIAFlags & C_IAFlags_PositionIsAlreadyComputed)
	||	(_p_stStruct->hCineinfo->ucVolIAFlags & C_VolIAFlags_InitJustCopiedInCurrent)
	||	(_p_stStruct->hCineinfo->eState == CAM_e_State_GoToOptimal)
	)
	{
		MTH3D_M_vNullVector(&_p_stStruct->hCineinfo->stLastFailedOrigin);
		MTH3D_M_vNullVector(&_p_stStruct->hCineinfo->stLastFailedWanted);
/* 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 } */
		_p_stStruct->hCineinfo->ucPerIAFlags &= ~C_PerIAFlags_ConstraintMoveVisFailure;
	}
 
	/* Valid for one trame only */
	cJustBetterPos = 0;
}
