navPath.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 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 "torqueRecast.h"
  23. #include "navPath.h"
  24. #include "console/consoleTypes.h"
  25. #include "console/engineAPI.h"
  26. #include "console/typeValidators.h"
  27. #include "scene/sceneRenderState.h"
  28. #include "gfx/gfxDrawUtil.h"
  29. #include "renderInstance/renderPassManager.h"
  30. #include "gfx/primBuilder.h"
  31. #include "core/stream/bitStream.h"
  32. #include "math/mathIO.h"
  33. #include <DetourDebugDraw.h>
  34. extern bool gEditingMission;
  35. IMPLEMENT_CO_NETOBJECT_V1(NavPath);
  36. NavPath::NavPath() :
  37. mFrom(0.0f, 0.0f, 0.0f),
  38. mTo(0.0f, 0.0f, 0.0f)
  39. {
  40. mTypeMask |= MarkerObjectType;
  41. mMesh = NULL;
  42. mWaypoints = NULL;
  43. mFrom.set(0, 0, 0);
  44. mFromSet = false;
  45. mTo.set(0, 0, 0);
  46. mToSet = false;
  47. mLength = 0.0f;
  48. mIsLooping = false;
  49. mAlwaysRender = false;
  50. mXray = false;
  51. mQuery = dtAllocNavMeshQuery();
  52. }
  53. NavPath::~NavPath()
  54. {
  55. // Required for Detour.
  56. dtFreeNavMeshQuery(mQuery);
  57. mQuery = NULL;
  58. }
  59. bool NavPath::setProtectedMesh(void *obj, const char *index, const char *data)
  60. {
  61. NavMesh *mesh = NULL;
  62. NavPath *object = static_cast<NavPath*>(obj);
  63. if(Sim::findObject(data, mesh))
  64. object->mMesh = mesh;
  65. return false;
  66. }
  67. const char *NavPath::getProtectedMesh(void *obj, const char *data)
  68. {
  69. NavPath *object = static_cast<NavPath*>(obj);
  70. if(object->mMesh.isNull())
  71. return "";
  72. if(object->mMesh->getName())
  73. return object->mMesh->getName();
  74. else
  75. return object->mMesh->getIdString();
  76. }
  77. bool NavPath::setProtectedWaypoints(void *obj, const char *index, const char *data)
  78. {
  79. SimPath::Path *points = NULL;
  80. NavPath *object = static_cast<NavPath*>(obj);
  81. if(Sim::findObject(data, points))
  82. {
  83. object->mWaypoints = points;
  84. object->mIsLooping = points->isLooping();
  85. }
  86. else
  87. object->mWaypoints = NULL;
  88. return false;
  89. }
  90. bool NavPath::setProtectedFrom(void *obj, const char *index, const char *data)
  91. {
  92. NavPath *object = static_cast<NavPath*>(obj);
  93. if(dStrcmp(data, ""))
  94. {
  95. object->mFromSet = true;
  96. return true;
  97. }
  98. else
  99. {
  100. object->mFromSet = false;
  101. return false;
  102. }
  103. }
  104. bool NavPath::setProtectedTo(void *obj, const char *index, const char *data)
  105. {
  106. NavPath *object = static_cast<NavPath*>(obj);
  107. if(dStrcmp(data, ""))
  108. {
  109. object->mToSet = true;
  110. return true;
  111. }
  112. else
  113. {
  114. object->mToSet = false;
  115. return false;
  116. }
  117. }
  118. const char *NavPath::getProtectedFrom(void *obj, const char *data)
  119. {
  120. NavPath *object = static_cast<NavPath*>(obj);
  121. if(object->mFromSet)
  122. return data;
  123. else
  124. return "";
  125. }
  126. const char *NavPath::getProtectedTo(void *obj, const char *data)
  127. {
  128. NavPath *object = static_cast<NavPath*>(obj);
  129. if(object->mToSet)
  130. return data;
  131. else
  132. return "";
  133. }
  134. static IRangeValidator NaturalNumber(1, S32_MAX);
  135. void NavPath::initPersistFields()
  136. {
  137. addGroup("NavPath");
  138. addProtectedField("from", TypePoint3F, Offset(mFrom, NavPath),
  139. &setProtectedFrom, &getProtectedFrom,
  140. "World location this path starts at.");
  141. addProtectedField("to", TypePoint3F, Offset(mTo, NavPath),
  142. &setProtectedTo, &getProtectedTo,
  143. "World location this path should end at.");
  144. addProtectedField("mesh", TYPEID<NavMesh>(), Offset(mMesh, NavPath),
  145. &setProtectedMesh, &getProtectedMesh,
  146. "NavMesh object this path travels within.");
  147. addProtectedField("waypoints", TYPEID<SimPath::Path>(), Offset(mWaypoints, NavPath),
  148. &setProtectedWaypoints, &defaultProtectedGetFn,
  149. "Path containing waypoints for this NavPath to visit.");
  150. addField("isLooping", TypeBool, Offset(mIsLooping, NavPath),
  151. "Does this path loop?");
  152. endGroup("NavPath");
  153. addGroup("NavPath Render");
  154. addField("alwaysRender", TypeBool, Offset(mAlwaysRender, NavPath),
  155. "Render this NavPath even when not selected.");
  156. addField("xray", TypeBool, Offset(mXray, NavPath),
  157. "Render this NavPath through other objects.");
  158. endGroup("NavPath Render");
  159. Parent::initPersistFields();
  160. }
  161. bool NavPath::onAdd()
  162. {
  163. if(!Parent::onAdd())
  164. return false;
  165. // Ghost immediately if the editor's already open.
  166. if(gEditingMission)
  167. mNetFlags.set(Ghostable);
  168. // Automatically find a path if we can.
  169. if(isServerObject())
  170. plan();
  171. // Set initial world bounds and stuff.
  172. resize();
  173. // Finally, add us to the simulation.
  174. addToScene();
  175. return true;
  176. }
  177. void NavPath::onRemove()
  178. {
  179. Parent::onRemove();
  180. // Remove from simulation.
  181. removeFromScene();
  182. }
  183. bool NavPath::init()
  184. {
  185. // Check that enough data is provided.
  186. if(mMesh.isNull() || !mMesh->getNavMesh())
  187. return false;
  188. if(!(mFromSet && mToSet) && !(!mWaypoints.isNull() && mWaypoints->size()))
  189. return false;
  190. // Initialise query in Detour.
  191. if(dtStatusFailed(mQuery->init(mMesh->getNavMesh(), MaxPathLen)))
  192. return false;
  193. mPoints.clear();
  194. mVisitPoints.clear();
  195. mLength = 0.0f;
  196. // Send path data to clients who are ghosting this object.
  197. if(isServerObject())
  198. setMaskBits(PathMask);
  199. // Add points we need to visit in reverse order.
  200. if(mWaypoints && mWaypoints->size())
  201. {
  202. // Add destination. For looping paths, that includes 'from'.
  203. if(mIsLooping && mFromSet)
  204. mVisitPoints.push_back(mFrom);
  205. if(mToSet)
  206. mVisitPoints.push_front(mTo);
  207. // Add waypoints.
  208. for(S32 i = mWaypoints->size() - 1; i >= 0; i--)
  209. {
  210. SceneObject *s = dynamic_cast<SceneObject*>(mWaypoints->at(i));
  211. if(s)
  212. {
  213. mVisitPoints.push_back(s->getPosition());
  214. // This is potentially slow, but safe.
  215. if(!i && mIsLooping && !mFromSet)
  216. mVisitPoints.push_front(s->getPosition());
  217. }
  218. }
  219. // Add source (only ever specified by 'from').
  220. if(mFromSet)
  221. mVisitPoints.push_back(mFrom);
  222. }
  223. else
  224. {
  225. // Add (from,) to and from
  226. if(mIsLooping)
  227. mVisitPoints.push_back(mFrom);
  228. mVisitPoints.push_back(mTo);
  229. mVisitPoints.push_back(mFrom);
  230. }
  231. return true;
  232. }
  233. void NavPath::resize()
  234. {
  235. if(!mPoints.size())
  236. {
  237. mObjBox.set(Point3F(-0.5f, -0.5f, -0.5f),
  238. Point3F( 0.5f, 0.5f, 0.5f));
  239. resetWorldBox();
  240. setTransform(MatrixF(true));
  241. return;
  242. }
  243. // Grow a box to just fit over all our points.
  244. Point3F max(mPoints[0]), min(mPoints[0]), pos(0.0f);
  245. for(U32 i = 1; i < mPoints.size(); i++)
  246. {
  247. Point3F p = mPoints[i];
  248. max.x = getMax(max.x, p.x);
  249. max.y = getMax(max.y, p.y);
  250. max.z = getMax(max.z, p.z);
  251. min.x = getMin(min.x, p.x);
  252. min.y = getMin(min.y, p.y);
  253. min.z = getMin(min.z, p.z);
  254. pos += p;
  255. }
  256. pos /= mPoints.size();
  257. min -= Point3F(0.5f, 0.5f, 0.5f);
  258. max += Point3F(0.5f, 0.5f, 0.5f);
  259. mObjBox.set(min - pos, max - pos);
  260. MatrixF mat = Parent::getTransform();
  261. mat.setPosition(pos);
  262. Parent::setTransform(mat);
  263. }
  264. bool NavPath::plan()
  265. {
  266. if(!init())
  267. return false;
  268. visitNext();
  269. while(update());
  270. if(!finalise())
  271. return false;
  272. resize();
  273. return true;
  274. }
  275. bool NavPath::visitNext()
  276. {
  277. U32 s = mVisitPoints.size();
  278. if(s < 2)
  279. return false;
  280. // Current leg of journey.
  281. Point3F start = mVisitPoints[s-1];
  282. Point3F end = mVisitPoints[s-2];
  283. // Convert to Detour-friendly coordinates and data structures.
  284. F32 from[] = {start.x, start.z, -start.y};
  285. F32 to[] = {end.x, end.z, -end.y};
  286. F32 extents[] = {1.0f, 1.0f, 1.0f};
  287. dtPolyRef startRef, endRef;
  288. if(dtStatusFailed(mQuery->findNearestPoly(from, extents, &mFilter, &startRef, from)) || !startRef)
  289. {
  290. Con::errorf("No NavMesh polygon near visit point (%g, %g, %g) of NavPath %s",
  291. start.x, start.y, start.z, getIdString());
  292. return false;
  293. }
  294. if(dtStatusFailed(mQuery->findNearestPoly(to, extents, &mFilter, &endRef, to)) || !startRef)
  295. {
  296. Con::errorf("No NavMesh polygon near visit point (%g, %g, %g) of NavPath %s",
  297. end.x, end.y, end.z, getIdString());
  298. return false;
  299. }
  300. // Init sliced pathfind.
  301. mStatus = mQuery->initSlicedFindPath(startRef, endRef, from, to, &mFilter);
  302. if(dtStatusFailed(mStatus))
  303. return false;
  304. return true;
  305. }
  306. bool NavPath::update()
  307. {
  308. // StatusInProgress means a query is underway.
  309. if(dtStatusInProgress(mStatus))
  310. mStatus = mQuery->updateSlicedFindPath(INT_MAX, NULL);
  311. // StatusSucceeded means the query found its destination.
  312. if(dtStatusSucceed(mStatus))
  313. {
  314. // Finalize the path. Need to use the static path length cap again.
  315. dtPolyRef path[MaxPathLen];
  316. S32 pathLen;
  317. mStatus = mQuery->finalizeSlicedFindPath(path, &pathLen, MaxPathLen);
  318. // Apparently stuff can go wrong during finalizing, so check the status again.
  319. if(dtStatusSucceed(mStatus) && pathLen)
  320. {
  321. // These next few blocks are straight from Detour example code.
  322. F32 straightPath[MaxPathLen * 3];
  323. S32 straightPathLen;
  324. dtPolyRef straightPathPolys[MaxPathLen];
  325. U8 straightPathFlags[MaxPathLen];
  326. U32 s = mVisitPoints.size();
  327. Point3F start = mVisitPoints[s-1];
  328. Point3F end = mVisitPoints[s-2];
  329. F32 from[] = {start.x, start.z, -start.y};
  330. F32 to[] = {end.x, end.z, -end.y};
  331. // Straightens out the path.
  332. mQuery->findStraightPath(from, to, path, pathLen,
  333. straightPath, straightPathFlags,
  334. straightPathPolys, &straightPathLen, MaxPathLen);
  335. // Convert Detour point path to list of Torque points.
  336. s = mPoints.size();
  337. mPoints.increment(straightPathLen);
  338. for(U32 i = 0; i < straightPathLen; i++)
  339. {
  340. F32 *f = straightPath + i * 3;
  341. mPoints[s + i] = RCtoDTS(f);
  342. // Accumulate length if we're not the first vertex.
  343. if(s > 0 || i > 0)
  344. mLength += (mPoints[s+i] - mPoints[s+i-1]).len();
  345. }
  346. if(isServerObject())
  347. setMaskBits(PathMask);
  348. }
  349. else
  350. return false;
  351. // Check to see where we still need to visit.
  352. if(mVisitPoints.size() > 1)
  353. {
  354. //Next leg of the journey.
  355. mVisitPoints.pop_back();
  356. return visitNext();
  357. }
  358. else
  359. {
  360. // Finished!
  361. return false;
  362. }
  363. }
  364. else if(dtStatusFailed(mStatus))
  365. {
  366. // Something went wrong in planning.
  367. return false;
  368. }
  369. return true;
  370. }
  371. bool NavPath::finalise()
  372. {
  373. // Stop ticking.
  374. setProcessTick(false);
  375. // Reset world bounds and stuff.
  376. resize();
  377. return dtStatusSucceed(mStatus);
  378. }
  379. void NavPath::processTick(const Move *move)
  380. {
  381. if(dtStatusInProgress(mStatus))
  382. update();
  383. }
  384. Point3F NavPath::getNode(S32 idx)
  385. {
  386. if(idx < getCount() && idx >= 0)
  387. return mPoints[idx];
  388. Con::errorf("Trying to access out-of-bounds path index %d (path length: %d)!", idx, getCount());
  389. return Point3F(0,0,0);
  390. }
  391. S32 NavPath::getCount()
  392. {
  393. return mPoints.size();
  394. }
  395. void NavPath::onEditorEnable()
  396. {
  397. mNetFlags.set(Ghostable);
  398. }
  399. void NavPath::onEditorDisable()
  400. {
  401. mNetFlags.clear(Ghostable);
  402. }
  403. void NavPath::inspectPostApply()
  404. {
  405. plan();
  406. }
  407. void NavPath::onDeleteNotify(SimObject *obj)
  408. {
  409. if(obj == (SimObject*)mMesh)
  410. {
  411. mMesh = NULL;
  412. plan();
  413. }
  414. }
  415. void NavPath::prepRenderImage(SceneRenderState *state)
  416. {
  417. ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
  418. ri->renderDelegate.bind(this, &NavPath::renderSimple);
  419. ri->type = RenderPassManager::RIT_Editor;
  420. ri->translucentSort = true;
  421. ri->defaultKey = 1;
  422. state->getRenderPass()->addInst(ri);
  423. }
  424. void NavPath::renderSimple(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat)
  425. {
  426. if(overrideMat)
  427. return;
  428. if(state->isReflectPass() || !(isSelected() || mAlwaysRender))
  429. return;
  430. GFXDrawUtil *drawer = GFX->getDrawUtil();
  431. GFXStateBlockDesc desc;
  432. desc.setZReadWrite(true, false);
  433. desc.setBlend(true);
  434. desc.setCullMode(GFXCullNone);
  435. if(isSelected())
  436. {
  437. drawer->drawCube(desc, getWorldBox(), ColorI(136, 255, 228, 5));
  438. desc.setFillModeWireframe();
  439. drawer->drawCube(desc, getWorldBox(), ColorI::BLACK);
  440. }
  441. desc.setZReadWrite(!mXray, false);
  442. ColorI pathColour(255, 0, 255);
  443. if(!mIsLooping)
  444. {
  445. desc.setFillModeSolid();
  446. if(mFromSet) drawer->drawCube(desc, Point3F(0.2f, 0.2f, 0.2f), mFrom, pathColour);
  447. if(mToSet) drawer->drawCube(desc, Point3F(0.2f, 0.2f, 0.2f), mTo, pathColour);
  448. }
  449. GFXStateBlockRef sb = GFX->createStateBlock(desc);
  450. GFX->setStateBlock(sb);
  451. PrimBuild::color3i(pathColour.red, pathColour.green, pathColour.blue);
  452. PrimBuild::begin(GFXLineStrip, mPoints.size());
  453. for (U32 i = 0; i < mPoints.size(); i++)
  454. PrimBuild::vertex3fv(mPoints[i]);
  455. PrimBuild::end();
  456. }
  457. U32 NavPath::packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
  458. {
  459. U32 retMask = Parent::packUpdate(conn, mask, stream);
  460. stream->writeFlag(mIsLooping);
  461. stream->writeFlag(mAlwaysRender);
  462. stream->writeFlag(mXray);
  463. if(stream->writeFlag(mFromSet))
  464. mathWrite(*stream, mFrom);
  465. if(stream->writeFlag(mToSet))
  466. mathWrite(*stream, mTo);
  467. if(stream->writeFlag(mask & PathMask))
  468. {
  469. stream->writeInt(mPoints.size(), 32);
  470. for(U32 i = 0; i < mPoints.size(); i++)
  471. mathWrite(*stream, mPoints[i]);
  472. }
  473. return retMask;
  474. }
  475. void NavPath::unpackUpdate(NetConnection *conn, BitStream *stream)
  476. {
  477. Parent::unpackUpdate(conn, stream);
  478. mIsLooping = stream->readFlag();
  479. mAlwaysRender = stream->readFlag();
  480. mXray = stream->readFlag();
  481. if((mFromSet = stream->readFlag()) == true)
  482. mathRead(*stream, &mFrom);
  483. if((mToSet = stream->readFlag()) == true)
  484. mathRead(*stream, &mTo);
  485. if(stream->readFlag())
  486. {
  487. mPoints.clear();
  488. mPoints.setSize(stream->readInt(32));
  489. for(U32 i = 0; i < mPoints.size(); i++)
  490. {
  491. Point3F p;
  492. mathRead(*stream, &p);
  493. mPoints[i] = p;
  494. }
  495. resize();
  496. }
  497. }
  498. DefineEngineMethod(NavPath, replan, bool, (),,
  499. "@brief Find a path using the already-specified path properties.")
  500. {
  501. return object->plan();
  502. }
  503. DefineEngineMethod(NavPath, getCount, S32, (),,
  504. "@brief Return the number of nodes in this path.")
  505. {
  506. return object->getCount();
  507. }
  508. DefineEngineMethod(NavPath, getNode, Point3F, (S32 idx),,
  509. "@brief Get a specified node along the path.")
  510. {
  511. return object->getNode(idx);
  512. }
  513. DefineEngineMethod(NavPath, getLength, F32, (),,
  514. "@brief Get the length of this path in Torque units (i.e. the total distance it covers).")
  515. {
  516. return object->getLength();
  517. }