bool CIntersectionAssistanceUnit::TestForIntersectionAtLocation(const eTestMethod testMethod, const Matrix34& wMat, EntityId testEntityId, EntityId ignoreEnt, QuatT& outAdjustedResult, const bool bCentreOnFocalEnt /* = false */, bool bRenderOnFail /* = true */, const int index /* = -1*/)
{
    // Build an OOBB that surrounds this entity, test for intersection between that and world
    IEntity* pEntity = gEnv->pEntitySystem->GetEntity(testEntityId);
    if(pEntity)
        {
            IPhysicalEntity* pPhysical = pEntity->GetPhysics();
            if(pPhysical)
                {
                    OBB entOBB;
                    AABB entAABB;
                    pEntity->GetLocalBounds(entAABB);
                    entOBB.SetOBBfromAABB(Quat(IDENTITY), entAABB);

                    // Do Primitive world intersection
                    primitives::box physBox;
                    physBox.bOriented = 1;

                    // LSpace
                    physBox.center = entOBB.c;
                    physBox.Basis = entOBB.m33;
                    physBox.size.x = entOBB.h.x;
                    physBox.size.y = entOBB.h.y;
                    physBox.size.z = entOBB.h.z;

                    // WSpace
                    physBox.center					= wMat.TransformPoint(physBox.center);
                    physBox.Basis					  *= Matrix33(wMat).GetInverted();

                    // Optional tweak - We can get away with a little bit of scaling down (if edges are slightly embedded the physics pushes them out easily)
                    physBox.size = physBox.size.scale(kPhysBoxScaleFactor);

                    // adjust
                    Vec3 vAdjustments(0.0f,0.0f,0.0f);
                    if(bCentreOnFocalEnt && m_focalEntityId)
                        {
                            Vec3 vDesiredPos = CalculateTargetAdjustPoint(pEntity, wMat, physBox.center);
                            vAdjustments = (vDesiredPos - physBox.center);
                            physBox.center += vAdjustments;
                        }

                    IEntity* pIgnoreEnt = gEnv->pEntitySystem->GetEntity(ignoreEnt);
                    IPhysicalEntity* pIgnorePhys = pIgnoreEnt ? pIgnoreEnt->GetPhysics() : NULL;

                    // Test
                    if(testMethod == eTM_Immediate
#ifndef _RELEASE
                            || g_pGameCVars->pl_pickAndThrow.intersectionAssistDebugEnabled >= 1
#endif // #ifndef _RELEASE
                      )
                        {
                            geom_contact *contacts;
                            intersection_params params;
                            float numHits = gEnv->pPhysicalWorld->PrimitiveWorldIntersection(primitives::box::type, &physBox, Vec3(ZERO),
                                            ent_static|ent_terrain, &contacts, 0,
                                            3, &params, 0, 0, &pIgnorePhys, pIgnorePhys ? 1 : 0);

                            // Debug
#ifndef _RELEASE
                            if(g_pGameCVars->pl_pickAndThrow.intersectionAssistDebugEnabled)
                                {

                                    const bool bIntersect = numHits <= 0.0f ? false : true;
                                    if(bRenderOnFail || !bIntersect)
                                        {
                                            const ColorB colorPositive = ColorB(16, 96, 16);
                                            const ColorB colorNegative = ColorB(128, 0, 0);
                                            const ColorB colorSelected = ColorB(0,255,0);

                                            if(numHits > 0.0f)
                                                {
                                                    gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere(contacts->pt, 0.1f, colorPositive);
                                                }

                                            OBB finalOBB;
                                            finalOBB.SetOBB(Matrix33(IDENTITY), physBox.size, Vec3(0.0f,0.0f,0.0f));
                                            Matrix34 drawMat = wMat;
                                            drawMat.AddTranslation(physBox.center - wMat.GetTranslation());
                                            if(index != -1 && index == m_currentBestIndex)
                                                {
                                                    gEnv->pRenderer->GetIRenderAuxGeom()->DrawOBB(finalOBB, drawMat, false, colorSelected, eBBD_Faceted);
                                                }
                                            else
                                                {
                                                    gEnv->pRenderer->GetIRenderAuxGeom()->DrawOBB(finalOBB, drawMat, false, bIntersect ? colorNegative : colorPositive, eBBD_Faceted);
                                                }

                                        }
                                }
#endif //#ifndef RELEASE

                            // If we performed an adjust, make sure we pass out the QuatT representing the FINAL ENTITY POSITION that passed/failed (not the phys box etc)
                            outAdjustedResult.t = wMat.GetTranslation() + vAdjustments;
                            outAdjustedResult.q = Quat(wMat);

#ifndef _RELEASE
                            // allow optional debug drawing of last known good positions by retaining non adjusted position
                            if(g_pGameCVars->pl_pickAndThrow.intersectionAssistDebugEnabled == 1)
                                {
                                    outAdjustedResult.t = wMat.GetTranslation();
                                }
#endif // #ifndef _RELEASE

                            return (numHits > 0.0f);
                        }
                    else
                        {
                            // QUEUE primitive intersection check
                            outAdjustedResult.t = wMat.GetTranslation() + vAdjustments;
                            outAdjustedResult.q = Quat(wMat);
                            CRY_ASSERT(index >= 0);
                            m_intersectionTester.DoCheck(index,physBox,outAdjustedResult,pIgnorePhys);
                            return false;
                        }
                }
        }

    return false;
}
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));
}