navPath.cpp 16 KB

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