2
0

pathManager.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "gfx/gfxDevice.h"
  23. #include "scene/pathManager.h"
  24. #include "sim/netConnection.h"
  25. #include "core/stream/bitStream.h"
  26. #include "scene/simPath.h"
  27. #include "math/mathIO.h"
  28. #include "scene/sceneRenderState.h"
  29. #include "scene/sceneManager.h"
  30. #include "platform/profiler.h"
  31. #include "core/module.h"
  32. #include "console/engineAPI.h"
  33. extern bool gEditingMission;
  34. namespace {
  35. U32 countNumBits(U32 n)
  36. {
  37. U32 count = 0;
  38. while (n != 0) {
  39. n >>= 1;
  40. count++;
  41. }
  42. return count ? count : 1;
  43. }
  44. } // namespace {}
  45. MODULE_BEGIN( PathManager )
  46. MODULE_INIT
  47. {
  48. AssertFatal(gClientPathManager == NULL && gServerPathManager == NULL, "Error, already initialized the path manager!");
  49. gClientPathManager = new PathManager(false);
  50. gServerPathManager = new PathManager(true);
  51. }
  52. MODULE_SHUTDOWN
  53. {
  54. AssertFatal(gClientPathManager != NULL && gServerPathManager != NULL, "Error, path manager not initialized!");
  55. delete gClientPathManager;
  56. gClientPathManager = NULL;
  57. delete gServerPathManager;
  58. gServerPathManager = NULL;
  59. }
  60. MODULE_END;
  61. //--------------------------------------------------------------------------
  62. //-------------------------------------- PathManagerEvent
  63. //
  64. class PathManagerEvent : public NetEvent
  65. {
  66. public:
  67. U32 modifiedPath;
  68. bool clearPaths;
  69. PathManager::PathEntry path;
  70. public:
  71. typedef NetEvent Parent;
  72. PathManagerEvent() : modifiedPath(0), clearPaths(false) { }
  73. void pack(NetConnection*, BitStream*);
  74. void write(NetConnection*, BitStream*);
  75. void unpack(NetConnection*, BitStream*);
  76. void process(NetConnection*);
  77. DECLARE_CONOBJECT(PathManagerEvent);
  78. };
  79. void PathManagerEvent::pack(NetConnection*, BitStream* stream)
  80. {
  81. // Write out the modified path...
  82. stream->write(modifiedPath);
  83. stream->writeFlag(clearPaths);
  84. stream->write(path.totalTime);
  85. stream->write(path.looping);
  86. stream->write(path.positions.size());
  87. // This is here for safety. You can remove it if you want to try your luck at bigger sizes. -- BJG
  88. AssertWarn(path.positions.size() < 1500/40, "Warning! Path size is pretty big - may cause packet overrun!");
  89. // Each one of these is about 8 floats and 2 ints
  90. // so we'll say it's about 40 bytes in size, which is where the 40 in the above calc comes from.
  91. for (U32 j = 0; j < path.positions.size(); j++)
  92. {
  93. mathWrite(*stream, path.positions[j]);
  94. mathWrite(*stream, path.rotations[j]);
  95. stream->write(path.msToNext[j]);
  96. stream->write(path.smoothingType[j]);
  97. }
  98. }
  99. void PathManagerEvent::write(NetConnection*nc, BitStream *stream)
  100. {
  101. pack(nc, stream);
  102. }
  103. void PathManagerEvent::unpack(NetConnection*, BitStream* stream)
  104. {
  105. // Read in the modified path...
  106. stream->read(&modifiedPath);
  107. clearPaths = stream->readFlag();
  108. stream->read(&path.totalTime);
  109. stream->read(&path.looping);
  110. U32 numPoints;
  111. stream->read(&numPoints);
  112. path.positions.setSize(numPoints);
  113. path.rotations.setSize(numPoints);
  114. path.msToNext.setSize(numPoints);
  115. path.smoothingType.setSize(numPoints);
  116. for (U32 j = 0; j < path.positions.size(); j++)
  117. {
  118. mathRead(*stream, &path.positions[j]);
  119. mathRead(*stream, &path.rotations[j]);
  120. stream->read(&path.msToNext[j]);
  121. stream->read(&path.smoothingType[j]);
  122. }
  123. }
  124. void PathManagerEvent::process(NetConnection*)
  125. {
  126. if (clearPaths)
  127. {
  128. // Clear out all the client's paths...
  129. gClientPathManager->clearPaths();
  130. }
  131. AssertFatal(modifiedPath <= gClientPathManager->mPaths.size(), "Error out of bounds path!");
  132. if (modifiedPath == gClientPathManager->mPaths.size()) {
  133. PathManager::PathEntry *pe = new PathManager::PathEntry;
  134. *pe = path;
  135. gClientPathManager->mPaths.push_back(pe);
  136. }
  137. else
  138. *(gClientPathManager->mPaths[modifiedPath]) = path;
  139. }
  140. IMPLEMENT_CO_NETEVENT_V1(PathManagerEvent);
  141. // Will be internalized once the @internal tag is working
  142. ConsoleDocClass( PathManagerEvent,
  143. "@brief Class responsible for the registration, transmission, and management "
  144. "of paths on client and server.\n\n"
  145. "For internal use only, not intended for use in TorqueScript or game development\n\n"
  146. "@internal\n"
  147. );
  148. //--------------------------------------------------------------------------
  149. //-------------------------------------- PathManager Implementation
  150. //
  151. PathManager* gClientPathManager = NULL;
  152. PathManager* gServerPathManager = NULL;
  153. //--------------------------------------------------------------------------
  154. PathManager::PathManager(const bool isServer)
  155. {
  156. VECTOR_SET_ASSOCIATION(mPaths);
  157. mIsServer = isServer;
  158. }
  159. PathManager::~PathManager()
  160. {
  161. clearPaths();
  162. }
  163. void PathManager::clearPaths()
  164. {
  165. for (U32 i = 0; i < mPaths.size(); i++)
  166. delete mPaths[i];
  167. mPaths.setSize(0);
  168. #ifdef TORQUE_DEBUG
  169. // This gets rid of the memory used by the vector.
  170. // Prevents it from showing up in memory leak logs.
  171. mPaths.compact();
  172. #endif
  173. }
  174. DefineEngineFunction( clearServerPaths, void, ( ), , "")
  175. {
  176. gServerPathManager->clearPaths();
  177. }
  178. DefineEngineFunction( clearClientPaths, void, ( ), , "")
  179. {
  180. gClientPathManager->clearPaths();
  181. }
  182. //--------------------------------------------------------------------------
  183. U32 PathManager::allocatePathId()
  184. {
  185. mPaths.increment();
  186. mPaths.last() = new PathEntry;
  187. return (mPaths.size() - 1);
  188. }
  189. void PathManager::updatePath(const U32 id,
  190. const Vector<Point3F>& positions,
  191. const Vector<QuatF>& rotations,
  192. const Vector<U32>& times,
  193. const Vector<U32>& smoothingTypes,
  194. const bool looping)
  195. {
  196. AssertFatal(mIsServer == true, "PathManager::updatePath: Error, must be called on the server side");
  197. AssertFatal(id < mPaths.size(), "PathManager::updatePath: error, id out of range");
  198. AssertFatal(positions.size() == times.size() && positions.size() == smoothingTypes.size(), "Error, times and positions must match!");
  199. PathEntry& rEntry = *mPaths[id];
  200. rEntry.positions = positions;
  201. rEntry.rotations = rotations;
  202. rEntry.msToNext = times;
  203. rEntry.smoothingType = smoothingTypes;
  204. rEntry.looping = looping;
  205. rEntry.totalTime = 0;
  206. for (S32 i = 0; i < S32(rEntry.msToNext.size()); i++)
  207. rEntry.totalTime += rEntry.msToNext[i];
  208. transmitPath(id);
  209. }
  210. //--------------------------------------------------------------------------
  211. void PathManager::transmitPaths(NetConnection* nc)
  212. {
  213. AssertFatal(mIsServer, "Error, cannot call transmitPaths on client path manager!");
  214. // Send over paths
  215. for(S32 i = 0; i < mPaths.size(); i++)
  216. {
  217. PathManagerEvent* event = new PathManagerEvent;
  218. event->clearPaths = (i == 0);
  219. event->modifiedPath = i;
  220. event->path = *(mPaths[i]);
  221. nc->postNetEvent(event);
  222. }
  223. }
  224. void PathManager::transmitPath(const U32 id)
  225. {
  226. AssertFatal(mIsServer, "Error, cannot call transmitNewPath on client path manager!");
  227. // Post to all active clients that have already received their paths...
  228. //
  229. SimGroup* pClientGroup = Sim::getClientGroup();
  230. for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++) {
  231. NetConnection* nc = dynamic_cast<NetConnection*>(*itr);
  232. if (nc && nc->missionPathsSent())
  233. {
  234. // Transmit the updated path...
  235. PathManagerEvent* event = new PathManagerEvent;
  236. event->modifiedPath = id;
  237. event->clearPaths = false;
  238. event->path = *(mPaths[id]);
  239. nc->postNetEvent(event);
  240. }
  241. }
  242. }
  243. void PathManager::getPathPosition(const U32 id,
  244. const F64 msPosition,
  245. Point3F& rPosition,
  246. QuatF &rotation)
  247. {
  248. AssertFatal(isValidPath(id), "Error, this is not a valid path!");
  249. PROFILE_START(PathManGetPos);
  250. // Ok, query holds our path information...
  251. F64 ms = msPosition;
  252. //Looping vs. non-looping splines
  253. F32 msTotal;
  254. if (mPaths[id]->looping)
  255. msTotal = mPaths[id]->totalTime;
  256. else
  257. //total time minus last nodes time
  258. msTotal = mPaths[id]->totalTime - mPaths[id]->msToNext[mPaths[id]->msToNext.size() - 1];
  259. if (ms > msTotal)
  260. ms = msTotal;
  261. S32 startNode = 0;
  262. while (ms > mPaths[id]->msToNext[startNode]) {
  263. ms -= mPaths[id]->msToNext[startNode];
  264. startNode++;
  265. }
  266. S32 endNode;
  267. //Looping splines
  268. if (mPaths[id]->looping)
  269. endNode = (startNode + 1) % mPaths[id]->positions.size();
  270. //Non-looping splines
  271. else
  272. endNode = getMin(startNode + 1, mPaths[id]->positions.size() - 1);
  273. Point3F& rStart = mPaths[id]->positions[startNode];
  274. Point3F& rEnd = mPaths[id]->positions[endNode];
  275. F64 interp = ms / F32(mPaths[id]->msToNext[startNode]);
  276. if(mPaths[id]->smoothingType[startNode] == Marker::SmoothingTypeLinear)
  277. {
  278. rPosition = (rStart * (1.0 - interp)) + (rEnd * interp);
  279. }
  280. else if(mPaths[id]->smoothingType[startNode] == Marker::SmoothingTypeAccelerate)
  281. {
  282. interp = mSin(interp * M_PI - (M_PI / 2)) * 0.5 + 0.5;
  283. rPosition = (rStart * (1.0 - interp)) + (rEnd * interp);
  284. }
  285. else if(mPaths[id]->smoothingType[startNode] == Marker::SmoothingTypeSpline)
  286. {
  287. S32 preStart = startNode - 1;
  288. S32 postEnd = endNode + 1;
  289. //Looping splines
  290. if (mPaths[id]->looping)
  291. {
  292. if (postEnd >= mPaths[id]->positions.size())
  293. postEnd = 0;
  294. if (preStart < 0)
  295. preStart = mPaths[id]->positions.size() - 1;
  296. }
  297. //Non-looping splines
  298. else
  299. {
  300. if (postEnd >= mPaths[id]->positions.size())
  301. postEnd = mPaths[id]->positions.size() - 1;
  302. if (preStart < 0)
  303. preStart = 0;
  304. }
  305. Point3F p0 = mPaths[id]->positions[preStart];
  306. Point3F p1 = rStart;
  307. Point3F p2 = rEnd;
  308. Point3F p3 = mPaths[id]->positions[postEnd];
  309. rPosition.x = mCatmullrom(interp, p0.x, p1.x, p2.x, p3.x);
  310. rPosition.y = mCatmullrom(interp, p0.y, p1.y, p2.y, p3.y);
  311. rPosition.z = mCatmullrom(interp, p0.z, p1.z, p2.z, p3.z);
  312. }
  313. rotation.interpolate( mPaths[id]->rotations[startNode], mPaths[id]->rotations[endNode], interp );
  314. PROFILE_END();
  315. }
  316. U32 PathManager::getPathTotalTime(const U32 id) const
  317. {
  318. AssertFatal(isValidPath(id), "Error, this is not a valid path!");
  319. return mPaths[id]->totalTime;
  320. }
  321. U32 PathManager::getPathNumWaypoints(const U32 id) const
  322. {
  323. AssertFatal(isValidPath(id), "Error, this is not a valid path!");
  324. return mPaths[id]->positions.size();
  325. }
  326. U32 PathManager::getWaypointTime(const U32 id, const U32 wayPoint) const
  327. {
  328. AssertFatal(isValidPath(id), "Error, this is not a valid path!");
  329. AssertFatal(wayPoint < getPathNumWaypoints(id), "Invalid waypoint!");
  330. U32 time = 0;
  331. for (U32 i = 0; i < wayPoint; i++)
  332. time += mPaths[id]->msToNext[i];
  333. return time;
  334. }
  335. U32 PathManager::getPathTimeBits(const U32 id)
  336. {
  337. AssertFatal(isValidPath(id), "Error, this is not a valid path!");
  338. return countNumBits(mPaths[id]->totalTime);
  339. }
  340. U32 PathManager::getPathWaypointBits(const U32 id)
  341. {
  342. AssertFatal(isValidPath(id), "Error, this is not a valid path!");
  343. return countNumBits(mPaths[id]->positions.size());
  344. }
  345. bool PathManager::dumpState(BitStream* stream) const
  346. {
  347. stream->write(mPaths.size());
  348. for (U32 i = 0; i < mPaths.size(); i++) {
  349. const PathEntry& rEntry = *mPaths[i];
  350. stream->write(rEntry.totalTime);
  351. stream->write(rEntry.positions.size());
  352. for (U32 j = 0; j < rEntry.positions.size(); j++) {
  353. mathWrite(*stream, rEntry.positions[j]);
  354. stream->write(rEntry.msToNext[j]);
  355. }
  356. }
  357. return stream->getStatus() == Stream::Ok;
  358. }
  359. bool PathManager::readState(BitStream* stream)
  360. {
  361. U32 i;
  362. for (i = 0; i < mPaths.size(); i++)
  363. delete mPaths[i];
  364. U32 numPaths;
  365. stream->read(&numPaths);
  366. mPaths.setSize(numPaths);
  367. for (i = 0; i < mPaths.size(); i++) {
  368. mPaths[i] = new PathEntry;
  369. PathEntry& rEntry = *mPaths[i];
  370. stream->read(&rEntry.totalTime);
  371. U32 numPositions;
  372. stream->read(&numPositions);
  373. rEntry.positions.setSize(numPositions);
  374. rEntry.msToNext.setSize(numPositions);
  375. for (U32 j = 0; j < rEntry.positions.size(); j++) {
  376. mathRead(*stream, &rEntry.positions[j]);
  377. stream->read(&rEntry.msToNext[j]);
  378. }
  379. }
  380. return stream->getStatus() == Stream::Ok;
  381. }
  382. F64 PathManager::getClosestTimeToPoint(const U32 id, const Point3F p)
  383. {
  384. //Ubiq: Ideally this algorithm would work directly by finding roots. However, it's a 5th order
  385. //polynomial (cannot be solved by radicals), so we're doing it iteratively instead!
  386. //Steps:
  387. //1) Termination condition: if the segment is shorter than L, return the midpoint
  388. //2) Otherwise, divide the spline-segment into S sub-segments (S+1 points to test)
  389. //2) Test these points against the given point and find the closest of them
  390. //4) Recurse within the 2 segments surrounding the closest point
  391. //NOTE: In a case where the spline comes near the point multiple times, the wrong local
  392. //minima of the spline may be chosen early on and then refined, like polishing a turd :)
  393. PROFILE_START(PathManager_getClosestTimeToPoint);
  394. F64 totalTime;
  395. if (mPaths[id]->looping)
  396. totalTime = mPaths[id]->totalTime;
  397. else
  398. totalTime = mPaths[id]->totalTime - mPaths[id]->msToNext[mPaths[id]->msToNext.size() - 1];
  399. F64 ret = getClosestTimeToPoint(id, p, 0.0f, totalTime);
  400. PROFILE_END();
  401. return ret;
  402. }
  403. F64 PathManager::getClosestTimeToPoint(const U32 id, const Point3F p, const F64 tMin, const F64 tMax)
  404. {
  405. F64 totalSize = tMax - tMin;
  406. //termination condition
  407. if (totalSize <= 25.0f)
  408. return (tMin + tMax) / 2.0f;
  409. U32 steps = getMax((F32)totalSize / 500.0f, 8.0f);
  410. F64 stepSize = totalSize / steps;
  411. F64 distBest = F32_MAX;
  412. F64 tBest = 0;
  413. for (U32 i = 0; i <= steps; i++)
  414. {
  415. F64 tTest = tMin + (stepSize * i);
  416. Point3F pTest; QuatF dummy;
  417. getPathPosition(id, tTest, pTest, dummy);
  418. F64 dist = (pTest - p).lenSquared(); //no need for square root
  419. if (dist < distBest)
  420. {
  421. tBest = tTest;
  422. distBest = dist;
  423. }
  424. }
  425. F64 tMinNew = tBest - stepSize;
  426. F64 tMaxNew = tBest + stepSize;
  427. if (mPaths[id]->looping)
  428. {
  429. while (tMinNew < 0)
  430. {
  431. tMinNew += getPathTotalTime(id);
  432. tMaxNew += getPathTotalTime(id);
  433. }
  434. while (tMaxNew > getPathTotalTime(id))
  435. {
  436. tMinNew -= getPathTotalTime(id);
  437. tMaxNew -= getPathTotalTime(id);
  438. }
  439. }
  440. else
  441. {
  442. tMinNew = getMax(tMin, tMinNew);
  443. tMaxNew = getMin(tMax, tMaxNew);
  444. }
  445. return getClosestTimeToPoint(id, p, tMinNew, tMaxNew);
  446. }