/** * @file llpathfindingnavmeshzone.cpp * @author William Todd Stinson * @brief A class for representing the zone of navmeshes containing and possible surrounding the current region. * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "llsd.h" #include "lluuid.h" #include "llagent.h" #include "llviewerregion.h" #include "llpathfindingnavmesh.h" #include "llpathfindingnavmeshzone.h" #include "llpathfindingmanager.h" #include "llviewercontrol.h" #include "llpathinglib.h" #include #include #include #define CENTER_REGION 99 //--------------------------------------------------------------------------- // LLPathfindingNavMeshZone //--------------------------------------------------------------------------- LLPathfindingNavMeshZone::LLPathfindingNavMeshZone() : mNavMeshLocationPtrs(), mNavMeshZoneRequestStatus(kNavMeshZoneRequestUnknown), mNavMeshZoneSignal() { } LLPathfindingNavMeshZone::~LLPathfindingNavMeshZone() { } LLPathfindingNavMeshZone::navmesh_zone_slot_t LLPathfindingNavMeshZone::registerNavMeshZoneListener(navmesh_zone_callback_t pNavMeshZoneCallback) { return mNavMeshZoneSignal.connect(pNavMeshZoneCallback); } void LLPathfindingNavMeshZone::initialize() { mNavMeshLocationPtrs.clear(); #ifdef XXX_STINSON_DEBUG_NAVMESH_ZONE LLViewerRegion *currentRegion = gAgent.getRegion(); if (currentRegion != NULL) { llinfos << "STINSON DEBUG: currentRegion: '" << currentRegion->getName() << "' (" << currentRegion->getRegionID().asString() << ")" << llendl; std::vector availableRegions; currentRegion->getNeighboringRegionsStatus( availableRegions ); std::vector neighborRegionsPtrs; currentRegion->getNeighboringRegions( neighborRegionsPtrs ); for (std::vector::const_iterator statusIter = availableRegions.begin(); statusIter != availableRegions.end(); ++statusIter) { LLViewerRegion *region = neighborRegionsPtrs[statusIter - availableRegions.begin()]; llinfos << "STINSON DEBUG: region #" << *statusIter << ": '" << region->getName() << "' (" << region->getRegionID().asString() << ")" << llendl; } } #endif // XXX_STINSON_DEBUG_NAVMESH_ZONE NavMeshLocationPtr centerNavMeshPtr(new NavMeshLocation(CENTER_REGION, boost::bind(&LLPathfindingNavMeshZone::handleNavMeshLocation, this))); mNavMeshLocationPtrs.push_back(centerNavMeshPtr); U32 neighborRegionDir = gSavedSettings.getU32("RetrieveNeighboringRegion"); if (neighborRegionDir != CENTER_REGION) { NavMeshLocationPtr neighborNavMeshPtr(new NavMeshLocation(neighborRegionDir, boost::bind(&LLPathfindingNavMeshZone::handleNavMeshLocation, this))); mNavMeshLocationPtrs.push_back(neighborNavMeshPtr); } } void LLPathfindingNavMeshZone::enable() { for (NavMeshLocationPtrs::iterator navMeshLocationPtrIter = mNavMeshLocationPtrs.begin(); navMeshLocationPtrIter != mNavMeshLocationPtrs.end(); ++navMeshLocationPtrIter) { NavMeshLocationPtr navMeshLocationPtr = *navMeshLocationPtrIter; navMeshLocationPtr->enable(); } } void LLPathfindingNavMeshZone::disable() { for (NavMeshLocationPtrs::iterator navMeshLocationPtrIter = mNavMeshLocationPtrs.begin(); navMeshLocationPtrIter != mNavMeshLocationPtrs.end(); ++navMeshLocationPtrIter) { NavMeshLocationPtr navMeshLocationPtr = *navMeshLocationPtrIter; navMeshLocationPtr->disable(); } } void LLPathfindingNavMeshZone::refresh() { llassert(LLPathingLib::getInstance() != NULL); if (LLPathingLib::getInstance() != NULL) { LLPathingLib::getInstance()->cleanupResidual(); } for (NavMeshLocationPtrs::iterator navMeshLocationPtrIter = mNavMeshLocationPtrs.begin(); navMeshLocationPtrIter != mNavMeshLocationPtrs.end(); ++navMeshLocationPtrIter) { NavMeshLocationPtr navMeshLocationPtr = *navMeshLocationPtrIter; navMeshLocationPtr->refresh(); } } LLPathfindingNavMeshZone::ENavMeshZoneStatus LLPathfindingNavMeshZone::getNavMeshZoneStatus() const { bool hasPending = false; bool hasBuilding = false; bool hasComplete = false; bool hasRepending = false; for (NavMeshLocationPtrs::const_iterator navMeshLocationPtrIter = mNavMeshLocationPtrs.begin(); navMeshLocationPtrIter != mNavMeshLocationPtrs.end(); ++navMeshLocationPtrIter) { const NavMeshLocationPtr navMeshLocationPtr = *navMeshLocationPtrIter; switch (navMeshLocationPtr->getNavMeshStatus()) { case LLPathfindingNavMeshStatus::kPending : hasPending = true; break; case LLPathfindingNavMeshStatus::kBuilding : hasBuilding = true; break; case LLPathfindingNavMeshStatus::kComplete : hasComplete = true; break; case LLPathfindingNavMeshStatus::kRepending : hasRepending = true; break; default : hasPending = true; llassert(0); break; } } ENavMeshZoneStatus zoneStatus = kNavMeshZoneComplete; if (hasRepending || (hasPending && hasBuilding)) { zoneStatus = kNavMeshZonePendingAndBuilding; } else if (hasComplete) { if (hasPending) { zoneStatus = kNavMeshZoneSomePending; } else if (hasBuilding) { zoneStatus = kNavMeshZoneSomeBuilding; } else { zoneStatus = kNavMeshZoneComplete; } } else if (hasPending) { zoneStatus = kNavMeshZonePending; } else if (hasBuilding) { zoneStatus = kNavMeshZoneBuilding; } return zoneStatus; } void LLPathfindingNavMeshZone::handleNavMeshLocation() { updateStatus(); } void LLPathfindingNavMeshZone::updateStatus() { bool hasRequestUnknown = false; bool hasRequestWaiting = false; bool hasRequestChecking = false; bool hasRequestNeedsUpdate = false; bool hasRequestStarted = false; bool hasRequestCompleted = false; bool hasRequestNotEnabled = false; bool hasRequestError = false; #ifdef XXX_STINSON_DEBUG_NAVMESH_ZONE llinfos << "STINSON DEBUG: Navmesh zone update BEGIN" << llendl; #endif // XXX_STINSON_DEBUG_NAVMESH_ZONE for (NavMeshLocationPtrs::const_iterator navMeshLocationPtrIter = mNavMeshLocationPtrs.begin(); navMeshLocationPtrIter != mNavMeshLocationPtrs.end(); ++navMeshLocationPtrIter) { const NavMeshLocationPtr navMeshLocationPtr = *navMeshLocationPtrIter; #ifdef XXX_STINSON_DEBUG_NAVMESH_ZONE llinfos << "STINSON DEBUG: region #" << navMeshLocationPtr->getDirection() << ": region(" << navMeshLocationPtr->getRegionUUID().asString() << ") status:" << navMeshLocationPtr->getRequestStatus() << llendl; #endif // XXX_STINSON_DEBUG_NAVMESH_ZONE switch (navMeshLocationPtr->getRequestStatus()) { case LLPathfindingNavMesh::kNavMeshRequestUnknown : hasRequestUnknown = true; break; case LLPathfindingNavMesh::kNavMeshRequestWaiting : hasRequestWaiting = true; break; case LLPathfindingNavMesh::kNavMeshRequestChecking : hasRequestChecking = true; break; case LLPathfindingNavMesh::kNavMeshRequestNeedsUpdate : hasRequestNeedsUpdate = true; break; case LLPathfindingNavMesh::kNavMeshRequestStarted : hasRequestStarted = true; break; case LLPathfindingNavMesh::kNavMeshRequestCompleted : hasRequestCompleted = true; break; case LLPathfindingNavMesh::kNavMeshRequestNotEnabled : hasRequestNotEnabled = true; break; case LLPathfindingNavMesh::kNavMeshRequestError : hasRequestError = true; break; default : hasRequestError = true; llassert(0); break; } } ENavMeshZoneRequestStatus zoneRequestStatus = kNavMeshZoneRequestUnknown; if (hasRequestWaiting) { zoneRequestStatus = kNavMeshZoneRequestWaiting; #ifdef XXX_STINSON_DEBUG_NAVMESH_ZONE llinfos << "STINSON DEBUG: Navmesh zone update is WAITING" << llendl; #endif // XXX_STINSON_DEBUG_NAVMESH_ZONE } else if (hasRequestNeedsUpdate) { zoneRequestStatus = kNavMeshZoneRequestNeedsUpdate; #ifdef XXX_STINSON_DEBUG_NAVMESH_ZONE llinfos << "STINSON DEBUG: Navmesh zone update is NEEDS UPDATE" << llendl; #endif // XXX_STINSON_DEBUG_NAVMESH_ZONE } else if (hasRequestChecking) { zoneRequestStatus = kNavMeshZoneRequestChecking; #ifdef XXX_STINSON_DEBUG_NAVMESH_ZONE llinfos << "STINSON DEBUG: Navmesh zone update is CHECKING" << llendl; #endif // XXX_STINSON_DEBUG_NAVMESH_ZONE } else if (hasRequestStarted) { zoneRequestStatus = kNavMeshZoneRequestStarted; #ifdef XXX_STINSON_DEBUG_NAVMESH_ZONE llinfos << "STINSON DEBUG: Navmesh zone update is STARTED" << llendl; #endif // XXX_STINSON_DEBUG_NAVMESH_ZONE } else if (hasRequestError) { zoneRequestStatus = kNavMeshZoneRequestError; #ifdef XXX_STINSON_DEBUG_NAVMESH_ZONE llinfos << "STINSON DEBUG: Navmesh zone update is ERROR" << llendl; #endif // XXX_STINSON_DEBUG_NAVMESH_ZONE } else if (hasRequestUnknown) { zoneRequestStatus = kNavMeshZoneRequestUnknown; #ifdef XXX_STINSON_DEBUG_NAVMESH_ZONE llinfos << "STINSON DEBUG: Navmesh zone update is UNKNOWN" << llendl; #endif // XXX_STINSON_DEBUG_NAVMESH_ZONE } else if (hasRequestCompleted) { zoneRequestStatus = kNavMeshZoneRequestCompleted; #ifdef XXX_STINSON_DEBUG_NAVMESH_ZONE llinfos << "STINSON DEBUG: Navmesh zone update is COMPLETED" << llendl; #endif // XXX_STINSON_DEBUG_NAVMESH_ZONE } else if (hasRequestNotEnabled) { zoneRequestStatus = kNavMeshZoneRequestNotEnabled; #ifdef XXX_STINSON_DEBUG_NAVMESH_ZONE llinfos << "STINSON DEBUG: Navmesh zone update is NOT ENABLED" << llendl; #endif // XXX_STINSON_DEBUG_NAVMESH_ZONE } else { zoneRequestStatus = kNavMeshZoneRequestError; #ifdef XXX_STINSON_DEBUG_NAVMESH_ZONE llinfos << "STINSON DEBUG: Navmesh zone update is BAD ERROR" << llendl; #endif // XXX_STINSON_DEBUG_NAVMESH_ZONE llassert(0); } if ((mNavMeshZoneRequestStatus != kNavMeshZoneRequestCompleted) && (zoneRequestStatus == kNavMeshZoneRequestCompleted)) { #ifdef XXX_STINSON_DEBUG_NAVMESH_ZONE llinfos << "STINSON DEBUG: Navmesh zone update is stitching" << llendl; #endif // XXX_STINSON_DEBUG_NAVMESH_ZONE llassert(LLPathingLib::getInstance() != NULL); if (LLPathingLib::getInstance() != NULL) { LLPathingLib::getInstance()->stitchNavMeshes(); } #ifdef XXX_STINSON_DEBUG_NAVMESH_ZONE llinfos << "STINSON DEBUG: Navmesh zone update stitching is done" << llendl; #endif // XXX_STINSON_DEBUG_NAVMESH_ZONE } mNavMeshZoneRequestStatus = zoneRequestStatus; mNavMeshZoneSignal(mNavMeshZoneRequestStatus); } //--------------------------------------------------------------------------- // LLPathfindingNavMeshZone::NavMeshLocation //--------------------------------------------------------------------------- LLPathfindingNavMeshZone::NavMeshLocation::NavMeshLocation(S32 pDirection, navmesh_location_callback_t pLocationCallback) : mDirection(pDirection), mRegionUUID(), mHasNavMesh(false), mNavMeshVersion(0U), mNavMeshStatus(LLPathfindingNavMeshStatus::kComplete), mLocationCallback(pLocationCallback), mRequestStatus(LLPathfindingNavMesh::kNavMeshRequestUnknown), mNavMeshSlot() { } LLPathfindingNavMeshZone::NavMeshLocation::~NavMeshLocation() { } void LLPathfindingNavMeshZone::NavMeshLocation::enable() { clear(); LLViewerRegion *region = getRegion(); if (region == NULL) { mRegionUUID.setNull(); } else { mRegionUUID = region->getRegionID(); mNavMeshSlot = LLPathfindingManager::getInstance()->registerNavMeshListenerForRegion(region, boost::bind(&LLPathfindingNavMeshZone::NavMeshLocation::handleNavMesh, this, _1, _2, _3)); } } void LLPathfindingNavMeshZone::NavMeshLocation::refresh() { LLViewerRegion *region = getRegion(); if (region == NULL) { llassert(mRegionUUID.isNull()); LLPathfindingNavMeshStatus newNavMeshStatus(mRegionUUID); LLSD::Binary nullData; handleNavMesh(LLPathfindingNavMesh::kNavMeshRequestNotEnabled, newNavMeshStatus, nullData); } else { llassert(mRegionUUID == region->getRegionID()); LLPathfindingManager::getInstance()->requestGetNavMeshForRegion(region); } } void LLPathfindingNavMeshZone::NavMeshLocation::disable() { clear(); } LLPathfindingNavMesh::ENavMeshRequestStatus LLPathfindingNavMeshZone::NavMeshLocation::getRequestStatus() const { return mRequestStatus; } LLPathfindingNavMeshStatus::ENavMeshStatus LLPathfindingNavMeshZone::NavMeshLocation::getNavMeshStatus() const { return mNavMeshStatus; } void LLPathfindingNavMeshZone::NavMeshLocation::handleNavMesh(LLPathfindingNavMesh::ENavMeshRequestStatus pNavMeshRequestStatus, const LLPathfindingNavMeshStatus &pNavMeshStatus, const LLSD::Binary &pNavMeshData) { llassert(mRegionUUID == pNavMeshStatus.getRegionUUID()); if ((pNavMeshRequestStatus == LLPathfindingNavMesh::kNavMeshRequestCompleted) && (!mHasNavMesh || (mNavMeshVersion != pNavMeshStatus.getVersion()))) { llassert(!pNavMeshData.empty()); mHasNavMesh = true; mNavMeshVersion = pNavMeshStatus.getVersion(); llassert(LLPathingLib::getInstance() != NULL); if (LLPathingLib::getInstance() != NULL) { LLPathingLib::getInstance()->extractNavMeshSrcFromLLSD(pNavMeshData, mDirection); } } mRequestStatus = pNavMeshRequestStatus; mNavMeshStatus = pNavMeshStatus.getStatus(); mLocationCallback(); } void LLPathfindingNavMeshZone::NavMeshLocation::clear() { mHasNavMesh = false; mRequestStatus = LLPathfindingNavMesh::kNavMeshRequestUnknown; mNavMeshStatus = LLPathfindingNavMeshStatus::kComplete; if (mNavMeshSlot.connected()) { mNavMeshSlot.disconnect(); } } LLViewerRegion *LLPathfindingNavMeshZone::NavMeshLocation::getRegion() const { LLViewerRegion *region = NULL; LLViewerRegion *currentRegion = gAgent.getRegion(); if (currentRegion != NULL) { if (mDirection == CENTER_REGION) { region = currentRegion; } else { //User wants to pull in a neighboring region std::vector availableRegions; currentRegion->getNeighboringRegionsStatus( availableRegions ); //Is the desired region in the available list std::vector::iterator foundElem = std::find(availableRegions.begin(),availableRegions.end(),mDirection); if ( foundElem != availableRegions.end() ) { std::vector neighborRegionsPtrs; currentRegion->getNeighboringRegions( neighborRegionsPtrs ); region = neighborRegionsPtrs[foundElem - availableRegions.begin()]; } } } return region; }