BVB Source Codes

CRYENGINE Show PathObstacles.cpp Source code

Return Download CRYENGINE: download PathObstacles.cpp Source code - Download CRYENGINE Source code - Type:.cpp
  1. // Copyright 2001-2016 Crytek GmbH / Crytek Group. All rights reserved.
  2.  
  3. /********************************************************************
  4.    -------------------------------------------------------------------------
  5.    File name:   PathObstacles.cpp
  6.    $Id$
  7.    Description:
  8.  
  9.    -------------------------------------------------------------------------
  10.    History:
  11.    - ?
  12.  
  13.  *********************************************************************/
  14.  
  15. #include "StdAfx.h"
  16. #include "PathObstacles.h"
  17. #include "AICollision.h"
  18. #include "DebugDrawContext.h"
  19. #include "PipeUser.h"
  20. #include "NavPath.h"
  21.  
  22. #include <Cry3DEngine/I3DEngine.h>
  23.  
  24. #include "Navigation/MNM/MNM.h"
  25. #include "Navigation/NavigationSystem/NavigationSystem.h"
  26.  
  27. #include <numeric>
  28.  
  29. #if defined(GetObject)
  30.         #undef GetObject
  31. #endif
  32.  
  33. static float criticalTopAlt = 0.5f;
  34. static float criticalBaseAlt = 2.0f;
  35.  
  36. static std::vector<Vec3> s_pts;
  37. static std::vector<int> s_addedObstacleIndices;
  38.  
  39. TPathObstacles CPathObstacles::s_pathObstacles;
  40. CPathObstacles::TCachedDynamicObstacleFlags CPathObstacles::s_dynamicObstacleFlags;
  41.  
  42. // NOTE Jun 4, 2007: <pvl> the comparison operators are used just for debugging ATM.
  43. inline bool operator==(const SPathObstacleCircle2D& lhs, const SPathObstacleCircle2D& rhs)
  44. {
  45.         return lhs.center == rhs.center && lhs.radius == rhs.radius;
  46. }
  47.  
  48. inline bool operator==(const SPathObstacleShape2D& lhs, const SPathObstacleShape2D& rhs)
  49. {
  50.         return IsEquivalent(lhs.aabb, rhs.aabb, 0.0f) && lhs.pts == rhs.pts;
  51. }
  52.  
  53. bool operator==(const CPathObstacle& lhs, const CPathObstacle& rhs)
  54. {
  55.         if (lhs.GetType() != rhs.GetType())
  56.                 return false;
  57.  
  58.         CPathObstacle::EPathObstacleType type = lhs.GetType();
  59.  
  60.         switch (type)
  61.         {
  62.         case CPathObstacle::ePOT_Circle2D:
  63.                 return lhs.GetCircle2D() == rhs.GetCircle2D();
  64.                 break;
  65.         case CPathObstacle::ePOT_Shape2D:
  66.                 return lhs.GetShape2D() == rhs.GetShape2D();
  67.                 break;
  68.         default:
  69.                 AIAssert(0 && "Unhandled obstacle type in operator==(CPathObstacle, CPathObstacle)");
  70.                 break;
  71.         }
  72.         return false;
  73. }
  74.  
  75. inline bool operator==(const TPathObstacles& lhs, const TPathObstacles& rhs)
  76. {
  77.         if (lhs.size() != rhs.size())
  78.                 return false;
  79.  
  80.         const int numElems = lhs.size();
  81.  
  82.         for (int i = 0; i < numElems; ++i)
  83.                 if (lhs[i] != rhs[i])
  84.                         return false;
  85.  
  86.         return true;
  87. }
  88.  
  89. // NOTE Jun 4, 2007: <pvl> for consistency checking while debugging
  90. unsigned int SPathObstacleShape2D::GetHash() const
  91. {
  92.         unsigned int hash = 0;
  93.         TVectorOfVectors::const_iterator it = pts.begin();
  94.         TVectorOfVectors::const_iterator end = pts.end();
  95.         for (; it != end; ++it)
  96.                 hash += HashFromVec3(*it, 0.0f, 1.0f);
  97.         return hash;
  98. }
  99.  
  100. unsigned int SPathObstacleCircle2D::GetHash() const
  101. {
  102.         return HashFromVec3(center, 0.0f, 1.0f) + HashFromFloat(radius, 0.0f, 1.0f);
  103. }
  104.  
  105. unsigned int CPathObstacle::GetHash() const
  106. {
  107.         unsigned int hash = HashFromFloat(float (m_type), 0.0f, 1.0f);
  108.  
  109.         switch (m_type)
  110.         {
  111.         case CPathObstacle::ePOT_Circle2D:
  112.                 return hash + GetCircle2D().GetHash();
  113.                 break;
  114.         case CPathObstacle::ePOT_Shape2D:
  115.                 return hash + GetShape2D().GetHash();
  116.                 break;
  117.         default:
  118.                 AIAssert(0 && "Unhandled obstacle type in CPathObstacle::GetHash()");
  119.                 break;
  120.         }
  121.         return 0;
  122. }
  123. /*
  124.    static unsigned int GetHash (const TPathObstacles & obstacles)
  125.    {
  126.    unsigned int hash = 0;
  127.  
  128.    TPathObstacles::const_iterator it = obstacles.begin ();
  129.    TPathObstacles::const_iterator end = obstacles.end ();
  130.    for ( ; it != end; ++it)
  131.     hash += (*it)->GetHash ();
  132.    return hash;
  133.    }
  134.  */
  135. static void ClearObstacles(TPathObstacles& obstacles)
  136. {
  137.         obstacles.clear();
  138. }
  139.  
  140. static void DeepCopyObstacles(TPathObstacles& dst, const TPathObstacles& src)
  141. {
  142.         for (uint32 i = 0; i < src.size(); ++i)
  143.         {
  144.                 dst.push_back(new CPathObstacle(*src[i].get()));
  145.         }
  146. }
  147.  
  148. static bool ObstacleDrawingIsOnForActor(const CAIActor* pAIActor)
  149. {
  150.         if (gAIEnv.CVars.DebugDraw > 0)
  151.         {
  152.                 const char* pathName = gAIEnv.CVars.DrawPathAdjustment;
  153.                 if (*pathName && (!strcmp(pathName, "all") || (pAIActor && !strcmp(pAIActor->GetName(), pathName))))
  154.                         return true;
  155.         }
  156.  
  157.         return false;
  158. }
  159.  
  160. //===================================================================
  161. // CPathObstacle
  162. //===================================================================
  163. CPathObstacle::CPathObstacle(EPathObstacleType type) : m_type(type)
  164. {
  165.         switch (m_type)
  166.         {
  167.         case ePOT_Circle2D:
  168.                 m_pData = new SPathObstacleCircle2D;
  169.                 break;
  170.         case ePOT_Shape2D:
  171.                 m_pData = new SPathObstacleShape2D;
  172.                 break;
  173.         case ePOT_Unset:
  174.                 m_pData = 0;
  175.                 break;
  176.         default:
  177.                 m_pData = 0;
  178.                 AIError("CPathObstacle: Unhandled type: %d", type);
  179.                 break;
  180.         }
  181. }
  182.  
  183. //===================================================================
  184. // CPathObstacle
  185. //===================================================================
  186. CPathObstacle::~CPathObstacle()
  187. {
  188.         Free();
  189. }
  190.  
  191. void CPathObstacle::Free()
  192. {
  193.         if (m_pData)
  194.         {
  195.                 switch (m_type)
  196.                 {
  197.                 case ePOT_Circle2D:
  198.                         delete &GetCircle2D();
  199.                         break;
  200.                 case ePOT_Shape2D:
  201.                         delete &GetShape2D();
  202.                         break;
  203.                 default:
  204.                         AIError("~CPathObstacle Unhandled type: %d", m_type);
  205.                         break;
  206.                 }
  207.                 m_type = ePOT_Unset;
  208.                 m_pData = 0;
  209.         }
  210. }
  211.  
  212. //===================================================================
  213. // operator=
  214. //===================================================================
  215. CPathObstacle& CPathObstacle::operator=(const CPathObstacle& other)
  216. {
  217.         if (this == &other)
  218.                 return *this;
  219.         Free();
  220.         m_type = other.GetType();
  221.         switch (m_type)
  222.         {
  223.         case ePOT_Circle2D:
  224.                 m_pData = new SPathObstacleCircle2D;
  225.                 *((SPathObstacleCircle2D*) m_pData) = other.GetCircle2D();
  226.                 break;
  227.         case ePOT_Shape2D:
  228.                 m_pData = new SPathObstacleShape2D;
  229.                 *((SPathObstacleShape2D*) m_pData) = other.GetShape2D();
  230.                 break;
  231.         default:
  232.                 AIError("CPathObstacle::operator= Unhandled type: %d", m_type);
  233.                 return *this;
  234.         }
  235.         return *this;
  236. }
  237.  
  238. //===================================================================
  239. // CPathObstacle
  240. //===================================================================
  241. CPathObstacle::CPathObstacle(const CPathObstacle& other)
  242. {
  243.         m_type = ePOT_Unset;
  244.         m_pData = 0;
  245.         *this = other;
  246. }
  247.  
  248. //===================================================================
  249. // SCachedObstacle
  250. //===================================================================
  251. //#define CHECK_CACHED_OBST_CONSISTENCY
  252. struct SCachedObstacle
  253. {
  254.         void Reset()
  255.         {
  256.                 entity = 0;
  257.                 entityHash = 0;
  258.                 extraRadius = 0.0f;
  259.                 shapes.clear();
  260. #ifdef CHECK_CACHED_OBST_CONSISTENCY
  261.                 shapesHash = 0;
  262. #endif
  263.                 debugBoxes.clear();
  264.                 stl::free_container(shapes);
  265.         }
  266.  
  267.         IPhysicalEntity*                       entity;
  268.         unsigned                               entityHash;
  269.         float                                  extraRadius;
  270.         TPathObstacles                         shapes;
  271. #ifdef CHECK_CACHED_OBST_CONSISTENCY
  272.         unsigned                               shapesHash;
  273. #endif
  274.         std::vector<CPathObstacles::SDebugBox> debugBoxes;
  275. };
  276.  
  277. /// In a suit_demonstration level 32 results in about 80% cache hits
  278. int CPathObstacles::s_obstacleCacheSize = 32;
  279. CPathObstacles::TCachedObstacles CPathObstacles::s_cachedObstacles;
  280.  
  281. //===================================================================
  282. // GetOrClearCachedObstacle
  283. //===================================================================
  284. SCachedObstacle* CPathObstacles::GetOrClearCachedObstacle(IPhysicalEntity* entity, float extraRadius)
  285. {
  286.         if (s_obstacleCacheSize == 0)
  287.                 return 0;
  288.  
  289.         unsigned entityHash = GetHashFromEntities(&entity, 1);
  290.  
  291.         const TCachedObstacles::reverse_iterator itEnd = s_cachedObstacles.rend();
  292.         const TCachedObstacles::reverse_iterator itBegin = s_cachedObstacles.rbegin();
  293.         for (TCachedObstacles::reverse_iterator it = s_cachedObstacles.rbegin(); it != itEnd; ++it)
  294.         {
  295.                 SCachedObstacle& cachedObstacle = **it;
  296.                 if (cachedObstacle.entity != entity || cachedObstacle.extraRadius != extraRadius)
  297.                         continue;
  298.                 if (cachedObstacle.entityHash == entityHash)
  299.                 {
  300.                         s_cachedObstacles.erase(it.base() - 1);
  301.                         s_cachedObstacles.push_back(&cachedObstacle);
  302. #ifdef CHECK_CACHED_OBST_CONSISTENCY
  303.                         if (cachedObstacle.shapesHash != GetHash(cachedObstacle.shapes))
  304.                                 AIError("corrupted obstacle!");
  305. #endif
  306.                         return &cachedObstacle;
  307.                 }
  308.                 delete *(it.base() - 1);
  309.                 s_cachedObstacles.erase(it.base() - 1);
  310.                 return 0;
  311.         }
  312.         return 0;
  313. }
  314.  
  315. //===================================================================
  316. // AddCachedObstacle
  317. //===================================================================
  318. void CPathObstacles::AddCachedObstacle(struct SCachedObstacle* obstacle)
  319. {
  320.         if (s_cachedObstacles.size() == s_obstacleCacheSize)
  321.         {
  322.                 delete s_cachedObstacles.front();
  323.                 s_cachedObstacles.erase(s_cachedObstacles.begin());
  324.         }
  325.         if (s_obstacleCacheSize > 0)
  326.                 s_cachedObstacles.push_back(obstacle);
  327. }
  328.  
  329. //===================================================================
  330. // PopOldestCachedObstacle
  331. //===================================================================
  332. struct SCachedObstacle* CPathObstacles::GetNewCachedObstacle()
  333. {
  334.         if (s_obstacleCacheSize == 0)
  335.                 return 0;
  336.         if ((int)s_cachedObstacles.size() < s_obstacleCacheSize)
  337.                 return new SCachedObstacle();
  338.         SCachedObstacle* obs = s_cachedObstacles.front();
  339.         s_cachedObstacles.erase(s_cachedObstacles.begin());
  340.         obs->Reset();
  341.         return obs;
  342. }
  343.  
  344. //===================================================================
  345. // CombineObstacleShape2DPair
  346. //===================================================================
  347. static bool CombineObstacleShape2DPair(SPathObstacleShape2D& shape1, SPathObstacleShape2D& shape2)
  348. {
  349.         if (!Overlap::Polygon_Polygon2D<TVectorOfVectors>(shape1.pts, shape2.pts, &shape1.aabb, &shape2.aabb))
  350.                 return false;
  351.  
  352.         const float boundingBox1z = shape1.aabb.GetCenter().z;
  353.         const float boundingBox2z = shape2.aabb.GetCenter().z;
  354.         const float maxZDifferenceToCombineObstacles = 1.5f;
  355.         const bool shapesShouldBeCombined = abs(boundingBox1z - boundingBox2z) < maxZDifferenceToCombineObstacles;
  356.         if (shapesShouldBeCombined)
  357.         {
  358.                 shape2.pts.insert(shape2.pts.end(), shape1.pts.begin(), shape1.pts.end());
  359.                 ConvexHull2D(shape1.pts, shape2.pts);
  360.                 shape1.CalcAABB();
  361.                 return true;
  362.         }
  363.         return false;
  364. }
  365.  
  366. //===================================================================
  367. // CombineObstaclePair
  368. // If possible adds the second obstacle (which may become unuseable) to
  369. // the first and returns true.
  370. // If not possible nothing gets modified and returns false
  371. //===================================================================
  372. static bool CombineObstaclePair(CPathObstaclePtr ob1, CPathObstaclePtr ob2)
  373. {
  374.         if (ob1->GetType() != ob2->GetType())
  375.                 return false;
  376.  
  377.         if (ob1->GetType() == CPathObstacle::ePOT_Shape2D)
  378.                 return CombineObstacleShape2DPair(ob1->GetShape2D(), ob2->GetShape2D());
  379.  
  380.         return false;
  381. }
  382.  
  383. //===================================================================
  384. // CombineObstacles
  385. //===================================================================
  386. static void CombineObstacles(TPathObstacles& combinedObstacles, const TPathObstacles& obstacles)
  387. {
  388.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  389.  
  390.         combinedObstacles = obstacles;
  391.  
  392.         for (TPathObstacles::iterator it = combinedObstacles.begin(); it != combinedObstacles.end(); ++it)
  393.         {
  394.                 if ((*it)->GetType() == CPathObstacle::ePOT_Shape2D)
  395.                         (*it)->GetShape2D().CalcAABB();
  396.         }
  397.  
  398. StartAgain:
  399.         for (TPathObstacles::iterator it = combinedObstacles.begin(); it != combinedObstacles.end(); ++it)
  400.         {
  401.                 for (TPathObstacles::iterator itOther = it + 1; itOther != combinedObstacles.end(); ++itOther)
  402.                 {
  403.                         if (CombineObstaclePair(*it, *itOther))
  404.                         {
  405.                                 itOther->swap(combinedObstacles.back());
  406.                                 combinedObstacles.pop_back();
  407.                                 goto StartAgain;
  408.                         }
  409.                 }
  410.         }
  411. }
  412.  
  413. //===================================================================
  414. // SimplifyObstacle
  415. //===================================================================
  416. static void SimplifyObstacle(CPathObstaclePtr ob)
  417. {
  418.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  419.  
  420.         if (ob->GetType() == CPathObstacle::ePOT_Circle2D)
  421.         {
  422.                 static const float diagScale = 1.0f / sqrtf(2.0f);
  423.                 const SPathObstacleCircle2D& circle = ob->GetCircle2D();
  424.                 CPathObstacle newOb(CPathObstacle::ePOT_Shape2D);
  425.                 SPathObstacleShape2D& shape2D = newOb.GetShape2D();
  426.                 shape2D.pts.push_back(circle.center + diagScale * Vec3(-circle.radius, -circle.radius, 0.0f));
  427.                 shape2D.pts.push_back(circle.center + Vec3(0.0f, -circle.radius, 0.0f));
  428.                 shape2D.pts.push_back(circle.center + diagScale * Vec3(circle.radius, -circle.radius, 0.0f));
  429.                 shape2D.pts.push_back(circle.center + Vec3(circle.radius, 0.0f, 0.0f));
  430.                 shape2D.pts.push_back(circle.center + diagScale * Vec3(circle.radius, circle.radius, 0.0f));
  431.                 shape2D.pts.push_back(circle.center + Vec3(0.0f, circle.radius, 0.0f));
  432.                 shape2D.pts.push_back(circle.center + diagScale * Vec3(-circle.radius, circle.radius, 0.0f));
  433.                 shape2D.pts.push_back(circle.center + Vec3(-circle.radius, 0.0f, 0.0f));
  434.                 *ob = newOb;
  435.         }
  436. }
  437.  
  438. //===================================================================
  439. // SimplifyObstacles
  440. //===================================================================
  441. static void SimplifyObstacles(TPathObstacles& obstacles)
  442. {
  443.         for (TPathObstacles::iterator it = obstacles.begin(); it != obstacles.end(); ++it)
  444.                 SimplifyObstacle(*it);
  445. }
  446.  
  447. //===================================================================
  448. // AddEntityBoxesToObstacles
  449. //===================================================================
  450.  
  451. namespace PathObstaclesPredicates
  452. {
  453. float ReturnZAccumulatedValue(const float& accumulatedValue, const Vec3& position)
  454. {
  455.         return accumulatedValue + position.z;
  456. }
  457.  
  458. struct AssignZValueToVector
  459. {
  460.         AssignZValueToVector(const float zValue)
  461.                 : m_zValue(zValue)
  462.         {
  463.         }
  464.  
  465.         void operator()(Vec3& position)
  466.         {
  467.                 position.z = m_zValue;
  468.         }
  469.  
  470.         const float m_zValue;
  471. };
  472. }
  473.  
  474. bool CPathObstacles::AddEntityBoxesToObstacles(IPhysicalEntity* entity, TPathObstacles& obstacles,
  475.                                                float extraRadius, float terrainZ, const bool debug) const
  476. {
  477.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  478.  
  479.         static int cacheHits = 0;
  480.         static int cacheMisses = 0;
  481.  
  482.         static int numPerOutput = 100;
  483.         if (cacheHits + cacheMisses > numPerOutput)
  484.         {
  485.                 AILogComment("PathObstacles: cache hits = %d misses = %d", cacheHits, cacheMisses);
  486.                 cacheMisses = cacheHits = 0;
  487.         }
  488.  
  489.         SCachedObstacle* cachedObstacle = GetOrClearCachedObstacle(entity, extraRadius);
  490.         if (cachedObstacle)
  491.         {
  492. #ifdef PATHOBSTACLES_DEBUG
  493.                 if (debug)
  494.                         m_debugPathAdjustmentBoxes.insert(m_debugPathAdjustmentBoxes.end(), cachedObstacle->debugBoxes.begin(), cachedObstacle->debugBoxes.end());
  495. #endif
  496.                 obstacles.insert(obstacles.end(), cachedObstacle->shapes.begin(), cachedObstacle->shapes.end());
  497.                 ++cacheHits;
  498.                 return true;
  499.         }
  500.         ++cacheMisses;
  501.  
  502.         // put the obstacles into a temporary and then combine/copy at the end
  503.         ClearObstacles(s_pathObstacles);
  504.  
  505.         s_pts.reserve(256);
  506.         s_pts.clear();
  507.  
  508.         {
  509.                 pe_status_nparts statusNParts;
  510.                 int nParts = entity->GetStatus(&statusNParts);
  511.  
  512.                 pe_status_pos statusPos;
  513.                 if (!entity->GetStatus(&statusPos))
  514.                         return false;
  515.  
  516.                 pe_params_part paramsPart;
  517.                 for (statusPos.ipart = 0, paramsPart.ipart = 0; statusPos.ipart < nParts; ++statusPos.ipart, ++paramsPart.ipart)
  518.                 {
  519.                         if (!entity->GetParams(&paramsPart))
  520.                                 continue;
  521.  
  522.                         if (!(paramsPart.flagsAND & geom_colltype_player))
  523.                                 continue;
  524.  
  525.                         if (!entity->GetStatus(&statusPos))
  526.                                 continue;
  527.  
  528.                         if (!statusPos.pGeomProxy)
  529.                                 continue;
  530.  
  531.                         primitives::box box;
  532.                         statusPos.pGeomProxy->GetBBox(&box);
  533.  
  534.                         Vec3 center = box.center * statusPos.scale;
  535.                         Vec3 size = box.size * statusPos.scale;
  536.                         size += Vec3(extraRadius);
  537.  
  538.                         Vec3 worldCenter = statusPos.pos + statusPos.q * center;
  539.                         Matrix33 orientationTM = Matrix33(statusPos.q) * box.Basis.GetTransposed();
  540.  
  541.                         if ((worldCenter.z <= terrainZ + criticalBaseAlt) || (worldCenter.z >= terrainZ + criticalTopAlt))
  542.                                 worldCenter.z = terrainZ + 0.1f;
  543.  
  544.                         s_pts.push_back(worldCenter + orientationTM * Vec3(size.x, size.y, size.z));
  545.                         s_pts.push_back(worldCenter + orientationTM * Vec3(size.x, size.y, -size.z));
  546.                         s_pts.push_back(worldCenter + orientationTM * Vec3(size.x, -size.y, size.z));
  547.                         s_pts.push_back(worldCenter + orientationTM * Vec3(size.x, -size.y, -size.z));
  548.                         s_pts.push_back(worldCenter + orientationTM * Vec3(-size.x, size.y, size.z));
  549.                         s_pts.push_back(worldCenter + orientationTM * Vec3(-size.x, size.y, -size.z));
  550.                         s_pts.push_back(worldCenter + orientationTM * Vec3(-size.x, -size.y, size.z));
  551.                         s_pts.push_back(worldCenter + orientationTM * Vec3(-size.x, -size.y, -size.z));
  552.                 }
  553.         }
  554.  
  555.         // compute the height of all points
  556.         float pointsMinZ = std::numeric_limits<float>::max();
  557.         float pointsMaxZ = std::numeric_limits<float>::min();
  558.         for (std::vector<Vec3>::const_iterator it = s_pts.begin(); it != s_pts.end(); ++it)
  559.         {
  560.                 pointsMinZ = std::min(pointsMinZ, it->z);
  561.                 pointsMaxZ = std::max(pointsMaxZ, it->z);
  562.         }
  563.  
  564.         s_pathObstacles.push_back(new CPathObstacle(CPathObstacle::ePOT_Shape2D));
  565.         s_pathObstacles.back()->GetShape2D().minZ = pointsMinZ;
  566.         s_pathObstacles.back()->GetShape2D().maxZ = pointsMaxZ;
  567.         ConvexHull2D(s_pathObstacles.back()->GetShape2D().pts, s_pts);
  568.  
  569.         std::vector<Vec3>& points = s_pathObstacles.back()->GetShape2D().pts;
  570.  
  571.         unsigned int pointAmount = points.size();
  572.         if (pointAmount > 0)
  573.         {
  574.                 std::for_each(points.begin(), points.end(), PathObstaclesPredicates::AssignZValueToVector(terrainZ));
  575.         }
  576.  
  577.         AIAssert(!s_pathObstacles.empty());
  578.  
  579.         obstacles.insert(obstacles.end(), s_pathObstacles.begin(), s_pathObstacles.end());
  580.  
  581.         // update cache
  582.         cachedObstacle = GetNewCachedObstacle();
  583.         if (cachedObstacle)
  584.         {
  585.                 cachedObstacle->entity = entity;
  586.                 cachedObstacle->entityHash = GetHashFromEntities(&entity, 1);
  587.                 cachedObstacle->extraRadius = extraRadius;
  588.  
  589.                 cachedObstacle->shapes.insert(cachedObstacle->shapes.end(), s_pathObstacles.begin(), s_pathObstacles.end());
  590. #ifdef CHECK_CACHED_OBST_CONSISTENCY
  591.                 cachedObstacle->shapesHash = GetHash(cachedObstacle->shapes);
  592. #endif
  593.  
  594. #ifdef PATHOBSTACLES_DEBUG
  595.                 if (debug)
  596.                 {
  597.                         uint32 origDebugBoxesSize = m_debugPathAdjustmentBoxes.size();
  598.                         cachedObstacle->debugBoxes.insert(cachedObstacle->debugBoxes.end(), m_debugPathAdjustmentBoxes.begin() + origDebugBoxesSize, m_debugPathAdjustmentBoxes.end());
  599.                 }
  600. #endif
  601.  
  602.                 AddCachedObstacle(cachedObstacle);
  603.         }
  604.         // NOTE Jun 3, 2007: <pvl> mostly for debugging - so that s_pathObstacles
  605.         // doesn't confuse reference counts.  Shouldn't cost more than clearing an
  606.         // already cleared container.  Can be removed if necessary.
  607.         ClearObstacles(s_pathObstacles);
  608.  
  609.         return true;
  610. }
  611.  
  612. //===================================================================
  613. // SSphereSorter
  614. // Arbitrary sort order - but ignores z.
  615. //===================================================================
  616. struct SSphereSorter
  617. {
  618.         bool operator()(const Sphere& lhs, const Sphere& rhs) const
  619.         {
  620.                 if (lhs.center.x < rhs.center.x)
  621.                         return true;
  622.                 else if (lhs.center.x > rhs.center.x)
  623.                         return false;
  624.                 else if (lhs.center.y < rhs.center.y)
  625.                         return true;
  626.                 else if (lhs.center.y > rhs.center.y)
  627.                         return false;
  628.                 else if (lhs.radius < rhs.radius)
  629.                         return true;
  630.                 else if (lhs.radius > rhs.radius)
  631.                         return false;
  632.                 else
  633.                         return false;
  634.         }
  635. };
  636.  
  637. //===================================================================
  638. // SSphereEq
  639. //===================================================================
  640. struct SSphereEq
  641. {
  642.         bool operator()(const Sphere& lhs, const Sphere& rhs) const
  643.         {
  644.                 return IsEquivalent(lhs.center, rhs.center, 0.1f) && fabs(lhs.radius - rhs.radius) < 0.1f;
  645.         }
  646. };
  647.  
  648. //===================================================================
  649. // IsInNavigationMesh
  650. //===================================================================
  651. bool IsInNavigationMesh(const NavigationMeshID meshID, const Vec3& point, const float verticalRangeMeters, const float horizontalRangeMeters)
  652. {
  653.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  654.  
  655.         return gAIEnv.pNavigationSystem->IsLocationInMesh(meshID, point);
  656. }
  657.  
  658. //===================================================================
  659. // GetPathObstacles_AIObject
  660. //===================================================================
  661. void CPathObstacles::GetPathObstacles_AIObject(CAIObject* pObject, SPathObstaclesInfo& pathObstaclesInfo, float maximumDistanceToPath) const
  662. {
  663.         assert(pObject);
  664.  
  665.         // Special case - Ignore actors who are inside vehicles
  666.         CAIActor* pActor = pObject->CastToCAIActor();
  667.         IAIActorProxy* pActorProxy = (pActor ? pActor->GetProxy() : NULL);
  668.         if (!pActorProxy || pActorProxy->GetLinkedVehicleEntityId() == 0)
  669.         {
  670.                 CAIActor::ENavInteraction navInteraction = pathObstaclesInfo.pAIActor ? CAIActor::GetNavInteraction(pathObstaclesInfo.pAIActor, pObject) : CAIActor::NI_IGNORE;
  671.                 if (navInteraction == CAIActor::NI_STEER)
  672.                 {
  673.                         static const float maxSpeedSq = square(0.3f);
  674.                         if (pObject->GetVelocity().GetLengthSquared() <= maxSpeedSq)
  675.                         {
  676.                                 const NavigationMeshID meshID = pathObstaclesInfo.pNavPath->GetMeshID();
  677.                                 const bool usingMNM = (meshID != NavigationMeshID(0));
  678.                                 const Vec3 objectPos = pObject->GetPhysicsPos();
  679.  
  680.                                 const bool considerObject = IsInNavigationMesh(meshID, objectPos, 2.0f, pathObstaclesInfo.minAvRadius + 0.5f);
  681.                                 if (considerObject)
  682.                                 {
  683.                                         Vec3 pathPos(ZERO);
  684.                                         float distAlongPath = 0.0f;
  685.                                         const float distToPath = pathObstaclesInfo.pNavPath->GetDistToPath(pathPos, distAlongPath, objectPos, pathObstaclesInfo.maxDistToCheckAhead, true);
  686.                                         if (distToPath >= 0.0f && distToPath <= maximumDistanceToPath && distToPath <= pathObstaclesInfo.maxPathDeviation)
  687.                                         {
  688.                                                 //FIXME: Don't allocate at run-time for something like this...
  689.                                                 CPathObstaclePtr pPathObstacle = new CPathObstacle(CPathObstacle::ePOT_Circle2D);
  690.                                                 SPathObstacleCircle2D& pathObstacleCircle2D = pPathObstacle->GetCircle2D();
  691.                                                 pathObstacleCircle2D.center = objectPos;
  692.                                                 pathObstacleCircle2D.radius = pathObstaclesInfo.minAvRadius + 0.5f;
  693.  
  694.                                                 pathObstaclesInfo.dynamicObstacleSpheres.push_back(Sphere(pathObstacleCircle2D.center, pathObstacleCircle2D.radius));
  695.  
  696.                                                 pathObstaclesInfo.foundObjectsAABB.Add(objectPos, pathObstaclesInfo.minAvRadius + pObject->GetRadius());
  697.                                                 pathObstaclesInfo.outObstacles.push_back(pPathObstacle);
  698.                                         }
  699.                                 }
  700.                         }
  701.                 }
  702.         }
  703. }
  704.  
  705. //===================================================================
  706. // GetPathObstacles_Vehicle
  707. //===================================================================
  708. void CPathObstacles::GetPathObstacles_Vehicle(CAIObject* pObject, SPathObstaclesInfo& pathObstaclesInfo) const
  709. {
  710.         assert(pObject);
  711.  
  712.         IPhysicalEntity* pPhysicalEntity = pObject->GetPhysics();
  713.         if (pPhysicalEntity)
  714.         {
  715.                 bool bIgnore = false;
  716.  
  717.                 // Ignore all vehicles if we don't care about them, per our avoidance ability, so they are ignored later! (See below)
  718.                 if ((pathObstaclesInfo.movementAbility.avoidanceAbilities & eAvoidance_Vehicles) != eAvoidance_Vehicles)
  719.                 {
  720.                         bIgnore = true;
  721.                 }
  722.                 else
  723.                 {
  724.                         static const float maxSpeedSq = square(0.3f);
  725.                         CAIActor::ENavInteraction navInteraction = pathObstaclesInfo.pAIActor ? CAIActor::GetNavInteraction(pathObstaclesInfo.pAIActor, pObject) : CAIActor::NI_IGNORE;
  726.                         if (navInteraction != CAIActor::NI_STEER || pObject->GetVelocity().GetLengthSquared() > maxSpeedSq)
  727.                         {
  728.                                 bIgnore = true;
  729.                         }
  730.                 }
  731.  
  732.                 // Ignoring it means we treat is as if it was checked, so physical entity check will skip it later.
  733.                 // Not ignoring means we want to use its physical check, so we get an accurate hull shape for it.
  734.                 stl::push_back_unique(bIgnore ? pathObstaclesInfo.checkedPhysicsEntities : pathObstaclesInfo.queuedPhysicsEntities, pPhysicalEntity);
  735.         }
  736. }
  737.  
  738. //===================================================================
  739. // GetPathObstacles_PhysicalEntity
  740. //===================================================================
  741. void CPathObstacles::GetPathObstacles_PhysicalEntity(IPhysicalEntity* pPhysicalEntity,
  742.                                                      SPathObstaclesInfo& pathObstaclesInfo, bool bIsPushable, float fCullShapeScale,
  743.                                                      const CNavPath& navPath) const
  744. {
  745.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  746.         assert(pPhysicalEntity);
  747.  
  748.         if (pPhysicalEntity && !stl::find(pathObstaclesInfo.checkedPhysicsEntities, pPhysicalEntity))
  749.         {
  750.                 pe_params_bbox params_bbox;
  751.                 pPhysicalEntity->GetParams(&params_bbox);
  752.  
  753.                 const Vec3& bboxMin = params_bbox.BBox[0];
  754.                 const Vec3& bboxMax = params_bbox.BBox[1];
  755.  
  756.                 const Vec3 testPosition = 0.5f * (bboxMin + bboxMax);
  757.  
  758.                 // If using MNM and the object was of the type considered for the mesh regeneration
  759.                 // then skip it from the obstacle calculation
  760.                 bool isConsideredInMNMGeneration = NavigationSystemUtils::IsDynamicObjectPartOfTheMNMGenerationProcess(pPhysicalEntity);
  761.                 if (isConsideredInMNMGeneration)
  762.                         return;
  763.  
  764.                 const NavigationMeshID meshID = pathObstaclesInfo.pNavPath->GetMeshID();
  765.  
  766.                 bool usedAsDynamicObstacle = IsPhysicalEntityUsedAsDynamicObstacle(pPhysicalEntity);
  767.                 const bool considerObject = usedAsDynamicObstacle && IsInNavigationMesh(meshID, testPosition, (bboxMax.z - bboxMin.z) * 0.5f, (bboxMax.x - bboxMin.x) * 0.5f);
  768.  
  769.                 if (considerObject)
  770.                 {
  771.                         const float boxRadius = fCullShapeScale * 0.5f * Distance::Point_Point(params_bbox.BBox[0], params_bbox.BBox[1]);
  772.                         const float downwardsCheckDistance = (params_bbox.BBox[1].z - params_bbox.BBox[0].z) + 0.1f;  // allow checking further 10cm into the ground, just to be on the safe side
  773.  
  774.                         Vec3 closestPointOnMesh(ZERO);
  775.                         if (!gAIEnv.pNavigationSystem->GetGroundLocationInMesh(meshID, testPosition, downwardsCheckDistance, 2 * boxRadius, &closestPointOnMesh))
  776.                                 return;
  777.  
  778.                         const float groundZ = closestPointOnMesh.z; // p3DEngine->GetTerrainEleva|tion(testPosition.x, testPosition.y);
  779.                         const float altTop = params_bbox.BBox[1].z - groundZ;
  780.                         const float altBase = params_bbox.BBox[0].z - groundZ;
  781.  
  782.                         if (altTop >= criticalTopAlt && altBase <= criticalBaseAlt)
  783.                         {
  784.  
  785.                                 Vec3 pathPos(ZERO);
  786.                                 float distAlongPath = 0.0f;
  787.                                 const float distToPath = pathObstaclesInfo.pNavPath->GetDistToPath(pathPos, distAlongPath, testPosition, pathObstaclesInfo.maxDistToCheckAhead, true);
  788.                                 if (distToPath >= 0.0f && distToPath <= pathObstaclesInfo.maxPathDeviation)
  789.                                 {
  790.                                         const bool obstacleIsSmall = boxRadius < gAIEnv.CVars.ObstacleSizeThreshold;
  791.                                         const int actorType = pathObstaclesInfo.pAIActor ? pathObstaclesInfo.pAIActor->GetType() : AIOBJECT_ACTOR;
  792.  
  793.                                         float extraRadius = (actorType == AIOBJECT_VEHICLE && obstacleIsSmall ? gAIEnv.CVars.ExtraVehicleAvoidanceRadiusSmall : pathObstaclesInfo.minAvRadius);
  794.                                         if (bIsPushable && pathObstaclesInfo.movementAbility.pushableObstacleWeakAvoidance)
  795.                                         {
  796.                                                 // Partial avoidance - scale down radius
  797.                                                 extraRadius = pathObstaclesInfo.movementAbility.pushableObstacleAvoidanceRadius;
  798.                                         }
  799.                                         const bool debug = ObstacleDrawingIsOnForActor(pathObstaclesInfo.pAIActor);
  800.                                         if (AddEntityBoxesToObstacles(pPhysicalEntity, pathObstaclesInfo.outObstacles, extraRadius, groundZ, debug))
  801.                                         {
  802.                                                 pathObstaclesInfo.foundObjectsAABB.Add(testPosition, pathObstaclesInfo.minAvRadius + boxRadius);
  803.                                                 pathObstaclesInfo.dynamicObstacleSpheres.push_back(Sphere(testPosition, boxRadius));
  804.                                         }
  805.                                 }
  806.                         }
  807.                 }
  808.         }
  809. }
  810.  
  811. //===================================================================
  812. // GetPathObstacles_DamageRegion
  813. //===================================================================
  814. void CPathObstacles::GetPathObstacles_DamageRegion(const Sphere& damageRegionSphere, SPathObstaclesInfo& pathObstaclesInfo) const
  815. {
  816.         Vec3 pathPos(ZERO);
  817.         float distAlongPath = 0.0f;
  818.         float distToPath = pathObstaclesInfo.pNavPath->GetDistToPath(pathPos, distAlongPath, damageRegionSphere.center, pathObstaclesInfo.maxDistToCheckAhead, true);
  819.         if (distToPath >= 0.0f && distToPath <= pathObstaclesInfo.maxPathDeviation)
  820.         {
  821.                 static float safetyRadius = 2.0f;
  822.                 const float actualRadius = safetyRadius + damageRegionSphere.radius * 1.5f; // game code actually uses AABB
  823.  
  824.                 //FIXME: Don't allocate at run-time for something like this...
  825.                 CPathObstaclePtr pPathObstacle = new CPathObstacle(CPathObstacle::ePOT_Circle2D);
  826.                 SPathObstacleCircle2D& pathObstacleCircle2D = pPathObstacle->GetCircle2D();
  827.                 pathObstacleCircle2D.center = damageRegionSphere.center;
  828.                 pathObstacleCircle2D.radius = actualRadius;
  829.  
  830.                 pathObstaclesInfo.dynamicObstacleSpheres.push_back(Sphere(damageRegionSphere.center, damageRegionSphere.radius * 1.5f));
  831.  
  832.                 pathObstaclesInfo.foundObjectsAABB.Add(damageRegionSphere.center, actualRadius);
  833.                 pathObstaclesInfo.outObstacles.push_back(pPathObstacle);
  834.         }
  835. }
  836.  
  837. //===================================================================
  838. // IsObstaclePushable
  839. //===================================================================
  840. bool CPathObstacles::IsObstaclePushable(const CAIActor* pAIActor, IPhysicalEntity* pEntity, SPathObstaclesInfo& pathObstaclesInfo, float& outCullShapeScale) const
  841. {
  842.         assert(pAIActor);
  843.         assert(pEntity);
  844.  
  845.         outCullShapeScale = 1.0f;
  846.  
  847.         pe_params_flags params_flags;
  848.         pEntity->GetParams(&params_flags);
  849.         bool bIsPushable = ((params_flags.flags & pef_pushable_by_players) == pef_pushable_by_players);
  850.  
  851.         // Check mass to see if object needs to be fully avoided
  852.         if (bIsPushable)
  853.         {
  854.                 pe_status_dynamics status_dynamics;
  855.                 pEntity->GetStatus(&status_dynamics);
  856.                 const float fMass = status_dynamics.mass;
  857.  
  858.                 const float fPushableMassMin = pathObstaclesInfo.movementAbility.pushableObstacleMassMin;
  859.                 const float fPushableMassMax = pathObstaclesInfo.movementAbility.pushableObstacleMassMax;
  860.  
  861.                 if (fMass < fPushableMassMin)
  862.                 {
  863.                         // bIsPushable stays true
  864.                         assert(bIsPushable);
  865.                 }
  866.                 else if (fMass > fPushableMassMax)
  867.                 {
  868.                         // Too heavy, so fully avoid it
  869.                         outCullShapeScale = 1.0f;
  870.                         bIsPushable = false;
  871.                 }
  872.                 else
  873.                 {
  874.                         // Somewhere in-between; scale the cull shape to try to avoid it
  875.                         const float fPushableMassRange = fPushableMassMax - fPushableMassMin;
  876.                         outCullShapeScale = (fPushableMassRange > FLT_EPSILON ? (fMass - fPushableMassMin) * __fres(fPushableMassRange) : 1.0f);
  877.                         bIsPushable = false;
  878.                 }
  879.         }
  880.  
  881.         return bIsPushable;
  882. }
  883.  
  884. //===================================================================
  885. // IsPhysicalEntityUsedAsDynamicObstacle
  886. //===================================================================
  887.  
  888. bool CPathObstacles::IsPhysicalEntityUsedAsDynamicObstacle(IPhysicalEntity* pPhysicalEntity) const
  889. {
  890.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  891.  
  892.         assert(pPhysicalEntity);
  893.         bool usedAsDynamicObstacle = true;
  894.         IEntity* pEntity = gEnv->pEntitySystem->GetEntityFromPhysics(pPhysicalEntity);
  895.         if (pEntity)
  896.         {
  897.                 const EntityId entityId = pEntity->GetId();
  898.                 TCachedDynamicObstacleFlags::const_iterator it = s_dynamicObstacleFlags.find(entityId);
  899.                 if (it != s_dynamicObstacleFlags.end())
  900.                 {
  901.                         usedAsDynamicObstacle = it->second;
  902.                 }
  903.                 else
  904.                 {
  905.                         IScriptTable* pScriptTable = pEntity->GetScriptTable();
  906.                         SmartScriptTable pPropertiesTable;
  907.                         if (pScriptTable && pScriptTable->GetValue("Properties", pPropertiesTable))
  908.                         {
  909.                                 SmartScriptTable pAITable;
  910.                                 if (pPropertiesTable->GetValue("AI", pAITable))
  911.                                         pAITable->GetValue("bUsedAsDynamicObstacle", usedAsDynamicObstacle);
  912.                         }
  913.  
  914.                         if (s_dynamicObstacleFlags.size() >= s_maxDynamicObstacleFlags)
  915.                         {
  916.                                 AIWarning("s_dynamicObstacleFlags grows more than %d elements. It will be now cleared.", s_maxDynamicObstacleFlags);
  917.                                 TCachedDynamicObstacleFlags emptyMap;
  918.                                 s_dynamicObstacleFlags.swap(emptyMap);
  919.                         }
  920.  
  921.                         s_dynamicObstacleFlags[entityId] = usedAsDynamicObstacle;
  922.                 }
  923.         }
  924.  
  925.         return usedAsDynamicObstacle;
  926. }
  927.  
  928. //===================================================================
  929. // GetPathObstacles
  930. //===================================================================
  931. void CPathObstacles::GetPathObstacles(TPathObstacles& obstacles, const AgentMovementAbility& movementAbility,
  932.                                       const CNavPath* pNavPath, const CAIActor* pAIActor)
  933. {
  934.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  935.         AIAssert(pNavPath);
  936.  
  937.         ClearObstacles(obstacles);
  938.  
  939.         if (gAIEnv.CVars.AdjustPathsAroundDynamicObstacles == 0)
  940.                 return;
  941.  
  942.         if (pNavPath->Empty())
  943.                 return;
  944.  
  945.         const bool usingMNM = (pNavPath->GetMeshID() != NavigationMeshID(0));
  946.         const IAISystem::ENavigationType navTypeFilter = usingMNM ? (IAISystem::ENavigationType)(IAISystem::NAV_FLIGHT | IAISystem::NAV_VOLUME) : (IAISystem::ENavigationType)(IAISystem::NAV_FLIGHT | IAISystem::NAV_VOLUME | IAISystem::NAV_UNSET);
  947.         const IAISystem::ENavigationType navType = pNavPath->GetPath().front().navType;
  948.  
  949.         if (navType & navTypeFilter)
  950.                 return;
  951.  
  952.         // The information relating to this path obstacles calculation
  953.         static const float maxPathDeviation = 15.0f;
  954.         SPathObstaclesInfo pathObstaclesInfo(obstacles, movementAbility, pNavPath, pAIActor, maxPathDeviation);
  955.  
  956. #ifdef PATHOBSTACLES_DEBUG
  957.         const bool bDebug = ObstacleDrawingIsOnForActor(pAIActor);
  958.         if (bDebug)
  959.                 m_debugPathAdjustmentBoxes.resize(0);
  960. #endif
  961.  
  962.         const float minActorAvRadius = gAIEnv.CVars.MinActorDynamicObstacleAvoidanceRadius;
  963.         const float minVehicleAvRadius = gAIEnv.CVars.ExtraVehicleAvoidanceRadiusBig;
  964.  
  965.         const int actorType = pAIActor ? pAIActor->GetType() : AIOBJECT_ACTOR;
  966.         pathObstaclesInfo.minAvRadius = 0.125f + max((actorType != AIOBJECT_ACTOR ? minVehicleAvRadius : minActorAvRadius), movementAbility.pathRadius);
  967.  
  968.         static const float maxSpeedScale = 1.0f;
  969.         pathObstaclesInfo.maxSpeed = movementAbility.movementSpeeds.GetRange(AgentMovementSpeeds::AMS_COMBAT, AgentMovementSpeeds::AMU_RUN).max;
  970.  
  971.         pathObstaclesInfo.maxDistToCheckAhead = movementAbility.pathRegenIntervalDuringTrace > .0f ?
  972.                                                 maxSpeedScale * pathObstaclesInfo.maxSpeed * movementAbility.pathRegenIntervalDuringTrace :
  973.                                                 pNavPath->GetMaxDistanceFromStart();
  974.  
  975.         const CPipeUser* pipeUser = pAIActor ? pAIActor->CastToCPipeUser() : NULL;
  976.         if (pipeUser && pipeUser->ShouldConsiderActorsAsPathObstacles())
  977.         {
  978.                 //// Check actors
  979.                 if ((movementAbility.avoidanceAbilities & eAvoidance_Actors))
  980.                 {
  981.                         ActorLookUp& lookUp = *gAIEnv.pActorLookUp;
  982.                         size_t activeActorCount = lookUp.GetActiveCount();
  983.  
  984.                         for (size_t actorIndex = 0; actorIndex < activeActorCount; ++actorIndex)
  985.                         {
  986.                                 CAIObject* pObject = lookUp.GetActor<CAIObject>(actorIndex);
  987.                                 assert(pObject);
  988.  
  989.                                 if ((pObject != pAIActor) && (pObject->GetType() == AIOBJECT_ACTOR))
  990.                                 {
  991.                                         const float maximumActorDistanceToPath = 4.0f;
  992.                                         GetPathObstacles_AIObject(pObject, pathObstaclesInfo, maximumActorDistanceToPath);
  993.                                 }
  994.                         }
  995.                 }
  996.         }
  997.  
  998.         // Check vehicles (which are also physical entities)
  999.         {
  1000.                 AutoAIObjectIter pVehicleIter(gAIEnv.pAIObjectManager->GetFirstAIObject(OBJFILTER_TYPE, AIOBJECT_VEHICLE));
  1001.                 while (IAIObject* pObject = pVehicleIter->GetObject())
  1002.                 {
  1003.                         CAIObject* pCObject = (CAIObject*)pObject;
  1004.                         assert(pCObject && pCObject->GetType() == AIOBJECT_VEHICLE);
  1005.  
  1006.                         if (pCObject && pCObject != pAIActor)
  1007.                                 GetPathObstacles_Vehicle(pCObject, pathObstaclesInfo);
  1008.  
  1009.                         pVehicleIter->Next();
  1010.                 }
  1011.         }
  1012.  
  1013.         // Check dynamic physical entities
  1014.         {
  1015.                 AABB obstacleAABB = pNavPath->GetAABB(pathObstaclesInfo.maxDistToCheckAhead);
  1016.                 obstacleAABB.min -= Vec3(maxPathDeviation, maxPathDeviation, maxPathDeviation);
  1017.                 obstacleAABB.max += Vec3(maxPathDeviation, maxPathDeviation, maxPathDeviation);
  1018.  
  1019.                 PhysicalEntityListAutoPtr pEntities;
  1020.                 const uint32 nEntityCount = GetEntitiesFromAABB(pEntities, obstacleAABB, AICE_DYNAMIC);
  1021.                 for (uint32 nEntity = 0; nEntity < nEntityCount; ++nEntity)
  1022.                 {
  1023.                         IPhysicalEntity* pPhysicalEntity = pEntities[nEntity];
  1024.                         assert(pPhysicalEntity && pAIActor);
  1025.  
  1026.                         IPhysicalEntity* pSelfEntity = pAIActor ? pAIActor->GetPhysics(true) : NULL;
  1027.                         if (pSelfEntity && pSelfEntity == pPhysicalEntity)
  1028.                                 continue;
  1029.  
  1030.                         if (pPhysicalEntity)
  1031.                         {
  1032.                                 float fCullShapeScale = 1.0f;
  1033.                                 const bool bIsPushable = IsObstaclePushable(pAIActor, pPhysicalEntity, pathObstaclesInfo, fCullShapeScale);
  1034.  
  1035.                                 if ((bIsPushable && (movementAbility.avoidanceAbilities & eAvoidance_PushableObstacle) == eAvoidance_PushableObstacle) ||
  1036.                                     (!bIsPushable && (movementAbility.avoidanceAbilities & eAvoidance_StaticObstacle) == eAvoidance_StaticObstacle) ||
  1037.                                     stl::find(pathObstaclesInfo.queuedPhysicsEntities, pPhysicalEntity))
  1038.                                 {
  1039.                                         GetPathObstacles_PhysicalEntity(pPhysicalEntity, pathObstaclesInfo, bIsPushable, fCullShapeScale, *pNavPath);
  1040.                                 }
  1041.                         }
  1042.                 }
  1043.         }
  1044.  
  1045.         // Check damage regions
  1046.         if (movementAbility.avoidanceAbilities & eAvoidance_DamageRegion)
  1047.         {
  1048.                 CAISystem* pAISystem = GetAISystem();
  1049.                 const CAISystem::TDamageRegions& damageRegions = pAISystem->GetDamageRegions();
  1050.                 if (!damageRegions.empty())
  1051.                 {
  1052.                         CAISystem::TDamageRegions::const_iterator itDamageRegion = damageRegions.begin();
  1053.                         CAISystem::TDamageRegions::const_iterator itDamageRegionEnd = damageRegions.end();
  1054.                         for (; itDamageRegion != itDamageRegionEnd; ++itDamageRegion)
  1055.                         {
  1056.                                 const Sphere& damageRegionSphere = itDamageRegion->second;
  1057.                                 GetPathObstacles_DamageRegion(damageRegionSphere, pathObstaclesInfo);
  1058.                         }
  1059.                 }
  1060.         }
  1061. }
  1062.  
  1063. //===================================================================
  1064. // CPathObstacles
  1065. //===================================================================
  1066. CPathObstacles::CPathObstacles()
  1067.         : m_lastCalculateTime(0.0f), m_lastCalculatePos(ZERO), m_lastCalculatePathVersion(-1)
  1068. {
  1069. }
  1070.  
  1071. //===================================================================
  1072. // CPathObstacles
  1073. //===================================================================
  1074. CPathObstacles::~CPathObstacles()
  1075. {
  1076. }
  1077.  
  1078. //===================================================================
  1079. // CalculateObstaclesAroundActor
  1080. //===================================================================
  1081. void CPathObstacles::CalculateObstaclesAroundActor(const CPipeUser* pPipeUser)
  1082. {
  1083.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  1084.  
  1085.         IF_UNLIKELY (!pPipeUser)
  1086.                 return;
  1087.  
  1088.         const float criticalDist = 2.0f;
  1089.         const int64 criticalTimeMs = 2000;
  1090.  
  1091.         CTimeValue now(GetAISystem()->GetFrameStartTime());
  1092.         int64 deltaTimeMs = (now - m_lastCalculateTime).GetMilliSecondsAsInt64();
  1093.  
  1094.         Vec3 pipeUserPos = pPipeUser->GetPos();
  1095.  
  1096.         if ((deltaTimeMs < criticalTimeMs)
  1097.             && (pPipeUser->m_Path.GetVersion() == m_lastCalculatePathVersion)
  1098.             && pipeUserPos.IsEquivalent(m_lastCalculatePos, criticalDist))
  1099.         {
  1100.                 return;
  1101.         }
  1102.  
  1103.         m_lastCalculatePos = pipeUserPos;
  1104.         m_lastCalculateTime = now;
  1105.  
  1106.         m_lastCalculatePathVersion = pPipeUser->m_Path.GetVersion();
  1107.  
  1108.         ClearObstacles(m_combinedObstacles);
  1109.         ClearObstacles(m_simplifiedObstacles);
  1110.  
  1111.         GetPathObstacles(m_simplifiedObstacles, pPipeUser->m_movementAbility, &pPipeUser->m_Path, pPipeUser);
  1112.  
  1113.         SimplifyObstacles(m_simplifiedObstacles);
  1114.  
  1115.         // NOTE Mai 24, 2007: <pvl> CombineObstacles() messes up its second argument
  1116.         // so if we're going to debug draw simplified obstacles later, we need to
  1117.         // keep m_simplifiedObstacles intact by passing a copy to CombineObstacles().
  1118.         // TPathObstacles is a container of smart pointers so we need to perform
  1119.         // a proper deep copy.
  1120.         // UPDATE Jun 4, 2007: <pvl> in fact, both arguments of CombineObstacleShape2DPair()
  1121.         // can get changed (the first one gets the convex hull of both and the second one
  1122.         // contains all the points).  So if any obstacle in m_simplifiedObstacles
  1123.         // comes from the cache and overlaps with anything else, it will be corrupted.
  1124.         // So let's just perform a deep copy here every time.
  1125.         TPathObstacles tempSimplifiedObstacles;
  1126.         tempSimplifiedObstacles.reserve(m_simplifiedObstacles.size());
  1127.  
  1128.         DeepCopyObstacles(tempSimplifiedObstacles, m_simplifiedObstacles);
  1129.         CombineObstacles(m_combinedObstacles, tempSimplifiedObstacles);
  1130. }
  1131.  
  1132. //===================================================================
  1133. // CalculateObstaclesAroundLocation
  1134. //===================================================================
  1135. void CPathObstacles::CalculateObstaclesAroundLocation(const Vec3& location, const AgentMovementAbility& movementAbility, const CNavPath* pNavPath)
  1136. {
  1137.         const float criticalDist = 2.0f;
  1138.         const int64 criticalTimeMs = 2000;
  1139.  
  1140.         CTimeValue now(GetAISystem()->GetFrameStartTime());
  1141.         int64 deltaTimeMs = (now - m_lastCalculateTime).GetMilliSecondsAsInt64();
  1142.  
  1143.         // Danny todo: this is/would be a nice optimisation to make - however when finding hidespots
  1144.         // we get here before a path is calculated, so the path obstacle list is invalid (since it
  1145.         // depends on the path).
  1146.         if (deltaTimeMs < criticalTimeMs && pNavPath->GetVersion() == m_lastCalculatePathVersion &&
  1147.             location.IsEquivalent(m_lastCalculatePos, criticalDist))
  1148.                 return;
  1149.  
  1150.         m_lastCalculatePos = location;
  1151.         m_lastCalculateTime = now;
  1152.         m_lastCalculatePathVersion = pNavPath->GetVersion();
  1153.  
  1154.         ClearObstacles(m_combinedObstacles);
  1155.         ClearObstacles(m_simplifiedObstacles);
  1156.  
  1157.         GetPathObstacles(m_simplifiedObstacles, movementAbility, pNavPath, 0);
  1158.         SimplifyObstacles(m_simplifiedObstacles);
  1159.  
  1160.         // NOTE Mai 24, 2007: <pvl> CombineObstacles() messes up its second argument
  1161.         // so if we're going to debug draw simplified obstacles later, we need to
  1162.         // keep m_simplifiedObstacles intact by passing a copy to CombineObstacles().
  1163.         // TPathObstacles is a container of smart pointers so we need to perform
  1164.         // a proper deep copy.
  1165.         // UPDATE Jun 4, 2007: <pvl> in fact, both arguments of CombineObstacleShape2DPair()
  1166.         // can get changed (the first one gets the convex hull of both and the second one
  1167.         // contains all the points).  So if any obstacle in m_simplifiedObstacles
  1168.         // comes from the cache and overlaps with anything else, it will be corrupted.
  1169.         // So let's just perform a deep copy here every time.
  1170.  
  1171.         TPathObstacles tempSimplifiedObstacles;
  1172.         tempSimplifiedObstacles.reserve(m_simplifiedObstacles.size());
  1173.  
  1174.         DeepCopyObstacles(tempSimplifiedObstacles, m_simplifiedObstacles);
  1175.         // ATTN Jun 1, 2007: <pvl> costly!  Please remind me to remove this if I forget somehow! :)
  1176.         //AIAssert (tempSimplifiedObstacles == m_simplifiedObstacles);
  1177.         CombineObstacles(m_combinedObstacles, tempSimplifiedObstacles);
  1178.  
  1179. }
  1180.  
  1181. //===================================================================
  1182. // IsPointInsideObstacles
  1183. //===================================================================
  1184. bool CPathObstacles::IsPointInsideObstacles(const Vec3& pt) const
  1185. {
  1186.         for (TPathObstacles::const_iterator it = m_combinedObstacles.begin(); it != m_combinedObstacles.end(); ++it)
  1187.         {
  1188.                 const CPathObstacle& ob = **it;
  1189.                 if (ob.GetType() != CPathObstacle::ePOT_Shape2D)
  1190.                         continue;
  1191.  
  1192.                 const SPathObstacleShape2D& shape2D = ob.GetShape2D();
  1193.                 if (shape2D.pts.empty())
  1194.                         continue;
  1195.  
  1196.                 if (Overlap::Point_Polygon2D(pt, shape2D.pts, &shape2D.aabb))
  1197.                         return true;
  1198.         }
  1199.         return false;
  1200. }
  1201.  
  1202. //===================================================================
  1203. // IsLineSegmentIntersectingObstaclesOrCloseToThem
  1204. //===================================================================
  1205. bool CPathObstacles::IsLineSegmentIntersectingObstaclesOrCloseToThem(const Lineseg& linesegToTest, float maxDistanceToConsiderClose) const
  1206. {
  1207.         float maxDistanceToConsiderCloseSqr = square(maxDistanceToConsiderClose);
  1208.  
  1209.         for (TPathObstacles::const_iterator it = m_combinedObstacles.begin(); it != m_combinedObstacles.end(); ++it)
  1210.         {
  1211.                 const CPathObstacle& ob = **it;
  1212.                 if (ob.GetType() != CPathObstacle::ePOT_Shape2D)
  1213.                         continue;
  1214.  
  1215.                 const SPathObstacleShape2D& shape2d = ob.GetShape2D();
  1216.                 if (shape2d.pts.empty())
  1217.                         continue;
  1218.  
  1219.                 if (Overlap::Lineseg_Polygon2D(linesegToTest, shape2d.pts, &shape2d.aabb))
  1220.                         return true;
  1221.  
  1222.                 TVectorOfVectors::const_iterator itEnd = shape2d.pts.end();
  1223.                 TVectorOfVectors::const_iterator it1 = shape2d.pts.begin();
  1224.                 TVectorOfVectors::const_iterator it2 = it1;
  1225.                 ++it2;
  1226.  
  1227.                 while (it2 != itEnd)
  1228.                 {
  1229.                         if (Distance::Lineseg_Lineseg2DSq<float>(linesegToTest, Lineseg(*it1, *it2)) <= maxDistanceToConsiderCloseSqr)
  1230.                                 return true;
  1231.                         ++it1;
  1232.                         ++it2;
  1233.                 }
  1234.         }
  1235.         return false;
  1236. }
  1237.  
  1238. //===================================================================
  1239. // IsPathIntersectingObstacles
  1240. //===================================================================
  1241. bool CPathObstacles::IsPathIntersectingObstacles(const NavigationMeshID meshID, const Vec3& start,
  1242.                                                  const Vec3& end, float radius) const
  1243. {
  1244.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  1245.  
  1246.         bool bResult = false;
  1247.  
  1248.         const Lineseg lineseg(start, end);
  1249.         const float radiusSq = radius * radius;
  1250.         for (TPathObstacles::const_iterator it = m_combinedObstacles.begin(); it != m_combinedObstacles.end(); ++it)
  1251.         {
  1252.                 const CPathObstacle& ob = **it;
  1253.                 if (ob.GetType() != CPathObstacle::ePOT_Shape2D)
  1254.                         continue;
  1255.  
  1256.                 //TODO: Investigate - can we make sure no empty shapes are stored in the container, to avoid these checks?
  1257.                 const SPathObstacleShape2D& shape2D = ob.GetShape2D();
  1258.                 if (shape2D.pts.empty())
  1259.                         continue;
  1260.  
  1261.                 Vec3 vIntersectionPoint(ZERO);
  1262.                 if (Overlap::Lineseg_AABB2D(lineseg, shape2D.aabb) &&
  1263.                     Intersect::Lineseg_Polygon2D(lineseg, shape2D.pts, vIntersectionPoint)
  1264.                     )
  1265.                 {
  1266.                         // Francesco: since the obstacle could be on a different layer than where the player is walking
  1267.                         // we need to check if the position on the ground of the intersection with the polygon is at than approximate
  1268.                         // ground height as the obstacle itself
  1269.                         Vec3 groundPointOnMesh(vIntersectionPoint);
  1270.                         gAIEnv.pNavigationSystem->GetGroundLocationInMesh(meshID, vIntersectionPoint, 1.5f, 0.5f, &groundPointOnMesh);
  1271.                         const float obstacleZ = shape2D.aabb.min.z;
  1272.                         const float intersectionPointZ = groundPointOnMesh.z;
  1273.                         const float maxZDifference = 0.5f;
  1274.                         const bool isObstacleOnTheSameLayerAsThePathSegment = abs(intersectionPointZ - obstacleZ) < maxZDifference;
  1275.                         if (!isObstacleOnTheSameLayerAsThePathSegment)
  1276.                                 continue;
  1277.  
  1278.                         // Ignore if shape is within our pass radius or the intersection is close enough to the end
  1279.                         bool bValid = true;
  1280.                         if (radius > FLT_EPSILON)
  1281.                         {
  1282.                                 const float fDistanceToEnd = end.GetSquaredDistance2D(vIntersectionPoint);
  1283.                                 bValid = (fDistanceToEnd > radiusSq || !Overlap::Sphere_AABB2D(Sphere(start, radius), shape2D.aabb));
  1284.                         }
  1285.  
  1286.                         if (bValid)
  1287.                         {
  1288.                                 bResult = true;
  1289.                                 break;
  1290.                         }
  1291.                 }
  1292.         }
  1293.  
  1294.         return bResult;
  1295. }
  1296.  
  1297. //===================================================================
  1298. // GetPointOutsideObstacles
  1299. //===================================================================
  1300. Vec3 CPathObstacles::GetPointOutsideObstacles(const Vec3& pt, float extraDist) const
  1301. {
  1302.         FUNCTION_PROFILER(gEnv->pSystem, PROFILE_AI);
  1303.  
  1304.         Vec3 newPos = pt;
  1305.  
  1306.         for (TPathObstacles::const_iterator it = m_combinedObstacles.begin(); it != m_combinedObstacles.end(); ++it)
  1307.         {
  1308.                 const CPathObstacle& ob = **it;
  1309.                 if (ob.GetType() != CPathObstacle::ePOT_Shape2D)
  1310.                         continue;
  1311.  
  1312.                 const SPathObstacleShape2D& shape2D = ob.GetShape2D();
  1313.                 if (shape2D.pts.empty())
  1314.                         continue;
  1315.  
  1316.                 if (Overlap::Point_Polygon2D(newPos, shape2D.pts, &shape2D.aabb))
  1317.                 {
  1318.                         (void) Distance::Point_Polygon2DSq(newPos, shape2D.pts, newPos);
  1319.                         newPos.z = pt.z;
  1320.                         Vec3 polyMidPoint = std::accumulate(shape2D.pts.begin(), shape2D.pts.end(), Vec3(ZERO)) / (float)shape2D.pts.size();
  1321.                         Vec3 dir = (newPos - polyMidPoint);
  1322.                         dir.z = 0.0f;
  1323.                         dir.NormalizeSafe();
  1324.                         newPos += extraDist * dir;
  1325.                 }
  1326.         }
  1327.         return newPos;
  1328. }
  1329.  
  1330. //===================================================================
  1331. // Reset
  1332. //===================================================================
  1333. void CPathObstacles::Reset()
  1334. {
  1335. #ifdef PATHOBSTACLES_DEBUG
  1336.         m_debugPathAdjustmentBoxes.clear();
  1337. #endif
  1338.         ClearObstacles(m_simplifiedObstacles);
  1339.         ClearObstacles(m_combinedObstacles);
  1340. }
  1341.  
  1342. void CPathObstacles::ResetOfStaticData()
  1343. {
  1344.         while (!s_cachedObstacles.empty())
  1345.         {
  1346.                 delete s_cachedObstacles.back();
  1347.                 s_cachedObstacles.pop_back();
  1348.         }
  1349.         stl::free_container(s_cachedObstacles);
  1350.         stl::free_container(s_pathObstacles);
  1351.         stl::free_container(s_dynamicObstacleFlags);
  1352.         stl::free_container(s_pts);
  1353.         stl::free_container(s_addedObstacleIndices);
  1354. }
  1355.  
  1356. //===================================================================
  1357. // DebugDraw
  1358. //===================================================================
  1359. void CPathObstacles::DebugDraw() const
  1360. {
  1361. #ifdef PATHOBSTACLES_DEBUG
  1362.         CDebugDrawContext dc;
  1363.  
  1364.         for (unsigned i = 0; i < m_debugPathAdjustmentBoxes.size(); ++i)
  1365.         {
  1366.                 const SDebugBox& box = m_debugPathAdjustmentBoxes[i];
  1367.                 Matrix34 mat34;
  1368.                 mat34.SetRotation33(Matrix33(box.q));
  1369.                 mat34.SetTranslation(box.pos);
  1370.                 dc->DrawOBB(box.obb, mat34, false, ColorB(0, 1, 0), eBBD_Extremes_Color_Encoded);
  1371.                 dc->DrawOBB(box.obb, mat34, false, ColorB(0, 1, 0), eBBD_Extremes_Color_Encoded);
  1372.         }
  1373.  
  1374.         if (gAIEnv.CVars.DebugDraw != 0)
  1375.         {
  1376.                 for (TPathObstacles::const_iterator it = m_simplifiedObstacles.begin(); it != m_simplifiedObstacles.end(); ++it)
  1377.                 {
  1378.                         const CPathObstacle& obstacle = **it;
  1379.                         if (obstacle.GetType() == CPathObstacle::ePOT_Shape2D && !obstacle.GetShape2D().pts.empty())
  1380.                         {
  1381.                                 const TVectorOfVectors& pts = obstacle.GetShape2D().pts;
  1382.                                 for (unsigned i = 0; i < pts.size(); ++i)
  1383.                                 {
  1384.                                         unsigned j = (i + 1) % pts.size();
  1385.                                         Vec3 v1 = pts[i];
  1386.                                         Vec3 v2 = pts[j];
  1387.                                         dc->DrawLine(v1, Col_Red, v2, Col_Red);
  1388.                                 }
  1389.                         }
  1390.                 }
  1391.                 for (TPathObstacles::const_iterator it = m_combinedObstacles.begin(); it != m_combinedObstacles.end(); ++it)
  1392.                 {
  1393.                         ColorF col(1, 1, 1);
  1394.                         const CPathObstacle& obstacle = **it;
  1395.                         if (obstacle.GetType() == CPathObstacle::ePOT_Shape2D && !obstacle.GetShape2D().pts.empty())
  1396.                         {
  1397.                                 const TVectorOfVectors& pts = obstacle.GetShape2D().pts;
  1398.                                 for (unsigned i = 0; i < pts.size(); ++i)
  1399.                                 {
  1400.                                         unsigned j = (i + 1) % pts.size();
  1401.                                         Vec3 v1 = pts[i];
  1402.                                         Vec3 v2 = pts[j];
  1403.                                         dc->DrawLine(v1, Col_White, v2, Col_White);
  1404.  
  1405.                                         // draw 2 more horizontal lines: each at the min. and max. height of the obstacle
  1406.                                         v1.z = obstacle.GetShape2D().minZ;
  1407.                                         v2.z = obstacle.GetShape2D().minZ;
  1408.                                         dc->DrawLine(v1, Col_LightGray, v2, Col_LightGray);
  1409.                                         v1.z = obstacle.GetShape2D().maxZ;
  1410.                                         v2.z = obstacle.GetShape2D().maxZ;
  1411.                                         dc->DrawLine(v1, Col_LightGray, v2, Col_LightGray);
  1412.  
  1413.                                         // draw a vertical line to connect the min. and max. height
  1414.                                         v1.z = obstacle.GetShape2D().minZ;
  1415.                                         v2 = v1;
  1416.                                         v2.z = obstacle.GetShape2D().maxZ;
  1417.                                         dc->DrawLine(v1, Col_LightGray, v2, Col_LightGray);
  1418.                                 }
  1419.                         }
  1420.                 }
  1421.         }
  1422. #endif
  1423. }
  1424.  
downloadPathObstacles.cpp Source code - Download CRYENGINE Source code
Related Source Codes/Software:
postal - 2017-06-11
reactide - Reactide is the first dedicated IDE for React web ... 2017-06-11
rkt - rkt is a pod-native container engine for Linux. It... 2017-06-11
uWebSockets - Tiny WebSockets https://for... 2017-06-11
realworld - TodoMVC for the RealWorld - Exemplary fullstack Me... 2017-06-11
CRYENGINE - CRYENGINE is a powerful real-time game development... 2017-06-11
goreplay - GoReplay is an open-source tool for capturing and ... 2017-06-10
pyenv - Simple Python version management 2017-06-10
redux-saga - An alternative side effect model for Redux apps ... 2017-06-10
angular-starter - 2017-06-10

 Back to top