navPath.cpp
Engine/source/navigation/navPath.cpp
Public Variables
bool
For frame signal.
ValidIterations (1, S32_MAX)
Public Functions
DefineEngineMethod(NavPath , getFlags , S32 , (S32 idx) , "@brief Get <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> specified node along the path." )
DefineEngineMethod(NavPath , getLength , F32 , () , "@brief Get the length of this path." )
DefineEngineMethod(NavPath , getNode , Point3F , (S32 idx) , "@brief Get <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> specified node along the path." )
DefineEngineMethod(NavPath , onNavMeshUpdate , void , (const char *data) , "@brief <a href="/coding/file/sqliteobject_8cpp/#sqliteobject_8cpp_1a858cd1af41f1e4420d94dfe65b6a56a4">Callback</a> when this path's <a href="/coding/class/classnavmesh/">NavMesh</a> is loaded or rebuilt." )
DefineEngineMethod(NavPath , onNavMeshUpdateBox , void , (const char *data) , "@brief <a href="/coding/file/sqliteobject_8cpp/#sqliteobject_8cpp_1a858cd1af41f1e4420d94dfe65b6a56a4">Callback</a> when <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> particular area in this path's <a href="/coding/class/classnavmesh/">NavMesh</a> is rebuilt." )
DefineEngineMethod(NavPath , plan , bool , () , "@brief Find <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> path using the already-specified path properties." )
DefineEngineMethod(NavPath , size , S32 , () , "@brief Return the number of nodes in this path." )
Detailed Description
Public Variables
bool gEditingMission
For frame signal.
IRangeValidator ValidIterations (1, S32_MAX)
Public Functions
DefineEngineMethod(NavPath , getFlags , S32 , (S32 idx) , "@brief Get <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> specified node along the path." )
DefineEngineMethod(NavPath , getLength , F32 , () , "@brief Get the length of this path." )
DefineEngineMethod(NavPath , getNode , Point3F , (S32 idx) , "@brief Get <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> specified node along the path." )
DefineEngineMethod(NavPath , onNavMeshUpdate , void , (const char *data) , "@brief <a href="/coding/file/sqliteobject_8cpp/#sqliteobject_8cpp_1a858cd1af41f1e4420d94dfe65b6a56a4">Callback</a> when this path's <a href="/coding/class/classnavmesh/">NavMesh</a> is loaded or rebuilt." )
DefineEngineMethod(NavPath , onNavMeshUpdateBox , void , (const char *data) , "@brief <a href="/coding/file/sqliteobject_8cpp/#sqliteobject_8cpp_1a858cd1af41f1e4420d94dfe65b6a56a4">Callback</a> when <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> particular area in this path's <a href="/coding/class/classnavmesh/">NavMesh</a> is rebuilt." )
DefineEngineMethod(NavPath , plan , bool , () , "@brief Find <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> path using the already-specified path properties." )
DefineEngineMethod(NavPath , size , S32 , () , "@brief Return the number of nodes in this path." )
IMPLEMENT_CO_NETOBJECT_V1(NavPath )
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2014 Daniel Buckmaster 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "torqueRecast.h" 25#include "navPath.h" 26#include "duDebugDrawTorque.h" 27 28#include "console/consoleTypes.h" 29#include "console/engineAPI.h" 30#include "console/typeValidators.h" 31#include "math/mathTypes.h" 32 33#include "scene/sceneRenderState.h" 34#include "gfx/gfxDrawUtil.h" 35#include "renderInstance/renderPassManager.h" 36#include "gfx/primBuilder.h" 37#include "core/stream/bitStream.h" 38#include "math/mathIO.h" 39 40#include <DetourDebugDraw.h> 41#include <climits> 42 43extern bool gEditingMission; 44 45IMPLEMENT_CO_NETOBJECT_V1(NavPath); 46 47NavPath::NavPath() : 48 mFrom(0.0f, 0.0f, 0.0f), 49 mTo(0.0f, 0.0f, 0.0f) 50{ 51 mTypeMask |= MarkerObjectType; 52 53 mMesh = NULL; 54 mWaypoints = NULL; 55 56 mFrom.set(0, 0, 0); 57 mFromSet = false; 58 mTo.set(0, 0, 0); 59 mToSet = false; 60 mLength = 0.0f; 61 62 mCurIndex = -1; 63 mIsLooping = false; 64 mAutoUpdate = false; 65 mIsSliced = false; 66 67 mMaxIterations = 1; 68 69 mAlwaysRender = false; 70 mXray = false; 71 mRenderSearch = false; 72 73 mQuery = NULL; 74 mStatus = DT_FAILURE; 75} 76 77NavPath::~NavPath() 78{ 79 dtFreeNavMeshQuery(mQuery); 80 mQuery = NULL; 81} 82 83void NavPath::checkAutoUpdate() 84{ 85 EventManager *em = NavMesh::getEventManager(); 86 em->removeAll(this); 87 if(mMesh) 88 { 89 if(mAutoUpdate) 90 { 91 em->subscribe(this, "NavMeshRemoved"); 92 em->subscribe(this, "NavMeshUpdate"); 93 em->subscribe(this, "NavMeshUpdateBox"); 94 em->subscribe(this, "NavMeshObstacleAdded"); 95 em->subscribe(this, "NavMeshObstacleRemoved"); 96 } 97 } 98} 99 100bool NavPath::setProtectedMesh(void *obj, const char *index, const char *data) 101{ 102 NavPath *object = static_cast<NavPath*>(obj); 103 104 if(Sim::findObject(data, object->mMesh)) 105 object->checkAutoUpdate(); 106 107 return true; 108} 109 110bool NavPath::setProtectedWaypoints(void *obj, const char *index, const char *data) 111{ 112 SimPath::Path *points = NULL; 113 NavPath *object = static_cast<NavPath*>(obj); 114 115 if(Sim::findObject(data, points)) 116 { 117 object->mWaypoints = points; 118 object->mIsLooping = points->isLooping(); 119 } 120 else 121 object->mWaypoints = NULL; 122 123 return false; 124} 125 126bool NavPath::setProtectedAutoUpdate(void *obj, const char *index, const char *data) 127{ 128 NavPath *object = static_cast<NavPath*>(obj); 129 130 object->mAutoUpdate = dAtob(data); 131 object->checkAutoUpdate(); 132 133 return false; 134} 135 136bool NavPath::setProtectedFrom(void *obj, const char *index, const char *data) 137{ 138 NavPath *object = static_cast<NavPath*>(obj); 139 140 if(String::compare(data, "")) 141 { 142 object->mFromSet = true; 143 return true; 144 } 145 else 146 { 147 object->mFromSet = false; 148 return false; 149 } 150} 151 152bool NavPath::setProtectedTo(void *obj, const char *index, const char *data) 153{ 154 NavPath *object = static_cast<NavPath*>(obj); 155 156 if(String::compare(data, "")) 157 { 158 object->mToSet = true; 159 return true; 160 } 161 else 162 { 163 object->mToSet = false; 164 return false; 165 } 166} 167 168const char *NavPath::getProtectedFrom(void *obj, const char *data) 169{ 170 NavPath *object = static_cast<NavPath*>(obj); 171 172 if(object->mFromSet) 173 return data; 174 else 175 return StringTable->EmptyString(); 176} 177 178const char *NavPath::getProtectedTo(void *obj, const char *data) 179{ 180 NavPath *object = static_cast<NavPath*>(obj); 181 182 if(object->mToSet) 183 return data; 184 else 185 return StringTable->EmptyString(); 186} 187 188IRangeValidator ValidIterations(1, S32_MAX); 189 190void NavPath::initPersistFields() 191{ 192 addGroup("NavPath"); 193 194 addProtectedField("from", TypePoint3F, Offset(mFrom, NavPath), 195 &setProtectedFrom, &getProtectedFrom, 196 "World location this path starts at."); 197 addProtectedField("to", TypePoint3F, Offset(mTo, NavPath), 198 &setProtectedTo, &getProtectedTo, 199 "World location this path should end at."); 200 201 addProtectedField("mesh", TypeRealString, Offset(mMeshName, NavPath), 202 &setProtectedMesh, &defaultProtectedGetFn, 203 "Name of the NavMesh object this path travels within."); 204 addProtectedField("waypoints", TYPEID<SimPath::Path>(), Offset(mWaypoints, NavPath), 205 &setProtectedWaypoints, &defaultProtectedGetFn, 206 "Path containing waypoints for this NavPath to visit."); 207 208 addField("isLooping", TypeBool, Offset(mIsLooping, NavPath), 209 "Does this path loop?"); 210 addField("isSliced", TypeBool, Offset(mIsSliced, NavPath), 211 "Plan this path over multiple updates instead of all at once."); 212 addFieldV("maxIterations", TypeS32, Offset(mMaxIterations, NavPath), &ValidIterations, 213 "Maximum iterations of path planning this path does per tick."); 214 addProtectedField("autoUpdate", TypeBool, Offset(mAutoUpdate, NavPath), 215 &setProtectedAutoUpdate, &defaultProtectedGetFn, 216 "If set, this path will automatically replan when its navigation mesh changes."); 217 218 endGroup("NavPath"); 219 220 addGroup("Flags"); 221 222 addField("allowWalk", TypeBool, Offset(mLinkTypes.walk, NavPath), 223 "Allow the path to use dry land."); 224 addField("allowJump", TypeBool, Offset(mLinkTypes.jump, NavPath), 225 "Allow the path to use jump links."); 226 addField("allowDrop", TypeBool, Offset(mLinkTypes.drop, NavPath), 227 "Allow the path to use drop links."); 228 addField("allowSwim", TypeBool, Offset(mLinkTypes.swim, NavPath), 229 "Allow the path to move in water."); 230 addField("allowLedge", TypeBool, Offset(mLinkTypes.ledge, NavPath), 231 "Allow the path to jump ledges."); 232 addField("allowClimb", TypeBool, Offset(mLinkTypes.climb, NavPath), 233 "Allow the path to use climb links."); 234 addField("allowTeleport", TypeBool, Offset(mLinkTypes.teleport, NavPath), 235 "Allow the path to use teleporters."); 236 237 endGroup("Flags"); 238 239 addGroup("NavPath Render"); 240 241 addField("alwaysRender", TypeBool, Offset(mAlwaysRender, NavPath), 242 "Render this NavPath even when not selected."); 243 addField("xray", TypeBool, Offset(mXray, NavPath), 244 "Render this NavPath through other objects."); 245 addField("renderSearch", TypeBool, Offset(mRenderSearch, NavPath), 246 "Render the closed list of this NavPath's search."); 247 248 endGroup("NavPath Render"); 249 250 Parent::initPersistFields(); 251} 252 253bool NavPath::onAdd() 254{ 255 if(!Parent::onAdd()) 256 return false; 257 258 if(gEditingMission) 259 mNetFlags.set(Ghostable); 260 261 resize(); 262 263 addToScene(); 264 265 if(isServerObject()) 266 { 267 mQuery = dtAllocNavMeshQuery(); 268 if(!mQuery) 269 return false; 270 checkAutoUpdate(); 271 if(!plan()) 272 setProcessTick(true); 273 } 274 275 return true; 276} 277 278void NavPath::onRemove() 279{ 280 Parent::onRemove(); 281 282 removeFromScene(); 283} 284 285bool NavPath::init() 286{ 287 mStatus = DT_FAILURE; 288 289 // Check that all the right data is provided. 290 if(!mMesh || !mMesh->getNavMesh()) 291 return false; 292 if(!(mFromSet && mToSet) && !(mWaypoints && mWaypoints->size())) 293 return false; 294 295 // Initialise our query. 296 if(dtStatusFailed(mQuery->init(mMesh->getNavMesh(), MaxPathLen))) 297 return false; 298 299 mPoints.clear(); 300 mFlags.clear(); 301 mVisitPoints.clear(); 302 mLength = 0.0f; 303 304 if(isServerObject()) 305 setMaskBits(PathMask); 306 307 // Add points we need to visit in reverse order. 308 if(mWaypoints && mWaypoints->size()) 309 { 310 if(mIsLooping && mFromSet) 311 mVisitPoints.push_back(mFrom); 312 if(mToSet) 313 mVisitPoints.push_front(mTo); 314 for(S32 i = mWaypoints->size() - 1; i >= 0; i--) 315 { 316 SceneObject *s = dynamic_cast<SceneObject*>(mWaypoints->at(i)); 317 if(s) 318 { 319 mVisitPoints.push_back(s->getPosition()); 320 // This is potentially slow, but safe. 321 if(!i && mIsLooping && !mFromSet) 322 mVisitPoints.push_front(s->getPosition()); 323 } 324 } 325 if(mFromSet) 326 mVisitPoints.push_back(mFrom); 327 } 328 else 329 { 330 if(mIsLooping) 331 mVisitPoints.push_back(mFrom); 332 mVisitPoints.push_back(mTo); 333 mVisitPoints.push_back(mFrom); 334 } 335 336 return true; 337} 338 339void NavPath::resize() 340{ 341 if(!mPoints.size()) 342 { 343 mObjBox.set(Point3F(-0.5f, -0.5f, -0.5f), 344 Point3F( 0.5f, 0.5f, 0.5f)); 345 resetWorldBox(); 346 setTransform(MatrixF(true)); 347 return; 348 } 349 350 Point3F max(mPoints[0]), min(mPoints[0]), pos(0.0f); 351 for(U32 i = 1; i < mPoints.size(); i++) 352 { 353 Point3F p = mPoints[i]; 354 max.x = getMax(max.x, p.x); 355 max.y = getMax(max.y, p.y); 356 max.z = getMax(max.z, p.z); 357 min.x = getMin(min.x, p.x); 358 min.y = getMin(min.y, p.y); 359 min.z = getMin(min.z, p.z); 360 pos += p; 361 } 362 pos /= mPoints.size(); 363 min -= Point3F(0.5f, 0.5f, 0.5f); 364 max += Point3F(0.5f, 0.5f, 0.5f); 365 366 mObjBox.set(min - pos, max - pos); 367 MatrixF mat = Parent::getTransform(); 368 mat.setPosition(pos); 369 Parent::setTransform(mat); 370} 371 372bool NavPath::plan() 373{ 374 PROFILE_SCOPE(NavPath_plan); 375 // Initialise filter. 376 mFilter.setIncludeFlags(mLinkTypes.getFlags()); 377 378 // Initialise query and visit locations. 379 if(!init()) 380 return false; 381 382 if(mIsSliced) 383 return planSliced(); 384 else 385 return planInstant(); 386} 387 388bool NavPath::planSliced() 389{ 390 bool visited = visitNext(); 391 392 if(visited) 393 setProcessTick(true); 394 395 return visited; 396} 397 398bool NavPath::planInstant() 399{ 400 setProcessTick(false); 401 visitNext(); 402 S32 store = mMaxIterations; 403 mMaxIterations = INT_MAX; 404 while(update()); 405 mMaxIterations = store; 406 return finalise(); 407} 408 409bool NavPath::visitNext() 410{ 411 U32 s = mVisitPoints.size(); 412 if(s < 2) 413 return false; 414 415 // Current leg of journey. 416 Point3F &start = mVisitPoints[s-1]; 417 Point3F &end = mVisitPoints[s-2]; 418 419 // Drop to height of statics. 420 RayInfo info; 421 if(getContainer()->castRay(start, start - Point3F(0, 0, mMesh->mWalkableHeight * 2.0f), StaticObjectType, &info)) 422 start = info.point; 423 if(getContainer()->castRay(end + Point3F(0, 0, 0.1f), end - Point3F(0, 0, mMesh->mWalkableHeight * 2.0f), StaticObjectType, &info)) 424 end = info.point; 425 426 // Convert to Detour-friendly coordinates and data structures. 427 F32 from[] = {start.x, start.z, -start.y}; 428 F32 to[] = {end.x, end.z, -end.y}; 429 F32 extx = mMesh->mWalkableRadius * 4.0f; 430 F32 extz = mMesh->mWalkableHeight; 431 F32 extents[] = {extx, extz, extx}; 432 dtPolyRef startRef, endRef; 433 434 if(dtStatusFailed(mQuery->findNearestPoly(from, extents, &mFilter, &startRef, NULL)) || !startRef) 435 { 436 //Con::errorf("No NavMesh polygon near visit point (%g, %g, %g) of NavPath %s", 437 //start.x, start.y, start.z, getIdString()); 438 return false; 439 } 440 441 if(dtStatusFailed(mQuery->findNearestPoly(to, extents, &mFilter, &endRef, NULL)) || !endRef) 442 { 443 //Con::errorf("No NavMesh polygon near visit point (%g, %g, %g) of NavPath %s", 444 //end.x, end.y, end.z, getIdString()); 445 return false; 446 } 447 448 // Init sliced pathfind. 449 mStatus = mQuery->initSlicedFindPath(startRef, endRef, from, to, &mFilter); 450 if(dtStatusFailed(mStatus)) 451 return false; 452 453 return true; 454} 455 456bool NavPath::update() 457{ 458 PROFILE_SCOPE(NavPath_update); 459 if(dtStatusInProgress(mStatus)) 460 mStatus = mQuery->updateSlicedFindPath(mMaxIterations, NULL); 461 if(dtStatusSucceed(mStatus)) 462 { 463 // Add points from this leg. 464 dtPolyRef path[MaxPathLen]; 465 S32 pathLen; 466 mStatus = mQuery->finalizeSlicedFindPath(path, &pathLen, MaxPathLen); 467 if(dtStatusSucceed(mStatus) && pathLen) 468 { 469 F32 straightPath[MaxPathLen * 3]; 470 S32 straightPathLen; 471 dtPolyRef straightPathPolys[MaxPathLen]; 472 U8 straightPathFlags[MaxPathLen]; 473 474 U32 s = mVisitPoints.size(); 475 Point3F start = mVisitPoints[s-1]; 476 Point3F end = mVisitPoints[s-2]; 477 F32 from[] = {start.x, start.z, -start.y}; 478 F32 to[] = {end.x, end.z, -end.y}; 479 480 mQuery->findStraightPath(from, to, path, pathLen, 481 straightPath, straightPathFlags, 482 straightPathPolys, &straightPathLen, MaxPathLen); 483 484 s = mPoints.size(); 485 mPoints.increment(straightPathLen); 486 mFlags.increment(straightPathLen); 487 for(U32 i = 0; i < straightPathLen; i++) 488 { 489 F32 *f = straightPath + i * 3; 490 mPoints[s + i] = RCtoDTS(f); 491 mMesh->getNavMesh()->getPolyFlags(straightPathPolys[i], &mFlags[s + i]); 492 // Add to length 493 if(s > 0 || i > 0) 494 mLength += (mPoints[s+i] - mPoints[s+i-1]).len(); 495 } 496 497 if(isServerObject()) 498 setMaskBits(PathMask); 499 } 500 else 501 return false; 502 // Check to see where we still need to visit. 503 if(mVisitPoints.size() > 1) 504 { 505 //Next leg of the journey. 506 mVisitPoints.pop_back(); 507 return visitNext(); 508 } 509 else 510 { 511 // Finished! 512 return false; 513 } 514 } 515 else if(dtStatusFailed(mStatus)) 516 { 517 // Something went wrong in planning. 518 return false; 519 } 520 return true; 521} 522 523bool NavPath::finalise() 524{ 525 setProcessTick(false); 526 527 resize(); 528 529 return success(); 530} 531 532void NavPath::processTick(const Move *move) 533{ 534 PROFILE_SCOPE(NavPath_processTick); 535 if(!mMesh) 536 if(Sim::findObject(mMeshName.c_str(), mMesh)) 537 plan(); 538 if(dtStatusInProgress(mStatus)) 539 update(); 540} 541 542Point3F NavPath::getNode(S32 idx) const 543{ 544 if(idx < size() && idx >= 0) 545 return mPoints[idx]; 546 return Point3F(0,0,0); 547} 548 549U16 NavPath::getFlags(S32 idx) const 550{ 551 if(idx < size() && idx >= 0) 552 return mFlags[idx]; 553 return 0; 554} 555 556S32 NavPath::size() const 557{ 558 return mPoints.size(); 559} 560 561void NavPath::onEditorEnable() 562{ 563 mNetFlags.set(Ghostable); 564} 565 566void NavPath::onEditorDisable() 567{ 568 mNetFlags.clear(Ghostable); 569} 570 571void NavPath::inspectPostApply() 572{ 573 plan(); 574} 575 576void NavPath::onDeleteNotify(SimObject *obj) 577{ 578 if(obj == (SimObject*)mMesh) 579 { 580 mMesh = NULL; 581 plan(); 582 } 583} 584 585void NavPath::prepRenderImage(SceneRenderState *state) 586{ 587 ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>(); 588 ri->renderDelegate.bind(this, &NavPath::renderSimple); 589 ri->type = RenderPassManager::RIT_Editor; 590 ri->translucentSort = true; 591 ri->defaultKey = 1; 592 state->getRenderPass()->addInst(ri); 593} 594 595void NavPath::renderSimple(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat) 596{ 597 if(overrideMat) 598 return; 599 600 if(state->isReflectPass() || !(isSelected() || mAlwaysRender)) 601 return; 602 603 GFXDrawUtil *drawer = GFX->getDrawUtil(); 604 GFXStateBlockDesc desc; 605 desc.setZReadWrite(true, false); 606 desc.setBlend(true); 607 desc.setCullMode(GFXCullNone); 608 609 if(isSelected()) 610 { 611 drawer->drawCube(desc, getWorldBox(), ColorI(136, 255, 228, 5)); 612 desc.setFillModeWireframe(); 613 drawer->drawCube(desc, getWorldBox(), ColorI::BLACK); 614 } 615 616 desc.setZReadWrite(!mXray, false); 617 618 ColorI pathColour(255, 0, 255); 619 620 if(!mIsLooping) 621 { 622 desc.setFillModeSolid(); 623 if(mFromSet) drawer->drawCube(desc, Point3F(0.2f, 0.2f, 0.2f), mFrom, pathColour); 624 if(mToSet) drawer->drawCube(desc, Point3F(0.2f, 0.2f, 0.2f), mTo, pathColour); 625 } 626 627 GFXStateBlockRef sb = GFX->createStateBlock(desc); 628 GFX->setStateBlock(sb); 629 630 PrimBuild::color3i(pathColour.red, pathColour.green, pathColour.blue); 631 632 PrimBuild::begin(GFXLineStrip, mPoints.size()); 633 for (U32 i = 0; i < mPoints.size(); i++) 634 PrimBuild::vertex3fv(mPoints[i]); 635 PrimBuild::end(); 636 637 if(mRenderSearch && getServerObject()) 638 { 639 NavPath *np = static_cast<NavPath*>(getServerObject()); 640 if(np->mQuery && !dtStatusSucceed(np->mStatus)) 641 { 642 duDebugDrawTorque dd; 643 dd.overrideColor(duRGBA(250, 20, 20, 255)); 644 duDebugDrawNavMeshNodes(&dd, *np->mQuery); 645 dd.render(); 646 } 647 } 648} 649 650U32 NavPath::packUpdate(NetConnection *conn, U32 mask, BitStream *stream) 651{ 652 U32 retMask = Parent::packUpdate(conn, mask, stream); 653 654 stream->writeFlag(mIsLooping); 655 stream->writeFlag(mAlwaysRender); 656 stream->writeFlag(mXray); 657 stream->writeFlag(mRenderSearch); 658 659 if(stream->writeFlag(mFromSet)) 660 mathWrite(*stream, mFrom); 661 if(stream->writeFlag(mToSet)) 662 mathWrite(*stream, mTo); 663 664 if(stream->writeFlag(mask & PathMask)) 665 { 666 stream->writeInt(mPoints.size(), 32); 667 for(U32 i = 0; i < mPoints.size(); i++) 668 { 669 mathWrite(*stream, mPoints[i]); 670 stream->writeInt(mFlags[i], 16); 671 } 672 } 673 674 return retMask; 675} 676 677void NavPath::unpackUpdate(NetConnection *conn, BitStream *stream) 678{ 679 Parent::unpackUpdate(conn, stream); 680 681 mIsLooping = stream->readFlag(); 682 mAlwaysRender = stream->readFlag(); 683 mXray = stream->readFlag(); 684 mRenderSearch = stream->readFlag(); 685 686 if((mFromSet = stream->readFlag()) == true) 687 mathRead(*stream, &mFrom); 688 if((mToSet = stream->readFlag()) == true) 689 mathRead(*stream, &mTo); 690 691 if(stream->readFlag()) 692 { 693 mPoints.clear(); 694 mFlags.clear(); 695 mPoints.setSize(stream->readInt(32)); 696 mFlags.setSize(mPoints.size()); 697 for(U32 i = 0; i < mPoints.size(); i++) 698 { 699 Point3F p; 700 mathRead(*stream, &p); 701 mPoints[i] = p; 702 mFlags[i] = stream->readInt(16); 703 } 704 resize(); 705 } 706} 707 708DefineEngineMethod(NavPath, plan, bool, (),, 709 "@brief Find a path using the already-specified path properties.") 710{ 711 return object->plan(); 712} 713 714DefineEngineMethod(NavPath, onNavMeshUpdate, void, (const char *data),, 715 "@brief Callback when this path's NavMesh is loaded or rebuilt.") 716{ 717 if(object->mMesh && !String::compare(data, object->mMesh->getIdString())) 718 object->plan(); 719} 720 721DefineEngineMethod(NavPath, onNavMeshUpdateBox, void, (const char *data),, 722 "@brief Callback when a particular area in this path's NavMesh is rebuilt.") 723{ 724 String s(data); 725 U32 space = s.find(' '); 726 if(space != String::NPos) 727 { 728 String id = s.substr(0, space); 729 if(!object->mMesh || id.compare(object->mMesh->getIdString())) 730 return; 731 String boxstr = s.substr(space + 1); 732 Box3F box; 733 castConsoleTypeFromString(box, boxstr.c_str()); 734 if(object->getWorldBox().isOverlapped(box)) 735 object->plan(); 736 } 737} 738 739DefineEngineMethod(NavPath, size, S32, (),, 740 "@brief Return the number of nodes in this path.") 741{ 742 return object->size(); 743} 744 745DefineEngineMethod(NavPath, getNode, Point3F, (S32 idx),, 746 "@brief Get a specified node along the path.") 747{ 748 return object->getNode(idx); 749} 750 751DefineEngineMethod(NavPath, getFlags, S32, (S32 idx),, 752 "@brief Get a specified node along the path.") 753{ 754 return (S32)object->getFlags(idx); 755} 756 757DefineEngineMethod(NavPath, getLength, F32, (),, 758 "@brief Get the length of this path.") 759{ 760 return object->getLength(); 761} 762