1 #include "navigatorimpl.hpp"
2 #include "debug.hpp"
3 #include "settingsutils.hpp"
4 
5 #include <components/esm/loadpgrd.hpp>
6 #include <components/misc/coordinateconverter.hpp>
7 
8 namespace DetourNavigator
9 {
NavigatorImpl(const Settings & settings)10     NavigatorImpl::NavigatorImpl(const Settings& settings)
11         : mSettings(settings)
12         , mNavMeshManager(mSettings)
13         , mUpdatesEnabled(true)
14     {
15     }
16 
addAgent(const osg::Vec3f & agentHalfExtents)17     void NavigatorImpl::addAgent(const osg::Vec3f& agentHalfExtents)
18     {
19         if(agentHalfExtents.length2() <= 0)
20             return;
21         ++mAgents[agentHalfExtents];
22         mNavMeshManager.addAgent(agentHalfExtents);
23     }
24 
removeAgent(const osg::Vec3f & agentHalfExtents)25     void NavigatorImpl::removeAgent(const osg::Vec3f& agentHalfExtents)
26     {
27         const auto it = mAgents.find(agentHalfExtents);
28         if (it == mAgents.end())
29             return;
30         if (it->second > 0)
31             --it->second;
32     }
33 
addObject(const ObjectId id,const osg::ref_ptr<const osg::Object> & holder,const btHeightfieldTerrainShape & shape,const btTransform & transform)34     bool NavigatorImpl::addObject(const ObjectId id, const osg::ref_ptr<const osg::Object>& holder,
35         const btHeightfieldTerrainShape& shape, const btTransform& transform)
36     {
37         const CollisionShape collisionShape {holder, shape};
38         return mNavMeshManager.addObject(id, collisionShape, transform, AreaType_ground);
39     }
40 
addObject(const ObjectId id,const ObjectShapes & shapes,const btTransform & transform)41     bool NavigatorImpl::addObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform)
42     {
43         const CollisionShape collisionShape {shapes.mShapeInstance, *shapes.mShapeInstance->getCollisionShape()};
44         bool result = mNavMeshManager.addObject(id, collisionShape, transform, AreaType_ground);
45         if (const btCollisionShape* const avoidShape = shapes.mShapeInstance->getAvoidCollisionShape())
46         {
47             const ObjectId avoidId(avoidShape);
48             const CollisionShape collisionShape {shapes.mShapeInstance, *avoidShape};
49             if (mNavMeshManager.addObject(avoidId, collisionShape, transform, AreaType_null))
50             {
51                 updateAvoidShapeId(id, avoidId);
52                 result = true;
53             }
54         }
55         return result;
56     }
57 
addObject(const ObjectId id,const DoorShapes & shapes,const btTransform & transform)58     bool NavigatorImpl::addObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform)
59     {
60         if (addObject(id, static_cast<const ObjectShapes&>(shapes), transform))
61         {
62             const osg::Vec3f start = toNavMeshCoordinates(mSettings, shapes.mConnectionStart);
63             const osg::Vec3f end = toNavMeshCoordinates(mSettings, shapes.mConnectionEnd);
64             mNavMeshManager.addOffMeshConnection(id, start, end, AreaType_door);
65             mNavMeshManager.addOffMeshConnection(id, end, start, AreaType_door);
66             return true;
67         }
68         return false;
69     }
70 
updateObject(const ObjectId id,const ObjectShapes & shapes,const btTransform & transform)71     bool NavigatorImpl::updateObject(const ObjectId id, const ObjectShapes& shapes, const btTransform& transform)
72     {
73         const CollisionShape collisionShape {shapes.mShapeInstance, *shapes.mShapeInstance->getCollisionShape()};
74         bool result = mNavMeshManager.updateObject(id, collisionShape, transform, AreaType_ground);
75         if (const btCollisionShape* const avoidShape = shapes.mShapeInstance->getAvoidCollisionShape())
76         {
77             const ObjectId avoidId(avoidShape);
78             const CollisionShape collisionShape {shapes.mShapeInstance, *avoidShape};
79             if (mNavMeshManager.updateObject(avoidId, collisionShape, transform, AreaType_null))
80             {
81                 updateAvoidShapeId(id, avoidId);
82                 result = true;
83             }
84         }
85         return result;
86     }
87 
updateObject(const ObjectId id,const DoorShapes & shapes,const btTransform & transform)88     bool NavigatorImpl::updateObject(const ObjectId id, const DoorShapes& shapes, const btTransform& transform)
89     {
90         return updateObject(id, static_cast<const ObjectShapes&>(shapes), transform);
91     }
92 
removeObject(const ObjectId id)93     bool NavigatorImpl::removeObject(const ObjectId id)
94     {
95         bool result = mNavMeshManager.removeObject(id);
96         const auto avoid = mAvoidIds.find(id);
97         if (avoid != mAvoidIds.end())
98             result = mNavMeshManager.removeObject(avoid->second) || result;
99         const auto water = mWaterIds.find(id);
100         if (water != mWaterIds.end())
101             result = mNavMeshManager.removeObject(water->second) || result;
102         mNavMeshManager.removeOffMeshConnections(id);
103         return result;
104     }
105 
addWater(const osg::Vec2i & cellPosition,const int cellSize,const btScalar level,const btTransform & transform)106     bool NavigatorImpl::addWater(const osg::Vec2i& cellPosition, const int cellSize, const btScalar level,
107         const btTransform& transform)
108     {
109         return mNavMeshManager.addWater(cellPosition, cellSize,
110             btTransform(transform.getBasis(), btVector3(transform.getOrigin().x(), transform.getOrigin().y(), level)));
111     }
112 
removeWater(const osg::Vec2i & cellPosition)113     bool NavigatorImpl::removeWater(const osg::Vec2i& cellPosition)
114     {
115         return mNavMeshManager.removeWater(cellPosition);
116     }
117 
addPathgrid(const ESM::Cell & cell,const ESM::Pathgrid & pathgrid)118     void NavigatorImpl::addPathgrid(const ESM::Cell& cell, const ESM::Pathgrid& pathgrid)
119     {
120         Misc::CoordinateConverter converter(&cell);
121         for (auto edge : pathgrid.mEdges)
122         {
123             const auto src = Misc::Convert::makeOsgVec3f(converter.toWorldPoint(pathgrid.mPoints[edge.mV0]));
124             const auto dst = Misc::Convert::makeOsgVec3f(converter.toWorldPoint(pathgrid.mPoints[edge.mV1]));
125             mNavMeshManager.addOffMeshConnection(
126                 ObjectId(&pathgrid),
127                 toNavMeshCoordinates(mSettings, src),
128                 toNavMeshCoordinates(mSettings, dst),
129                 AreaType_pathgrid
130             );
131         }
132     }
133 
removePathgrid(const ESM::Pathgrid & pathgrid)134     void NavigatorImpl::removePathgrid(const ESM::Pathgrid& pathgrid)
135     {
136         mNavMeshManager.removeOffMeshConnections(ObjectId(&pathgrid));
137     }
138 
update(const osg::Vec3f & playerPosition)139     void NavigatorImpl::update(const osg::Vec3f& playerPosition)
140     {
141         if (!mUpdatesEnabled)
142             return;
143         removeUnusedNavMeshes();
144         for (const auto& v : mAgents)
145             mNavMeshManager.update(playerPosition, v.first);
146     }
147 
updatePlayerPosition(const osg::Vec3f & playerPosition)148     void NavigatorImpl::updatePlayerPosition(const osg::Vec3f& playerPosition)
149     {
150         const TilePosition tilePosition = getTilePosition(mSettings, toNavMeshCoordinates(mSettings, playerPosition));
151         if (mLastPlayerPosition.has_value() && *mLastPlayerPosition == tilePosition)
152             return;
153         update(playerPosition);
154         mLastPlayerPosition = tilePosition;
155     }
156 
setUpdatesEnabled(bool enabled)157     void NavigatorImpl::setUpdatesEnabled(bool enabled)
158     {
159         mUpdatesEnabled = enabled;
160     }
161 
wait(Loading::Listener & listener,WaitConditionType waitConditionType)162     void NavigatorImpl::wait(Loading::Listener& listener, WaitConditionType waitConditionType)
163     {
164         mNavMeshManager.wait(listener, waitConditionType);
165     }
166 
getNavMesh(const osg::Vec3f & agentHalfExtents) const167     SharedNavMeshCacheItem NavigatorImpl::getNavMesh(const osg::Vec3f& agentHalfExtents) const
168     {
169         return mNavMeshManager.getNavMesh(agentHalfExtents);
170     }
171 
getNavMeshes() const172     std::map<osg::Vec3f, SharedNavMeshCacheItem> NavigatorImpl::getNavMeshes() const
173     {
174         return mNavMeshManager.getNavMeshes();
175     }
176 
getSettings() const177     const Settings& NavigatorImpl::getSettings() const
178     {
179         return mSettings;
180     }
181 
reportStats(unsigned int frameNumber,osg::Stats & stats) const182     void NavigatorImpl::reportStats(unsigned int frameNumber, osg::Stats& stats) const
183     {
184         mNavMeshManager.reportStats(frameNumber, stats);
185     }
186 
getRecastMeshTiles()187     RecastMeshTiles NavigatorImpl::getRecastMeshTiles()
188     {
189         return mNavMeshManager.getRecastMeshTiles();
190     }
191 
updateAvoidShapeId(const ObjectId id,const ObjectId avoidId)192     void NavigatorImpl::updateAvoidShapeId(const ObjectId id, const ObjectId avoidId)
193     {
194         updateId(id, avoidId, mWaterIds);
195     }
196 
updateWaterShapeId(const ObjectId id,const ObjectId waterId)197     void NavigatorImpl::updateWaterShapeId(const ObjectId id, const ObjectId waterId)
198     {
199         updateId(id, waterId, mWaterIds);
200     }
201 
updateId(const ObjectId id,const ObjectId updateId,std::unordered_map<ObjectId,ObjectId> & ids)202     void NavigatorImpl::updateId(const ObjectId id, const ObjectId updateId, std::unordered_map<ObjectId, ObjectId>& ids)
203     {
204         auto inserted = ids.insert(std::make_pair(id, updateId));
205         if (!inserted.second)
206         {
207             mNavMeshManager.removeObject(inserted.first->second);
208             inserted.first->second = updateId;
209         }
210     }
211 
removeUnusedNavMeshes()212     void NavigatorImpl::removeUnusedNavMeshes()
213     {
214         for (auto it = mAgents.begin(); it != mAgents.end();)
215         {
216             if (it->second == 0 && mNavMeshManager.reset(it->first))
217                 it = mAgents.erase(it);
218             else
219                 ++it;
220         }
221     }
222 
getMaxNavmeshAreaRealRadius() const223     float NavigatorImpl::getMaxNavmeshAreaRealRadius() const
224     {
225         const auto& settings = getSettings();
226         return getRealTileSize(settings) * getMaxNavmeshAreaRadius(settings);
227     }
228 }
229