void CExactPositioningTrigger::Update( float frameTime, Vec3 userPos, Quat userOrient, bool allowTriggering )
{
	if (m_state == eS_Invalid)
		return;

	CRY_ASSERT(m_pos.IsValid());
	CRY_ASSERT(m_userPos.IsValid());
	CRY_ASSERT(m_orient.IsValid());
	CRY_ASSERT(m_userOrient.IsValid());
	CRY_ASSERT(m_posSize.IsValid());
	CRY_ASSERT(NumberValid(m_cosOrientTolerance));

	CRY_ASSERT(NumberValid(frameTime));
	CRY_ASSERT(userPos.IsValid());
	CRY_ASSERT(userOrient.IsValid());

	m_userPos = userPos;
	m_userOrient = userOrient;

	if (m_state == eS_Initializing)
		m_state = eS_Before;

	Plane threshold;
	threshold.SetPlane( m_orient.GetColumn1(), m_pos );
	if (threshold.DistFromPlane(userPos) >= 0.0f)
	{
		if (m_sideTime < 0.0f)
			m_sideTime = 0.0f;
		else
			m_sideTime += frameTime;
	}
	else
	{
		if (m_sideTime > 0.0f)
			m_sideTime = 0.0f;
		else
			m_sideTime -= frameTime;
	}

	Vec3 curDir = userOrient.GetColumn1();
	Vec3 wantDir = m_orient.GetColumn1();

	if (m_state == eS_Before)
	{
		OBB triggerBox;
		triggerBox.SetOBB( Matrix33(m_orient), m_posSize+Vec3(0.5f,0.5f,0), ZERO );
		if (Overlap::Point_OBB(m_userPos, m_pos, triggerBox))
			m_state = eS_Optimizing;
	}

	if ((m_state == eS_Optimizing) && allowTriggering)
	{
#ifdef INCLUDE_EXACTPOS_DEBUGGING
		bool debug = (CAnimationGraphCVars::Get().m_debugExactPos != 0);
		CPersistantDebug* pPD = CCryAction::GetCryAction()->GetPersistantDebug();
#endif

		Vec3 bump(0.0f, 0.0f, 0.1f);

		Vec3 posDistanceError = m_userPos - m_pos;
		if ( posDistanceError.z > -1.0f && posDistanceError.z < 1.0f )
			posDistanceError.z = 0;

		Vec3 orientFwd = m_orient.GetColumn1(); orientFwd.z = 0.0f; orientFwd.Normalize();
		Vec3 rotAnimMovementWanted = orientFwd * m_animMovementLength;

		Vec3 userFwd = m_userOrient.GetColumn1(); userFwd.z = 0.0f; userFwd.Normalize();
		Vec3 rotAnimMovementUser = userFwd * m_animMovementLength;

		float cosRotError = orientFwd.Dot( userFwd );
		float rotError = CLAMP(m_cosOrientTolerance - cosRotError, 0.0f, 1.0f);
		//Vec3 rotDistanceError = rotAnimMovementUser - rotAnimMovementWanted;

		float fwdDistance = fabsf(orientFwd.Dot( posDistanceError ));
		float sideDistance = max( 0.0f, sqrtf( MAX(0,posDistanceError.GetLengthSquared2D() - sqr(fwdDistance)) ) - m_width );

		float deltaFwd = m_oldFwdDir < fwdDistance ? fwdDistance - m_oldFwdDir : 0.0f;
		m_oldFwdDir = fwdDistance;
		fwdDistance += deltaFwd * 0.5f;
		deltaFwd = max(0.1f, deltaFwd);

		f32 distanceError = sqrtf(sqr(fwdDistance) + sqr(sideDistance)); // posDistanceError.len() * m_distanceErrorFactor;
		f32 temp = 1.0f-sqr(1.0f-rotError*rotError);
		temp = max(temp,0.0f); //never do a sqrtf with a negative value 
		f32 orientError = sqrtf(temp) * m_animMovementLength; // rotDistanceError.len();
		f32 totalDistanceError = distanceError + orientError;
		if (((m_distanceError * 1.05f) < distanceError) && ((m_orientError * 1.05f) < orientError) && (totalDistanceError < deltaFwd) ||
			(totalDistanceError < deltaFwd*0.5f))
		{ // found local minimum in distance error, force triggering.
			m_state = eS_Triggered;
			m_oldFwdDir = 0.0f;

#ifdef INCLUDE_EXACTPOS_DEBUGGING
			if (debug)
			{
				pPD->Begin("AnimationTrigger LocalMinima Triggered", false);
				pPD->AddPlanarDisc(m_pos + bump, 0.0f, m_distanceError, ColorF(0,1,0,0.5), 10.0f);
			}
#endif
		}
		else
		{
			m_distanceError = m_distanceError > distanceError ? distanceError : m_distanceError * 0.999f; // should timeout in ~2 secs. on 50 FPS
			m_orientError = m_orientError > orientError ? orientError : m_orientError - 0.0001f;

#ifdef INCLUDE_EXACTPOS_DEBUGGING
			if (debug)
			{
				pPD->Begin("AnimationTrigger LocalMinima Optimizing", true);
				pPD->AddPlanarDisc(m_pos + bump, 0.0f, m_distanceError, ColorF(1,1,0,0.5), 10.0f);
			}
#endif
		}

#ifdef INCLUDE_EXACTPOS_DEBUGGING
		if (debug)
		{
			pPD->AddLine(m_userPos + bump, m_pos + bump, ColorF(1,0,0,1), 10.0f);
			pPD->AddLine(m_userPos + rotAnimMovementUser + bump, m_pos + rotAnimMovementWanted + bump, ColorF(1,0,0,1), 10.0f);
			pPD->AddLine(m_pos + bump, m_pos + rotAnimMovementWanted + bump, ColorF(1,0.5,0,1), 10.0f);
			pPD->AddLine(m_userPos + bump, m_pos + rotAnimMovementUser + bump, ColorF(1,0.5,0,1), 10.0f);
		}
#endif
	}

	CRY_ASSERT(m_pos.IsValid());
	CRY_ASSERT(m_userPos.IsValid());
	CRY_ASSERT(m_orient.IsValid());
	CRY_ASSERT(m_userOrient.IsValid());
	CRY_ASSERT(m_posSize.IsValid());
	CRY_ASSERT(NumberValid(m_cosOrientTolerance));
}
void CVehicleSeatActionRotateTurret::DoUpdate(const float frameTime)
{
	FUNCTION_PROFILER(GetISystem(), PROFILE_ACTION);

	if (gEnv->IsClient() && m_pVehicle->IsProbablyDistant() && !m_pVehicle->GetGameObject()->IsProbablyVisible())
		return;

	// AI use the aim goal to control their rotation (remote usage too)
	if (!m_aimGoal.IsZero())
	{
		UpdateAimGoal();
	}

	// now update each rotation type
	for (int i = 0; i < eVTRT_NumRotationTypes; ++i)
	{
		if (m_rotations[i].m_pPart)
		{
			MaintainPartRotationWorldSpace((EVehicleTurretRotationType)i);
		}
	}

	// Cache the helper position before applying any rotation
	IActor* pActor        = m_pSeat->GetPassengerActor();
	bool    checkRotation = (m_rotTestHelpers[0] && m_rotTestHelpers[1] && pActor);
	Vec3    oldHelperPos  = checkRotation ? m_rotTestHelpers[1]->GetWorldSpaceTranslation() : Vec3(ZERO);

	Matrix34 oldMatrices[eVTRT_NumRotationTypes];

	for (int i = 0; i < eVTRT_NumRotationTypes; ++i)
	{
		if (m_rotations[i].m_pPart)
		{
			oldMatrices[i] = m_rotations[i].m_pPart->GetLocalBaseTM();
			UpdatePartRotation((EVehicleTurretRotationType)i, frameTime);
		}
	}

	// Check for turret collisions
	if (checkRotation)
	{
		// need to test the new rotations before applying them. Sweep a sphere between the two helpers and check for collisions...
		static IPhysicalEntity* pSkipEntities[10];
		int                     numSkip = m_pVehicle->GetSkipEntities(pSkipEntities, 10);
		primitives::sphere      sphere;
		sphere.center = m_rotTestHelpers[0]->GetWorldSpaceTranslation();
		sphere.r      = m_rotTestRadius;

		geom_contact* pContact = NULL;
		Vec3          dir      = m_rotTestHelpers[1]->GetWorldSpaceTranslation() - sphere.center;
		float         hit      = gEnv->pPhysicalWorld->PrimitiveWorldIntersection(sphere.type, &sphere, dir, ent_static | ent_terrain | ent_rigid | ent_sleeping_rigid, &pContact, 0, (geom_colltype_player << rwi_colltype_bit) | rwi_stop_at_pierceable, 0, 0, 0, pSkipEntities, numSkip);
		if (hit > 0.001f && pContact)
		{
			// there was a collision. check whether the barrel is moving towards the collision point or not... if not, ignore the collision.
#if ENABLE_VEHICLE_DEBUG
			if (VehicleCVars().v_debugdraw > 0)
			{
				CPersistantDebug* pPD = CCryAction::GetCryAction()->GetPersistantDebug();
				pPD->Begin("VehicleCannon", false);

				ColorF col(1.0f, 0.0f, 0.0f, 1.0f);
				if (pContact && hit > 0.0f)
				{
					pPD->AddSphere(pContact->pt, 0.1f, col, 30.0f);
				}
			}
#endif
			Vec3 endPos  = m_rotTestHelpers[1]->GetWorldSpaceTranslation();
			Vec3 moveDir = endPos - oldHelperPos;
			Vec3 hitDir  = pContact->pt - oldHelperPos;

			if (moveDir.Dot(hitDir) > 0.0f)
			{
				// reset as though the rotation never happened.
				for (int i = 0; i < eVTRT_NumRotationTypes; ++i)
				{
					if (m_rotations[i].m_pPart)
					{
						CVehiclePartBase* pPart = m_rotations[i].m_pPart;
						pPart->SetLocalBaseTM(oldMatrices[i]);
						const Matrix34 &worldTM = pPart->GetWorldTM();
						m_rotations[i].m_prevWorldQuat = Quat(worldTM);
						m_rotations[i].m_orientation.Set(Quat(Matrix33(oldMatrices[i])));
					}
				}
			}
		}
	}

	m_aimGoalPriority = 0;
}