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; }